Merge tag '0.9.17' into mare-classic
* tag '0.9.17': add census popup on connection api update census update heave fewer redraws as main method for data application, minor fixes remove unnecessary exists check add visibility for loaded mods size for pair, use menu bar for settings, remove settings button fix staging issues add download throttling, change header of mare, fix reverting players when going offline/paused when not visible use name for glamourer revert fix startup breaking add inner exception stacktraces calc correct button size wording add permission popup ui fix getting identifier during zoning indent nonscaled remove unnecessary usings ui icon boogaloo fix cache dict wtf add normalized icons add owner/moderator/pinned user icons check tokentime more precisely in both directions more cleanup fix sorting and cleanup make local groups more usable for pause/resume fix outlined font rework creation of popout windows into factory and some refactoring in general make syncshell admin ui to standalone window remove close button on intro ui do not allow to open main ui without finishing setup readonly bla wait for plugin disposal fix palette wording fix palette application and add experimental less redraws option some minor fixes check for timezone idk adjust token handling fix total user count in syncshell (distinct by UIDs) fix text alignment fix some shit maybe idk some fixes I guess fix offset for transfer bar at the bottom, use async collections, clear filter on tab change + add button to clear, require ctrl for align syncshells blah Some display options for DTR tooltip (#66) add ordering adjust api to latest rework main ui add total count on mouseover, make syncshell windows non-blocking fix token for character change, add online count to syncshells and groups argh fix broken font in header add more options for the compactui fix icons and text of buttons being static in place remove logspam
This commit is contained in:
@@ -336,7 +336,7 @@ public sealed class FileCacheManager : IDisposable
|
|||||||
|
|
||||||
private void AddHashedFile(FileCacheEntity fileCache)
|
private void AddHashedFile(FileCacheEntity fileCache)
|
||||||
{
|
{
|
||||||
if (!_fileCaches.TryGetValue(fileCache.Hash, out var entries))
|
if (!_fileCaches.TryGetValue(fileCache.Hash, out var entries) || entries is null)
|
||||||
{
|
{
|
||||||
_fileCaches[fileCache.Hash] = entries = [];
|
_fileCaches[fileCache.Hash] = entries = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,34 +24,16 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
|||||||
_configurationService = configurationService;
|
_configurationService = configurationService;
|
||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
|
|
||||||
PlayerPersistentDataKey = _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult() + "_" + _dalamudUtil.GetWorldIdAsync().GetAwaiter().GetResult();
|
|
||||||
|
|
||||||
SemiTransientResources.TryAdd(ObjectKind.Player, new HashSet<string>(StringComparer.Ordinal));
|
|
||||||
if (_configurationService.Current.PlayerPersistentTransientCache.TryGetValue(PlayerPersistentDataKey, out var gamePaths))
|
|
||||||
{
|
|
||||||
int restored = 0;
|
|
||||||
foreach (var gamePath in gamePaths)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(gamePath)) continue;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Logger.LogDebug("Loaded persistent transient resource {path}", gamePath);
|
|
||||||
SemiTransientResources[ObjectKind.Player].Add(gamePath);
|
|
||||||
restored++;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogWarning(ex, "Error during loading persistent transient resource {path}", gamePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Logger.LogDebug("Restored {restored}/{total} semi persistent resources", restored, gamePaths.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
Mediator.Subscribe<PenumbraResourceLoadMessage>(this, Manager_PenumbraResourceLoadEvent);
|
Mediator.Subscribe<PenumbraResourceLoadMessage>(this, Manager_PenumbraResourceLoadEvent);
|
||||||
Mediator.Subscribe<PenumbraModSettingChangedMessage>(this, (_) => Manager_PenumbraModSettingChanged());
|
Mediator.Subscribe<PenumbraModSettingChangedMessage>(this, (_) => Manager_PenumbraModSettingChanged());
|
||||||
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => DalamudUtil_FrameworkUpdate());
|
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => DalamudUtil_FrameworkUpdate());
|
||||||
Mediator.Subscribe<ClassJobChangedMessage>(this, (_) => DalamudUtil_ClassJobChanged());
|
Mediator.Subscribe<ClassJobChangedMessage>(this, (msg) =>
|
||||||
|
{
|
||||||
|
if (_playerRelatedPointers.Contains(msg.gameObjectHandler))
|
||||||
|
{
|
||||||
|
DalamudUtil_ClassJobChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
Mediator.Subscribe<AddWatchedGameObjectHandler>(this, (msg) =>
|
Mediator.Subscribe<AddWatchedGameObjectHandler>(this, (msg) =>
|
||||||
{
|
{
|
||||||
_playerRelatedPointers.Add(msg.Handler);
|
_playerRelatedPointers.Add(msg.Handler);
|
||||||
@@ -62,8 +44,41 @@ public sealed class TransientResourceManager : DisposableMediatorSubscriberBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private string PlayerPersistentDataKey { get; }
|
private string PlayerPersistentDataKey => _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult() + "_" + _dalamudUtil.GetHomeWorldIdAsync().GetAwaiter().GetResult();
|
||||||
private ConcurrentDictionary<ObjectKind, HashSet<string>> SemiTransientResources { get; } = new();
|
private ConcurrentDictionary<ObjectKind, HashSet<string>>? _semiTransientResources = null;
|
||||||
|
private ConcurrentDictionary<ObjectKind, HashSet<string>> SemiTransientResources
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_semiTransientResources == null)
|
||||||
|
{
|
||||||
|
_semiTransientResources = new();
|
||||||
|
_semiTransientResources.TryAdd(ObjectKind.Player, new HashSet<string>(StringComparer.Ordinal));
|
||||||
|
if (_configurationService.Current.PlayerPersistentTransientCache.TryGetValue(PlayerPersistentDataKey, out var gamePaths))
|
||||||
|
{
|
||||||
|
int restored = 0;
|
||||||
|
foreach (var gamePath in gamePaths)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(gamePath)) continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.LogDebug("Loaded persistent transient resource {path}", gamePath);
|
||||||
|
SemiTransientResources[ObjectKind.Player].Add(gamePath);
|
||||||
|
restored++;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogWarning(ex, "Error during loading persistent transient resource {path}", gamePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger.LogDebug("Restored {restored}/{total} semi persistent resources", restored, gamePaths.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _semiTransientResources;
|
||||||
|
}
|
||||||
|
}
|
||||||
private ConcurrentDictionary<IntPtr, HashSet<string>> TransientResources { get; } = new();
|
private ConcurrentDictionary<IntPtr, HashSet<string>> TransientResources { get; } = new();
|
||||||
|
|
||||||
public void CleanUpSemiTransientResources(ObjectKind objectKind, List<FileReplacement>? fileReplacement = null)
|
public void CleanUpSemiTransientResources(ObjectKind objectKind, List<FileReplacement>? fileReplacement = null)
|
||||||
|
|||||||
@@ -36,6 +36,13 @@ internal sealed class DalamudLogger : ILogger
|
|||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
sb.AppendLine($"[{_name}]{{{(int)logLevel}}} {state}: {exception?.Message}");
|
sb.AppendLine($"[{_name}]{{{(int)logLevel}}} {state}: {exception?.Message}");
|
||||||
sb.AppendLine(exception?.StackTrace);
|
sb.AppendLine(exception?.StackTrace);
|
||||||
|
var innerException = exception?.InnerException;
|
||||||
|
while (innerException != null)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"InnerException {innerException}: {innerException.Message}");
|
||||||
|
sb.AppendLine(innerException.StackTrace);
|
||||||
|
innerException = innerException.InnerException;
|
||||||
|
}
|
||||||
if (logLevel == LogLevel.Warning)
|
if (logLevel == LogLevel.Warning)
|
||||||
_pluginLog.Warning(sb.ToString());
|
_pluginLog.Warning(sb.ToString());
|
||||||
else if (logLevel == LogLevel.Error)
|
else if (logLevel == LogLevel.Error)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
private readonly ICallGateSubscriber<string?, string?, object> _customizePlusOnScaleUpdate;
|
private readonly ICallGateSubscriber<string?, string?, object> _customizePlusOnScaleUpdate;
|
||||||
private readonly ICallGateSubscriber<Character?, object> _customizePlusRevertCharacter;
|
private readonly ICallGateSubscriber<Character?, object> _customizePlusRevertCharacter;
|
||||||
private readonly ICallGateSubscriber<string, Character?, object> _customizePlusSetBodyScaleToCharacter;
|
private readonly ICallGateSubscriber<string, Character?, object> _customizePlusSetBodyScaleToCharacter;
|
||||||
|
private readonly DalamudPluginInterface _pi;
|
||||||
private readonly DalamudUtilService _dalamudUtil;
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
private readonly ICallGateSubscriber<(int, int)> _glamourerApiVersions;
|
private readonly ICallGateSubscriber<(int, int)> _glamourerApiVersions;
|
||||||
private readonly ICallGateSubscriber<string, GameObject?, uint, object>? _glamourerApplyAll;
|
private readonly ICallGateSubscriber<string, GameObject?, uint, object>? _glamourerApplyAll;
|
||||||
@@ -47,7 +48,6 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
private readonly ICallGateSubscriber<Character, object> _palettePlusRemoveCharaPalette;
|
private readonly ICallGateSubscriber<Character, object> _palettePlusRemoveCharaPalette;
|
||||||
private readonly ICallGateSubscriber<Character, string, object> _palettePlusSetCharaPalette;
|
private readonly ICallGateSubscriber<Character, string, object> _palettePlusSetCharaPalette;
|
||||||
private readonly FuncSubscriber<string, string, Dictionary<string, string>, string, int, PenumbraApiEc> _penumbraAddTemporaryMod;
|
private readonly FuncSubscriber<string, string, Dictionary<string, string>, string, int, PenumbraApiEc> _penumbraAddTemporaryMod;
|
||||||
private readonly FuncSubscriber<(int, int)> _penumbraApiVersion;
|
|
||||||
private readonly FuncSubscriber<string, int, bool, PenumbraApiEc> _penumbraAssignTemporaryCollection;
|
private readonly FuncSubscriber<string, int, bool, PenumbraApiEc> _penumbraAssignTemporaryCollection;
|
||||||
private readonly FuncSubscriber<string, string, TextureType, bool, Task> _penumbraConvertTextureFile;
|
private readonly FuncSubscriber<string, string, TextureType, bool, Task> _penumbraConvertTextureFile;
|
||||||
private readonly FuncSubscriber<string, PenumbraApiEc> _penumbraCreateNamedTemporaryCollection;
|
private readonly FuncSubscriber<string, PenumbraApiEc> _penumbraCreateNamedTemporaryCollection;
|
||||||
@@ -64,7 +64,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
private readonly FuncSubscriber<string, PenumbraApiEc> _penumbraRemoveTemporaryCollection;
|
private readonly FuncSubscriber<string, PenumbraApiEc> _penumbraRemoveTemporaryCollection;
|
||||||
private readonly FuncSubscriber<string, string, int, PenumbraApiEc> _penumbraRemoveTemporaryMod;
|
private readonly FuncSubscriber<string, string, int, PenumbraApiEc> _penumbraRemoveTemporaryMod;
|
||||||
private readonly FuncSubscriber<string> _penumbraResolveModDir;
|
private readonly FuncSubscriber<string> _penumbraResolveModDir;
|
||||||
private readonly FuncSubscriber<string[], string[], (string[], string[][])> _penumbraResolvePaths;
|
private readonly FuncSubscriber<string[], string[], Task<(string[], string[][])>> _penumbraResolvePaths;
|
||||||
private readonly ParamsFuncSubscriber<ushort, IReadOnlyDictionary<string, string[]>?[]> _penumbraResourcePaths;
|
private readonly ParamsFuncSubscriber<ushort, IReadOnlyDictionary<string, string[]>?[]> _penumbraResourcePaths;
|
||||||
private readonly SemaphoreSlim _redrawSemaphore = new(2);
|
private readonly SemaphoreSlim _redrawSemaphore = new(2);
|
||||||
private readonly uint LockCode = 0x6D617265;
|
private readonly uint LockCode = 0x6D617265;
|
||||||
@@ -80,6 +80,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public IpcManager(ILogger<IpcManager> logger, DalamudPluginInterface pi, DalamudUtilService dalamudUtil, MareMediator mediator) : base(logger, mediator)
|
public IpcManager(ILogger<IpcManager> logger, DalamudPluginInterface pi, DalamudUtilService dalamudUtil, MareMediator mediator) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
|
_pi = pi;
|
||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
|
|
||||||
_penumbraInit = Penumbra.Api.Ipc.Initialized.Subscriber(pi, PenumbraInit);
|
_penumbraInit = Penumbra.Api.Ipc.Initialized.Subscriber(pi, PenumbraInit);
|
||||||
@@ -87,7 +88,6 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
_penumbraResolveModDir = Penumbra.Api.Ipc.GetModDirectory.Subscriber(pi);
|
_penumbraResolveModDir = Penumbra.Api.Ipc.GetModDirectory.Subscriber(pi);
|
||||||
_penumbraRedraw = Penumbra.Api.Ipc.RedrawObjectByName.Subscriber(pi);
|
_penumbraRedraw = Penumbra.Api.Ipc.RedrawObjectByName.Subscriber(pi);
|
||||||
_penumbraRedrawObject = Penumbra.Api.Ipc.RedrawObject.Subscriber(pi);
|
_penumbraRedrawObject = Penumbra.Api.Ipc.RedrawObject.Subscriber(pi);
|
||||||
_penumbraApiVersion = Penumbra.Api.Ipc.ApiVersions.Subscriber(pi);
|
|
||||||
_penumbraObjectIsRedrawn = Penumbra.Api.Ipc.GameObjectRedrawn.Subscriber(pi, RedrawEvent);
|
_penumbraObjectIsRedrawn = Penumbra.Api.Ipc.GameObjectRedrawn.Subscriber(pi, RedrawEvent);
|
||||||
_penumbraGetMetaManipulations = Penumbra.Api.Ipc.GetPlayerMetaManipulations.Subscriber(pi);
|
_penumbraGetMetaManipulations = Penumbra.Api.Ipc.GetPlayerMetaManipulations.Subscriber(pi);
|
||||||
_penumbraRemoveTemporaryMod = Penumbra.Api.Ipc.RemoveTemporaryMod.Subscriber(pi);
|
_penumbraRemoveTemporaryMod = Penumbra.Api.Ipc.RemoveTemporaryMod.Subscriber(pi);
|
||||||
@@ -95,7 +95,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
_penumbraCreateNamedTemporaryCollection = Penumbra.Api.Ipc.CreateNamedTemporaryCollection.Subscriber(pi);
|
_penumbraCreateNamedTemporaryCollection = Penumbra.Api.Ipc.CreateNamedTemporaryCollection.Subscriber(pi);
|
||||||
_penumbraRemoveTemporaryCollection = Penumbra.Api.Ipc.RemoveTemporaryCollectionByName.Subscriber(pi);
|
_penumbraRemoveTemporaryCollection = Penumbra.Api.Ipc.RemoveTemporaryCollectionByName.Subscriber(pi);
|
||||||
_penumbraAssignTemporaryCollection = Penumbra.Api.Ipc.AssignTemporaryCollection.Subscriber(pi);
|
_penumbraAssignTemporaryCollection = Penumbra.Api.Ipc.AssignTemporaryCollection.Subscriber(pi);
|
||||||
_penumbraResolvePaths = Penumbra.Api.Ipc.ResolvePlayerPaths.Subscriber(pi);
|
_penumbraResolvePaths = Penumbra.Api.Ipc.ResolvePlayerPathsAsync.Subscriber(pi);
|
||||||
_penumbraEnabled = Penumbra.Api.Ipc.GetEnabledState.Subscriber(pi);
|
_penumbraEnabled = Penumbra.Api.Ipc.GetEnabledState.Subscriber(pi);
|
||||||
_penumbraModSettingChanged = Penumbra.Api.Ipc.ModSettingChanged.Subscriber(pi, (change, arg1, arg, b) =>
|
_penumbraModSettingChanged = Penumbra.Api.Ipc.ModSettingChanged.Subscriber(pi, (change, arg1, arg, b) =>
|
||||||
{
|
{
|
||||||
@@ -157,7 +157,14 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => PeriodicApiStateCheck());
|
Mediator.Subscribe<DelayedFrameworkUpdateMessage>(this, (_) => PeriodicApiStateCheck());
|
||||||
|
|
||||||
PeriodicApiStateCheck();
|
try
|
||||||
|
{
|
||||||
|
PeriodicApiStateCheck();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogWarning(ex, "Failed to check for some IPC, plugin not installed?");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Initialized => CheckPenumbraApiInternal() && CheckGlamourerApiInternal();
|
public bool Initialized => CheckPenumbraApiInternal() && CheckGlamourerApiInternal();
|
||||||
@@ -286,7 +293,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GlamourerRevert(ILogger logger, GameObjectHandler handler, Guid applicationId, CancellationToken token)
|
public async Task GlamourerRevert(ILogger logger, string name, GameObjectHandler handler, Guid applicationId, CancellationToken token)
|
||||||
{
|
{
|
||||||
if ((!CheckGlamourerApi()) || _dalamudUtil.IsZoning) return;
|
if ((!CheckGlamourerApi()) || _dalamudUtil.IsZoning) return;
|
||||||
try
|
try
|
||||||
@@ -297,7 +304,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId);
|
logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId);
|
||||||
_glamourerUnlock.InvokeFunc(handler.Name, LockCode);
|
_glamourerUnlock.InvokeFunc(name, LockCode);
|
||||||
logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevert", applicationId);
|
logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevert", applicationId);
|
||||||
_glamourerRevert.InvokeAction(chara, LockCode);
|
_glamourerRevert.InvokeAction(chara, LockCode);
|
||||||
logger.LogDebug("[{appid}] Calling On IPC: PenumbraRedraw", applicationId);
|
logger.LogDebug("[{appid}] Calling On IPC: PenumbraRedraw", applicationId);
|
||||||
@@ -315,6 +322,26 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task GlamourerRevertByNameAsync(ILogger logger, string name, Guid applicationId)
|
||||||
|
{
|
||||||
|
if ((!CheckGlamourerApi()) || _dalamudUtil.IsZoning) return;
|
||||||
|
|
||||||
|
await _dalamudUtil.RunOnFrameworkThread(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
logger.LogDebug("[{appid}] Calling On IPC: GlamourerRevertByName", applicationId);
|
||||||
|
_glamourerRevertByName.InvokeAction(name, LockCode);
|
||||||
|
logger.LogDebug("[{appid}] Calling On IPC: GlamourerUnlockName", applicationId);
|
||||||
|
_glamourerUnlock.InvokeFunc(name, LockCode);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogWarning(ex, "Error during Glamourer RevertByName");
|
||||||
|
}
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
public void GlamourerRevertByName(ILogger logger, string name, Guid applicationId)
|
public void GlamourerRevertByName(ILogger logger, string name, Guid applicationId)
|
||||||
{
|
{
|
||||||
if ((!CheckGlamourerApi()) || _dalamudUtil.IsZoning) return;
|
if ((!CheckGlamourerApi()) || _dalamudUtil.IsZoning) return;
|
||||||
@@ -569,7 +596,7 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public async Task<(string[] forward, string[][] reverse)> PenumbraResolvePathsAsync(string[] forward, string[] reverse)
|
public async Task<(string[] forward, string[][] reverse)> PenumbraResolvePathsAsync(string[] forward, string[] reverse)
|
||||||
{
|
{
|
||||||
return await _dalamudUtil.RunOnFrameworkThread(() => _penumbraResolvePaths.Invoke(forward, reverse)).ConfigureAwait(false);
|
return await _penumbraResolvePaths.Invoke(forward, reverse).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PenumbraSetManipulationDataAsync(ILogger logger, Guid applicationId, string collName, string manipulationData)
|
public async Task PenumbraSetManipulationDataAsync(ILogger logger, Guid applicationId, string collName, string manipulationData)
|
||||||
@@ -700,20 +727,23 @@ public sealed class IpcManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
private bool CheckPenumbraApiInternal()
|
private bool CheckPenumbraApiInternal()
|
||||||
{
|
{
|
||||||
bool apiAvailable = false;
|
bool penumbraAvailable = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
apiAvailable = _penumbraApiVersion.Invoke() is { Item1: 4, Item2: >= 21 } && _penumbraEnabled.Invoke();
|
penumbraAvailable = (_pi.InstalledPlugins
|
||||||
_shownPenumbraUnavailable = _shownPenumbraUnavailable && !apiAvailable;
|
.FirstOrDefault(p => string.Equals(p.InternalName, "Penumbra", StringComparison.OrdinalIgnoreCase))
|
||||||
return apiAvailable;
|
?.Version ?? new Version(0, 0, 0, 0)) >= new Version(0, 8, 1, 6);
|
||||||
|
penumbraAvailable &= _penumbraEnabled.Invoke();
|
||||||
|
_shownPenumbraUnavailable = _shownPenumbraUnavailable && !penumbraAvailable;
|
||||||
|
return penumbraAvailable;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return apiAvailable;
|
return penumbraAvailable;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (!apiAvailable && !_shownPenumbraUnavailable)
|
if (!penumbraAvailable && !_shownPenumbraUnavailable)
|
||||||
{
|
{
|
||||||
_shownPenumbraUnavailable = true;
|
_shownPenumbraUnavailable = true;
|
||||||
Mediator.Publish(new NotificationMessage("Penumbra inactive", "Your Penumbra installation is not active or out of date. Update Penumbra and/or the Enable Mods setting in Penumbra to continue to use Mare.", NotificationType.Error));
|
Mediator.Publish(new NotificationMessage("Penumbra inactive", "Your Penumbra installation is not active or out of date. Update Penumbra and/or the Enable Mods setting in Penumbra to continue to use Mare.", NotificationType.Error));
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ public class MareConfig : IMareConfiguration
|
|||||||
public string CacheFolder { get; set; } = string.Empty;
|
public string CacheFolder { get; set; } = string.Empty;
|
||||||
public bool DisableOptionalPluginWarnings { get; set; } = false;
|
public bool DisableOptionalPluginWarnings { get; set; } = false;
|
||||||
public bool EnableDtrEntry { get; set; } = false;
|
public bool EnableDtrEntry { get; set; } = false;
|
||||||
|
public bool ShowUidInDtrTooltip { get; set; } = true;
|
||||||
|
public bool PreferNoteInDtrTooltip { get; set; } = false;
|
||||||
public bool EnableRightClickMenus { get; set; } = true;
|
public bool EnableRightClickMenus { get; set; } = true;
|
||||||
public NotificationLocation ErrorNotification { get; set; } = NotificationLocation.Both;
|
public NotificationLocation ErrorNotification { get; set; } = NotificationLocation.Both;
|
||||||
public string ExportFolder { get; set; } = string.Empty;
|
public string ExportFolder { get; set; } = string.Empty;
|
||||||
@@ -22,13 +24,17 @@ public class MareConfig : IMareConfiguration
|
|||||||
public bool OpenGposeImportOnGposeStart { get; set; } = false;
|
public bool OpenGposeImportOnGposeStart { get; set; } = false;
|
||||||
public bool OpenPopupOnAdd { get; set; } = true;
|
public bool OpenPopupOnAdd { get; set; } = true;
|
||||||
public int ParallelDownloads { get; set; } = 10;
|
public int ParallelDownloads { get; set; } = 10;
|
||||||
|
public int DownloadSpeedLimitInBytes { get; set; } = 0;
|
||||||
|
public DownloadSpeeds DownloadSpeedType { get; set; } = DownloadSpeeds.MBps;
|
||||||
public bool PreferNotesOverNamesForVisible { get; set; } = false;
|
public bool PreferNotesOverNamesForVisible { get; set; } = false;
|
||||||
public float ProfileDelay { get; set; } = 1.5f;
|
public float ProfileDelay { get; set; } = 1.5f;
|
||||||
public bool ProfilePopoutRight { get; set; } = false;
|
public bool ProfilePopoutRight { get; set; } = false;
|
||||||
public bool ProfilesAllowNsfw { get; set; } = false;
|
public bool ProfilesAllowNsfw { get; set; } = false;
|
||||||
public bool ProfilesShow { get; set; } = true;
|
public bool ProfilesShow { get; set; } = true;
|
||||||
|
public bool ShowSyncshellUsersInVisible { get; set; } = true;
|
||||||
public bool ShowCharacterNameInsteadOfNotesForVisible { get; set; } = false;
|
public bool ShowCharacterNameInsteadOfNotesForVisible { get; set; } = false;
|
||||||
public bool ShowOfflineUsersSeparately { get; set; } = true;
|
public bool ShowOfflineUsersSeparately { get; set; } = true;
|
||||||
|
public bool ShowSyncshellOfflineUsersSeparately { get; set; } = true;
|
||||||
public bool GroupUpSyncshells { get; set; } = true;
|
public bool GroupUpSyncshells { get; set; } = true;
|
||||||
public bool ShowOnlineNotifications { get; set; } = false;
|
public bool ShowOnlineNotifications { get; set; } = false;
|
||||||
public bool ShowOnlineNotificationsOnlyForIndividualPairs { get; set; } = true;
|
public bool ShowOnlineNotificationsOnlyForIndividualPairs { get; set; } = true;
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace MareSynchronos.MareConfiguration.Models;
|
||||||
|
|
||||||
|
public enum DownloadSpeeds
|
||||||
|
{
|
||||||
|
Bps,
|
||||||
|
KBps,
|
||||||
|
MBps
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dalamud.ContextMenu" Version="1.3.1" />
|
<PackageReference Include="Dalamud.ContextMenu" Version="1.3.1" />
|
||||||
<PackageReference Include="DalamudPackager" Version="2.1.12" />
|
<PackageReference Include="DalamudPackager" Version="2.1.12" />
|
||||||
|
<PackageReference Include="Downloader" Version="3.0.6" />
|
||||||
<PackageReference Include="lz4net" Version="1.0.15.93" />
|
<PackageReference Include="lz4net" Version="1.0.15.93" />
|
||||||
<PackageReference Include="Meziantou.Analyzer" Version="2.0.92">
|
<PackageReference Include="Meziantou.Analyzer" Version="2.0.92">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@@ -39,7 +40,7 @@
|
|||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.11" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.11" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.11" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="7.0.11" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||||
<PackageReference Include="Penumbra.Api" Version="1.0.11" />
|
<PackageReference Include="Penumbra.Api" Version="1.0.12" />
|
||||||
<PackageReference Include="Penumbra.String" Version="1.0.4" />
|
<PackageReference Include="Penumbra.String" Version="1.0.4" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.2" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.0.2" />
|
||||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.11.0.78383">
|
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.11.0.78383">
|
||||||
|
|||||||
@@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
public enum PlayerChanges
|
public enum PlayerChanges
|
||||||
{
|
{
|
||||||
Heels = 1,
|
ModFiles = 1,
|
||||||
Customize = 2,
|
ModManip = 2,
|
||||||
Palette = 3,
|
Glamourer = 3,
|
||||||
Honorific = 4,
|
Customize = 4,
|
||||||
ModFiles = 5,
|
Heels = 5,
|
||||||
ModManip = 6,
|
Palette = 6,
|
||||||
Glamourer = 7
|
Honorific = 7,
|
||||||
|
ForcedRedraw = 8,
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ public class MareCharaFileManager : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
if ((await dalamudUtil.RunOnFrameworkThread(() => item.Value.CurrentAddress()).ConfigureAwait(false)) != nint.Zero)
|
if ((await dalamudUtil.RunOnFrameworkThread(() => item.Value.CurrentAddress()).ConfigureAwait(false)) != nint.Zero)
|
||||||
{
|
{
|
||||||
await _ipcManager.GlamourerRevert(logger, item.Value, Guid.NewGuid(), cts.Token).ConfigureAwait(false);
|
await _ipcManager.GlamourerRevert(logger, item.Value.Name, item.Value, Guid.NewGuid(), cts.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using MareSynchronos.API.Dto.User;
|
using MareSynchronos.API.Dto.User;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
using MareSynchronos.Interop;
|
using MareSynchronos.Interop;
|
||||||
|
using MareSynchronos.MareConfiguration;
|
||||||
using MareSynchronos.PlayerData.Handlers;
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
@@ -20,12 +21,13 @@ public class PairHandlerFactory
|
|||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly MareMediator _mareMediator;
|
private readonly MareMediator _mareMediator;
|
||||||
|
private readonly MareConfigService _mareConfigService;
|
||||||
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
private readonly PluginWarningNotificationService _pluginWarningNotificationManager;
|
||||||
|
|
||||||
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
|
public PairHandlerFactory(ILoggerFactory loggerFactory, GameObjectHandlerFactory gameObjectHandlerFactory, IpcManager ipcManager,
|
||||||
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
|
FileDownloadManagerFactory fileDownloadManagerFactory, DalamudUtilService dalamudUtilService,
|
||||||
PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime,
|
PluginWarningNotificationService pluginWarningNotificationManager, IHostApplicationLifetime hostApplicationLifetime,
|
||||||
FileCacheManager fileCacheManager, MareMediator mareMediator)
|
FileCacheManager fileCacheManager, MareMediator mareMediator, MareConfigService mareConfigService)
|
||||||
{
|
{
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
||||||
@@ -36,12 +38,13 @@ public class PairHandlerFactory
|
|||||||
_hostApplicationLifetime = hostApplicationLifetime;
|
_hostApplicationLifetime = hostApplicationLifetime;
|
||||||
_fileCacheManager = fileCacheManager;
|
_fileCacheManager = fileCacheManager;
|
||||||
_mareMediator = mareMediator;
|
_mareMediator = mareMediator;
|
||||||
|
_mareConfigService = mareConfigService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PairHandler Create(OnlineUserIdentDto onlineUserIdentDto)
|
public PairHandler Create(OnlineUserIdentDto onlineUserIdentDto)
|
||||||
{
|
{
|
||||||
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), onlineUserIdentDto, _gameObjectHandlerFactory,
|
return new PairHandler(_loggerFactory.CreateLogger<PairHandler>(), onlineUserIdentDto, _gameObjectHandlerFactory,
|
||||||
_ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime,
|
_ipcManager, _fileDownloadManagerFactory.Create(), _pluginWarningNotificationManager, _dalamudUtilService, _hostApplicationLifetime,
|
||||||
_fileCacheManager, _mareMediator);
|
_fileCacheManager, _mareMediator, _mareConfigService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
|||||||
private bool _haltProcessing = false;
|
private bool _haltProcessing = false;
|
||||||
private bool _ignoreSendAfterRedraw = false;
|
private bool _ignoreSendAfterRedraw = false;
|
||||||
private int _ptrNullCounter = 0;
|
private int _ptrNullCounter = 0;
|
||||||
|
private byte _classJob = 0;
|
||||||
private CancellationTokenSource _zoningCts = new();
|
private CancellationTokenSource _zoningCts = new();
|
||||||
|
|
||||||
public GameObjectHandler(ILogger<GameObjectHandler> logger, PerformanceCollectorService performanceCollector,
|
public GameObjectHandler(ILogger<GameObjectHandler> logger, PerformanceCollectorService performanceCollector,
|
||||||
@@ -98,6 +99,10 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
|||||||
ModelFilesInSlotLoaded
|
ModelFilesInSlotLoaded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte RaceId { get; private set; }
|
||||||
|
public byte Gender { get; private set; }
|
||||||
|
public byte TribeId { get; private set; }
|
||||||
|
|
||||||
public IntPtr Address { get; private set; }
|
public IntPtr Address { get; private set; }
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
public ObjectKind ObjectKind { get; }
|
public ObjectKind ObjectKind { get; }
|
||||||
@@ -215,6 +220,14 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
|||||||
if (((DrawObject*)DrawObjectAddress)->Object.GetObjectType() == ObjectType.CharacterBase
|
if (((DrawObject*)DrawObjectAddress)->Object.GetObjectType() == ObjectType.CharacterBase
|
||||||
&& ((CharacterBase*)DrawObjectAddress)->GetModelType() == CharacterBase.ModelType.Human)
|
&& ((CharacterBase*)DrawObjectAddress)->GetModelType() == CharacterBase.ModelType.Human)
|
||||||
{
|
{
|
||||||
|
var classJob = chara->CharacterData.ClassJob;
|
||||||
|
if (classJob != _classJob)
|
||||||
|
{
|
||||||
|
Logger.LogTrace("[{this}] classjob changed from {old} to {new}", this, _classJob, classJob);
|
||||||
|
_classJob = classJob;
|
||||||
|
Mediator.Publish(new ClassJobChangedMessage(this));
|
||||||
|
}
|
||||||
|
|
||||||
equipDiff = CompareAndUpdateEquipByteData((byte*)&((Human*)DrawObjectAddress)->Head);
|
equipDiff = CompareAndUpdateEquipByteData((byte*)&((Human*)DrawObjectAddress)->Head);
|
||||||
|
|
||||||
ref var mh = ref chara->DrawData.Weapon(WeaponSlot.MainHand);
|
ref var mh = ref chara->DrawData.Weapon(WeaponSlot.MainHand);
|
||||||
@@ -244,6 +257,19 @@ public sealed class GameObjectHandler : DisposableMediatorSubscriberBase
|
|||||||
if (((DrawObject*)DrawObjectAddress)->Object.GetObjectType() == ObjectType.CharacterBase
|
if (((DrawObject*)DrawObjectAddress)->Object.GetObjectType() == ObjectType.CharacterBase
|
||||||
&& ((CharacterBase*)DrawObjectAddress)->GetModelType() == CharacterBase.ModelType.Human)
|
&& ((CharacterBase*)DrawObjectAddress)->GetModelType() == CharacterBase.ModelType.Human)
|
||||||
{
|
{
|
||||||
|
var gender = ((Human*)DrawObjectAddress)->Customize.Sex;
|
||||||
|
var raceId = ((Human*)DrawObjectAddress)->Customize.Race;
|
||||||
|
var tribeId = ((Human*)DrawObjectAddress)->Customize.Clan;
|
||||||
|
|
||||||
|
if (_isOwnedObject && ObjectKind == ObjectKind.Player
|
||||||
|
&& (gender != Gender || raceId != RaceId || tribeId != TribeId))
|
||||||
|
{
|
||||||
|
Mediator.Publish(new CensusUpdateMessage(gender, raceId, tribeId));
|
||||||
|
Gender = gender;
|
||||||
|
RaceId = raceId;
|
||||||
|
TribeId = tribeId;
|
||||||
|
}
|
||||||
|
|
||||||
customizeDiff = CompareAndUpdateCustomizeData(((Human*)DrawObjectAddress)->Customize.Data);
|
customizeDiff = CompareAndUpdateCustomizeData(((Human*)DrawObjectAddress)->Customize.Data);
|
||||||
if (customizeDiff)
|
if (customizeDiff)
|
||||||
Logger.LogTrace("Checking [{this}] customize data as human from draw obj, result: {diff}", this, customizeDiff);
|
Logger.LogTrace("Checking [{this}] customize data as human from draw obj, result: {diff}", this, customizeDiff);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using MareSynchronos.API.Dto.User;
|
using MareSynchronos.API.Dto.User;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
using MareSynchronos.Interop;
|
using MareSynchronos.Interop;
|
||||||
|
using MareSynchronos.MareConfiguration;
|
||||||
using MareSynchronos.PlayerData.Factories;
|
using MareSynchronos.PlayerData.Factories;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
@@ -21,6 +22,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
private readonly DalamudUtilService _dalamudUtil;
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
private readonly FileDownloadManager _downloadManager;
|
private readonly FileDownloadManager _downloadManager;
|
||||||
private readonly FileCacheManager _fileDbManager;
|
private readonly FileCacheManager _fileDbManager;
|
||||||
|
private readonly MareConfigService _mareConfigService;
|
||||||
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
private readonly GameObjectHandlerFactory _gameObjectHandlerFactory;
|
||||||
private readonly IpcManager _ipcManager;
|
private readonly IpcManager _ipcManager;
|
||||||
private readonly IHostApplicationLifetime _lifetime;
|
private readonly IHostApplicationLifetime _lifetime;
|
||||||
@@ -34,13 +36,16 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
private bool _forceApplyMods = false;
|
private bool _forceApplyMods = false;
|
||||||
private bool _isVisible;
|
private bool _isVisible;
|
||||||
private string _penumbraCollection;
|
private string _penumbraCollection;
|
||||||
|
private bool _redrawOnNextApplication = false;
|
||||||
|
public long LastAppliedDataSize { get; private set; }
|
||||||
|
|
||||||
public PairHandler(ILogger<PairHandler> logger, OnlineUserIdentDto onlineUser,
|
public PairHandler(ILogger<PairHandler> logger, OnlineUserIdentDto onlineUser,
|
||||||
GameObjectHandlerFactory gameObjectHandlerFactory,
|
GameObjectHandlerFactory gameObjectHandlerFactory,
|
||||||
IpcManager ipcManager, FileDownloadManager transferManager,
|
IpcManager ipcManager, FileDownloadManager transferManager,
|
||||||
PluginWarningNotificationService pluginWarningNotificationManager,
|
PluginWarningNotificationService pluginWarningNotificationManager,
|
||||||
DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime,
|
DalamudUtilService dalamudUtil, IHostApplicationLifetime lifetime,
|
||||||
FileCacheManager fileDbManager, MareMediator mediator) : base(logger, mediator)
|
FileCacheManager fileDbManager, MareMediator mediator,
|
||||||
|
MareConfigService mareConfigService) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
OnlineUser = onlineUser;
|
OnlineUser = onlineUser;
|
||||||
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
_gameObjectHandlerFactory = gameObjectHandlerFactory;
|
||||||
@@ -50,7 +55,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
_lifetime = lifetime;
|
_lifetime = lifetime;
|
||||||
_fileDbManager = fileDbManager;
|
_fileDbManager = fileDbManager;
|
||||||
|
_mareConfigService = mareConfigService;
|
||||||
_penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult();
|
_penumbraCollection = _ipcManager.PenumbraCreateTemporaryCollectionAsync(logger, OnlineUser.User.UID).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||||
|
|
||||||
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
|
Mediator.Subscribe<FrameworkUpdateMessage>(this, (_) => FrameworkUpdate());
|
||||||
@@ -70,6 +75,15 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
_charaHandler = null;
|
_charaHandler = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Mediator.Subscribe<ClassJobChangedMessage>(this, (msg) =>
|
||||||
|
{
|
||||||
|
if (msg.gameObjectHandler == _charaHandler)
|
||||||
|
{
|
||||||
|
_redrawOnNextApplication = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
LastAppliedDataSize = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsVisible
|
public bool IsVisible
|
||||||
@@ -127,6 +141,12 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
_forceApplyMods = false;
|
_forceApplyMods = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_redrawOnNextApplication && charaDataToUpdate.TryGetValue(ObjectKind.Player, out var player))
|
||||||
|
{
|
||||||
|
player.Add(PlayerChanges.ForcedRedraw);
|
||||||
|
_redrawOnNextApplication = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (charaDataToUpdate.TryGetValue(ObjectKind.Player, out var playerChanges))
|
if (charaDataToUpdate.TryGetValue(ObjectKind.Player, out var playerChanges))
|
||||||
{
|
{
|
||||||
_pluginWarningNotificationManager.NotifyForMissingPlugins(OnlineUser.User, PlayerName!, playerChanges);
|
_pluginWarningNotificationManager.NotifyForMissingPlugins(OnlineUser.User, PlayerName!, playerChanges);
|
||||||
@@ -173,22 +193,35 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
if (_lifetime.ApplicationStopping.IsCancellationRequested) return;
|
if (_lifetime.ApplicationStopping.IsCancellationRequested) return;
|
||||||
|
|
||||||
if (_dalamudUtil is { IsZoning: false, IsInCutscene: false })
|
if (_dalamudUtil is { IsZoning: false, IsInCutscene: false } && !string.IsNullOrEmpty(name))
|
||||||
{
|
{
|
||||||
Logger.LogTrace("[{applicationId}] Restoring state for {name} ({OnlineUser})", applicationId, name, OnlineUser);
|
Logger.LogTrace("[{applicationId}] Restoring state for {name} ({OnlineUser})", applicationId, name, OnlineUser);
|
||||||
|
Logger.LogDebug("[{applicationId}] Removing Temp Collection for {name} ({user})", applicationId, name, OnlineUser);
|
||||||
_ipcManager.PenumbraRemoveTemporaryCollectionAsync(Logger, applicationId, _penumbraCollection).GetAwaiter().GetResult();
|
_ipcManager.PenumbraRemoveTemporaryCollectionAsync(Logger, applicationId, _penumbraCollection).GetAwaiter().GetResult();
|
||||||
|
if (!IsVisible)
|
||||||
foreach (KeyValuePair<ObjectKind, List<FileReplacementData>> item in _cachedData?.FileReplacements ?? [])
|
|
||||||
{
|
{
|
||||||
try
|
Logger.LogDebug("[{applicationId}] Restoring Glamourer for {name} ({user})", applicationId, name, OnlineUser);
|
||||||
|
_ipcManager.GlamourerRevertByNameAsync(Logger, name, applicationId).GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
cts.CancelAfter(TimeSpan.FromSeconds(60));
|
||||||
|
|
||||||
|
foreach (KeyValuePair<ObjectKind, List<FileReplacementData>> item in _cachedData?.FileReplacements ?? [])
|
||||||
{
|
{
|
||||||
RevertCustomizationDataAsync(item.Key, name, applicationId).GetAwaiter().GetResult();
|
try
|
||||||
}
|
{
|
||||||
catch (InvalidOperationException ex)
|
RevertCustomizationDataAsync(item.Key, name, applicationId, cts.Token).GetAwaiter().GetResult();
|
||||||
{
|
}
|
||||||
Logger.LogWarning(ex, "Failed disposing player (not present anymore?)");
|
catch (InvalidOperationException ex)
|
||||||
break;
|
{
|
||||||
|
Logger.LogWarning(ex, "Failed disposing player (not present anymore?)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cts.CancelDispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,15 +296,15 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PlayerChanges.ModFiles:
|
case PlayerChanges.ForcedRedraw:
|
||||||
case PlayerChanges.ModManip:
|
await _ipcManager.PenumbraRedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
token.ThrowIfCancellationRequested();
|
token.ThrowIfCancellationRequested();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changes.Value.Contains(PlayerChanges.ModFiles) || changes.Value.Contains(PlayerChanges.ModManip) || changes.Value.Contains(PlayerChanges.Glamourer))
|
|
||||||
await _ipcManager.PenumbraRedrawAsync(Logger, handler, applicationId, token).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -361,6 +394,11 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
if (updateModdedPaths)
|
if (updateModdedPaths)
|
||||||
{
|
{
|
||||||
await _ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, moddedPaths).ConfigureAwait(false);
|
await _ipcManager.PenumbraSetTemporaryModsAsync(Logger, _applicationId, _penumbraCollection, moddedPaths).ConfigureAwait(false);
|
||||||
|
LastAppliedDataSize = -1;
|
||||||
|
foreach (var path in moddedPaths.Select(v => new FileInfo(v.Value)).Where(p => p.Exists))
|
||||||
|
{
|
||||||
|
LastAppliedDataSize += path.Length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (updateManip)
|
if (updateManip)
|
||||||
@@ -453,14 +491,11 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
_ipcManager.PenumbraAssignTemporaryCollectionAsync(Logger, _penumbraCollection, _charaHandler.GetGameObject()!.ObjectIndex).GetAwaiter().GetResult();
|
_ipcManager.PenumbraAssignTemporaryCollectionAsync(Logger, _penumbraCollection, _charaHandler.GetGameObject()!.ObjectIndex).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RevertCustomizationDataAsync(ObjectKind objectKind, string name, Guid applicationId)
|
private async Task RevertCustomizationDataAsync(ObjectKind objectKind, string name, Guid applicationId, CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
nint address = _dalamudUtil.GetPlayerCharacterFromCachedTableByIdent(OnlineUser.Ident);
|
nint address = _dalamudUtil.GetPlayerCharacterFromCachedTableByIdent(OnlineUser.Ident);
|
||||||
if (address == nint.Zero) return;
|
if (address == nint.Zero) return;
|
||||||
|
|
||||||
var cancelToken = new CancellationTokenSource();
|
|
||||||
cancelToken.CancelAfter(TimeSpan.FromSeconds(60));
|
|
||||||
|
|
||||||
Logger.LogDebug("[{applicationId}] Reverting all Customization for {alias}/{name} {objectKind}", applicationId, OnlineUser.User.AliasOrUID, name, objectKind);
|
Logger.LogDebug("[{applicationId}] Reverting all Customization for {alias}/{name} {objectKind}", applicationId, OnlineUser.User.AliasOrUID, name, objectKind);
|
||||||
|
|
||||||
if (objectKind == ObjectKind.Player)
|
if (objectKind == ObjectKind.Player)
|
||||||
@@ -468,7 +503,7 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => address, isWatched: false).ConfigureAwait(false);
|
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Player, () => address, isWatched: false).ConfigureAwait(false);
|
||||||
tempHandler.CompareNameAndThrow(name);
|
tempHandler.CompareNameAndThrow(name);
|
||||||
Logger.LogDebug("[{applicationId}] Restoring Customization and Equipment for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name);
|
Logger.LogDebug("[{applicationId}] Restoring Customization and Equipment for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name);
|
||||||
await _ipcManager.GlamourerRevert(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
await _ipcManager.GlamourerRevert(Logger, name, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
|
||||||
tempHandler.CompareNameAndThrow(name);
|
tempHandler.CompareNameAndThrow(name);
|
||||||
Logger.LogDebug("[{applicationId}] Restoring Heels for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name);
|
Logger.LogDebug("[{applicationId}] Restoring Heels for {alias}/{name}", applicationId, OnlineUser.User.AliasOrUID, name);
|
||||||
await _ipcManager.HeelsRestoreOffsetForPlayerAsync(address).ConfigureAwait(false);
|
await _ipcManager.HeelsRestoreOffsetForPlayerAsync(address).ConfigureAwait(false);
|
||||||
@@ -489,8 +524,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
await _ipcManager.CustomizePlusRevertAsync(minionOrMount).ConfigureAwait(false);
|
await _ipcManager.CustomizePlusRevertAsync(minionOrMount).ConfigureAwait(false);
|
||||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => minionOrMount, isWatched: false).ConfigureAwait(false);
|
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.MinionOrMount, () => minionOrMount, isWatched: false).ConfigureAwait(false);
|
||||||
await _ipcManager.GlamourerRevert(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
|
||||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (objectKind == ObjectKind.Pet)
|
else if (objectKind == ObjectKind.Pet)
|
||||||
@@ -500,8 +535,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
await _ipcManager.CustomizePlusRevertAsync(pet).ConfigureAwait(false);
|
await _ipcManager.CustomizePlusRevertAsync(pet).ConfigureAwait(false);
|
||||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => pet, isWatched: false).ConfigureAwait(false);
|
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => pet, isWatched: false).ConfigureAwait(false);
|
||||||
await _ipcManager.GlamourerRevert(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
|
||||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (objectKind == ObjectKind.Companion)
|
else if (objectKind == ObjectKind.Companion)
|
||||||
@@ -511,12 +546,10 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
await _ipcManager.CustomizePlusRevertAsync(companion).ConfigureAwait(false);
|
await _ipcManager.CustomizePlusRevertAsync(companion).ConfigureAwait(false);
|
||||||
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => companion, isWatched: false).ConfigureAwait(false);
|
using GameObjectHandler tempHandler = await _gameObjectHandlerFactory.Create(ObjectKind.Pet, () => companion, isWatched: false).ConfigureAwait(false);
|
||||||
await _ipcManager.GlamourerRevert(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
await _ipcManager.GlamourerRevert(Logger, tempHandler.Name, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
|
||||||
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken.Token).ConfigureAwait(false);
|
await _ipcManager.PenumbraRedrawAsync(Logger, tempHandler, applicationId, cancelToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cancelToken.CancelDispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<FileReplacementData> TryCalculateModdedDictionary(Guid applicationBase, CharacterData charaData, out Dictionary<string, string> moddedDictionary, CancellationToken token)
|
private List<FileReplacementData> TryCalculateModdedDictionary(Guid applicationBase, CharacterData charaData, out Dictionary<string, string> moddedDictionary, CancellationToken token)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ public class Pair
|
|||||||
public bool IsVisible => CachedPlayer?.IsVisible ?? false;
|
public bool IsVisible => CachedPlayer?.IsVisible ?? false;
|
||||||
public CharacterData? LastReceivedCharacterData { get; set; }
|
public CharacterData? LastReceivedCharacterData { get; set; }
|
||||||
public string? PlayerName => CachedPlayer?.PlayerName ?? string.Empty;
|
public string? PlayerName => CachedPlayer?.PlayerName ?? string.Empty;
|
||||||
|
public long LastAppliedDataSize => CachedPlayer?.LastAppliedDataSize ?? -1;
|
||||||
|
|
||||||
public UserData UserData => UserPair?.User ?? GroupPair.First().Value.User;
|
public UserData UserData => UserPair?.User ?? GroupPair.First().Value.User;
|
||||||
|
|
||||||
@@ -58,9 +59,11 @@ public class Pair
|
|||||||
SeStringBuilder seStringBuilder = new();
|
SeStringBuilder seStringBuilder = new();
|
||||||
SeStringBuilder seStringBuilder2 = new();
|
SeStringBuilder seStringBuilder2 = new();
|
||||||
SeStringBuilder seStringBuilder3 = new();
|
SeStringBuilder seStringBuilder3 = new();
|
||||||
|
SeStringBuilder seStringBuilder4 = new();
|
||||||
var openProfileSeString = seStringBuilder.AddUiForeground(526).AddText(" ").AddUiForegroundOff().AddText("Open Profile").Build();
|
var openProfileSeString = seStringBuilder.AddUiForeground(526).AddText(" ").AddUiForegroundOff().AddText("Open Profile").Build();
|
||||||
var reapplyDataSeString = seStringBuilder2.AddUiForeground(526).AddText(" ").AddUiForegroundOff().AddText("Reapply last data").Build();
|
var reapplyDataSeString = seStringBuilder2.AddUiForeground(526).AddText(" ").AddUiForegroundOff().AddText("Reapply last data").Build();
|
||||||
var cyclePauseState = seStringBuilder3.AddUiForeground(526).AddText(" ").AddUiForegroundOff().AddText("Cycle pause state").Build();
|
var cyclePauseState = seStringBuilder3.AddUiForeground(526).AddText(" ").AddUiForegroundOff().AddText("Cycle pause state").Build();
|
||||||
|
var changePermissions = seStringBuilder4.AddUiForeground(526).AddText(" ").AddUiForegroundOff().AddText("Change Permissions").Build();
|
||||||
args.AddCustomItem(new GameObjectContextMenuItem(openProfileSeString, (a) =>
|
args.AddCustomItem(new GameObjectContextMenuItem(openProfileSeString, (a) =>
|
||||||
{
|
{
|
||||||
_mediator.Publish(new ProfileOpenStandaloneMessage(this));
|
_mediator.Publish(new ProfileOpenStandaloneMessage(this));
|
||||||
@@ -69,6 +72,10 @@ public class Pair
|
|||||||
{
|
{
|
||||||
ApplyLastReceivedData(forced: true);
|
ApplyLastReceivedData(forced: true);
|
||||||
}, useDalamudIndicator: false));
|
}, useDalamudIndicator: false));
|
||||||
|
args.AddCustomItem(new GameObjectContextMenuItem(changePermissions, (a) =>
|
||||||
|
{
|
||||||
|
_mediator.Publish(new OpenPermissionWindow(this));
|
||||||
|
}, useDalamudIndicator: false));
|
||||||
args.AddCustomItem(new GameObjectContextMenuItem(cyclePauseState, (a) =>
|
args.AddCustomItem(new GameObjectContextMenuItem(cyclePauseState, (a) =>
|
||||||
{
|
{
|
||||||
_mediator.Publish(new CyclePauseMessage(UserData));
|
_mediator.Publish(new CyclePauseMessage(UserData));
|
||||||
|
|||||||
@@ -29,12 +29,13 @@ namespace MareSynchronos;
|
|||||||
public sealed class Plugin : IDalamudPlugin
|
public sealed class Plugin : IDalamudPlugin
|
||||||
{
|
{
|
||||||
private readonly CancellationTokenSource _pluginCts = new();
|
private readonly CancellationTokenSource _pluginCts = new();
|
||||||
|
private readonly Task _hostBuilderRunTask;
|
||||||
|
|
||||||
public Plugin(DalamudPluginInterface pluginInterface, ICommandManager commandManager, IDataManager gameData,
|
public Plugin(DalamudPluginInterface pluginInterface, ICommandManager commandManager, IDataManager gameData,
|
||||||
IFramework framework, IObjectTable objectTable, IClientState clientState, ICondition condition, IChatGui chatGui,
|
IFramework framework, IObjectTable objectTable, IClientState clientState, ICondition condition, IChatGui chatGui,
|
||||||
IGameGui gameGui, IDtrBar dtrBar, IPluginLog pluginLog)
|
IGameGui gameGui, IDtrBar dtrBar, IPluginLog pluginLog)
|
||||||
{
|
{
|
||||||
_ = new HostBuilder()
|
_hostBuilderRunTask = new HostBuilder()
|
||||||
.UseContentRoot(pluginInterface.ConfigDirectory.FullName)
|
.UseContentRoot(pluginInterface.ConfigDirectory.FullName)
|
||||||
.ConfigureLogging(lb =>
|
.ConfigureLogging(lb =>
|
||||||
{
|
{
|
||||||
@@ -86,22 +87,11 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton((s) => new ServerTagConfigService(pluginInterface.ConfigDirectory.FullName));
|
collection.AddSingleton((s) => new ServerTagConfigService(pluginInterface.ConfigDirectory.FullName));
|
||||||
collection.AddSingleton((s) => new TransientConfigService(pluginInterface.ConfigDirectory.FullName));
|
collection.AddSingleton((s) => new TransientConfigService(pluginInterface.ConfigDirectory.FullName));
|
||||||
collection.AddSingleton((s) => new ConfigurationMigrator(s.GetRequiredService<ILogger<ConfigurationMigrator>>(), pluginInterface));
|
collection.AddSingleton((s) => new ConfigurationMigrator(s.GetRequiredService<ILogger<ConfigurationMigrator>>(), pluginInterface));
|
||||||
collection.AddSingleton((s) => new HubFactory(s.GetRequiredService<ILogger<HubFactory>>(), s.GetRequiredService<MareMediator>(),
|
collection.AddSingleton<HubFactory>();
|
||||||
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<MareConfigService>(),
|
|
||||||
s.GetRequiredService<TokenProvider>(), pluginLog));
|
|
||||||
|
|
||||||
// func factory method singletons
|
|
||||||
collection.AddSingleton(s =>
|
|
||||||
new Func<Pair, StandaloneProfileUi>((pair) =>
|
|
||||||
new StandaloneProfileUi(s.GetRequiredService<ILogger<StandaloneProfileUi>>(),
|
|
||||||
s.GetRequiredService<MareMediator>(),
|
|
||||||
s.GetRequiredService<UiSharedService>(),
|
|
||||||
s.GetRequiredService<ServerConfigurationManager>(),
|
|
||||||
s.GetRequiredService<MareProfileManager>(),
|
|
||||||
s.GetRequiredService<PairManager>(), pair)));
|
|
||||||
|
|
||||||
// add scoped services
|
// add scoped services
|
||||||
collection.AddScoped<PeriodicFileScanner>();
|
collection.AddScoped<PeriodicFileScanner>();
|
||||||
|
collection.AddScoped<UiFactory>();
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, SettingsUi>();
|
collection.AddScoped<WindowMediatorSubscriberBase, SettingsUi>();
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, CompactUi>();
|
collection.AddScoped<WindowMediatorSubscriberBase, CompactUi>();
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, GposeUi>();
|
collection.AddScoped<WindowMediatorSubscriberBase, GposeUi>();
|
||||||
@@ -109,23 +99,24 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>();
|
collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>();
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, PopoutProfileUi>();
|
collection.AddScoped<WindowMediatorSubscriberBase, PopoutProfileUi>();
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, DataAnalysisUi>();
|
collection.AddScoped<WindowMediatorSubscriberBase, DataAnalysisUi>();
|
||||||
|
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, EditProfileUi>((s) => new EditProfileUi(s.GetRequiredService<ILogger<EditProfileUi>>(),
|
collection.AddScoped<WindowMediatorSubscriberBase, EditProfileUi>((s) => new EditProfileUi(s.GetRequiredService<ILogger<EditProfileUi>>(),
|
||||||
s.GetRequiredService<MareMediator>(), s.GetRequiredService<ApiController>(), pluginInterface.UiBuilder, s.GetRequiredService<UiSharedService>(),
|
s.GetRequiredService<MareMediator>(), s.GetRequiredService<ApiController>(), pluginInterface.UiBuilder, s.GetRequiredService<UiSharedService>(),
|
||||||
s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareProfileManager>()));
|
s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareProfileManager>()));
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, PopupHandler>();
|
collection.AddScoped<WindowMediatorSubscriberBase, PopupHandler>();
|
||||||
collection.AddScoped<IPopupHandler, ReportPopupHandler>();
|
collection.AddScoped<IPopupHandler, ReportPopupHandler>();
|
||||||
collection.AddScoped<IPopupHandler, BanUserPopupHandler>();
|
collection.AddScoped<IPopupHandler, BanUserPopupHandler>();
|
||||||
collection.AddScoped<IPopupHandler, SyncshellAdminPopupHandler>();
|
|
||||||
collection.AddScoped<CacheCreationService>();
|
collection.AddScoped<CacheCreationService>();
|
||||||
collection.AddScoped<TransientResourceManager>();
|
collection.AddScoped<TransientResourceManager>();
|
||||||
collection.AddScoped<PlayerDataFactory>();
|
collection.AddScoped<PlayerDataFactory>();
|
||||||
collection.AddScoped<OnlinePlayerManager>();
|
collection.AddScoped<OnlinePlayerManager>();
|
||||||
collection.AddScoped((s) => new UiService(s.GetRequiredService<ILogger<UiService>>(), pluginInterface, s.GetRequiredService<MareConfigService>(),
|
collection.AddScoped((s) => new UiService(s.GetRequiredService<ILogger<UiService>>(), pluginInterface.UiBuilder, s.GetRequiredService<MareConfigService>(),
|
||||||
s.GetRequiredService<WindowSystem>(), s.GetServices<WindowMediatorSubscriberBase>(), s.GetRequiredService<Func<Pair, StandaloneProfileUi>>(),
|
s.GetRequiredService<WindowSystem>(), s.GetServices<WindowMediatorSubscriberBase>(),
|
||||||
|
s.GetRequiredService<UiFactory>(),
|
||||||
s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareMediator>()));
|
s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareMediator>()));
|
||||||
collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService<PerformanceCollectorService>(),
|
collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService<PerformanceCollectorService>(),
|
||||||
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<PeriodicFileScanner>(), s.GetRequiredService<ApiController>(),
|
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<PeriodicFileScanner>(), s.GetRequiredService<ApiController>(),
|
||||||
s.GetRequiredService<MareMediator>()));
|
s.GetRequiredService<MareMediator>(), s.GetRequiredService<MareConfigService>()));
|
||||||
collection.AddScoped((s) => new NotificationService(s.GetRequiredService<ILogger<NotificationService>>(),
|
collection.AddScoped((s) => new NotificationService(s.GetRequiredService<ILogger<NotificationService>>(),
|
||||||
s.GetRequiredService<MareMediator>(), pluginInterface.UiBuilder, chatGui, s.GetRequiredService<MareConfigService>()));
|
s.GetRequiredService<MareMediator>(), pluginInterface.UiBuilder, chatGui, s.GetRequiredService<MareConfigService>()));
|
||||||
collection.AddScoped((s) => new UiSharedService(s.GetRequiredService<ILogger<UiSharedService>>(), s.GetRequiredService<IpcManager>(), s.GetRequiredService<ApiController>(),
|
collection.AddScoped((s) => new UiSharedService(s.GetRequiredService<ILogger<UiSharedService>>(), s.GetRequiredService<IpcManager>(), s.GetRequiredService<ApiController>(),
|
||||||
@@ -147,5 +138,6 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
{
|
{
|
||||||
_pluginCts.Cancel();
|
_pluginCts.Cancel();
|
||||||
_pluginCts.Dispose();
|
_pluginCts.Dispose();
|
||||||
|
Task.WaitAny(_hostBuilderRunTask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using Dalamud.Game.Command;
|
using Dalamud.Game.Command;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using MareSynchronos.FileCache;
|
using MareSynchronos.FileCache;
|
||||||
|
using MareSynchronos.MareConfiguration;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Services.ServerConfiguration;
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
using MareSynchronos.UI;
|
using MareSynchronos.UI;
|
||||||
@@ -16,13 +17,14 @@ public sealed class CommandManagerService : IDisposable
|
|||||||
private readonly ApiController _apiController;
|
private readonly ApiController _apiController;
|
||||||
private readonly ICommandManager _commandManager;
|
private readonly ICommandManager _commandManager;
|
||||||
private readonly MareMediator _mediator;
|
private readonly MareMediator _mediator;
|
||||||
|
private readonly MareConfigService _mareConfigService;
|
||||||
private readonly PerformanceCollectorService _performanceCollectorService;
|
private readonly PerformanceCollectorService _performanceCollectorService;
|
||||||
private readonly PeriodicFileScanner _periodicFileScanner;
|
private readonly PeriodicFileScanner _periodicFileScanner;
|
||||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||||
|
|
||||||
public CommandManagerService(ICommandManager commandManager, PerformanceCollectorService performanceCollectorService,
|
public CommandManagerService(ICommandManager commandManager, PerformanceCollectorService performanceCollectorService,
|
||||||
ServerConfigurationManager serverConfigurationManager, PeriodicFileScanner periodicFileScanner,
|
ServerConfigurationManager serverConfigurationManager, PeriodicFileScanner periodicFileScanner,
|
||||||
ApiController apiController, MareMediator mediator)
|
ApiController apiController, MareMediator mediator, MareConfigService mareConfigService)
|
||||||
{
|
{
|
||||||
_commandManager = commandManager;
|
_commandManager = commandManager;
|
||||||
_performanceCollectorService = performanceCollectorService;
|
_performanceCollectorService = performanceCollectorService;
|
||||||
@@ -30,6 +32,7 @@ public sealed class CommandManagerService : IDisposable
|
|||||||
_periodicFileScanner = periodicFileScanner;
|
_periodicFileScanner = periodicFileScanner;
|
||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
_mediator = mediator;
|
_mediator = mediator;
|
||||||
|
_mareConfigService = mareConfigService;
|
||||||
_commandManager.AddHandler(_commandName, new CommandInfo(OnCommand)
|
_commandManager.AddHandler(_commandName, new CommandInfo(OnCommand)
|
||||||
{
|
{
|
||||||
HelpMessage = "Opens the Mare Synchronos UI"
|
HelpMessage = "Opens the Mare Synchronos UI"
|
||||||
@@ -48,7 +51,10 @@ public sealed class CommandManagerService : IDisposable
|
|||||||
if (splitArgs == null || splitArgs.Length == 0)
|
if (splitArgs == null || splitArgs.Length == 0)
|
||||||
{
|
{
|
||||||
// Interpret this as toggling the UI
|
// Interpret this as toggling the UI
|
||||||
_mediator.Publish(new UiToggleMessage(typeof(CompactUi)));
|
if (_mareConfigService.Current.HasValidSetup())
|
||||||
|
_mediator.Publish(new UiToggleMessage(typeof(CompactUi)));
|
||||||
|
else
|
||||||
|
_mediator.Publish(new UiToggleMessage(typeof(IntroUi)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +106,7 @@ public sealed class CommandManagerService : IDisposable
|
|||||||
}
|
}
|
||||||
else if (string.Equals(splitArgs[0], "analyze", StringComparison.OrdinalIgnoreCase))
|
else if (string.Equals(splitArgs[0], "analyze", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
_mediator.Publish(new OpenDataAnalysisUiMessage());
|
_mediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,7 @@ public class DalamudUtilService : IHostedService
|
|||||||
|
|
||||||
public async Task<string> GetPlayerNameHashedAsync()
|
public async Task<string> GetPlayerNameHashedAsync()
|
||||||
{
|
{
|
||||||
return await RunOnFrameworkThread(() => (GetPlayerName() + GetWorldId()).GetHash256()).ConfigureAwait(false);
|
return await RunOnFrameworkThread(() => (GetPlayerName() + GetHomeWorldId()).GetHash256()).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IntPtr GetPlayerPointer()
|
public IntPtr GetPlayerPointer()
|
||||||
@@ -187,17 +187,28 @@ public class DalamudUtilService : IHostedService
|
|||||||
return await RunOnFrameworkThread(GetPlayerPointer).ConfigureAwait(false);
|
return await RunOnFrameworkThread(GetPlayerPointer).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint GetWorldId()
|
public uint GetHomeWorldId()
|
||||||
{
|
{
|
||||||
EnsureIsOnFramework();
|
EnsureIsOnFramework();
|
||||||
return _clientState.LocalPlayer!.HomeWorld.Id;
|
return _clientState.LocalPlayer!.HomeWorld.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint GetWorldId()
|
||||||
|
{
|
||||||
|
EnsureIsOnFramework();
|
||||||
|
return _clientState.LocalPlayer!.CurrentWorld.Id;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<uint> GetWorldIdAsync()
|
public async Task<uint> GetWorldIdAsync()
|
||||||
{
|
{
|
||||||
return await RunOnFrameworkThread(GetWorldId).ConfigureAwait(false);
|
return await RunOnFrameworkThread(GetWorldId).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<uint> GetHomeWorldIdAsync()
|
||||||
|
{
|
||||||
|
return await RunOnFrameworkThread(GetHomeWorldId).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
public unsafe bool IsGameObjectPresent(IntPtr key)
|
public unsafe bool IsGameObjectPresent(IntPtr key)
|
||||||
{
|
{
|
||||||
return _objectTable.Any(f => f.Address == key);
|
return _objectTable.Any(f => f.Address == key);
|
||||||
@@ -494,17 +505,6 @@ public class DalamudUtilService : IHostedService
|
|||||||
_mediator.Publish(new DalamudLogoutMessage());
|
_mediator.Publish(new DalamudLogoutMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_clientState.LocalPlayer != null && _clientState.LocalPlayer.IsValid())
|
|
||||||
{
|
|
||||||
var newclassJobId = _clientState.LocalPlayer.ClassJob.Id;
|
|
||||||
|
|
||||||
if (_classJobId != newclassJobId)
|
|
||||||
{
|
|
||||||
_classJobId = newclassJobId;
|
|
||||||
_mediator.Publish(new ClassJobChangedMessage(_classJobId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_mediator.Publish(new DelayedFrameworkUpdateMessage());
|
_mediator.Publish(new DelayedFrameworkUpdateMessage());
|
||||||
|
|
||||||
_delayedFrameworkUpdateCheck = DateTime.Now;
|
_delayedFrameworkUpdateCheck = DateTime.Now;
|
||||||
|
|||||||
@@ -15,11 +15,10 @@ namespace MareSynchronos.Services.Mediator;
|
|||||||
public record SwitchToIntroUiMessage : MessageBase;
|
public record SwitchToIntroUiMessage : MessageBase;
|
||||||
public record SwitchToMainUiMessage : MessageBase;
|
public record SwitchToMainUiMessage : MessageBase;
|
||||||
public record OpenSettingsUiMessage : MessageBase;
|
public record OpenSettingsUiMessage : MessageBase;
|
||||||
public record OpenDataAnalysisUiMessage : MessageBase;
|
|
||||||
public record DalamudLoginMessage : MessageBase;
|
public record DalamudLoginMessage : MessageBase;
|
||||||
public record DalamudLogoutMessage : MessageBase;
|
public record DalamudLogoutMessage : MessageBase;
|
||||||
public record FrameworkUpdateMessage : SameThreadMessage;
|
public record FrameworkUpdateMessage : SameThreadMessage;
|
||||||
public record ClassJobChangedMessage(uint? ClassJob) : MessageBase;
|
public record ClassJobChangedMessage(GameObjectHandler gameObjectHandler) : MessageBase;
|
||||||
public record DelayedFrameworkUpdateMessage : SameThreadMessage;
|
public record DelayedFrameworkUpdateMessage : SameThreadMessage;
|
||||||
public record ZoneSwitchStartMessage : MessageBase;
|
public record ZoneSwitchStartMessage : MessageBase;
|
||||||
public record ZoneSwitchEndMessage : MessageBase;
|
public record ZoneSwitchEndMessage : MessageBase;
|
||||||
@@ -55,9 +54,9 @@ public record CharacterDataCreatedMessage(CharacterData CharacterData) : SameThr
|
|||||||
public record CharacterDataAnalyzedMessage : MessageBase;
|
public record CharacterDataAnalyzedMessage : MessageBase;
|
||||||
public record PenumbraStartRedrawMessage(IntPtr Address) : MessageBase;
|
public record PenumbraStartRedrawMessage(IntPtr Address) : MessageBase;
|
||||||
public record PenumbraEndRedrawMessage(IntPtr Address) : MessageBase;
|
public record PenumbraEndRedrawMessage(IntPtr Address) : MessageBase;
|
||||||
public record HubReconnectingMessage(Exception? Exception) : MessageBase;
|
public record HubReconnectingMessage(Exception? Exception) : SameThreadMessage;
|
||||||
public record HubReconnectedMessage(string? Arg) : MessageBase;
|
public record HubReconnectedMessage(string? Arg) : SameThreadMessage;
|
||||||
public record HubClosedMessage(Exception? Exception) : MessageBase;
|
public record HubClosedMessage(Exception? Exception) : SameThreadMessage;
|
||||||
public record DownloadReadyMessage(Guid RequestId) : MessageBase;
|
public record DownloadReadyMessage(Guid RequestId) : MessageBase;
|
||||||
public record DownloadStartedMessage(GameObjectHandler DownloadId, Dictionary<string, FileDownloadStatus> DownloadStatus) : MessageBase;
|
public record DownloadStartedMessage(GameObjectHandler DownloadId, Dictionary<string, FileDownloadStatus> DownloadStatus) : MessageBase;
|
||||||
public record DownloadFinishedMessage(GameObjectHandler DownloadId) : MessageBase;
|
public record DownloadFinishedMessage(GameObjectHandler DownloadId) : MessageBase;
|
||||||
@@ -72,9 +71,10 @@ public record RemoveWindowMessage(WindowMediatorSubscriberBase Window) : Message
|
|||||||
public record PairHandlerVisibleMessage(PairHandler Player) : MessageBase;
|
public record PairHandlerVisibleMessage(PairHandler Player) : MessageBase;
|
||||||
public record OpenReportPopupMessage(Pair PairToReport) : MessageBase;
|
public record OpenReportPopupMessage(Pair PairToReport) : MessageBase;
|
||||||
public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFullInfoDto) : MessageBase;
|
public record OpenBanUserPopupMessage(Pair PairToBan, GroupFullInfoDto GroupFullInfoDto) : MessageBase;
|
||||||
public record JoinSyncshellPopupMessage() : MessageBase;
|
public record OpenSyncshellAdminPanel(GroupFullInfoDto GroupInfo) : MessageBase;
|
||||||
public record OpenCreateSyncshellPopupMessage() : MessageBase;
|
public record OpenPermissionWindow(Pair Pair) : MessageBase;
|
||||||
public record OpenSyncshellAdminPanelPopupMessage(GroupFullInfoDto GroupInfo) : MessageBase;
|
public record DownloadLimitChangedMessage() : SameThreadMessage;
|
||||||
|
public record CensusUpdateMessage(byte Gender, byte RaceId, byte TribeId) : MessageBase;
|
||||||
|
|
||||||
#pragma warning restore S2094
|
#pragma warning restore S2094
|
||||||
#pragma warning restore MA0048 // File name must match type name
|
#pragma warning restore MA0048 // File name must match type name
|
||||||
@@ -62,7 +62,7 @@ public class ServerConfigurationManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
var charaName = _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult();
|
var charaName = _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult();
|
||||||
var worldId = _dalamudUtil.GetWorldIdAsync().GetAwaiter().GetResult();
|
var worldId = _dalamudUtil.GetHomeWorldIdAsync().GetAwaiter().GetResult();
|
||||||
if (!currentServer.Authentications.Any() && currentServer.SecretKeys.Any())
|
if (!currentServer.Authentications.Any() && currentServer.SecretKeys.Any())
|
||||||
{
|
{
|
||||||
currentServer.Authentications.Add(new Authentication()
|
currentServer.Authentications.Add(new Authentication()
|
||||||
@@ -136,7 +136,7 @@ public class ServerConfigurationManager
|
|||||||
server.Authentications.Add(new Authentication()
|
server.Authentications.Add(new Authentication()
|
||||||
{
|
{
|
||||||
CharacterName = _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult(),
|
CharacterName = _dalamudUtil.GetPlayerNameAsync().GetAwaiter().GetResult(),
|
||||||
WorldId = _dalamudUtil.GetWorldIdAsync().GetAwaiter().GetResult(),
|
WorldId = _dalamudUtil.GetHomeWorldIdAsync().GetAwaiter().GetResult(),
|
||||||
SecretKeyIdx = addLastSecretKey ? server.SecretKeys.Last().Key : -1,
|
SecretKeyIdx = addLastSecretKey ? server.SecretKeys.Last().Key : -1,
|
||||||
});
|
});
|
||||||
Save();
|
Save();
|
||||||
|
|||||||
52
MareSynchronos/Services/UiFactory.cs
Normal file
52
MareSynchronos/Services/UiFactory.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using MareSynchronos.API.Dto.Group;
|
||||||
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
|
using MareSynchronos.UI;
|
||||||
|
using MareSynchronos.UI.Components.Popup;
|
||||||
|
using MareSynchronos.WebAPI;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Services;
|
||||||
|
|
||||||
|
public class UiFactory
|
||||||
|
{
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
|
private readonly MareMediator _mareMediator;
|
||||||
|
private readonly ApiController _apiController;
|
||||||
|
private readonly UiSharedService _uiSharedService;
|
||||||
|
private readonly PairManager _pairManager;
|
||||||
|
private readonly ServerConfigurationManager _serverConfigManager;
|
||||||
|
private readonly MareProfileManager _mareProfileManager;
|
||||||
|
|
||||||
|
public UiFactory(ILoggerFactory loggerFactory, MareMediator mareMediator, ApiController apiController,
|
||||||
|
UiSharedService uiSharedService, PairManager pairManager, ServerConfigurationManager serverConfigManager,
|
||||||
|
MareProfileManager mareProfileManager)
|
||||||
|
{
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
_mareMediator = mareMediator;
|
||||||
|
_apiController = apiController;
|
||||||
|
_uiSharedService = uiSharedService;
|
||||||
|
_pairManager = pairManager;
|
||||||
|
_serverConfigManager = serverConfigManager;
|
||||||
|
_mareProfileManager = mareProfileManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SyncshellAdminUI CreateSyncshellAdminUi(GroupFullInfoDto dto)
|
||||||
|
{
|
||||||
|
return new SyncshellAdminUI(_loggerFactory.CreateLogger<SyncshellAdminUI>(), _mareMediator,
|
||||||
|
_apiController, _uiSharedService, _pairManager, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StandaloneProfileUi CreateStandaloneProfileUi(Pair pair)
|
||||||
|
{
|
||||||
|
return new StandaloneProfileUi(_loggerFactory.CreateLogger<StandaloneProfileUi>(), _mareMediator,
|
||||||
|
_uiSharedService, _serverConfigManager, _mareProfileManager, _pairManager, pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PermissionWindowUI CreatePermissionPopupUi(Pair pair)
|
||||||
|
{
|
||||||
|
return new PermissionWindowUI(_loggerFactory.CreateLogger<PermissionWindowUI>(), pair,
|
||||||
|
_mareMediator, _uiSharedService, _apiController);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using Dalamud.Interface.ImGuiFileDialog;
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.ImGuiFileDialog;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin;
|
|
||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.MareConfiguration;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.UI;
|
using MareSynchronos.UI;
|
||||||
|
using MareSynchronos.UI.Components.Popup;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MareSynchronos.Services;
|
namespace MareSynchronos.Services;
|
||||||
@@ -12,28 +12,31 @@ namespace MareSynchronos.Services;
|
|||||||
public sealed class UiService : DisposableMediatorSubscriberBase
|
public sealed class UiService : DisposableMediatorSubscriberBase
|
||||||
{
|
{
|
||||||
private readonly List<WindowMediatorSubscriberBase> _createdWindows = [];
|
private readonly List<WindowMediatorSubscriberBase> _createdWindows = [];
|
||||||
private readonly DalamudPluginInterface _dalamudPluginInterface;
|
private readonly UiBuilder _uiBuilder;
|
||||||
private readonly FileDialogManager _fileDialogManager;
|
private readonly FileDialogManager _fileDialogManager;
|
||||||
private readonly ILogger<UiService> _logger;
|
private readonly ILogger<UiService> _logger;
|
||||||
private readonly MareConfigService _mareConfigService;
|
private readonly MareConfigService _mareConfigService;
|
||||||
private readonly WindowSystem _windowSystem;
|
private readonly WindowSystem _windowSystem;
|
||||||
|
private readonly UiFactory _uiFactory;
|
||||||
|
|
||||||
public UiService(ILogger<UiService> logger, DalamudPluginInterface dalamudPluginInterface,
|
public UiService(ILogger<UiService> logger, UiBuilder uiBuilder,
|
||||||
MareConfigService mareConfigService, WindowSystem windowSystem,
|
MareConfigService mareConfigService, WindowSystem windowSystem,
|
||||||
IEnumerable<WindowMediatorSubscriberBase> windows, Func<Pair, StandaloneProfileUi> standaloneProfileUiFactory,
|
IEnumerable<WindowMediatorSubscriberBase> windows,
|
||||||
FileDialogManager fileDialogManager, MareMediator mareMediator) : base(logger, mareMediator)
|
UiFactory uiFactory, FileDialogManager fileDialogManager,
|
||||||
|
MareMediator mareMediator) : base(logger, mareMediator)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_logger.LogTrace("Creating {type}", GetType().Name);
|
_logger.LogTrace("Creating {type}", GetType().Name);
|
||||||
_dalamudPluginInterface = dalamudPluginInterface;
|
_uiBuilder = uiBuilder;
|
||||||
_mareConfigService = mareConfigService;
|
_mareConfigService = mareConfigService;
|
||||||
_windowSystem = windowSystem;
|
_windowSystem = windowSystem;
|
||||||
|
_uiFactory = uiFactory;
|
||||||
_fileDialogManager = fileDialogManager;
|
_fileDialogManager = fileDialogManager;
|
||||||
|
|
||||||
_dalamudPluginInterface.UiBuilder.DisableGposeUiHide = true;
|
_uiBuilder.DisableGposeUiHide = true;
|
||||||
_dalamudPluginInterface.UiBuilder.Draw += Draw;
|
_uiBuilder.Draw += Draw;
|
||||||
_dalamudPluginInterface.UiBuilder.OpenConfigUi += ToggleUi;
|
_uiBuilder.OpenConfigUi += ToggleUi;
|
||||||
_dalamudPluginInterface.UiBuilder.OpenMainUi += ToggleMainUi;
|
_uiBuilder.OpenMainUi += ToggleMainUi;
|
||||||
|
|
||||||
foreach (var window in windows)
|
foreach (var window in windows)
|
||||||
{
|
{
|
||||||
@@ -45,7 +48,29 @@ public sealed class UiService : DisposableMediatorSubscriberBase
|
|||||||
if (!_createdWindows.Exists(p => p is StandaloneProfileUi ui
|
if (!_createdWindows.Exists(p => p is StandaloneProfileUi ui
|
||||||
&& string.Equals(ui.Pair.UserData.AliasOrUID, msg.Pair.UserData.AliasOrUID, StringComparison.Ordinal)))
|
&& string.Equals(ui.Pair.UserData.AliasOrUID, msg.Pair.UserData.AliasOrUID, StringComparison.Ordinal)))
|
||||||
{
|
{
|
||||||
var window = standaloneProfileUiFactory(msg.Pair);
|
var window = _uiFactory.CreateStandaloneProfileUi(msg.Pair);
|
||||||
|
_createdWindows.Add(window);
|
||||||
|
_windowSystem.AddWindow(window);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Mediator.Subscribe<OpenSyncshellAdminPanel>(this, (msg) =>
|
||||||
|
{
|
||||||
|
if (!_createdWindows.Exists(p => p is SyncshellAdminUI ui
|
||||||
|
&& string.Equals(ui.GroupFullInfo.GID, msg.GroupInfo.GID, StringComparison.Ordinal)))
|
||||||
|
{
|
||||||
|
var window = _uiFactory.CreateSyncshellAdminUi(msg.GroupInfo);
|
||||||
|
_createdWindows.Add(window);
|
||||||
|
_windowSystem.AddWindow(window);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Mediator.Subscribe<OpenPermissionWindow>(this, (msg) =>
|
||||||
|
{
|
||||||
|
if (!_createdWindows.Exists(p => p is PermissionWindowUI ui
|
||||||
|
&& msg.Pair == ui.Pair))
|
||||||
|
{
|
||||||
|
var window = _uiFactory.CreatePermissionPopupUi(msg.Pair);
|
||||||
_createdWindows.Add(window);
|
_createdWindows.Add(window);
|
||||||
_windowSystem.AddWindow(window);
|
_windowSystem.AddWindow(window);
|
||||||
}
|
}
|
||||||
@@ -88,8 +113,9 @@ public sealed class UiService : DisposableMediatorSubscriberBase
|
|||||||
window.Dispose();
|
window.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_dalamudPluginInterface.UiBuilder.Draw -= Draw;
|
_uiBuilder.Draw -= Draw;
|
||||||
_dalamudPluginInterface.UiBuilder.OpenConfigUi -= ToggleUi;
|
_uiBuilder.OpenConfigUi -= ToggleUi;
|
||||||
|
_uiBuilder.OpenMainUi -= ToggleMainUi;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Draw()
|
private void Draw()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Dalamud.Interface;
|
|||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Components;
|
using Dalamud.Interface.Components;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using MareSynchronos.API.Data.Extensions;
|
using MareSynchronos.API.Data.Extensions;
|
||||||
@@ -113,9 +114,9 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
$"It is highly recommended to keep Mare Synchronos up to date. Open /xlplugins and update the plugin.", ImGuiColors.DalamudRed);
|
$"It is highly recommended to keep Mare Synchronos up to date. Open /xlplugins and update the plugin.", ImGuiColors.DalamudRed);
|
||||||
}
|
}
|
||||||
|
|
||||||
UiSharedService.DrawWithID("header", DrawUIDHeader);
|
using (ImRaii.PushId("header")) DrawUIDHeader();
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
UiSharedService.DrawWithID("serverstatus", DrawServerStatus);
|
using (ImRaii.PushId("serverstatus")) DrawServerStatus();
|
||||||
|
|
||||||
if (_apiController.ServerState is ServerState.Connected)
|
if (_apiController.ServerState is ServerState.Connected)
|
||||||
{
|
{
|
||||||
@@ -159,17 +160,17 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
if (!hasShownSyncShells)
|
if (!hasShownSyncShells)
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID("pairlist", DrawPairList);
|
using (ImRaii.PushId("pairlist")) DrawPairList();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID("syncshells", _groupPanel.DrawSyncshells);
|
using (ImRaii.PushId("syncshells")) _groupPanel.DrawSyncshells();
|
||||||
}
|
}
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
UiSharedService.DrawWithID("transfers", DrawTransfers);
|
using (ImRaii.PushId("transfers")) DrawTransfers();
|
||||||
TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight;
|
TransferPartHeight = ImGui.GetCursorPosY() - TransferPartHeight;
|
||||||
UiSharedService.DrawWithID("group-user-popup", () => _selectPairsForGroupUi.Draw(_pairManager.DirectPairs));
|
using (ImRaii.PushId("group-user-popup")) _selectPairsForGroupUi.Draw(_pairManager.DirectPairs);
|
||||||
UiSharedService.DrawWithID("grouping-popup", () => _selectGroupForPairUi.Draw());
|
using (ImRaii.PushId("grouping-popup")) _selectGroupForPairUi.Draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null)
|
if (_configService.Current.OpenPopupOnAdd && _pairManager.LastAddedUser != null)
|
||||||
@@ -191,7 +192,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
UiSharedService.TextWrapped($"You have successfully added {_lastAddedUser.UserData.AliasOrUID}. Set a local note for the user in the field below:");
|
UiSharedService.TextWrapped($"You have successfully added {_lastAddedUser.UserData.AliasOrUID}. Set a local note for the user in the field below:");
|
||||||
ImGui.InputTextWithHint("##noteforuser", $"Note for {_lastAddedUser.UserData.AliasOrUID}", ref _lastAddedUserComment, 100);
|
ImGui.InputTextWithHint("##noteforuser", $"Note for {_lastAddedUser.UserData.AliasOrUID}", ref _lastAddedUserComment, 100);
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Note"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Save, "Save Note"))
|
||||||
{
|
{
|
||||||
_serverManager.SetNoteForUid(_lastAddedUser.UserData.UID, _lastAddedUserComment);
|
_serverManager.SetNoteForUid(_lastAddedUser.UserData.UID, _lastAddedUserComment);
|
||||||
_lastAddedUser = null;
|
_lastAddedUser = null;
|
||||||
@@ -226,7 +227,7 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
if (keys.Any())
|
if (keys.Any())
|
||||||
{
|
{
|
||||||
if (_secretKeyIdx == -1) _secretKeyIdx = keys.First().Key;
|
if (_secretKeyIdx == -1) _secretKeyIdx = keys.First().Key;
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add current character with secret key"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Plus, "Add current character with secret key"))
|
||||||
{
|
{
|
||||||
_serverManager.CurrentServer!.Authentications.Add(new MareConfiguration.Models.Authentication()
|
_serverManager.CurrentServer!.Authentications.Add(new MareConfiguration.Models.Authentication()
|
||||||
{
|
{
|
||||||
@@ -344,10 +345,10 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
private void DrawPairList()
|
private void DrawPairList()
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID("addpair", DrawAddPair);
|
using (ImRaii.PushId("addpair")) DrawAddPair();
|
||||||
UiSharedService.DrawWithID("pairs", DrawPairs);
|
using (ImRaii.PushId("pairs")) DrawPairs();
|
||||||
TransferPartHeight = ImGui.GetCursorPosY();
|
TransferPartHeight = ImGui.GetCursorPosY();
|
||||||
UiSharedService.DrawWithID("filter", DrawFilter);
|
using (ImRaii.PushId("filter")) DrawFilter();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawPairs()
|
private void DrawPairs()
|
||||||
@@ -498,9 +499,9 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.TextUnformatted("No downloads in progress");
|
ImGui.TextUnformatted("No downloads in progress");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Mare Character Data Analysis", WindowContentWidth))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Mare Character Data Analysis", WindowContentWidth))
|
||||||
{
|
{
|
||||||
Mediator.Publish(new OpenDataAnalysisUiMessage());
|
Mediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ namespace MareSynchronos.UI.Components;
|
|||||||
public class DrawGroupPair : DrawPairBase
|
public class DrawGroupPair : DrawPairBase
|
||||||
{
|
{
|
||||||
protected readonly MareMediator _mediator;
|
protected readonly MareMediator _mediator;
|
||||||
private static string _banReason = string.Empty;
|
|
||||||
private readonly GroupPairFullInfoDto _fullInfoDto;
|
private readonly GroupPairFullInfoDto _fullInfoDto;
|
||||||
private readonly GroupFullInfoDto _group;
|
private readonly GroupFullInfoDto _group;
|
||||||
|
|
||||||
@@ -248,7 +247,7 @@ public class DrawGroupPair : DrawPairBase
|
|||||||
if ((userIsModerator || userIsOwner) && !(entryIsMod || entryIsOwner))
|
if ((userIsModerator || userIsOwner) && !(entryIsMod || entryIsOwner))
|
||||||
{
|
{
|
||||||
var pinText = entryIsPinned ? "Unpin user" : "Pin user";
|
var pinText = entryIsPinned ? "Unpin user" : "Pin user";
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Thumbtack, pinText))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Thumbtack, pinText))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
var userInfo = _fullInfoDto.GroupPairStatusInfo ^ GroupUserInfo.IsPinned;
|
var userInfo = _fullInfoDto.GroupPairStatusInfo ^ GroupUserInfo.IsPinned;
|
||||||
@@ -256,14 +255,14 @@ public class DrawGroupPair : DrawPairBase
|
|||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Pin this user to the Syncshell. Pinned users will not be deleted in case of a manually initiated Syncshell clean");
|
UiSharedService.AttachToolTip("Pin this user to the Syncshell. Pinned users will not be deleted in case of a manually initiated Syncshell clean");
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Remove user") && UiSharedService.CtrlPressed())
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Remove user") && UiSharedService.CtrlPressed())
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
_ = _apiController.GroupRemoveUser(_fullInfoDto);
|
_ = _apiController.GroupRemoveUser(_fullInfoDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
UiSharedService.AttachToolTip("Hold CTRL and click to remove user " + (_pair.UserData.AliasOrUID) + " from Syncshell");
|
UiSharedService.AttachToolTip("Hold CTRL and click to remove user " + (_pair.UserData.AliasOrUID) + " from Syncshell");
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.UserSlash, "Ban User"))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
_mediator.Publish(new OpenBanUserPopupMessage(_pair, _group));
|
_mediator.Publish(new OpenBanUserPopupMessage(_pair, _group));
|
||||||
@@ -274,7 +273,7 @@ public class DrawGroupPair : DrawPairBase
|
|||||||
if (userIsOwner)
|
if (userIsOwner)
|
||||||
{
|
{
|
||||||
string modText = entryIsMod ? "Demod user" : "Mod user";
|
string modText = entryIsMod ? "Demod user" : "Mod user";
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.UserShield, modText) && UiSharedService.CtrlPressed())
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.UserShield, modText) && UiSharedService.CtrlPressed())
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
var userInfo = _fullInfoDto.GroupPairStatusInfo ^ GroupUserInfo.IsModerator;
|
var userInfo = _fullInfoDto.GroupPairStatusInfo ^ GroupUserInfo.IsModerator;
|
||||||
@@ -282,7 +281,7 @@ public class DrawGroupPair : DrawPairBase
|
|||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Hold CTRL to change the moderator status for " + (_fullInfoDto.UserAliasOrUID) + Environment.NewLine +
|
UiSharedService.AttachToolTip("Hold CTRL to change the moderator status for " + (_fullInfoDto.UserAliasOrUID) + Environment.NewLine +
|
||||||
"Moderators can kick, ban/unban, pin/unpin users and clear the Syncshell.");
|
"Moderators can kick, ban/unban, pin/unpin users and clear the Syncshell.");
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed())
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Crown, "Transfer Ownership") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed())
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
_ = _apiController.GroupChangeOwnership(_fullInfoDto);
|
_ = _apiController.GroupChangeOwnership(_fullInfoDto);
|
||||||
@@ -293,13 +292,13 @@ public class DrawGroupPair : DrawPairBase
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
if (!_pair.IsPaused)
|
if (!_pair.IsPaused)
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.User, "Open Profile"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.User, "Open Profile"))
|
||||||
{
|
{
|
||||||
_displayHandler.OpenProfile(_pair);
|
_displayHandler.OpenProfile(_pair);
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Opens the profile for this user in a new window");
|
UiSharedService.AttachToolTip("Opens the profile for this user in a new window");
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.ExclamationTriangle, "Report Mare Profile"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ExclamationTriangle, "Report Mare Profile"))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
_mediator.Publish(new OpenReportPopupMessage(_pair));
|
_mediator.Publish(new OpenReportPopupMessage(_pair));
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Components;
|
using Dalamud.Interface.Components;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
@@ -66,7 +67,13 @@ public class DrawUserPair : DrawPairBase
|
|||||||
ImGui.PushFont(UiBuilder.IconFont);
|
ImGui.PushFont(UiBuilder.IconFont);
|
||||||
UiSharedService.ColorText(FontAwesomeIcon.Eye.ToIconString(), ImGuiColors.ParsedGreen);
|
UiSharedService.ColorText(FontAwesomeIcon.Eye.ToIconString(), ImGuiColors.ParsedGreen);
|
||||||
ImGui.PopFont();
|
ImGui.PopFont();
|
||||||
UiSharedService.AttachToolTip(_pair.UserData.AliasOrUID + " is visible: " + _pair.PlayerName!);
|
var visibleTooltip = _pair.UserData.AliasOrUID + " is visible: " + _pair.PlayerName!;
|
||||||
|
if (_pair.LastAppliedDataSize >= 0)
|
||||||
|
{
|
||||||
|
visibleTooltip += UiSharedService.TooltipSeparator +
|
||||||
|
"Loaded Mods Size: " + UiSharedService.ByteToString(_pair.LastAppliedDataSize, true);
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip(visibleTooltip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +97,7 @@ public class DrawUserPair : DrawPairBase
|
|||||||
{
|
{
|
||||||
var infoIconPosDist = windowEndX - barButtonSize.X - spacingX - pauseIconSize.X - spacingX;
|
var infoIconPosDist = windowEndX - barButtonSize.X - spacingX - pauseIconSize.X - spacingX;
|
||||||
var icon = FontAwesomeIcon.ExclamationTriangle;
|
var icon = FontAwesomeIcon.ExclamationTriangle;
|
||||||
var iconwidth = UiSharedService.GetIconSize(icon);
|
var iconwidth = UiSharedService.GetIconButtonSize(icon);
|
||||||
|
|
||||||
rightSideStart = infoIconPosDist - iconwidth.X;
|
rightSideStart = infoIconPosDist - iconwidth.X;
|
||||||
ImGui.SameLine(infoIconPosDist - iconwidth.X);
|
ImGui.SameLine(infoIconPosDist - iconwidth.X);
|
||||||
@@ -172,7 +179,7 @@ public class DrawUserPair : DrawPairBase
|
|||||||
}
|
}
|
||||||
if (ImGui.BeginPopup("User Flyout Menu"))
|
if (ImGui.BeginPopup("User Flyout Menu"))
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID($"buttons-{_pair.UserData.UID}", () => DrawPairedClientMenu(_pair));
|
using (ImRaii.PushId($"buttons-{_pair.UserData.UID}")) DrawPairedClientMenu(_pair);
|
||||||
ImGui.EndPopup();
|
ImGui.EndPopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +190,7 @@ public class DrawUserPair : DrawPairBase
|
|||||||
{
|
{
|
||||||
if (!entry.IsPaused)
|
if (!entry.IsPaused)
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.User, "Open Profile"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.User, "Open Profile"))
|
||||||
{
|
{
|
||||||
_displayHandler.OpenProfile(entry);
|
_displayHandler.OpenProfile(entry);
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
@@ -192,7 +199,7 @@ public class DrawUserPair : DrawPairBase
|
|||||||
}
|
}
|
||||||
if (entry.IsVisible)
|
if (entry.IsVisible)
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Sync, "Reload last data"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Sync, "Reload last data"))
|
||||||
{
|
{
|
||||||
entry.ApplyLastReceivedData(forced: true);
|
entry.ApplyLastReceivedData(forced: true);
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
@@ -200,13 +207,13 @@ public class DrawUserPair : DrawPairBase
|
|||||||
UiSharedService.AttachToolTip("This reapplies the last received character data to this character");
|
UiSharedService.AttachToolTip("This reapplies the last received character data to this character");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Cycle pause state"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PlayCircle, "Cycle pause state"))
|
||||||
{
|
{
|
||||||
_ = _apiController.CyclePause(entry.UserData);
|
_ = _apiController.CyclePause(entry.UserData);
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
}
|
}
|
||||||
var entryUID = entry.UserData.AliasOrUID;
|
var entryUID = entry.UserData.AliasOrUID;
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Folder, "Pair Groups"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Folder, "Pair Groups"))
|
||||||
{
|
{
|
||||||
_selectGroupForPairUi.Open(entry);
|
_selectGroupForPairUi.Open(entry);
|
||||||
}
|
}
|
||||||
@@ -215,7 +222,7 @@ public class DrawUserPair : DrawPairBase
|
|||||||
var isDisableSounds = entry.UserPair!.OwnPermissions.IsDisableSounds();
|
var isDisableSounds = entry.UserPair!.OwnPermissions.IsDisableSounds();
|
||||||
string disableSoundsText = isDisableSounds ? "Enable sound sync" : "Disable sound sync";
|
string disableSoundsText = isDisableSounds ? "Enable sound sync" : "Disable sound sync";
|
||||||
var disableSoundsIcon = isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute;
|
var disableSoundsIcon = isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute;
|
||||||
if (UiSharedService.IconTextButton(disableSoundsIcon, disableSoundsText))
|
if (UiSharedService.NormalizedIconTextButton(disableSoundsIcon, disableSoundsText))
|
||||||
{
|
{
|
||||||
var permissions = entry.UserPair.OwnPermissions;
|
var permissions = entry.UserPair.OwnPermissions;
|
||||||
permissions.SetDisableSounds(!isDisableSounds);
|
permissions.SetDisableSounds(!isDisableSounds);
|
||||||
@@ -225,7 +232,7 @@ public class DrawUserPair : DrawPairBase
|
|||||||
var isDisableAnims = entry.UserPair!.OwnPermissions.IsDisableAnimations();
|
var isDisableAnims = entry.UserPair!.OwnPermissions.IsDisableAnimations();
|
||||||
string disableAnimsText = isDisableAnims ? "Enable animation sync" : "Disable animation sync";
|
string disableAnimsText = isDisableAnims ? "Enable animation sync" : "Disable animation sync";
|
||||||
var disableAnimsIcon = isDisableAnims ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop;
|
var disableAnimsIcon = isDisableAnims ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop;
|
||||||
if (UiSharedService.IconTextButton(disableAnimsIcon, disableAnimsText))
|
if (UiSharedService.NormalizedIconTextButton(disableAnimsIcon, disableAnimsText))
|
||||||
{
|
{
|
||||||
var permissions = entry.UserPair.OwnPermissions;
|
var permissions = entry.UserPair.OwnPermissions;
|
||||||
permissions.SetDisableAnimations(!isDisableAnims);
|
permissions.SetDisableAnimations(!isDisableAnims);
|
||||||
@@ -235,14 +242,14 @@ public class DrawUserPair : DrawPairBase
|
|||||||
var isDisableVFX = entry.UserPair!.OwnPermissions.IsDisableVFX();
|
var isDisableVFX = entry.UserPair!.OwnPermissions.IsDisableVFX();
|
||||||
string disableVFXText = isDisableVFX ? "Enable VFX sync" : "Disable VFX sync";
|
string disableVFXText = isDisableVFX ? "Enable VFX sync" : "Disable VFX sync";
|
||||||
var disableVFXIcon = isDisableVFX ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle;
|
var disableVFXIcon = isDisableVFX ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle;
|
||||||
if (UiSharedService.IconTextButton(disableVFXIcon, disableVFXText))
|
if (UiSharedService.NormalizedIconTextButton(disableVFXIcon, disableVFXText))
|
||||||
{
|
{
|
||||||
var permissions = entry.UserPair.OwnPermissions;
|
var permissions = entry.UserPair.OwnPermissions;
|
||||||
permissions.SetDisableVFX(!isDisableVFX);
|
permissions.SetDisableVFX(!isDisableVFX);
|
||||||
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions));
|
_ = _apiController.UserSetPairPermissions(new UserPermissionsDto(entry.UserData, permissions));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Unpair Permanently") && UiSharedService.CtrlPressed())
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Unpair Permanently") && UiSharedService.CtrlPressed())
|
||||||
{
|
{
|
||||||
_ = _apiController.UserRemovePair(new(entry.UserData));
|
_ = _apiController.UserRemovePair(new(entry.UserData));
|
||||||
}
|
}
|
||||||
@@ -251,7 +258,7 @@ public class DrawUserPair : DrawPairBase
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
if (!entry.IsPaused)
|
if (!entry.IsPaused)
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.ExclamationTriangle, "Report Mare Profile"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ExclamationTriangle, "Report Mare Profile"))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
_mediator.Publish(new OpenReportPopupMessage(_pair));
|
_mediator.Publish(new OpenReportPopupMessage(_pair));
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Dalamud.Interface.Components;
|
using Dalamud.Interface.Components;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
@@ -62,8 +63,8 @@ internal sealed class GroupPanel
|
|||||||
|
|
||||||
public void DrawSyncshells()
|
public void DrawSyncshells()
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID("addsyncshell", DrawAddSyncshell);
|
using (ImRaii.PushId("addsyncshell")) DrawAddSyncshell();
|
||||||
UiSharedService.DrawWithID("syncshelllist", DrawSyncshellList);
|
using (ImRaii.PushId("syncshelllist")) DrawSyncshellList();
|
||||||
_mainUi.TransferPartHeight = ImGui.GetCursorPosY();
|
_mainUi.TransferPartHeight = ImGui.GetCursorPosY();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +266,7 @@ internal sealed class GroupPanel
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var buttonSizes = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars).X + UiSharedService.GetIconSize(FontAwesomeIcon.LockOpen).X;
|
var buttonSizes = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars).X + UiSharedService.GetIconButtonSize(FontAwesomeIcon.LockOpen).X;
|
||||||
ImGui.SetNextItemWidth(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 2);
|
ImGui.SetNextItemWidth(UiSharedService.GetWindowContentRegionWidth() - ImGui.GetCursorPosX() - buttonSizes - ImGui.GetStyle().ItemSpacing.X * 2);
|
||||||
if (ImGui.InputTextWithHint("", "Comment/Notes", ref _editGroupComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
|
if (ImGui.InputTextWithHint("", "Comment/Notes", ref _editGroupComment, 255, ImGuiInputTextFlags.EnterReturnsTrue))
|
||||||
{
|
{
|
||||||
@@ -280,7 +281,8 @@ internal sealed class GroupPanel
|
|||||||
UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel");
|
UiSharedService.AttachToolTip("Hit ENTER to save\nRight click to cancel");
|
||||||
}
|
}
|
||||||
|
|
||||||
UiSharedService.DrawWithID(groupDto.GID + "settings", () => DrawSyncShellButtons(groupDto, pairsInGroup));
|
|
||||||
|
using (ImRaii.PushId(groupDto.GID + "settings")) DrawSyncShellButtons(groupDto, pairsInGroup);
|
||||||
|
|
||||||
if (_showModalBanList && !_modalBanListOpened)
|
if (_showModalBanList && !_modalBanListOpened)
|
||||||
{
|
{
|
||||||
@@ -292,7 +294,7 @@ internal sealed class GroupPanel
|
|||||||
|
|
||||||
if (ImGui.BeginPopupModal("Manage Banlist for " + groupDto.GID, ref _showModalBanList, UiSharedService.PopupWindowFlags))
|
if (ImGui.BeginPopupModal("Manage Banlist for " + groupDto.GID, ref _showModalBanList, UiSharedService.PopupWindowFlags))
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server"))
|
||||||
{
|
{
|
||||||
_bannedUsers = ApiController.GroupGetBannedUsers(groupDto).Result;
|
_bannedUsers = ApiController.GroupGetBannedUsers(groupDto).Result;
|
||||||
}
|
}
|
||||||
@@ -321,7 +323,7 @@ internal sealed class GroupPanel
|
|||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
UiSharedService.TextWrapped(bannedUser.Reason);
|
UiSharedService.TextWrapped(bannedUser.Reason);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Check, "Unban#" + bannedUser.UID))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Check, "Unban#" + bannedUser.UID))
|
||||||
{
|
{
|
||||||
_ = ApiController.GroupUnbanUser(bannedUser);
|
_ = ApiController.GroupUnbanUser(bannedUser);
|
||||||
_bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
|
_bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
|
||||||
@@ -382,7 +384,7 @@ internal sealed class GroupPanel
|
|||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(-1);
|
ImGui.SetNextItemWidth(-1);
|
||||||
ImGui.SliderInt("Amount##bulkinvites", ref _bulkInviteCount, 1, 100);
|
ImGui.SliderInt("Amount##bulkinvites", ref _bulkInviteCount, 1, 100);
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.MailBulk, "Create invites"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.MailBulk, "Create invites"))
|
||||||
{
|
{
|
||||||
_bulkOneTimeInvites = ApiController.GroupCreateTempInvite(groupDto, _bulkInviteCount).Result;
|
_bulkOneTimeInvites = ApiController.GroupCreateTempInvite(groupDto, _bulkInviteCount).Result;
|
||||||
}
|
}
|
||||||
@@ -390,7 +392,7 @@ internal sealed class GroupPanel
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
UiSharedService.TextWrapped("A total of " + _bulkOneTimeInvites.Count + " invites have been created.");
|
UiSharedService.TextWrapped("A total of " + _bulkOneTimeInvites.Count + " invites have been created.");
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Copy, "Copy invites to clipboard"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Copy, "Copy invites to clipboard"))
|
||||||
{
|
{
|
||||||
ImGui.SetClipboardText(string.Join(Environment.NewLine, _bulkOneTimeInvites));
|
ImGui.SetClipboardText(string.Join(Environment.NewLine, _bulkOneTimeInvites));
|
||||||
}
|
}
|
||||||
@@ -434,7 +436,7 @@ internal sealed class GroupPanel
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
foreach (var entry in visibleUsers)
|
foreach (var entry in visibleUsers)
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient());
|
using (ImRaii.PushId(groupDto.GID + entry.UID)) entry.DrawPairedClient();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,7 +446,7 @@ internal sealed class GroupPanel
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
foreach (var entry in onlineUsers)
|
foreach (var entry in onlineUsers)
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient());
|
using (ImRaii.PushId(groupDto.GID + entry.UID)) entry.DrawPairedClient();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,7 +456,7 @@ internal sealed class GroupPanel
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
foreach (var entry in offlineUsers)
|
foreach (var entry in offlineUsers)
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID(groupDto.GID + entry.UID, () => entry.DrawPairedClient());
|
using (ImRaii.PushId(groupDto.GID + entry.UID)) entry.DrawPairedClient();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,8 +489,8 @@ internal sealed class GroupPanel
|
|||||||
var userSoundsIcon = userSoundsDisabled ? FontAwesomeIcon.VolumeOff : FontAwesomeIcon.VolumeUp;
|
var userSoundsIcon = userSoundsDisabled ? FontAwesomeIcon.VolumeOff : FontAwesomeIcon.VolumeUp;
|
||||||
var userVFXIcon = userVFXDisabled ? FontAwesomeIcon.Circle : FontAwesomeIcon.Sun;
|
var userVFXIcon = userVFXDisabled ? FontAwesomeIcon.Circle : FontAwesomeIcon.Sun;
|
||||||
|
|
||||||
var iconSize = UiSharedService.GetIconSize(infoIcon);
|
var iconSize = UiSharedService.GetIconButtonSize(infoIcon);
|
||||||
var diffLockUnlockIcons = showInfoIcon ? (UiSharedService.GetIconSize(infoIcon).X - iconSize.X) / 2 : 0;
|
var diffLockUnlockIcons = showInfoIcon ? (UiSharedService.GetIconButtonSize(infoIcon).X - iconSize.X) / 2 : 0;
|
||||||
var barbuttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
|
var barbuttonSize = UiSharedService.GetIconButtonSize(FontAwesomeIcon.Bars);
|
||||||
var isOwner = string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal);
|
var isOwner = string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal);
|
||||||
|
|
||||||
@@ -583,21 +585,21 @@ internal sealed class GroupPanel
|
|||||||
|
|
||||||
if (ImGui.BeginPopup("ShellPopup"))
|
if (ImGui.BeginPopup("ShellPopup"))
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.ArrowCircleLeft, "Leave Syncshell") && UiSharedService.CtrlPressed())
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ArrowCircleLeft, "Leave Syncshell") && UiSharedService.CtrlPressed())
|
||||||
{
|
{
|
||||||
_ = ApiController.GroupLeave(groupDto);
|
_ = ApiController.GroupLeave(groupDto);
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Hold CTRL and click to leave this Syncshell" + (!string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal) ? string.Empty : Environment.NewLine
|
UiSharedService.AttachToolTip("Hold CTRL and click to leave this Syncshell" + (!string.Equals(groupDto.OwnerUID, ApiController.UID, StringComparison.Ordinal) ? string.Empty : Environment.NewLine
|
||||||
+ "WARNING: This action is irreversible" + Environment.NewLine + "Leaving an owned Syncshell will transfer the ownership to a random person in the Syncshell."));
|
+ "WARNING: This action is irreversible" + Environment.NewLine + "Leaving an owned Syncshell will transfer the ownership to a random person in the Syncshell."));
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Copy, "Copy ID"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Copy, "Copy ID"))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
ImGui.SetClipboardText(groupDto.GroupAliasOrGID);
|
ImGui.SetClipboardText(groupDto.GroupAliasOrGID);
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Copy Syncshell ID to Clipboard");
|
UiSharedService.AttachToolTip("Copy Syncshell ID to Clipboard");
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Copy Notes"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StickyNote, "Copy Notes"))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
ImGui.SetClipboardText(UiSharedService.GetNotes(groupPairs));
|
ImGui.SetClipboardText(UiSharedService.GetNotes(groupPairs));
|
||||||
@@ -605,7 +607,7 @@ internal sealed class GroupPanel
|
|||||||
UiSharedService.AttachToolTip("Copies all your notes for all users in this Syncshell to the clipboard." + Environment.NewLine + "They can be imported via Settings -> Privacy -> Import Notes from Clipboard");
|
UiSharedService.AttachToolTip("Copies all your notes for all users in this Syncshell to the clipboard." + Environment.NewLine + "They can be imported via Settings -> Privacy -> Import Notes from Clipboard");
|
||||||
|
|
||||||
var soundsText = userSoundsDisabled ? "Enable sound sync" : "Disable sound sync";
|
var soundsText = userSoundsDisabled ? "Enable sound sync" : "Disable sound sync";
|
||||||
if (UiSharedService.IconTextButton(userSoundsIcon, soundsText))
|
if (UiSharedService.NormalizedIconTextButton(userSoundsIcon, soundsText))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
var perm = groupDto.GroupUserPermissions;
|
var perm = groupDto.GroupUserPermissions;
|
||||||
@@ -618,7 +620,7 @@ internal sealed class GroupPanel
|
|||||||
+ Environment.NewLine + "Note: this setting does not apply to individual pairs that are also in the syncshell.");
|
+ Environment.NewLine + "Note: this setting does not apply to individual pairs that are also in the syncshell.");
|
||||||
|
|
||||||
var animText = userAnimDisabled ? "Enable animations sync" : "Disable animations sync";
|
var animText = userAnimDisabled ? "Enable animations sync" : "Disable animations sync";
|
||||||
if (UiSharedService.IconTextButton(userAnimIcon, animText))
|
if (UiSharedService.NormalizedIconTextButton(userAnimIcon, animText))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
var perm = groupDto.GroupUserPermissions;
|
var perm = groupDto.GroupUserPermissions;
|
||||||
@@ -632,7 +634,7 @@ internal sealed class GroupPanel
|
|||||||
+ Environment.NewLine + "Note: this setting does not apply to individual pairs that are also in the syncshell.");
|
+ Environment.NewLine + "Note: this setting does not apply to individual pairs that are also in the syncshell.");
|
||||||
|
|
||||||
var vfxText = userVFXDisabled ? "Enable VFX sync" : "Disable VFX sync";
|
var vfxText = userVFXDisabled ? "Enable VFX sync" : "Disable VFX sync";
|
||||||
if (UiSharedService.IconTextButton(userVFXIcon, vfxText))
|
if (UiSharedService.NormalizedIconTextButton(userVFXIcon, vfxText))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
var perm = groupDto.GroupUserPermissions;
|
var perm = groupDto.GroupUserPermissions;
|
||||||
@@ -648,10 +650,10 @@ internal sealed class GroupPanel
|
|||||||
if (isOwner || groupDto.GroupUserInfo.IsModerator())
|
if (isOwner || groupDto.GroupUserInfo.IsModerator())
|
||||||
{
|
{
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Cog, "Open Admin Panel"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Cog, "Open Admin Panel"))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
_mainUi.Mediator.Publish(new OpenSyncshellAdminPanelPopupMessage(groupDto));
|
_mainUi.Mediator.Publish(new OpenSyncshellAdminPanel(groupDto));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -667,7 +669,7 @@ internal sealed class GroupPanel
|
|||||||
ImGui.BeginChild("list", new Vector2(_mainUi.WindowContentWidth, ySize), border: false);
|
ImGui.BeginChild("list", new Vector2(_mainUi.WindowContentWidth, ySize), border: false);
|
||||||
foreach (var entry in _pairManager.GroupPairs.OrderBy(g => g.Key.Group.AliasOrGID, StringComparer.OrdinalIgnoreCase).ToList())
|
foreach (var entry in _pairManager.GroupPairs.OrderBy(g => g.Key.Group.AliasOrGID, StringComparer.OrdinalIgnoreCase).ToList())
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID(entry.Key.Group.GID, () => DrawSyncshell(entry.Key, entry.Value));
|
using (ImRaii.PushId(entry.Key.Group.GID)) DrawSyncshell(entry.Key, entry.Value);
|
||||||
}
|
}
|
||||||
ImGui.EndChild();
|
ImGui.EndChild();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Components;
|
using Dalamud.Interface.Components;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using MareSynchronos.API.Data.Extensions;
|
using MareSynchronos.API.Data.Extensions;
|
||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.MareConfiguration;
|
||||||
@@ -81,7 +82,7 @@ public class PairGroupsUi
|
|||||||
|
|
||||||
if (ImGui.BeginPopup("Group Flyout Menu"))
|
if (ImGui.BeginPopup("Group Flyout Menu"))
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID($"buttons-{tag}", () => DrawGroupMenu(tag));
|
using (ImRaii.PushId($"buttons-{tag}")) DrawGroupMenu(tag);
|
||||||
ImGui.EndPopup();
|
ImGui.EndPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +114,7 @@ public class PairGroupsUi
|
|||||||
{
|
{
|
||||||
if (onlineUsers.Any() && onlineUsers.First() is DrawUserPair)
|
if (onlineUsers.Any() && onlineUsers.First() is DrawUserPair)
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID($"group-{tag}-buttons", () => DrawButtons(tag, allUsers.Cast<DrawUserPair>().Where(p => otherUidsTaggedWithTag!.Contains(p.UID)).ToList()));
|
using (ImRaii.PushId($"group-{tag}-buttons")) DrawButtons(tag, allUsers.Cast<DrawUserPair>().Where(p => otherUidsTaggedWithTag!.Contains(p.UID)).ToList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,13 +127,13 @@ public class PairGroupsUi
|
|||||||
|
|
||||||
private void DrawGroupMenu(string tag)
|
private void DrawGroupMenu(string tag)
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Users, "Add people to " + tag))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Users, "Add people to " + tag))
|
||||||
{
|
{
|
||||||
_selectGroupForPairUi.Open(tag);
|
_selectGroupForPairUi.Open(tag);
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip($"Add more users to Group {tag}");
|
UiSharedService.AttachToolTip($"Add more users to Group {tag}");
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete " + tag) && UiSharedService.CtrlPressed())
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Delete " + tag) && UiSharedService.CtrlPressed())
|
||||||
{
|
{
|
||||||
_tagHandler.RemoveTag(tag);
|
_tagHandler.RemoveTag(tag);
|
||||||
}
|
}
|
||||||
@@ -183,7 +184,7 @@ public class PairGroupsUi
|
|||||||
// These are all the OtherUIDs that are tagged with this tag
|
// These are all the OtherUIDs that are tagged with this tag
|
||||||
foreach (var pair in availablePairsInThisCategory)
|
foreach (var pair in availablePairsInThisCategory)
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID($"tag-{tag}-pair-${pair.UID}", () => pair.DrawPairedClient());
|
using (ImRaii.PushId($"tag-{tag}-pair-${pair.UID}")) pair.DrawPairedClient();
|
||||||
}
|
}
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
}
|
}
|
||||||
@@ -192,33 +193,33 @@ public class PairGroupsUi
|
|||||||
{
|
{
|
||||||
if (_mareConfig.Current.ShowVisibleUsersSeparately)
|
if (_mareConfig.Current.ShowVisibleUsersSeparately)
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID("$group-VisibleCustomTag", () => DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers));
|
using (ImRaii.PushId("$group-VisibleCustomTag")) DrawCategory(TagHandler.CustomVisibleTag, visibleUsers, allUsers);
|
||||||
}
|
}
|
||||||
foreach (var tag in tagsWithPairsInThem)
|
foreach (var tag in tagsWithPairsInThem)
|
||||||
{
|
{
|
||||||
if (_mareConfig.Current.ShowOfflineUsersSeparately)
|
if (_mareConfig.Current.ShowOfflineUsersSeparately)
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers, allUsers, visibleUsers));
|
using (ImRaii.PushId($"group-{tag}")) DrawCategory(tag, onlineUsers, allUsers, visibleUsers);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID($"group-{tag}", () => DrawCategory(tag, onlineUsers.Concat(offlineUsers).ToList(), allUsers, visibleUsers));
|
using (ImRaii.PushId($"group-{tag}")) DrawCategory(tag, onlineUsers.Concat(offlineUsers).ToList(), allUsers, visibleUsers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_mareConfig.Current.ShowOfflineUsersSeparately)
|
if (_mareConfig.Current.ShowOfflineUsersSeparately)
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag,
|
using (ImRaii.PushId($"group-OnlineCustomTag")) DrawCategory(TagHandler.CustomOnlineTag,
|
||||||
onlineUsers.Where(u => !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers));
|
onlineUsers.Where(u => !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers);
|
||||||
UiSharedService.DrawWithID($"group-OfflineCustomTag", () => DrawCategory(TagHandler.CustomOfflineTag,
|
using (ImRaii.PushId($"group-OfflineCustomTag")) DrawCategory(TagHandler.CustomOfflineTag,
|
||||||
offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers));
|
offlineUsers.Where(u => u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID($"group-OnlineCustomTag", () => DrawCategory(TagHandler.CustomOnlineTag,
|
using (ImRaii.PushId($"group-OnlineCustomTag")) DrawCategory(TagHandler.CustomOnlineTag,
|
||||||
onlineUsers.Concat(offlineUsers).Where(u => u.UserPair!.OtherPermissions.IsPaired() && !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers));
|
onlineUsers.Concat(offlineUsers).Where(u => u.UserPair!.OtherPermissions.IsPaired() && !_tagHandler.HasAnyTag(u.UID)).ToList(), allUsers);
|
||||||
}
|
}
|
||||||
UiSharedService.DrawWithID($"group-UnpairedCustomTag", () => DrawCategory(TagHandler.CustomUnpairedTag,
|
using (ImRaii.PushId($"group-UnpairedCustomTag")) DrawCategory(TagHandler.CustomUnpairedTag,
|
||||||
offlineUsers.Where(u => !u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers));
|
offlineUsers.Where(u => !u.UserPair!.OtherPermissions.IsPaired()).ToList(), allUsers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PauseRemainingPairs(List<DrawUserPair> availablePairs)
|
private void PauseRemainingPairs(List<DrawUserPair> availablePairs)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class BanUserPopupHandler : IPopupHandler
|
|||||||
UiSharedService.TextWrapped("User " + (_reportedPair.UserData.AliasOrUID) + " will be banned and removed from this Syncshell.");
|
UiSharedService.TextWrapped("User " + (_reportedPair.UserData.AliasOrUID) + " will be banned and removed from this Syncshell.");
|
||||||
ImGui.InputTextWithHint("##banreason", "Ban Reason", ref _banReason, 255);
|
ImGui.InputTextWithHint("##banreason", "Ban Reason", ref _banReason, 255);
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.UserSlash, "Ban User"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.UserSlash, "Ban User"))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
var reason = _banReason;
|
var reason = _banReason;
|
||||||
@@ -37,6 +37,10 @@ public class BanUserPopupHandler : IPopupHandler
|
|||||||
UiSharedService.TextWrapped("The reason will be displayed in the banlist. The current server-side alias if present (Vanity ID) will automatically be attached to the reason.");
|
UiSharedService.TextWrapped("The reason will be displayed in the banlist. The current server-side alias if present (Vanity ID) will automatically be attached to the reason.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnClose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public void Open(OpenBanUserPopupMessage message)
|
public void Open(OpenBanUserPopupMessage message)
|
||||||
{
|
{
|
||||||
_reportedPair = message.PairToBan;
|
_reportedPair = message.PairToBan;
|
||||||
|
|||||||
@@ -7,4 +7,6 @@ public interface IPopupHandler
|
|||||||
Vector2 PopupSize { get; }
|
Vector2 PopupSize { get; }
|
||||||
|
|
||||||
void DrawContent();
|
void DrawContent();
|
||||||
|
|
||||||
|
void OnClose();
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
@@ -43,15 +44,6 @@ public class PopupHandler : WindowMediatorSubscriberBase
|
|||||||
((BanUserPopupHandler)_currentHandler).Open(msg);
|
((BanUserPopupHandler)_currentHandler).Open(msg);
|
||||||
IsOpen = true;
|
IsOpen = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
Mediator.Subscribe<OpenSyncshellAdminPanelPopupMessage>(this, (msg) =>
|
|
||||||
{
|
|
||||||
IsOpen = true;
|
|
||||||
_openPopup = true;
|
|
||||||
_currentHandler = _handlers.OfType<SyncshellAdminPopupHandler>().Single();
|
|
||||||
((SyncshellAdminPopupHandler)_currentHandler).Open(msg.GroupInfo);
|
|
||||||
IsOpen = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
@@ -65,15 +57,22 @@ public class PopupHandler : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
var viewportSize = ImGui.GetWindowViewport().Size;
|
var viewportSize = ImGui.GetWindowViewport().Size;
|
||||||
ImGui.SetNextWindowSize(_currentHandler!.PopupSize);
|
ImGui.SetNextWindowSize(_currentHandler!.PopupSize * ImGuiHelpers.GlobalScale);
|
||||||
ImGui.SetNextWindowPos(viewportSize / 2, ImGuiCond.Always, new Vector2(0.5f));
|
ImGui.SetNextWindowPos(viewportSize / 2, ImGuiCond.Always, new Vector2(0.5f));
|
||||||
using var popup = ImRaii.Popup(WindowName, ImGuiWindowFlags.Modal);
|
using var popup = ImRaii.Popup(WindowName, ImGuiWindowFlags.Modal);
|
||||||
if (!popup) return;
|
if (!popup) return;
|
||||||
_currentHandler.DrawContent();
|
_currentHandler.DrawContent();
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Times, "Close"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Times, "Close"))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
|
_currentHandler.OnClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnClose()
|
||||||
|
{
|
||||||
|
base.OnClose();
|
||||||
|
_currentHandler?.OnClose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ internal class ReportPopupHandler : IPopupHandler
|
|||||||
|
|
||||||
using (ImRaii.Disabled(string.IsNullOrEmpty(_reportReason)))
|
using (ImRaii.Disabled(string.IsNullOrEmpty(_reportReason)))
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.ExclamationTriangle, "Send Report"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ExclamationTriangle, "Send Report"))
|
||||||
{
|
{
|
||||||
ImGui.CloseCurrentPopup();
|
ImGui.CloseCurrentPopup();
|
||||||
var reason = _reportReason;
|
var reason = _reportReason;
|
||||||
@@ -49,6 +49,10 @@ internal class ReportPopupHandler : IPopupHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void OnClose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public void Open(OpenReportPopupMessage msg)
|
public void Open(OpenReportPopupMessage msg)
|
||||||
{
|
{
|
||||||
_reportedPair = msg.PairToReport;
|
_reportedPair = msg.PairToReport;
|
||||||
|
|||||||
@@ -1,232 +0,0 @@
|
|||||||
using Dalamud.Interface;
|
|
||||||
using Dalamud.Interface.Colors;
|
|
||||||
using Dalamud.Interface.Utility.Raii;
|
|
||||||
using ImGuiNET;
|
|
||||||
using MareSynchronos.API.Data.Extensions;
|
|
||||||
using MareSynchronos.API.Dto.Group;
|
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
|
||||||
using MareSynchronos.WebAPI;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Numerics;
|
|
||||||
|
|
||||||
namespace MareSynchronos.UI.Components.Popup;
|
|
||||||
|
|
||||||
internal class SyncshellAdminPopupHandler : IPopupHandler
|
|
||||||
{
|
|
||||||
private readonly ApiController _apiController;
|
|
||||||
private readonly List<string> _oneTimeInvites = [];
|
|
||||||
private readonly PairManager _pairManager;
|
|
||||||
private readonly UiSharedService _uiSharedService;
|
|
||||||
private List<BannedGroupUserDto> _bannedUsers = [];
|
|
||||||
private GroupFullInfoDto _groupFullInfo = null!;
|
|
||||||
private bool _isModerator = false;
|
|
||||||
private bool _isOwner = false;
|
|
||||||
private int _multiInvites = 30;
|
|
||||||
private string _newPassword = string.Empty;
|
|
||||||
private bool _pwChangeSuccess = true;
|
|
||||||
|
|
||||||
public SyncshellAdminPopupHandler(ApiController apiController, UiSharedService uiSharedService, PairManager pairManager)
|
|
||||||
{
|
|
||||||
_apiController = apiController;
|
|
||||||
_uiSharedService = uiSharedService;
|
|
||||||
_pairManager = pairManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2 PopupSize => new(700, 500);
|
|
||||||
|
|
||||||
public void DrawContent()
|
|
||||||
{
|
|
||||||
if (!_isModerator && !_isOwner) return;
|
|
||||||
|
|
||||||
_groupFullInfo = _pairManager.Groups[_groupFullInfo.Group];
|
|
||||||
|
|
||||||
using (ImRaii.PushFont(_uiSharedService.UidFont))
|
|
||||||
ImGui.TextUnformatted(_groupFullInfo.GroupAliasOrGID + " Administrative Panel");
|
|
||||||
|
|
||||||
ImGui.Separator();
|
|
||||||
var perm = _groupFullInfo.GroupPermissions;
|
|
||||||
|
|
||||||
var inviteNode = ImRaii.TreeNode("Invites");
|
|
||||||
if (inviteNode)
|
|
||||||
{
|
|
||||||
bool isInvitesDisabled = perm.IsDisableInvites();
|
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(isInvitesDisabled ? FontAwesomeIcon.Unlock : FontAwesomeIcon.Lock,
|
|
||||||
isInvitesDisabled ? "Unlock Syncshell" : "Lock Syncshell"))
|
|
||||||
{
|
|
||||||
perm.SetDisableInvites(!isInvitesDisabled);
|
|
||||||
_ = _apiController.GroupChangeGroupPermissionState(new(_groupFullInfo.Group, perm));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.Dummy(new(2f));
|
|
||||||
|
|
||||||
UiSharedService.TextWrapped("One-time invites work as single-use passwords. Use those if you do not want to distribute your Syncshell password.");
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Single one-time invite"))
|
|
||||||
{
|
|
||||||
ImGui.SetClipboardText(_apiController.GroupCreateTempInvite(new(_groupFullInfo.Group), 1).Result.FirstOrDefault() ?? string.Empty);
|
|
||||||
}
|
|
||||||
UiSharedService.AttachToolTip("Creates a single-use password for joining the syncshell which is valid for 24h and copies it to the clipboard.");
|
|
||||||
ImGui.InputInt("##amountofinvites", ref _multiInvites);
|
|
||||||
ImGui.SameLine();
|
|
||||||
using (ImRaii.Disabled(_multiInvites <= 1 || _multiInvites > 100))
|
|
||||||
{
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Envelope, "Generate " + _multiInvites + " one-time invites"))
|
|
||||||
{
|
|
||||||
_oneTimeInvites.AddRange(_apiController.GroupCreateTempInvite(new(_groupFullInfo.Group), _multiInvites).Result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_oneTimeInvites.Any())
|
|
||||||
{
|
|
||||||
var invites = string.Join(Environment.NewLine, _oneTimeInvites);
|
|
||||||
ImGui.InputTextMultiline("Generated Multi Invites", ref invites, 5000, new(0, 0), ImGuiInputTextFlags.ReadOnly);
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Copy, "Copy Invites to clipboard"))
|
|
||||||
{
|
|
||||||
ImGui.SetClipboardText(invites);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inviteNode.Dispose();
|
|
||||||
|
|
||||||
var mgmtNode = ImRaii.TreeNode("User Management");
|
|
||||||
if (mgmtNode)
|
|
||||||
{
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Broom, "Clear Syncshell"))
|
|
||||||
{
|
|
||||||
_ = _apiController.GroupClear(new(_groupFullInfo.Group));
|
|
||||||
}
|
|
||||||
UiSharedService.AttachToolTip("This will remove all non-pinned, non-moderator users from the Syncshell");
|
|
||||||
|
|
||||||
ImGui.Dummy(new(2f));
|
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server"))
|
|
||||||
{
|
|
||||||
_bannedUsers = _apiController.GroupGetBannedUsers(new GroupDto(_groupFullInfo.Group)).Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui.BeginTable("bannedusertable" + _groupFullInfo.GID, 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY))
|
|
||||||
{
|
|
||||||
ImGui.TableSetupColumn("UID", ImGuiTableColumnFlags.None, 1);
|
|
||||||
ImGui.TableSetupColumn("Alias", ImGuiTableColumnFlags.None, 1);
|
|
||||||
ImGui.TableSetupColumn("By", ImGuiTableColumnFlags.None, 1);
|
|
||||||
ImGui.TableSetupColumn("Date", ImGuiTableColumnFlags.None, 2);
|
|
||||||
ImGui.TableSetupColumn("Reason", ImGuiTableColumnFlags.None, 3);
|
|
||||||
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 1);
|
|
||||||
|
|
||||||
ImGui.TableHeadersRow();
|
|
||||||
|
|
||||||
foreach (var bannedUser in _bannedUsers.ToList())
|
|
||||||
{
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextUnformatted(bannedUser.UID);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextUnformatted(bannedUser.UserAlias ?? string.Empty);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextUnformatted(bannedUser.BannedBy);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
ImGui.TextUnformatted(bannedUser.BannedOn.ToLocalTime().ToString(CultureInfo.CurrentCulture));
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
UiSharedService.TextWrapped(bannedUser.Reason);
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Check, "Unban#" + bannedUser.UID))
|
|
||||||
{
|
|
||||||
_ = _apiController.GroupUnbanUser(bannedUser);
|
|
||||||
_bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.EndTable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mgmtNode.Dispose();
|
|
||||||
|
|
||||||
var permNode = ImRaii.TreeNode("Permissions");
|
|
||||||
if (permNode)
|
|
||||||
{
|
|
||||||
bool isDisableAnimations = perm.IsDisableAnimations();
|
|
||||||
bool isDisableSounds = perm.IsDisableSounds();
|
|
||||||
bool isDisableVfx = perm.IsDisableVFX();
|
|
||||||
|
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
ImGui.Text("Sound Sync");
|
|
||||||
UiSharedService.BooleanToColoredIcon(!isDisableSounds);
|
|
||||||
ImGui.SameLine(230);
|
|
||||||
if (UiSharedService.IconTextButton(isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute,
|
|
||||||
isDisableSounds ? "Enable sound sync" : "Disable sound sync"))
|
|
||||||
{
|
|
||||||
perm.SetDisableSounds(!perm.IsDisableSounds());
|
|
||||||
_ = _apiController.GroupChangeGroupPermissionState(new(_groupFullInfo.Group, perm));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
ImGui.Text("Animation Sync");
|
|
||||||
UiSharedService.BooleanToColoredIcon(!isDisableAnimations);
|
|
||||||
ImGui.SameLine(230);
|
|
||||||
if (UiSharedService.IconTextButton(isDisableAnimations ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop,
|
|
||||||
isDisableAnimations ? "Enable animation sync" : "Disable animation sync"))
|
|
||||||
{
|
|
||||||
perm.SetDisableAnimations(!perm.IsDisableAnimations());
|
|
||||||
_ = _apiController.GroupChangeGroupPermissionState(new(_groupFullInfo.Group, perm));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
ImGui.Text("VFX Sync");
|
|
||||||
UiSharedService.BooleanToColoredIcon(!isDisableVfx);
|
|
||||||
ImGui.SameLine(230);
|
|
||||||
if (UiSharedService.IconTextButton(isDisableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle,
|
|
||||||
isDisableVfx ? "Enable VFX sync" : "Disable VFX sync"))
|
|
||||||
{
|
|
||||||
perm.SetDisableVFX(!perm.IsDisableVFX());
|
|
||||||
_ = _apiController.GroupChangeGroupPermissionState(new(_groupFullInfo.Group, perm));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
permNode.Dispose();
|
|
||||||
|
|
||||||
if (_isOwner)
|
|
||||||
{
|
|
||||||
var ownerNode = ImRaii.TreeNode("Owner Settings");
|
|
||||||
if (ownerNode)
|
|
||||||
{
|
|
||||||
ImGui.AlignTextToFramePadding();
|
|
||||||
ImGui.TextUnformatted("New Password");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.InputTextWithHint("##changepw", "Min 10 characters", ref _newPassword, 50);
|
|
||||||
ImGui.SameLine();
|
|
||||||
using (ImRaii.Disabled(_newPassword.Length < 10))
|
|
||||||
{
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Passport, "Change Password"))
|
|
||||||
{
|
|
||||||
_pwChangeSuccess = _apiController.GroupChangePassword(new GroupPasswordDto(_groupFullInfo.Group, _newPassword)).Result;
|
|
||||||
_newPassword = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UiSharedService.AttachToolTip("Password requires to be at least 10 characters long. This action is irreversible.");
|
|
||||||
|
|
||||||
if (!_pwChangeSuccess)
|
|
||||||
{
|
|
||||||
UiSharedService.ColorTextWrapped("Failed to change the password. Password requires to be at least 10 characters long.", ImGuiColors.DalamudYellow);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Syncshell") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed())
|
|
||||||
{
|
|
||||||
ImGui.CloseCurrentPopup();
|
|
||||||
_ = _apiController.GroupDelete(new(_groupFullInfo.Group));
|
|
||||||
}
|
|
||||||
UiSharedService.AttachToolTip("Hold CTRL and Shift and click to delete this Syncshell." + Environment.NewLine + "WARNING: this action is irreversible.");
|
|
||||||
}
|
|
||||||
ownerNode.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Open(GroupFullInfoDto groupFullInfo)
|
|
||||||
{
|
|
||||||
_groupFullInfo = groupFullInfo;
|
|
||||||
_isOwner = string.Equals(_groupFullInfo.OwnerUID, _apiController.UID, StringComparison.Ordinal);
|
|
||||||
_isModerator = _groupFullInfo.GroupUserInfo.IsModerator();
|
|
||||||
_newPassword = string.Empty;
|
|
||||||
_bannedUsers.Clear();
|
|
||||||
_oneTimeInvites.Clear();
|
|
||||||
_multiInvites = 30;
|
|
||||||
_pwChangeSuccess = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Components;
|
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
@@ -65,14 +65,14 @@ public class SelectGroupForPairUi
|
|||||||
{
|
{
|
||||||
foreach (var tag in tags)
|
foreach (var tag in tags)
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID($"groups-pair-{_pair.UserData.UID}-{tag}", () => DrawGroupName(_pair, tag));
|
using (ImRaii.PushId($"groups-pair-{_pair.UserData.UID}-{tag}")) DrawGroupName(_pair, tag);
|
||||||
}
|
}
|
||||||
ImGui.EndChild();
|
ImGui.EndChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
UiSharedService.FontText($"Create a new group for {name}.", UiBuilder.DefaultFont);
|
UiSharedService.FontText($"Create a new group for {name}.", UiBuilder.DefaultFont);
|
||||||
if (ImGuiComponents.IconButton(FontAwesomeIcon.Plus))
|
if (UiSharedService.NormalizedIconButton(FontAwesomeIcon.Plus))
|
||||||
{
|
{
|
||||||
HandleAddTag();
|
HandleAddTag();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
_hasUpdate = true;
|
_hasUpdate = true;
|
||||||
});
|
});
|
||||||
Mediator.Subscribe<OpenDataAnalysisUiMessage>(this, (_) => Toggle());
|
|
||||||
SizeConstraints = new()
|
SizeConstraints = new()
|
||||||
{
|
{
|
||||||
MinimumSize = new()
|
MinimumSize = new()
|
||||||
@@ -66,7 +65,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
ImGui.TextUnformatted("BC7 Conversion in progress: " + _conversionCurrentFileProgress + "/" + _texturesToConvert.Count);
|
ImGui.TextUnformatted("BC7 Conversion in progress: " + _conversionCurrentFileProgress + "/" + _texturesToConvert.Count);
|
||||||
UiSharedService.TextWrapped("Current file: " + _conversionCurrentFileName);
|
UiSharedService.TextWrapped("Current file: " + _conversionCurrentFileName);
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Cancel conversion"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StopCircle, "Cancel conversion"))
|
||||||
{
|
{
|
||||||
_conversionCancellationTokenSource.Cancel();
|
_conversionCancellationTokenSource.Cancel();
|
||||||
}
|
}
|
||||||
@@ -108,7 +107,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
UiSharedService.ColorTextWrapped($"Analyzing {_characterAnalyzer.CurrentFile}/{_characterAnalyzer.TotalFiles}",
|
UiSharedService.ColorTextWrapped($"Analyzing {_characterAnalyzer.CurrentFile}/{_characterAnalyzer.TotalFiles}",
|
||||||
ImGuiColors.DalamudYellow);
|
ImGuiColors.DalamudYellow);
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.StopCircle, "Cancel analysis"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StopCircle, "Cancel analysis"))
|
||||||
{
|
{
|
||||||
_characterAnalyzer.CancelAnalyze();
|
_characterAnalyzer.CancelAnalyze();
|
||||||
}
|
}
|
||||||
@@ -119,14 +118,14 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
UiSharedService.ColorTextWrapped("Some entries in the analysis have file size not determined yet, press the button below to analyze your current data",
|
UiSharedService.ColorTextWrapped("Some entries in the analysis have file size not determined yet, press the button below to analyze your current data",
|
||||||
ImGuiColors.DalamudYellow);
|
ImGuiColors.DalamudYellow);
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Start analysis (missing entries)"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PlayCircle, "Start analysis (missing entries)"))
|
||||||
{
|
{
|
||||||
_ = _characterAnalyzer.ComputeAnalysis(print: false);
|
_ = _characterAnalyzer.ComputeAnalysis(print: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Start analysis (recalculate all entries)"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PlayCircle, "Start analysis (recalculate all entries)"))
|
||||||
{
|
{
|
||||||
_ = _characterAnalyzer.ComputeAnalysis(print: false, recalculate: true);
|
_ = _characterAnalyzer.ComputeAnalysis(print: false, recalculate: true);
|
||||||
}
|
}
|
||||||
@@ -260,7 +259,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
Environment.NewLine + "- Conversion will convert all found texture duplicates (entries with more than 1 file path) automatically." +
|
Environment.NewLine + "- Conversion will convert all found texture duplicates (entries with more than 1 file path) automatically." +
|
||||||
Environment.NewLine + "- Converting textures to BC7 is a very expensive operation and, depending on the amount of textures to convert, will take a while to complete."
|
Environment.NewLine + "- Converting textures to BC7 is a very expensive operation and, depending on the amount of textures to convert, will take a while to complete."
|
||||||
, ImGuiColors.DalamudYellow);
|
, ImGuiColors.DalamudYellow);
|
||||||
if (_texturesToConvert.Count > 0 && UiSharedService.IconTextButton(FontAwesomeIcon.PlayCircle, "Start conversion of " + _texturesToConvert.Count + " texture(s)"))
|
if (_texturesToConvert.Count > 0 && UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PlayCircle, "Start conversion of " + _texturesToConvert.Count + " texture(s)"))
|
||||||
{
|
{
|
||||||
_conversionCancellationTokenSource = _conversionCancellationTokenSource.CancelRecreate();
|
_conversionCancellationTokenSource = _conversionCancellationTokenSource.CancelRecreate();
|
||||||
_conversionTask = _ipcManager.PenumbraConvertTextureFiles(_logger, _texturesToConvert, _conversionProgress, _conversionCancellationTokenSource.Token);
|
_conversionTask = _ipcManager.PenumbraConvertTextureFiles(_logger, _texturesToConvert, _conversionProgress, _conversionCancellationTokenSource.Token);
|
||||||
|
|||||||
@@ -122,9 +122,20 @@ public sealed class DtrEntry : IDisposable, IHostedService
|
|||||||
text = $"\uE044 {pairCount}";
|
text = $"\uE044 {pairCount}";
|
||||||
if (pairCount > 0)
|
if (pairCount > 0)
|
||||||
{
|
{
|
||||||
var visiblePairs = _pairManager.GetOnlineUserPairs()
|
IEnumerable<string> visiblePairs;
|
||||||
.Where(x => x.IsVisible)
|
if (_configService.Current.ShowUidInDtrTooltip)
|
||||||
.Select(x => string.Format("{0} ({1})", x.PlayerName, x.UserData.AliasOrUID));
|
{
|
||||||
|
visiblePairs = _pairManager.GetOnlineUserPairs()
|
||||||
|
.Where(x => x.IsVisible)
|
||||||
|
.Select(x => string.Format("{0} ({1})", _configService.Current.PreferNoteInDtrTooltip ? x.GetNote() ?? x.PlayerName : x.PlayerName, x.UserData.AliasOrUID ));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
visiblePairs = _pairManager.GetOnlineUserPairs()
|
||||||
|
.Where(x => x.IsVisible)
|
||||||
|
.Select(x => string.Format("{0}", _configService.Current.PreferNoteInDtrTooltip ? x.GetNote() ?? x.PlayerName : x.PlayerName));
|
||||||
|
}
|
||||||
|
|
||||||
tooltip = $"Mare Synchronos: Connected{Environment.NewLine}----------{Environment.NewLine}{string.Join(Environment.NewLine, visiblePairs)}";
|
tooltip = $"Mare Synchronos: Connected{Environment.NewLine}----------{Environment.NewLine}{string.Join(Environment.NewLine, visiblePairs)}";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ public class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
_uiSharedService.BigText("Profile Settings");
|
_uiSharedService.BigText("Profile Settings");
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.FileUpload, "Upload new profile picture"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.FileUpload, "Upload new profile picture"))
|
||||||
{
|
{
|
||||||
_fileDialogManager.OpenFileDialog("Select new Profile picture", ".png", (success, file) =>
|
_fileDialogManager.OpenFileDialog("Select new Profile picture", ".png", (success, file) =>
|
||||||
{
|
{
|
||||||
@@ -162,7 +162,7 @@ public class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Select and upload a new profile picture");
|
UiSharedService.AttachToolTip("Select and upload a new profile picture");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear uploaded profile picture"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Clear uploaded profile picture"))
|
||||||
{
|
{
|
||||||
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, "", Description: null));
|
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, "", Description: null));
|
||||||
}
|
}
|
||||||
@@ -211,13 +211,13 @@ public class EditProfileUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.EndChildFrame();
|
ImGui.EndChildFrame();
|
||||||
ImGui.PopFont();
|
ImGui.PopFont();
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Save, "Save Description"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Save, "Save Description"))
|
||||||
{
|
{
|
||||||
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, _descriptionText));
|
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, _descriptionText));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("Sets your profile description text");
|
UiSharedService.AttachToolTip("Sets your profile description text");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear Description"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Clear Description"))
|
||||||
{
|
{
|
||||||
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, ""));
|
_ = _apiController.UserSetProfile(new UserProfileDto(new UserData(_apiController.UID), Disabled: false, IsNSFW: null, ProfilePictureBase64: null, ""));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class GposeUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
if (!_mareCharaFileManager.CurrentlyWorking)
|
if (!_mareCharaFileManager.CurrentlyWorking)
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.FolderOpen, "Load MCDF"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.FolderOpen, "Load MCDF"))
|
||||||
{
|
{
|
||||||
_fileDialogManager.OpenFileDialog("Pick MCDF file", ".mcdf", (success, paths) =>
|
_fileDialogManager.OpenFileDialog("Pick MCDF file", ".mcdf", (success, paths) =>
|
||||||
{
|
{
|
||||||
@@ -59,7 +59,7 @@ public class GposeUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
UiSharedService.TextWrapped("Loaded file: " + _mareCharaFileManager.LoadedCharaFile.FilePath);
|
UiSharedService.TextWrapped("Loaded file: " + _mareCharaFileManager.LoadedCharaFile.FilePath);
|
||||||
UiSharedService.TextWrapped("File Description: " + _mareCharaFileManager.LoadedCharaFile.CharaFileData.Description);
|
UiSharedService.TextWrapped("File Description: " + _mareCharaFileManager.LoadedCharaFile.CharaFileData.Description);
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Check, "Apply loaded MCDF"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Check, "Apply loaded MCDF"))
|
||||||
{
|
{
|
||||||
_ = Task.Run(async () => await _mareCharaFileManager.ApplyMareCharaFile(_dalamudUtil.GposeTargetGameObject).ConfigureAwait(false));
|
_ = Task.Run(async () => await _mareCharaFileManager.ApplyMareCharaFile(_dalamudUtil.GposeTargetGameObject).ConfigureAwait(false));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace MareSynchronos.UI.Handlers;
|
|||||||
public class TagHandler
|
public class TagHandler
|
||||||
{
|
{
|
||||||
public const string CustomOfflineTag = "Mare_Offline";
|
public const string CustomOfflineTag = "Mare_Offline";
|
||||||
|
public const string CustomOfflineSyncshellTag = "Mare_OfflineSyncshell";
|
||||||
public const string CustomOnlineTag = "Mare_Online";
|
public const string CustomOnlineTag = "Mare_Online";
|
||||||
public const string CustomUnpairedTag = "Mare_Unpaired";
|
public const string CustomUnpairedTag = "Mare_Unpaired";
|
||||||
public const string CustomVisibleTag = "Mare_Visible";
|
public const string CustomVisibleTag = "Mare_Visible";
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
using MareSynchronos.Services.ServerConfiguration;
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
@@ -38,9 +39,9 @@ public class UidDisplayHandler
|
|||||||
if (!string.Equals(_editNickEntry, pair.UserData.UID, StringComparison.Ordinal))
|
if (!string.Equals(_editNickEntry, pair.UserData.UID, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
ImGui.SetCursorPosY(originalY);
|
ImGui.SetCursorPosY(originalY);
|
||||||
if (textIsUid) ImGui.PushFont(UiBuilder.MonoFont);
|
|
||||||
ImGui.TextUnformatted(playerText);
|
using (ImRaii.PushFont(UiBuilder.MonoFont, textIsUid)) ImGui.TextUnformatted(playerText);
|
||||||
if (textIsUid) ImGui.PopFont();
|
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
{
|
{
|
||||||
if (!string.Equals(_lastMouseOverUid, id))
|
if (!string.Equals(_lastMouseOverUid, id))
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public class IntroUi : WindowMediatorSubscriberBase
|
|||||||
_serverConfigurationManager = serverConfigurationManager;
|
_serverConfigurationManager = serverConfigurationManager;
|
||||||
|
|
||||||
IsOpen = false;
|
IsOpen = false;
|
||||||
|
ShowCloseButton = false;
|
||||||
|
RespectCloseHotkey = false;
|
||||||
|
|
||||||
SizeConstraints = new WindowSizeConstraints()
|
SizeConstraints = new WindowSizeConstraints()
|
||||||
{
|
{
|
||||||
@@ -60,9 +62,7 @@ public class IntroUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
if (!_configService.Current.AcceptedAgreement && !_readFirstPage)
|
if (!_configService.Current.AcceptedAgreement && !_readFirstPage)
|
||||||
{
|
{
|
||||||
if (_uiShared.UidFontBuilt) ImGui.PushFont(_uiShared.UidFont);
|
_uiShared.BigText("Welcome to Mare Synchronos");
|
||||||
ImGui.TextUnformatted("Welcome to Mare Synchronos");
|
|
||||||
if (_uiShared.UidFontBuilt) ImGui.PopFont();
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
UiSharedService.TextWrapped("Mare Synchronos is a plugin that will replicate your full current character state including all Penumbra mods to other paired Mare Synchronos users. " +
|
UiSharedService.TextWrapped("Mare Synchronos is a plugin that will replicate your full current character state including all Penumbra mods to other paired Mare Synchronos users. " +
|
||||||
"Note that you will have to have Penumbra as well as Glamourer installed to use this plugin.");
|
"Note that you will have to have Penumbra as well as Glamourer installed to use this plugin.");
|
||||||
|
|||||||
162
MareSynchronos/UI/PermissionWindowUI.cs
Normal file
162
MareSynchronos/UI/PermissionWindowUI.cs
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using ImGuiNET;
|
||||||
|
using MareSynchronos.API.Data.Enum;
|
||||||
|
using MareSynchronos.API.Data.Extensions;
|
||||||
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using MareSynchronos.Utils;
|
||||||
|
using MareSynchronos.WebAPI;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos.UI;
|
||||||
|
|
||||||
|
public class PermissionWindowUI : WindowMediatorSubscriberBase
|
||||||
|
{
|
||||||
|
public Pair Pair { get; init; }
|
||||||
|
|
||||||
|
private readonly UiSharedService _uiSharedService;
|
||||||
|
private readonly ApiController _apiController;
|
||||||
|
private UserPermissions _ownPermissions;
|
||||||
|
|
||||||
|
public PermissionWindowUI(ILogger<PermissionWindowUI> logger, Pair pair, MareMediator mediator, UiSharedService uiSharedService,
|
||||||
|
ApiController apiController) : base(logger, mediator, "Permissions for " + pair.UserData.AliasOrUID + "###MareSynchronosPermissions" + pair.UserData.UID)
|
||||||
|
{
|
||||||
|
Pair = pair;
|
||||||
|
_uiSharedService = uiSharedService;
|
||||||
|
_apiController = apiController;
|
||||||
|
_ownPermissions = pair.UserPair.OwnPermissions.DeepClone();
|
||||||
|
Flags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoResize;
|
||||||
|
SizeConstraints = new()
|
||||||
|
{
|
||||||
|
MinimumSize = new(450, 100),
|
||||||
|
MaximumSize = new(450, 500)
|
||||||
|
};
|
||||||
|
IsOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
var paused = _ownPermissions.IsPaused();
|
||||||
|
var disableSounds = _ownPermissions.IsDisableSounds();
|
||||||
|
var disableAnimations = _ownPermissions.IsDisableAnimations();
|
||||||
|
var disableVfx = _ownPermissions.IsDisableVFX();
|
||||||
|
var style = ImGui.GetStyle();
|
||||||
|
var indentSize = ImGui.GetFrameHeight() + style.ItemSpacing.X;
|
||||||
|
|
||||||
|
_uiSharedService.BigText("Permissions for " + Pair.UserData.AliasOrUID);
|
||||||
|
ImGuiHelpers.ScaledDummy(1f);
|
||||||
|
|
||||||
|
if (ImGui.Checkbox("Pause Sync", ref paused))
|
||||||
|
{
|
||||||
|
_ownPermissions.SetPaused(paused);
|
||||||
|
}
|
||||||
|
UiSharedService.DrawHelpText("Pausing will completely cease any sync with this user." + UiSharedService.TooltipSeparator
|
||||||
|
+ "Note: this is bidirectional, either user pausing will cease sync completely.");
|
||||||
|
var otherPerms = Pair.UserPair.OtherPermissions;
|
||||||
|
|
||||||
|
var otherIsPaused = otherPerms.IsPaused();
|
||||||
|
var otherDisableSounds = otherPerms.IsDisableSounds();
|
||||||
|
var otherDisableAnimations = otherPerms.IsDisableAnimations();
|
||||||
|
var otherDisableVFX = otherPerms.IsDisableVFX();
|
||||||
|
|
||||||
|
using (ImRaii.PushIndent(indentSize, false))
|
||||||
|
{
|
||||||
|
UiSharedService.BooleanToColoredIcon(!otherIsPaused, false);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.Text(Pair.UserData.AliasOrUID + " has " + (!otherIsPaused ? "not " : string.Empty) + "paused you");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(0.5f);
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGuiHelpers.ScaledDummy(0.5f);
|
||||||
|
|
||||||
|
if (ImGui.Checkbox("Disable Sounds", ref disableSounds))
|
||||||
|
{
|
||||||
|
_ownPermissions.SetDisableSounds(disableSounds);
|
||||||
|
}
|
||||||
|
UiSharedService.DrawHelpText("Disabling sounds will remove all sounds synced with this user on both sides." + UiSharedService.TooltipSeparator
|
||||||
|
+ "Note: this is bidirectional, either user disabling sound sync will stop sound sync on both sides.");
|
||||||
|
using (ImRaii.PushIndent(indentSize, false))
|
||||||
|
{
|
||||||
|
UiSharedService.BooleanToColoredIcon(!otherDisableSounds, false);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.Text(Pair.UserData.AliasOrUID + " has " + (!otherDisableSounds ? "not " : string.Empty) + "disabled sound sync with you");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.Checkbox("Disable Animations", ref disableAnimations))
|
||||||
|
{
|
||||||
|
_ownPermissions.SetDisableAnimations(disableAnimations);
|
||||||
|
}
|
||||||
|
UiSharedService.DrawHelpText("Disabling sounds will remove all animations synced with this user on both sides." + UiSharedService.TooltipSeparator
|
||||||
|
+ "Note: this is bidirectional, either user disabling animation sync will stop animation sync on both sides.");
|
||||||
|
using (ImRaii.PushIndent(indentSize, false))
|
||||||
|
{
|
||||||
|
UiSharedService.BooleanToColoredIcon(!otherDisableAnimations, false);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.Text(Pair.UserData.AliasOrUID + " has " + (!otherDisableAnimations ? "not " : string.Empty) + "disabled animation sync with you");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.Checkbox("Disable VFX", ref disableVfx))
|
||||||
|
{
|
||||||
|
_ownPermissions.SetDisableVFX(disableVfx);
|
||||||
|
}
|
||||||
|
UiSharedService.DrawHelpText("Disabling sounds will remove all VFX synced with this user on both sides." + UiSharedService.TooltipSeparator
|
||||||
|
+ "Note: this is bidirectional, either user disabling VFX sync will stop VFX sync on both sides.");
|
||||||
|
using (ImRaii.PushIndent(indentSize, false))
|
||||||
|
{
|
||||||
|
UiSharedService.BooleanToColoredIcon(!otherDisableVFX, false);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.Text(Pair.UserData.AliasOrUID + " has " + (!otherDisableVFX ? "not " : string.Empty) + "disabled VFX sync with you");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(0.5f);
|
||||||
|
ImGui.Separator();
|
||||||
|
ImGuiHelpers.ScaledDummy(0.5f);
|
||||||
|
|
||||||
|
bool hasChanges = _ownPermissions != Pair.UserPair.OwnPermissions;
|
||||||
|
|
||||||
|
using (ImRaii.Disabled(!hasChanges))
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.Save, "Save"))
|
||||||
|
{
|
||||||
|
_ = _apiController.UserSetPairPermissions(new(Pair.UserData, _ownPermissions));
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Save and apply all changes");
|
||||||
|
|
||||||
|
var rightSideButtons = UiSharedService.GetNormalizedIconTextButtonSize(Dalamud.Interface.FontAwesomeIcon.Undo, "Revert").X +
|
||||||
|
UiSharedService.GetNormalizedIconTextButtonSize(Dalamud.Interface.FontAwesomeIcon.ArrowsSpin, "Reset to Default").X;
|
||||||
|
var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
|
||||||
|
|
||||||
|
ImGui.SameLine(availableWidth - rightSideButtons);
|
||||||
|
|
||||||
|
using (ImRaii.Disabled(!hasChanges))
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.Undo, "Revert"))
|
||||||
|
{
|
||||||
|
_ownPermissions = Pair.UserPair.OwnPermissions.DeepClone();
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Revert all changes");
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(Dalamud.Interface.FontAwesomeIcon.ArrowsSpin, "Reset to Default"))
|
||||||
|
{
|
||||||
|
_ownPermissions.SetPaused(false);
|
||||||
|
_ownPermissions.SetDisableVFX(false);
|
||||||
|
_ownPermissions.SetDisableSounds(false);
|
||||||
|
_ownPermissions.SetDisableAnimations(false);
|
||||||
|
_ = _apiController.UserSetPairPermissions(new(Pair.UserData, _ownPermissions));
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("This will set all permissions to their default setting");
|
||||||
|
|
||||||
|
var ySize = ImGui.GetCursorPosY() + style.FramePadding.Y * ImGuiHelpers.GlobalScale + style.FrameBorderSize;
|
||||||
|
ImGui.SetWindowSize(new(400, ySize));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnClose()
|
||||||
|
{
|
||||||
|
Mediator.Publish(new RemoveWindowMessage(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using Dalamud.Interface.Internal;
|
using Dalamud.Interface.Internal;
|
||||||
|
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using MareSynchronos.API.Data.Extensions;
|
using MareSynchronos.API.Data.Extensions;
|
||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.MareConfiguration;
|
||||||
@@ -110,14 +111,14 @@ public class PopoutProfileUi : WindowMediatorSubscriberBase
|
|||||||
var rectMin = drawList.GetClipRectMin();
|
var rectMin = drawList.GetClipRectMin();
|
||||||
var rectMax = drawList.GetClipRectMax();
|
var rectMax = drawList.GetClipRectMax();
|
||||||
|
|
||||||
if (_uiSharedService.UidFontBuilt) ImGui.PushFont(_uiSharedService.UidFont);
|
using (ImRaii.PushFont(_uiSharedService.UidFont, _uiSharedService.UidFontBuilt))
|
||||||
UiSharedService.ColorText(_pair.UserData.AliasOrUID, ImGuiColors.HealerGreen);
|
UiSharedService.ColorText(_pair.UserData.AliasOrUID, ImGuiColors.HealerGreen);
|
||||||
if (_uiSharedService.UidFontBuilt) ImGui.PopFont();
|
|
||||||
ImGui.Dummy(new(spacing.Y, spacing.Y));
|
ImGuiHelpers.ScaledDummy(spacing.Y, spacing.Y);
|
||||||
var textPos = ImGui.GetCursorPosY();
|
var textPos = ImGui.GetCursorPosY();
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
var imagePos = ImGui.GetCursorPos();
|
var imagePos = ImGui.GetCursorPos();
|
||||||
ImGui.Dummy(new(256, 256 * ImGuiHelpers.GlobalScale + spacing.Y));
|
ImGuiHelpers.ScaledDummy(256, 256 * ImGuiHelpers.GlobalScale + spacing.Y);
|
||||||
var note = _serverManager.GetNoteForUid(_pair.UserData.UID);
|
var note = _serverManager.GetNoteForUid(_pair.UserData.UID);
|
||||||
if (!string.IsNullOrEmpty(note))
|
if (!string.IsNullOrEmpty(note))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using MareSynchronos.API.Data;
|
using MareSynchronos.API.Data;
|
||||||
@@ -19,6 +21,7 @@ using MareSynchronos.WebAPI.Files.Models;
|
|||||||
using MareSynchronos.WebAPI.SignalR.Utils;
|
using MareSynchronos.WebAPI.SignalR.Utils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Globalization;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
@@ -66,6 +69,8 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
_fileCompactor = fileCompactor;
|
_fileCompactor = fileCompactor;
|
||||||
_uiShared = uiShared;
|
_uiShared = uiShared;
|
||||||
|
AllowClickthrough = false;
|
||||||
|
AllowPinning = false;
|
||||||
|
|
||||||
SizeConstraints = new WindowSizeConstraints()
|
SizeConstraints = new WindowSizeConstraints()
|
||||||
{
|
{
|
||||||
@@ -139,6 +144,37 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
int maxParallelDownloads = _configService.Current.ParallelDownloads;
|
int maxParallelDownloads = _configService.Current.ParallelDownloads;
|
||||||
bool useAlternativeUpload = _configService.Current.UseAlternativeFileUpload;
|
bool useAlternativeUpload = _configService.Current.UseAlternativeFileUpload;
|
||||||
|
int downloadSpeedLimit = _configService.Current.DownloadSpeedLimitInBytes;
|
||||||
|
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted("Global Download Speed Limit");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.SetNextItemWidth(100);
|
||||||
|
if (ImGui.InputInt("###speedlimit", ref downloadSpeedLimit))
|
||||||
|
{
|
||||||
|
_configService.Current.DownloadSpeedLimitInBytes = downloadSpeedLimit;
|
||||||
|
_configService.Save();
|
||||||
|
Mediator.Publish(new DownloadLimitChangedMessage());
|
||||||
|
}
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.SetNextItemWidth(100);
|
||||||
|
_uiShared.DrawCombo("###speed", new[] { DownloadSpeeds.Bps, DownloadSpeeds.KBps, DownloadSpeeds.MBps },
|
||||||
|
(s) => s switch
|
||||||
|
{
|
||||||
|
DownloadSpeeds.Bps => "Byte/s",
|
||||||
|
DownloadSpeeds.KBps => "KB/s",
|
||||||
|
DownloadSpeeds.MBps => "MB/s",
|
||||||
|
_ => throw new NotSupportedException()
|
||||||
|
}, (s) =>
|
||||||
|
{
|
||||||
|
_configService.Current.DownloadSpeedType = s;
|
||||||
|
_configService.Save();
|
||||||
|
Mediator.Publish(new DownloadLimitChangedMessage());
|
||||||
|
}, _configService.Current.DownloadSpeedType);
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted("0 = No limit/infinite");
|
||||||
|
|
||||||
if (ImGui.SliderInt("Maximum Parallel Downloads", ref maxParallelDownloads, 1, 10))
|
if (ImGui.SliderInt("Maximum Parallel Downloads", ref maxParallelDownloads, 1, 10))
|
||||||
{
|
{
|
||||||
_configService.Current.ParallelDownloads = maxParallelDownloads;
|
_configService.Current.ParallelDownloads = maxParallelDownloads;
|
||||||
@@ -248,14 +284,14 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
foreach (var transfer in _fileTransferManager.CurrentUploads.ToArray())
|
foreach (var transfer in _fileTransferManager.CurrentUploads.ToArray())
|
||||||
{
|
{
|
||||||
var color = UiSharedService.UploadColor((transfer.Transferred, transfer.Total));
|
var color = UiSharedService.UploadColor((transfer.Transferred, transfer.Total));
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
var col = ImRaii.PushColor(ImGuiCol.Text, color);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted(transfer.Hash);
|
ImGui.TextUnformatted(transfer.Hash);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted(UiSharedService.ByteToString(transfer.Transferred));
|
ImGui.TextUnformatted(UiSharedService.ByteToString(transfer.Transferred));
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted(UiSharedService.ByteToString(transfer.Total));
|
ImGui.TextUnformatted(UiSharedService.ByteToString(transfer.Total));
|
||||||
ImGui.PopStyleColor();
|
col.Dispose();
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,13 +317,13 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.TextUnformatted(userName);
|
ImGui.TextUnformatted(userName);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted(entry.Key);
|
ImGui.TextUnformatted(entry.Key);
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
var col = ImRaii.PushColor(ImGuiCol.Text, color);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted(entry.Value.TransferredFiles + "/" + entry.Value.TotalFiles);
|
ImGui.TextUnformatted(entry.Value.TransferredFiles + "/" + entry.Value.TotalFiles);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted(UiSharedService.ByteToString(entry.Value.TransferredBytes) + "/" + UiSharedService.ByteToString(entry.Value.TotalBytes));
|
ImGui.TextUnformatted(UiSharedService.ByteToString(entry.Value.TransferredBytes) + "/" + UiSharedService.ByteToString(entry.Value.TotalBytes));
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.PopStyleColor();
|
col.Dispose();
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -324,7 +360,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.TreePop();
|
ImGui.TreePop();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Copy, "[DEBUG] Copy Last created Character Data to clipboard"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Copy, "[DEBUG] Copy Last created Character Data to clipboard"))
|
||||||
{
|
{
|
||||||
if (LastCreatedCharacterData != null)
|
if (LastCreatedCharacterData != null)
|
||||||
{
|
{
|
||||||
@@ -351,17 +387,16 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
UiSharedService.DrawHelpText("Enabling this can incur a (slight) performance impact. Enabling this for extended periods of time is not recommended.");
|
UiSharedService.DrawHelpText("Enabling this can incur a (slight) performance impact. Enabling this for extended periods of time is not recommended.");
|
||||||
|
|
||||||
if (!logPerformance) ImGui.BeginDisabled();
|
using var disabled = ImRaii.Disabled(!logPerformance);
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Print Performance Stats to /xllog"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StickyNote, "Print Performance Stats to /xllog"))
|
||||||
{
|
{
|
||||||
_performanceCollector.PrintPerformanceStats();
|
_performanceCollector.PrintPerformanceStats();
|
||||||
}
|
}
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Print Performance Stats (last 60s) to /xllog"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StickyNote, "Print Performance Stats (last 60s) to /xllog"))
|
||||||
{
|
{
|
||||||
_performanceCollector.PrintPerformanceStats(60);
|
_performanceCollector.PrintPerformanceStats(60);
|
||||||
}
|
}
|
||||||
if (!logPerformance) ImGui.EndDisabled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawFileStorageSettings()
|
private void DrawFileStorageSettings()
|
||||||
@@ -384,7 +419,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
if (!_mareCharaFileManager.CurrentlyWorking)
|
if (!_mareCharaFileManager.CurrentlyWorking)
|
||||||
{
|
{
|
||||||
ImGui.InputTextWithHint("Export Descriptor", "This description will be shown on loading the data", ref _exportDescription, 255);
|
ImGui.InputTextWithHint("Export Descriptor", "This description will be shown on loading the data", ref _exportDescription, 255);
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Save, "Export Character as MCDF"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Save, "Export Character as MCDF"))
|
||||||
{
|
{
|
||||||
string defaultFileName = string.IsNullOrEmpty(_exportDescription)
|
string defaultFileName = string.IsNullOrEmpty(_exportDescription)
|
||||||
? "export.mcdf"
|
? "export.mcdf"
|
||||||
@@ -452,14 +487,14 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (!_fileCompactor.MassCompactRunning)
|
if (!_fileCompactor.MassCompactRunning)
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.FileArchive, "Compact all files in storage"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.FileArchive, "Compact all files in storage"))
|
||||||
{
|
{
|
||||||
_ = Task.Run(() => _fileCompactor.CompactStorage(compress: true));
|
_ = Task.Run(() => _fileCompactor.CompactStorage(compress: true));
|
||||||
}
|
}
|
||||||
UiSharedService.AttachToolTip("This will run compression on all files in your current Mare Storage." + Environment.NewLine
|
UiSharedService.AttachToolTip("This will run compression on all files in your current Mare Storage." + Environment.NewLine
|
||||||
+ "You do not need to run this manually if you keep the file compactor enabled.");
|
+ "You do not need to run this manually if you keep the file compactor enabled.");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.File, "Decompact all files in storage"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.File, "Decompact all files in storage"))
|
||||||
{
|
{
|
||||||
_ = Task.Run(() => _fileCompactor.CompactStorage(compress: false));
|
_ = Task.Run(() => _fileCompactor.CompactStorage(compress: false));
|
||||||
}
|
}
|
||||||
@@ -475,7 +510,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.TextUnformatted("The file compactor is only available on Windows.");
|
ImGui.TextUnformatted("The file compactor is only available on Windows.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Dummy(new Vector2(10, 10));
|
ImGuiHelpers.ScaledDummy(new Vector2(10, 10));
|
||||||
ImGui.TextUnformatted("To clear the local storage accept the following disclaimer");
|
ImGui.TextUnformatted("To clear the local storage accept the following disclaimer");
|
||||||
ImGui.Indent();
|
ImGui.Indent();
|
||||||
ImGui.Checkbox("##readClearCache", ref _readClearCache);
|
ImGui.Checkbox("##readClearCache", ref _readClearCache);
|
||||||
@@ -485,7 +520,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
+ Environment.NewLine + "- This can make the situation of not getting other players data worse in situations of heavy file server load.");
|
+ Environment.NewLine + "- This can make the situation of not getting other players data worse in situations of heavy file server load.");
|
||||||
if (!_readClearCache)
|
if (!_readClearCache)
|
||||||
ImGui.BeginDisabled();
|
ImGui.BeginDisabled();
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Clear local storage") && UiSharedService.CtrlPressed() && _readClearCache)
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Clear local storage") && UiSharedService.CtrlPressed() && _readClearCache)
|
||||||
{
|
{
|
||||||
_ = Task.Run(() =>
|
_ = Task.Run(() =>
|
||||||
{
|
{
|
||||||
@@ -515,11 +550,11 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
_lastTab = "General";
|
_lastTab = "General";
|
||||||
UiSharedService.FontText("Notes", _uiShared.UidFont);
|
UiSharedService.FontText("Notes", _uiShared.UidFont);
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.StickyNote, "Export all your user notes to clipboard"))
|
||||||
{
|
{
|
||||||
ImGui.SetClipboardText(UiSharedService.GetNotes(_pairManager.DirectPairs.UnionBy(_pairManager.GroupPairs.SelectMany(p => p.Value), p => p.UserData, UserDataComparer.Instance).ToList()));
|
ImGui.SetClipboardText(UiSharedService.GetNotes(_pairManager.DirectPairs.UnionBy(_pairManager.GroupPairs.SelectMany(p => p.Value), p => p.UserData, UserDataComparer.Instance).ToList()));
|
||||||
}
|
}
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.FileImport, "Import notes from clipboard"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.FileImport, "Import notes from clipboard"))
|
||||||
{
|
{
|
||||||
_notesSuccessfullyApplied = null;
|
_notesSuccessfullyApplied = null;
|
||||||
var notes = ImGui.GetClipboardText();
|
var notes = ImGui.GetClipboardText();
|
||||||
@@ -558,6 +593,8 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
var profileOnRight = _configService.Current.ProfilePopoutRight;
|
var profileOnRight = _configService.Current.ProfilePopoutRight;
|
||||||
var enableRightClickMenu = _configService.Current.EnableRightClickMenus;
|
var enableRightClickMenu = _configService.Current.EnableRightClickMenus;
|
||||||
var enableDtrEntry = _configService.Current.EnableDtrEntry;
|
var enableDtrEntry = _configService.Current.EnableDtrEntry;
|
||||||
|
var showUidInDtrTooltip = _configService.Current.ShowUidInDtrTooltip;
|
||||||
|
var preferNoteInDtrTooltip = _configService.Current.PreferNoteInDtrTooltip;
|
||||||
var preferNotesInsteadOfName = _configService.Current.PreferNotesOverNamesForVisible;
|
var preferNotesInsteadOfName = _configService.Current.PreferNotesOverNamesForVisible;
|
||||||
|
|
||||||
if (ImGui.Checkbox("Enable Game Right Click Menu Entries", ref enableRightClickMenu))
|
if (ImGui.Checkbox("Enable Game Right Click Menu Entries", ref enableRightClickMenu))
|
||||||
@@ -574,6 +611,22 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
UiSharedService.DrawHelpText("This will add Mare connection status and visible pair count in the Server Info Bar.\nYou can further configure this through your Dalamud Settings.");
|
UiSharedService.DrawHelpText("This will add Mare connection status and visible pair count in the Server Info Bar.\nYou can further configure this through your Dalamud Settings.");
|
||||||
|
|
||||||
|
using (ImRaii.Disabled(!enableDtrEntry))
|
||||||
|
{
|
||||||
|
using var indent = ImRaii.PushIndent();
|
||||||
|
if (ImGui.Checkbox("Show visible character's UID in tooltip", ref showUidInDtrTooltip))
|
||||||
|
{
|
||||||
|
_configService.Current.ShowUidInDtrTooltip = showUidInDtrTooltip;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.Checkbox("Prefer notes over player names in tooltip", ref preferNoteInDtrTooltip))
|
||||||
|
{
|
||||||
|
_configService.Current.PreferNoteInDtrTooltip = preferNoteInDtrTooltip;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui.Checkbox("Show separate Visible group", ref showVisibleSeparate))
|
if (ImGui.Checkbox("Show separate Visible group", ref showVisibleSeparate))
|
||||||
{
|
{
|
||||||
_configService.Current.ShowVisibleUsersSeparately = showVisibleSeparate;
|
_configService.Current.ShowVisibleUsersSeparately = showVisibleSeparate;
|
||||||
@@ -622,13 +675,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
Mediator.Publish(new CompactUiChange(Vector2.Zero, Vector2.Zero));
|
Mediator.Publish(new CompactUiChange(Vector2.Zero, Vector2.Zero));
|
||||||
}
|
}
|
||||||
UiSharedService.DrawHelpText("Will show profiles on the right side of the main UI");
|
UiSharedService.DrawHelpText("Will show profiles on the right side of the main UI");
|
||||||
if (ImGui.Checkbox("Show profiles marked as NSFW", ref showNsfwProfiles))
|
|
||||||
{
|
|
||||||
Mediator.Publish(new ClearProfileDataMessage());
|
|
||||||
_configService.Current.ProfilesAllowNsfw = showNsfwProfiles;
|
|
||||||
_configService.Save();
|
|
||||||
}
|
|
||||||
UiSharedService.DrawHelpText("Will show profiles that have the NSFW tag enabled");
|
|
||||||
if (ImGui.SliderFloat("Hover Delay", ref profileDelay, 1, 10))
|
if (ImGui.SliderFloat("Hover Delay", ref profileDelay, 1, 10))
|
||||||
{
|
{
|
||||||
_configService.Current.ProfileDelay = profileDelay;
|
_configService.Current.ProfileDelay = profileDelay;
|
||||||
@@ -637,6 +683,13 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
UiSharedService.DrawHelpText("Delay until the profile should be displayed");
|
UiSharedService.DrawHelpText("Delay until the profile should be displayed");
|
||||||
if (!showProfiles) ImGui.EndDisabled();
|
if (!showProfiles) ImGui.EndDisabled();
|
||||||
ImGui.Unindent();
|
ImGui.Unindent();
|
||||||
|
if (ImGui.Checkbox("Show profiles marked as NSFW", ref showNsfwProfiles))
|
||||||
|
{
|
||||||
|
Mediator.Publish(new ClearProfileDataMessage());
|
||||||
|
_configService.Current.ProfilesAllowNsfw = showNsfwProfiles;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
UiSharedService.DrawHelpText("Will show profiles that have the NSFW tag enabled");
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
@@ -695,7 +748,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
UiSharedService.DrawHelpText("Enabling this will show a small notification (type: Info) in the bottom right corner when pairs go online.");
|
UiSharedService.DrawHelpText("Enabling this will show a small notification (type: Info) in the bottom right corner when pairs go online.");
|
||||||
|
|
||||||
if (!onlineNotifs) ImGui.BeginDisabled();
|
using var disabled = ImRaii.Disabled(!onlineNotifs);
|
||||||
if (ImGui.Checkbox("Notify only for individual pairs", ref onlineNotifsPairsOnly))
|
if (ImGui.Checkbox("Notify only for individual pairs", ref onlineNotifsPairsOnly))
|
||||||
{
|
{
|
||||||
_configService.Current.ShowOnlineNotificationsOnlyForIndividualPairs = onlineNotifsPairsOnly;
|
_configService.Current.ShowOnlineNotificationsOnlyForIndividualPairs = onlineNotifsPairsOnly;
|
||||||
@@ -708,7 +761,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
_configService.Save();
|
_configService.Save();
|
||||||
}
|
}
|
||||||
UiSharedService.DrawHelpText("Enabling this will only show online notifications (type: Info) for pairs where you have set an individual note.");
|
UiSharedService.DrawHelpText("Enabling this will only show online notifications (type: Info) for pairs where you have set an individual note.");
|
||||||
if (!onlineNotifs) ImGui.EndDisabled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawServerConfiguration()
|
private void DrawServerConfiguration()
|
||||||
@@ -717,7 +769,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
if (ApiController.ServerAlive)
|
if (ApiController.ServerAlive)
|
||||||
{
|
{
|
||||||
UiSharedService.FontText("Service Actions", _uiShared.UidFont);
|
UiSharedService.FontText("Service Actions", _uiShared.UidFont);
|
||||||
|
ImGuiHelpers.ScaledDummy(new Vector2(5, 5));
|
||||||
if (ImGui.Button("Delete all my files"))
|
if (ImGui.Button("Delete all my files"))
|
||||||
{
|
{
|
||||||
_deleteFilesPopupModalShown = true;
|
_deleteFilesPopupModalShown = true;
|
||||||
@@ -798,7 +850,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
var idx = _uiShared.DrawServiceSelection();
|
var idx = _uiShared.DrawServiceSelection();
|
||||||
|
|
||||||
ImGui.Dummy(new Vector2(10, 10));
|
ImGuiHelpers.ScaledDummy(new Vector2(10, 10));
|
||||||
|
|
||||||
var selectedServer = _serverConfigurationManager.GetServerByIndex(idx);
|
var selectedServer = _serverConfigurationManager.GetServerByIndex(idx);
|
||||||
if (selectedServer == _serverConfigurationManager.CurrentServer)
|
if (selectedServer == _serverConfigurationManager.CurrentServer)
|
||||||
@@ -817,59 +869,58 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
foreach (var item in selectedServer.Authentications.ToList())
|
foreach (var item in selectedServer.Authentications.ToList())
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID("selectedChara" + i, () =>
|
using var charaId = ImRaii.PushId("selectedChara" + i);
|
||||||
|
|
||||||
|
var worldIdx = (ushort)item.WorldId;
|
||||||
|
var data = _uiShared.WorldData.OrderBy(u => u.Value, StringComparer.Ordinal).ToDictionary(k => k.Key, k => k.Value);
|
||||||
|
if (!data.TryGetValue(worldIdx, out string? worldPreview))
|
||||||
{
|
{
|
||||||
var worldIdx = (ushort)item.WorldId;
|
worldPreview = data.First().Value;
|
||||||
var data = _uiShared.WorldData.OrderBy(u => u.Value, StringComparer.Ordinal).ToDictionary(k => k.Key, k => k.Value);
|
}
|
||||||
if (!data.TryGetValue(worldIdx, out string? worldPreview))
|
|
||||||
|
var secretKeyIdx = item.SecretKeyIdx;
|
||||||
|
var keys = selectedServer.SecretKeys;
|
||||||
|
if (!keys.TryGetValue(secretKeyIdx, out var secretKey))
|
||||||
|
{
|
||||||
|
secretKey = new();
|
||||||
|
}
|
||||||
|
var friendlyName = secretKey.FriendlyName;
|
||||||
|
|
||||||
|
if (ImGui.TreeNode($"chara", $"Character: {item.CharacterName}, World: {worldPreview}, Secret Key: {friendlyName}"))
|
||||||
|
{
|
||||||
|
var charaName = item.CharacterName;
|
||||||
|
if (ImGui.InputText("Character Name", ref charaName, 64))
|
||||||
{
|
{
|
||||||
worldPreview = data.First().Value;
|
item.CharacterName = charaName;
|
||||||
|
_serverConfigurationManager.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
var secretKeyIdx = item.SecretKeyIdx;
|
_uiShared.DrawCombo("World##" + item.CharacterName + i, data, (w) => w.Value,
|
||||||
var keys = selectedServer.SecretKeys;
|
(w) =>
|
||||||
if (!keys.TryGetValue(secretKeyIdx, out var secretKey))
|
|
||||||
{
|
|
||||||
secretKey = new();
|
|
||||||
}
|
|
||||||
var friendlyName = secretKey.FriendlyName;
|
|
||||||
|
|
||||||
if (ImGui.TreeNode($"chara", $"Character: {item.CharacterName}, World: {worldPreview}, Secret Key: {friendlyName}"))
|
|
||||||
{
|
|
||||||
var charaName = item.CharacterName;
|
|
||||||
if (ImGui.InputText("Character Name", ref charaName, 64))
|
|
||||||
{
|
{
|
||||||
item.CharacterName = charaName;
|
if (item.WorldId != w.Key)
|
||||||
_serverConfigurationManager.Save();
|
|
||||||
}
|
|
||||||
|
|
||||||
_uiShared.DrawCombo("World##" + item.CharacterName + i, data, (w) => w.Value,
|
|
||||||
(w) =>
|
|
||||||
{
|
{
|
||||||
if (item.WorldId != w.Key)
|
item.WorldId = w.Key;
|
||||||
{
|
_serverConfigurationManager.Save();
|
||||||
item.WorldId = w.Key;
|
}
|
||||||
_serverConfigurationManager.Save();
|
}, EqualityComparer<KeyValuePair<ushort, string>>.Default.Equals(data.FirstOrDefault(f => f.Key == worldIdx), default) ? data.First() : data.First(f => f.Key == worldIdx));
|
||||||
}
|
|
||||||
}, EqualityComparer<KeyValuePair<ushort, string>>.Default.Equals(data.FirstOrDefault(f => f.Key == worldIdx), default) ? data.First() : data.First(f => f.Key == worldIdx));
|
|
||||||
|
|
||||||
_uiShared.DrawCombo("Secret Key##" + item.CharacterName + i, keys, (w) => w.Value.FriendlyName,
|
_uiShared.DrawCombo("Secret Key##" + item.CharacterName + i, keys, (w) => w.Value.FriendlyName,
|
||||||
(w) =>
|
(w) =>
|
||||||
|
{
|
||||||
|
if (w.Key != item.SecretKeyIdx)
|
||||||
{
|
{
|
||||||
if (w.Key != item.SecretKeyIdx)
|
item.SecretKeyIdx = w.Key;
|
||||||
{
|
_serverConfigurationManager.Save();
|
||||||
item.SecretKeyIdx = w.Key;
|
}
|
||||||
_serverConfigurationManager.Save();
|
}, EqualityComparer<KeyValuePair<int, SecretKey>>.Default.Equals(keys.FirstOrDefault(f => f.Key == item.SecretKeyIdx), default) ? keys.First() : keys.First(f => f.Key == item.SecretKeyIdx));
|
||||||
}
|
|
||||||
}, EqualityComparer<KeyValuePair<int, SecretKey>>.Default.Equals(keys.FirstOrDefault(f => f.Key == item.SecretKeyIdx), default) ? keys.First() : keys.First(f => f.Key == item.SecretKeyIdx));
|
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Character") && UiSharedService.CtrlPressed())
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Delete Character") && UiSharedService.CtrlPressed())
|
||||||
_serverConfigurationManager.RemoveCharacterFromServer(idx, item);
|
_serverConfigurationManager.RemoveCharacterFromServer(idx, item);
|
||||||
UiSharedService.AttachToolTip("Hold CTRL to delete this entry.");
|
UiSharedService.AttachToolTip("Hold CTRL to delete this entry.");
|
||||||
|
|
||||||
ImGui.TreePop();
|
ImGui.TreePop();
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -878,14 +929,14 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
if (!selectedServer.Authentications.Exists(c => string.Equals(c.CharacterName, _uiShared.PlayerName, StringComparison.Ordinal)
|
if (!selectedServer.Authentications.Exists(c => string.Equals(c.CharacterName, _uiShared.PlayerName, StringComparison.Ordinal)
|
||||||
&& c.WorldId == _uiShared.WorldId))
|
&& c.WorldId == _uiShared.WorldId))
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.User, "Add current character"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.User, "Add current character"))
|
||||||
{
|
{
|
||||||
_serverConfigurationManager.AddCurrentCharacterToServer(idx);
|
_serverConfigurationManager.AddCurrentCharacterToServer(idx);
|
||||||
}
|
}
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add new character"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Plus, "Add new character"))
|
||||||
{
|
{
|
||||||
_serverConfigurationManager.AddEmptyCharacterToServer(idx);
|
_serverConfigurationManager.AddEmptyCharacterToServer(idx);
|
||||||
}
|
}
|
||||||
@@ -902,41 +953,39 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
foreach (var item in selectedServer.SecretKeys.ToList())
|
foreach (var item in selectedServer.SecretKeys.ToList())
|
||||||
{
|
{
|
||||||
UiSharedService.DrawWithID("key" + item.Key, () =>
|
using var id = ImRaii.PushId("key" + item.Key);
|
||||||
|
var friendlyName = item.Value.FriendlyName;
|
||||||
|
if (ImGui.InputText("Secret Key Display Name", ref friendlyName, 255))
|
||||||
{
|
{
|
||||||
var friendlyName = item.Value.FriendlyName;
|
item.Value.FriendlyName = friendlyName;
|
||||||
if (ImGui.InputText("Secret Key Display Name", ref friendlyName, 255))
|
_serverConfigurationManager.Save();
|
||||||
|
}
|
||||||
|
var key = item.Value.Key;
|
||||||
|
if (ImGui.InputText("Secret Key", ref key, 64))
|
||||||
|
{
|
||||||
|
item.Value.Key = key;
|
||||||
|
_serverConfigurationManager.Save();
|
||||||
|
}
|
||||||
|
if (!selectedServer.Authentications.Exists(p => p.SecretKeyIdx == item.Key))
|
||||||
|
{
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Delete Secret Key") && UiSharedService.CtrlPressed())
|
||||||
{
|
{
|
||||||
item.Value.FriendlyName = friendlyName;
|
selectedServer.SecretKeys.Remove(item.Key);
|
||||||
_serverConfigurationManager.Save();
|
_serverConfigurationManager.Save();
|
||||||
}
|
}
|
||||||
var key = item.Value.Key;
|
UiSharedService.AttachToolTip("Hold CTRL to delete this secret key entry");
|
||||||
if (ImGui.InputText("Secret Key", ref key, 64))
|
}
|
||||||
{
|
else
|
||||||
item.Value.Key = key;
|
{
|
||||||
_serverConfigurationManager.Save();
|
UiSharedService.ColorTextWrapped("This key is in use and cannot be deleted", ImGuiColors.DalamudYellow);
|
||||||
}
|
}
|
||||||
if (!selectedServer.Authentications.Exists(p => p.SecretKeyIdx == item.Key))
|
|
||||||
{
|
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Secret Key") && UiSharedService.CtrlPressed())
|
|
||||||
{
|
|
||||||
selectedServer.SecretKeys.Remove(item.Key);
|
|
||||||
_serverConfigurationManager.Save();
|
|
||||||
}
|
|
||||||
UiSharedService.AttachToolTip("Hold CTRL to delete this secret key entry");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UiSharedService.ColorTextWrapped("This key is in use and cannot be deleted", ImGuiColors.DalamudYellow);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (item.Key != selectedServer.SecretKeys.Keys.LastOrDefault())
|
if (item.Key != selectedServer.SecretKeys.Keys.LastOrDefault())
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add new Secret Key"))
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Plus, "Add new Secret Key"))
|
||||||
{
|
{
|
||||||
selectedServer.SecretKeys.Add(selectedServer.SecretKeys.Any() ? selectedServer.SecretKeys.Max(p => p.Key) + 1 : 0, new SecretKey()
|
selectedServer.SecretKeys.Add(selectedServer.SecretKeys.Any() ? selectedServer.SecretKeys.Max(p => p.Key) + 1 : 0, new SecretKey()
|
||||||
{
|
{
|
||||||
@@ -976,7 +1025,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
if (!isMain && selectedServer != _serverConfigurationManager.CurrentServer)
|
if (!isMain && selectedServer != _serverConfigurationManager.CurrentServer)
|
||||||
{
|
{
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Trash, "Delete Service") && UiSharedService.CtrlPressed())
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Delete Service") && UiSharedService.CtrlPressed())
|
||||||
{
|
{
|
||||||
_serverConfigurationManager.DeleteServer(selectedServer);
|
_serverConfigurationManager.DeleteServer(selectedServer);
|
||||||
}
|
}
|
||||||
@@ -990,7 +1039,21 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
private void DrawSettingsContent()
|
private void DrawSettingsContent()
|
||||||
{
|
{
|
||||||
_uiShared.PrintServerState();
|
if (_apiController.ServerState is ServerState.Connected)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted("Service " + _serverConfigurationManager.CurrentServer!.ServerName + ":");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextColored(ImGuiColors.ParsedGreen, "Available");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted("(");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextColored(ImGuiColors.ParsedGreen, _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture));
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted("Users Online");
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.TextUnformatted(")");
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.AlignTextToFramePadding();
|
ImGui.AlignTextToFramePadding();
|
||||||
ImGui.TextUnformatted("Community and Support:");
|
ImGui.TextUnformatted("Community and Support:");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using Dalamud.Interface.Internal;
|
using Dalamud.Interface.Internal;
|
||||||
|
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using MareSynchronos.API.Data.Extensions;
|
using MareSynchronos.API.Data.Extensions;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
@@ -76,9 +77,9 @@ public class StandaloneProfileUi : WindowMediatorSubscriberBase
|
|||||||
var rectMax = drawList.GetClipRectMax();
|
var rectMax = drawList.GetClipRectMax();
|
||||||
var headerSize = ImGui.GetCursorPosY() - ImGui.GetStyle().WindowPadding.Y;
|
var headerSize = ImGui.GetCursorPosY() - ImGui.GetStyle().WindowPadding.Y;
|
||||||
|
|
||||||
if (_uiSharedService.UidFontBuilt) ImGui.PushFont(_uiSharedService.UidFont);
|
using (ImRaii.PushFont(_uiSharedService.UidFont, _uiSharedService.UidFontBuilt))
|
||||||
UiSharedService.ColorText(Pair.UserData.AliasOrUID, ImGuiColors.HealerGreen);
|
UiSharedService.ColorText(Pair.UserData.AliasOrUID, ImGuiColors.HealerGreen);
|
||||||
if (_uiSharedService.UidFontBuilt) ImGui.PopFont();
|
|
||||||
ImGuiHelpers.ScaledDummy(new Vector2(spacing.Y, spacing.Y));
|
ImGuiHelpers.ScaledDummy(new Vector2(spacing.Y, spacing.Y));
|
||||||
var textPos = ImGui.GetCursorPosY() - headerSize;
|
var textPos = ImGui.GetCursorPosY() - headerSize;
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|||||||
252
MareSynchronos/UI/SyncshellAdminUI.cs
Normal file
252
MareSynchronos/UI/SyncshellAdminUI.cs
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Colors;
|
||||||
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using ImGuiNET;
|
||||||
|
using MareSynchronos.API.Data.Extensions;
|
||||||
|
using MareSynchronos.API.Dto.Group;
|
||||||
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using MareSynchronos.WebAPI;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace MareSynchronos.UI.Components.Popup;
|
||||||
|
|
||||||
|
public class SyncshellAdminUI : WindowMediatorSubscriberBase
|
||||||
|
{
|
||||||
|
private readonly ApiController _apiController;
|
||||||
|
private readonly bool _isModerator = false;
|
||||||
|
private readonly bool _isOwner = false;
|
||||||
|
private readonly List<string> _oneTimeInvites = [];
|
||||||
|
private readonly PairManager _pairManager;
|
||||||
|
private readonly UiSharedService _uiSharedService;
|
||||||
|
private List<BannedGroupUserDto> _bannedUsers = [];
|
||||||
|
private int _multiInvites;
|
||||||
|
private string _newPassword;
|
||||||
|
private bool _pwChangeSuccess;
|
||||||
|
public SyncshellAdminUI(ILogger<SyncshellAdminUI> logger, MareMediator mediator, ApiController apiController,
|
||||||
|
UiSharedService uiSharedService, PairManager pairManager, GroupFullInfoDto groupFullInfo)
|
||||||
|
: base(logger, mediator, "Syncshell Admin Panel (" + groupFullInfo.GID + ")")
|
||||||
|
{
|
||||||
|
GroupFullInfo = groupFullInfo;
|
||||||
|
_apiController = apiController;
|
||||||
|
_uiSharedService = uiSharedService;
|
||||||
|
_pairManager = pairManager;
|
||||||
|
_isOwner = string.Equals(GroupFullInfo.OwnerUID, _apiController.UID, StringComparison.Ordinal);
|
||||||
|
_isModerator = GroupFullInfo.GroupUserInfo.IsModerator();
|
||||||
|
_newPassword = string.Empty;
|
||||||
|
_multiInvites = 30;
|
||||||
|
_pwChangeSuccess = true;
|
||||||
|
IsOpen = true;
|
||||||
|
SizeConstraints = new WindowSizeConstraints()
|
||||||
|
{
|
||||||
|
MinimumSize = new(700, 500),
|
||||||
|
MaximumSize = new(700, 2000),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupFullInfoDto GroupFullInfo { get; private set; }
|
||||||
|
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
if (!_isModerator && !_isOwner) return;
|
||||||
|
|
||||||
|
GroupFullInfo = _pairManager.Groups[GroupFullInfo.Group];
|
||||||
|
|
||||||
|
using var id = ImRaii.PushId("syncshell_admin_" + GroupFullInfo.GID);
|
||||||
|
|
||||||
|
using (ImRaii.PushFont(_uiSharedService.UidFont))
|
||||||
|
ImGui.TextUnformatted(GroupFullInfo.GroupAliasOrGID + " Administrative Panel");
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
var perm = GroupFullInfo.GroupPermissions;
|
||||||
|
|
||||||
|
using var tabbar = ImRaii.TabBar("syncshell_tab_" + GroupFullInfo.GID);
|
||||||
|
|
||||||
|
if (tabbar)
|
||||||
|
{
|
||||||
|
var inviteTab = ImRaii.TabItem("Invites");
|
||||||
|
if (inviteTab)
|
||||||
|
{
|
||||||
|
bool isInvitesDisabled = perm.IsDisableInvites();
|
||||||
|
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(isInvitesDisabled ? FontAwesomeIcon.Unlock : FontAwesomeIcon.Lock,
|
||||||
|
isInvitesDisabled ? "Unlock Syncshell" : "Lock Syncshell"))
|
||||||
|
{
|
||||||
|
perm.SetDisableInvites(!isInvitesDisabled);
|
||||||
|
_ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(2f);
|
||||||
|
|
||||||
|
UiSharedService.TextWrapped("One-time invites work as single-use passwords. Use those if you do not want to distribute your Syncshell password.");
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Envelope, "Single one-time invite"))
|
||||||
|
{
|
||||||
|
ImGui.SetClipboardText(_apiController.GroupCreateTempInvite(new(GroupFullInfo.Group), 1).Result.FirstOrDefault() ?? string.Empty);
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Creates a single-use password for joining the syncshell which is valid for 24h and copies it to the clipboard.");
|
||||||
|
ImGui.InputInt("##amountofinvites", ref _multiInvites);
|
||||||
|
ImGui.SameLine();
|
||||||
|
using (ImRaii.Disabled(_multiInvites <= 1 || _multiInvites > 100))
|
||||||
|
{
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Envelope, "Generate " + _multiInvites + " one-time invites"))
|
||||||
|
{
|
||||||
|
_oneTimeInvites.AddRange(_apiController.GroupCreateTempInvite(new(GroupFullInfo.Group), _multiInvites).Result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_oneTimeInvites.Any())
|
||||||
|
{
|
||||||
|
var invites = string.Join(Environment.NewLine, _oneTimeInvites);
|
||||||
|
ImGui.InputTextMultiline("Generated Multi Invites", ref invites, 5000, new(0, 0), ImGuiInputTextFlags.ReadOnly);
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Copy, "Copy Invites to clipboard"))
|
||||||
|
{
|
||||||
|
ImGui.SetClipboardText(invites);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inviteTab.Dispose();
|
||||||
|
|
||||||
|
var mgmtTab = ImRaii.TabItem("User Management");
|
||||||
|
if (mgmtTab)
|
||||||
|
{
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Broom, "Clear Syncshell"))
|
||||||
|
{
|
||||||
|
_ = _apiController.GroupClear(new(GroupFullInfo.Group));
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("This will remove all non-pinned, non-moderator users from the Syncshell");
|
||||||
|
|
||||||
|
ImGuiHelpers.ScaledDummy(2f);
|
||||||
|
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Retweet, "Refresh Banlist from Server"))
|
||||||
|
{
|
||||||
|
_bannedUsers = _apiController.GroupGetBannedUsers(new GroupDto(GroupFullInfo.Group)).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.BeginTable("bannedusertable" + GroupFullInfo.GID, 6, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY))
|
||||||
|
{
|
||||||
|
ImGui.TableSetupColumn("UID", ImGuiTableColumnFlags.None, 1);
|
||||||
|
ImGui.TableSetupColumn("Alias", ImGuiTableColumnFlags.None, 1);
|
||||||
|
ImGui.TableSetupColumn("By", ImGuiTableColumnFlags.None, 1);
|
||||||
|
ImGui.TableSetupColumn("Date", ImGuiTableColumnFlags.None, 2);
|
||||||
|
ImGui.TableSetupColumn("Reason", ImGuiTableColumnFlags.None, 3);
|
||||||
|
ImGui.TableSetupColumn("Actions", ImGuiTableColumnFlags.None, 1);
|
||||||
|
|
||||||
|
ImGui.TableHeadersRow();
|
||||||
|
|
||||||
|
foreach (var bannedUser in _bannedUsers.ToList())
|
||||||
|
{
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(bannedUser.UID);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(bannedUser.UserAlias ?? string.Empty);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(bannedUser.BannedBy);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(bannedUser.BannedOn.ToLocalTime().ToString(CultureInfo.CurrentCulture));
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
UiSharedService.TextWrapped(bannedUser.Reason);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Check, "Unban##" + bannedUser.UID))
|
||||||
|
{
|
||||||
|
_ = _apiController.GroupUnbanUser(bannedUser);
|
||||||
|
_bannedUsers.RemoveAll(b => string.Equals(b.UID, bannedUser.UID, StringComparison.Ordinal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mgmtTab.Dispose();
|
||||||
|
|
||||||
|
var permissionTab = ImRaii.TabItem("Permissions");
|
||||||
|
if (permissionTab)
|
||||||
|
{
|
||||||
|
bool isDisableAnimations = perm.IsDisableAnimations();
|
||||||
|
bool isDisableSounds = perm.IsDisableSounds();
|
||||||
|
bool isDisableVfx = perm.IsDisableVFX();
|
||||||
|
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.Text("Sound Sync");
|
||||||
|
UiSharedService.BooleanToColoredIcon(!isDisableSounds);
|
||||||
|
ImGui.SameLine(230);
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(isDisableSounds ? FontAwesomeIcon.VolumeUp : FontAwesomeIcon.VolumeMute,
|
||||||
|
isDisableSounds ? "Enable sound sync" : "Disable sound sync"))
|
||||||
|
{
|
||||||
|
perm.SetDisableSounds(!perm.IsDisableSounds());
|
||||||
|
_ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.Text("Animation Sync");
|
||||||
|
UiSharedService.BooleanToColoredIcon(!isDisableAnimations);
|
||||||
|
ImGui.SameLine(230);
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(isDisableAnimations ? FontAwesomeIcon.Running : FontAwesomeIcon.Stop,
|
||||||
|
isDisableAnimations ? "Enable animation sync" : "Disable animation sync"))
|
||||||
|
{
|
||||||
|
perm.SetDisableAnimations(!perm.IsDisableAnimations());
|
||||||
|
_ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.Text("VFX Sync");
|
||||||
|
UiSharedService.BooleanToColoredIcon(!isDisableVfx);
|
||||||
|
ImGui.SameLine(230);
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(isDisableVfx ? FontAwesomeIcon.Sun : FontAwesomeIcon.Circle,
|
||||||
|
isDisableVfx ? "Enable VFX sync" : "Disable VFX sync"))
|
||||||
|
{
|
||||||
|
perm.SetDisableVFX(!perm.IsDisableVFX());
|
||||||
|
_ = _apiController.GroupChangeGroupPermissionState(new(GroupFullInfo.Group, perm));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
permissionTab.Dispose();
|
||||||
|
|
||||||
|
if (_isOwner)
|
||||||
|
{
|
||||||
|
var ownerTab = ImRaii.TabItem("Owner Settings");
|
||||||
|
if (ownerTab)
|
||||||
|
{
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted("New Password");
|
||||||
|
var availableWidth = ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
|
||||||
|
var buttonSize = UiSharedService.GetNormalizedIconTextButtonSize(FontAwesomeIcon.Passport, "Change Password").X;
|
||||||
|
var textSize = ImGui.CalcTextSize("New Password").X;
|
||||||
|
var spacing = ImGui.GetStyle().ItemSpacing.X;
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.SetNextItemWidth(availableWidth - buttonSize - textSize - spacing * 2);
|
||||||
|
ImGui.InputTextWithHint("##changepw", "Min 10 characters", ref _newPassword, 50);
|
||||||
|
ImGui.SameLine();
|
||||||
|
using (ImRaii.Disabled(_newPassword.Length < 10))
|
||||||
|
{
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Passport, "Change Password"))
|
||||||
|
{
|
||||||
|
_pwChangeSuccess = _apiController.GroupChangePassword(new GroupPasswordDto(GroupFullInfo.Group, _newPassword)).Result;
|
||||||
|
_newPassword = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Password requires to be at least 10 characters long. This action is irreversible.");
|
||||||
|
|
||||||
|
if (!_pwChangeSuccess)
|
||||||
|
{
|
||||||
|
UiSharedService.ColorTextWrapped("Failed to change the password. Password requires to be at least 10 characters long.", ImGuiColors.DalamudYellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Trash, "Delete Syncshell") && UiSharedService.CtrlPressed() && UiSharedService.ShiftPressed())
|
||||||
|
{
|
||||||
|
IsOpen = false;
|
||||||
|
_ = _apiController.GroupDelete(new(GroupFullInfo.Group));
|
||||||
|
}
|
||||||
|
UiSharedService.AttachToolTip("Hold CTRL and Shift and click to delete this Syncshell." + Environment.NewLine + "WARNING: this action is irreversible.");
|
||||||
|
}
|
||||||
|
ownerTab.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnClose()
|
||||||
|
{
|
||||||
|
Mediator.Publish(new RemoveWindowMessage(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,9 +18,7 @@ using MareSynchronos.Services;
|
|||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Services.ServerConfiguration;
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
using MareSynchronos.WebAPI;
|
using MareSynchronos.WebAPI;
|
||||||
using MareSynchronos.WebAPI.SignalR.Utils;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Globalization;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -127,15 +125,16 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public Dictionary<ushort, string> WorldData => _dalamudUtil.WorldData.Value;
|
public Dictionary<ushort, string> WorldData => _dalamudUtil.WorldData.Value;
|
||||||
|
|
||||||
public uint WorldId => _dalamudUtil.GetWorldId();
|
public uint WorldId => _dalamudUtil.GetHomeWorldId();
|
||||||
|
|
||||||
public const string TooltipSeparator = "--SEP--";
|
public const string TooltipSeparator = "--SEP--";
|
||||||
|
|
||||||
public static void AttachToolTip(string text)
|
public static void AttachToolTip(string text)
|
||||||
{
|
{
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
{
|
{
|
||||||
ImGui.BeginTooltip();
|
ImGui.BeginTooltip();
|
||||||
|
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 35f);
|
||||||
if (text.Contains(TooltipSeparator, StringComparison.Ordinal))
|
if (text.Contains(TooltipSeparator, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
var splitText = text.Split(TooltipSeparator, StringSplitOptions.RemoveEmptyEntries);
|
var splitText = text.Split(TooltipSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||||
@@ -149,13 +148,13 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.TextUnformatted(text);
|
||||||
}
|
}
|
||||||
|
ImGui.PopTextWrapPos();
|
||||||
ImGui.EndTooltip();
|
ImGui.EndTooltip();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void BooleanToColoredIcon(bool value, bool inline = true)
|
public static void BooleanToColoredIcon(bool value, bool inline = true)
|
||||||
{
|
{
|
||||||
using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
|
||||||
using var colorgreen = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen, value);
|
using var colorgreen = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen, value);
|
||||||
using var colorred = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed, !value);
|
using var colorred = ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed, !value);
|
||||||
|
|
||||||
@@ -163,11 +162,11 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
if (value)
|
if (value)
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted(FontAwesomeIcon.Check.ToIconString());
|
NormalizedIcon(FontAwesomeIcon.Check);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted(FontAwesomeIcon.Times.ToIconString());
|
NormalizedIcon(FontAwesomeIcon.Times);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,16 +206,14 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public static void ColorText(string text, Vector4 color)
|
public static void ColorText(string text, Vector4 color)
|
||||||
{
|
{
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
using var raiicolor = ImRaii.PushColor(ImGuiCol.Text, color);
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.TextUnformatted(text);
|
||||||
ImGui.PopStyleColor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ColorTextWrapped(string text, Vector4 color)
|
public static void ColorTextWrapped(string text, Vector4 color)
|
||||||
{
|
{
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, color);
|
using var raiicolor = ImRaii.PushColor(ImGuiCol.Text, color);
|
||||||
TextWrapped(text);
|
TextWrapped(text);
|
||||||
ImGui.PopStyleColor();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool CtrlPressed() => (GetKeyState(0xA2) & 0x8000) != 0 || (GetKeyState(0xA3) & 0x8000) != 0;
|
public static bool CtrlPressed() => (GetKeyState(0xA2) & 0x8000) != 0 || (GetKeyState(0xA3) & 0x8000) != 0;
|
||||||
@@ -224,50 +221,41 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
public static void DrawHelpText(string helpText)
|
public static void DrawHelpText(string helpText)
|
||||||
{
|
{
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
NormalizedIcon(FontAwesomeIcon.QuestionCircle, ImGui.GetColorU32(ImGuiCol.TextDisabled));
|
||||||
ImGui.SetWindowFontScale(0.8f);
|
AttachToolTip(helpText);
|
||||||
ImGui.TextDisabled(FontAwesomeIcon.Question.ToIconString());
|
|
||||||
ImGui.SetWindowFontScale(1.0f);
|
|
||||||
ImGui.PopFont();
|
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
{
|
|
||||||
ImGui.BeginTooltip();
|
|
||||||
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 35.0f);
|
|
||||||
ImGui.TextUnformatted(helpText);
|
|
||||||
ImGui.PopTextWrapPos();
|
|
||||||
ImGui.EndTooltip();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DrawOutlinedFont(string text, Vector4 fontColor, Vector4 outlineColor, int thickness)
|
public static void DrawOutlinedFont(string text, Vector4 fontColor, Vector4 outlineColor, int thickness)
|
||||||
{
|
{
|
||||||
var original = ImGui.GetCursorPos();
|
var original = ImGui.GetCursorPos();
|
||||||
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, outlineColor);
|
using (ImRaii.PushColor(ImGuiCol.Text, outlineColor))
|
||||||
ImGui.SetCursorPos(original with { Y = original.Y - thickness });
|
{
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.SetCursorPos(original with { Y = original.Y - thickness });
|
||||||
ImGui.SetCursorPos(original with { X = original.X - thickness });
|
ImGui.TextUnformatted(text);
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.SetCursorPos(original with { X = original.X - thickness });
|
||||||
ImGui.SetCursorPos(original with { Y = original.Y + thickness });
|
ImGui.TextUnformatted(text);
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.SetCursorPos(original with { Y = original.Y + thickness });
|
||||||
ImGui.SetCursorPos(original with { X = original.X + thickness });
|
ImGui.TextUnformatted(text);
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.SetCursorPos(original with { X = original.X + thickness });
|
||||||
ImGui.SetCursorPos(original with { X = original.X - thickness, Y = original.Y - thickness });
|
ImGui.TextUnformatted(text);
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.SetCursorPos(original with { X = original.X - thickness, Y = original.Y - thickness });
|
||||||
ImGui.SetCursorPos(original with { X = original.X + thickness, Y = original.Y + thickness });
|
ImGui.TextUnformatted(text);
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.SetCursorPos(original with { X = original.X + thickness, Y = original.Y + thickness });
|
||||||
ImGui.SetCursorPos(original with { X = original.X - thickness, Y = original.Y + thickness });
|
ImGui.TextUnformatted(text);
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.SetCursorPos(original with { X = original.X - thickness, Y = original.Y + thickness });
|
||||||
ImGui.SetCursorPos(original with { X = original.X + thickness, Y = original.Y - thickness });
|
ImGui.TextUnformatted(text);
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.SetCursorPos(original with { X = original.X + thickness, Y = original.Y - thickness });
|
||||||
ImGui.PopStyleColor();
|
ImGui.TextUnformatted(text);
|
||||||
|
}
|
||||||
|
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, fontColor);
|
using (ImRaii.PushColor(ImGuiCol.Text, fontColor))
|
||||||
ImGui.SetCursorPos(original);
|
{
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.SetCursorPos(original);
|
||||||
ImGui.SetCursorPos(original);
|
ImGui.TextUnformatted(text);
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.SetCursorPos(original);
|
||||||
ImGui.PopStyleColor();
|
ImGui.TextUnformatted(text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DrawOutlinedFont(ImDrawListPtr drawList, string text, Vector2 textPos, uint fontColor, uint outlineColor, int thickness)
|
public static void DrawOutlinedFont(ImDrawListPtr drawList, string text, Vector2 textPos, uint fontColor, uint outlineColor, int thickness)
|
||||||
@@ -293,13 +281,6 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
drawList.AddText(textPos, fontColor, text);
|
drawList.AddText(textPos, fontColor, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DrawWithID(string id, Action drawSubSection)
|
|
||||||
{
|
|
||||||
ImGui.PushID(id);
|
|
||||||
drawSubSection.Invoke();
|
|
||||||
ImGui.PopID();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void FontText(string text, ImFontPtr font, Vector4? color = null)
|
public static void FontText(string text, ImFontPtr font, Vector4? color = null)
|
||||||
{
|
{
|
||||||
using var pushedFont = ImRaii.PushFont(font);
|
using var pushedFont = ImRaii.PushFont(font);
|
||||||
@@ -309,22 +290,17 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public static Vector4 GetBoolColor(bool input) => input ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
|
public static Vector4 GetBoolColor(bool input) => input ? ImGuiColors.ParsedGreen : ImGuiColors.DalamudRed;
|
||||||
|
|
||||||
public static Vector4 GetCpuLoadColor(double input) => input < 50 ? ImGuiColors.ParsedGreen :
|
|
||||||
input < 90 ? ImGuiColors.DalamudYellow : ImGuiColors.DalamudRed;
|
|
||||||
|
|
||||||
public static Vector2 GetIconButtonSize(FontAwesomeIcon icon)
|
public static Vector2 GetIconButtonSize(FontAwesomeIcon icon)
|
||||||
{
|
{
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
||||||
var buttonSize = ImGuiHelpers.GetButtonSize(icon.ToIconString());
|
var buttonSize = ImGuiHelpers.GetButtonSize(icon.ToIconString());
|
||||||
ImGui.PopFont();
|
|
||||||
return buttonSize;
|
return buttonSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vector2 GetIconSize(FontAwesomeIcon icon)
|
public static Vector2 GetIconSize(FontAwesomeIcon icon)
|
||||||
{
|
{
|
||||||
ImGui.PushFont(UiBuilder.IconFont);
|
using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
||||||
var iconSize = ImGui.CalcTextSize(icon.ToIconString());
|
var iconSize = ImGui.CalcTextSize(icon.ToIconString());
|
||||||
ImGui.PopFont();
|
|
||||||
return iconSize;
|
return iconSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,35 +325,74 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
return ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
|
return ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IconTextButton(FontAwesomeIcon icon, string text, float? width = null, bool isInPopup = false)
|
public static Vector2 GetNormalizedIconTextButtonSize(FontAwesomeIcon icon, string text, float? width = null, bool isInPopup = false)
|
||||||
{
|
{
|
||||||
var wasClicked = false;
|
var iconData = GetIconData(icon);
|
||||||
|
|
||||||
var iconSize = GetIconSize(icon);
|
|
||||||
var textSize = ImGui.CalcTextSize(text);
|
var textSize = ImGui.CalcTextSize(text);
|
||||||
var padding = ImGui.GetStyle().FramePadding;
|
var padding = ImGui.GetStyle().FramePadding;
|
||||||
var spacing = ImGui.GetStyle().ItemSpacing;
|
var buttonSizeY = ImGui.GetFrameHeight();
|
||||||
var cursor = ImGui.GetCursorPos();
|
|
||||||
var drawList = ImGui.GetWindowDrawList();
|
|
||||||
var pos = ImGui.GetWindowPos();
|
|
||||||
|
|
||||||
Vector2 buttonSize;
|
|
||||||
var buttonSizeY = textSize.Y + padding.Y * 2;
|
|
||||||
var iconExtraSpacing = isInPopup ? padding.X * 2 : 0;
|
var iconExtraSpacing = isInPopup ? padding.X * 2 : 0;
|
||||||
|
|
||||||
var iconXoffset = iconSize.X <= iconSize.Y ? (iconSize.Y - iconSize.X) / 2f : 0;
|
|
||||||
var iconScaling = iconSize.X > iconSize.Y ? 1 / (iconSize.X / iconSize.Y) : 1;
|
|
||||||
|
|
||||||
if (width == null || width <= 0)
|
if (width == null || width <= 0)
|
||||||
{
|
{
|
||||||
var buttonSizeX = (iconScaling == 1 ? iconSize.Y : (iconSize.X * iconScaling))
|
var buttonSizeX = iconData.NormalizedIconScale.X + (padding.X * 3) + iconExtraSpacing + textSize.X;
|
||||||
+ textSize.X + padding.X * 2 + spacing.X + (iconXoffset * 2);
|
return new Vector2(buttonSizeX, buttonSizeY);
|
||||||
buttonSize = new Vector2(buttonSizeX + iconExtraSpacing, buttonSizeY);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buttonSize = new Vector2(width.Value, buttonSizeY);
|
return new Vector2(width.Value, buttonSizeY);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Vector2 NormalizedIconButtonSize(FontAwesomeIcon icon)
|
||||||
|
{
|
||||||
|
var iconData = GetIconData(icon);
|
||||||
|
var padding = ImGui.GetStyle().FramePadding;
|
||||||
|
|
||||||
|
return iconData.NormalizedIconScale with { X = iconData.NormalizedIconScale.X + padding.X * 2, Y = iconData.NormalizedIconScale.Y + padding.Y * 2 };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool NormalizedIconButton(FontAwesomeIcon icon)
|
||||||
|
{
|
||||||
|
bool wasClicked = false;
|
||||||
|
var iconData = GetIconData(icon);
|
||||||
|
var padding = ImGui.GetStyle().FramePadding;
|
||||||
|
var cursor = ImGui.GetCursorPos();
|
||||||
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
var pos = ImGui.GetWindowPos();
|
||||||
|
var scrollPosY = ImGui.GetScrollY();
|
||||||
|
var scrollPosX = ImGui.GetScrollX();
|
||||||
|
|
||||||
|
var buttonSize = NormalizedIconButtonSize(icon);
|
||||||
|
|
||||||
|
if (ImGui.Button("###" + icon.ToIconString(), buttonSize))
|
||||||
|
{
|
||||||
|
wasClicked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconData.IconScaling,
|
||||||
|
new(pos.X - scrollPosX + cursor.X + iconData.OffsetX + padding.X,
|
||||||
|
pos.Y - scrollPosY + cursor.Y + (buttonSize.Y - (iconData.IconSize.Y * iconData.IconScaling)) / 2f),
|
||||||
|
ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString());
|
||||||
|
|
||||||
|
return wasClicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool NormalizedIconTextButton(FontAwesomeIcon icon, string text, float? width = null, bool isInPopup = false)
|
||||||
|
{
|
||||||
|
var wasClicked = false;
|
||||||
|
|
||||||
|
var iconData = GetIconData(icon);
|
||||||
|
var textSize = ImGui.CalcTextSize(text);
|
||||||
|
var padding = ImGui.GetStyle().FramePadding;
|
||||||
|
var cursor = ImGui.GetCursorPos();
|
||||||
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
var pos = ImGui.GetWindowPos();
|
||||||
|
var scrollPosY = ImGui.GetScrollY();
|
||||||
|
var scrollPosX = ImGui.GetScrollX();
|
||||||
|
|
||||||
|
Vector2 buttonSize = GetNormalizedIconTextButtonSize(icon, text, width, isInPopup);
|
||||||
|
var iconExtraSpacing = isInPopup ? padding.X * 2 : 0;
|
||||||
|
|
||||||
using (ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.PopupBg), isInPopup))
|
using (ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.PopupBg), isInPopup))
|
||||||
{
|
{
|
||||||
@@ -387,19 +402,70 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconScaling,
|
|
||||||
new(pos.X + cursor.X + iconXoffset + padding.X,
|
|
||||||
pos.Y + cursor.Y + (buttonSizeY - (iconSize.Y * iconScaling)) / 2f),
|
|
||||||
ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString());
|
|
||||||
|
|
||||||
drawList.AddText(UiBuilder.DefaultFont, ImGui.GetFontSize(),
|
drawList.AddText(UiBuilder.DefaultFont, ImGui.GetFontSize(),
|
||||||
new(pos.X + cursor.X + (padding.X) + spacing.X + (iconSize.X * iconScaling) + (iconXoffset * 2) + iconExtraSpacing,
|
new(pos.X - scrollPosX + cursor.X + iconData.NormalizedIconScale.X + (padding.X * 2) + iconExtraSpacing,
|
||||||
pos.Y + cursor.Y + ((buttonSizeY - textSize.Y) / 2f)),
|
pos.Y - scrollPosY + cursor.Y + ((buttonSize.Y - textSize.Y) / 2f)),
|
||||||
ImGui.GetColorU32(ImGuiCol.Text), text);
|
ImGui.GetColorU32(ImGuiCol.Text), text);
|
||||||
|
|
||||||
|
drawList.AddText(UiBuilder.IconFont, ImGui.GetFontSize() * iconData.IconScaling,
|
||||||
|
new(pos.X - scrollPosX + cursor.X + iconData.OffsetX + padding.X,
|
||||||
|
pos.Y - scrollPosY + cursor.Y + (buttonSize.Y - (iconData.IconSize.Y * iconData.IconScaling)) / 2f),
|
||||||
|
ImGui.GetColorU32(ImGuiCol.Text), icon.ToIconString());
|
||||||
|
|
||||||
return wasClicked;
|
return wasClicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void NormalizedIcon(FontAwesomeIcon icon, uint color)
|
||||||
|
{
|
||||||
|
var cursorPos = ImGui.GetCursorPos();
|
||||||
|
var iconData = GetIconData(icon);
|
||||||
|
var drawList = ImGui.GetWindowDrawList();
|
||||||
|
var windowPos = ImGui.GetWindowPos();
|
||||||
|
var scrollPosX = ImGui.GetScrollX();
|
||||||
|
var scrollPosY = ImGui.GetScrollY();
|
||||||
|
var frameHeight = ImGui.GetFrameHeight();
|
||||||
|
|
||||||
|
var frameOffsetY = ((frameHeight - iconData.IconSize.Y * iconData.IconScaling) / 2f);
|
||||||
|
|
||||||
|
drawList.AddText(UiBuilder.IconFont, UiBuilder.IconFont.FontSize * iconData.IconScaling,
|
||||||
|
new(windowPos.X - scrollPosX + cursorPos.X + iconData.OffsetX,
|
||||||
|
windowPos.Y - scrollPosY + cursorPos.Y + frameOffsetY),
|
||||||
|
color, icon.ToIconString());
|
||||||
|
|
||||||
|
ImGui.Dummy(new(iconData.NormalizedIconScale.X, ImGui.GetFrameHeight()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void NormalizedIcon(FontAwesomeIcon icon, Vector4? color = null)
|
||||||
|
{
|
||||||
|
NormalizedIcon(icon, color == null ? ImGui.GetColorU32(ImGuiCol.Text) : ImGui.GetColorU32(color.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IconScaleData CalcIconScaleData(FontAwesomeIcon icon)
|
||||||
|
{
|
||||||
|
using var font = ImRaii.PushFont(UiBuilder.IconFont);
|
||||||
|
var iconSize = ImGui.CalcTextSize(icon.ToIconString());
|
||||||
|
var iconscaling = (iconSize.X < iconSize.Y ? (iconSize.Y - iconSize.X) / 2f : 0f, iconSize.X > iconSize.Y ? 1f / (iconSize.X / iconSize.Y) : 1f);
|
||||||
|
var normalized = iconscaling.Item2 == 1f ?
|
||||||
|
new Vector2(iconSize.Y, iconSize.Y)
|
||||||
|
: new((iconSize.X * iconscaling.Item2) + (iconscaling.Item1 * 2), (iconSize.X * iconscaling.Item2) + (iconscaling.Item1 * 2));
|
||||||
|
return new(iconSize, normalized, iconscaling.Item1, iconscaling.Item2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IconScaleData GetIconData(FontAwesomeIcon icon)
|
||||||
|
{
|
||||||
|
if (_iconData.TryGetValue(ImGuiHelpers.GlobalScale, out var iconCache))
|
||||||
|
{
|
||||||
|
if (iconCache.TryGetValue(icon, out var iconData)) return iconData;
|
||||||
|
return iconCache[icon] = CalcIconScaleData(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
_iconData.Add(ImGuiHelpers.GlobalScale, new());
|
||||||
|
return _iconData[ImGuiHelpers.GlobalScale][icon] = CalcIconScaleData(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record IconScaleData(Vector2 IconSize, Vector2 NormalizedIconScale, float OffsetX, float IconScaling);
|
||||||
|
private static Dictionary<float, Dictionary<FontAwesomeIcon, IconScaleData>> _iconData = new();
|
||||||
|
|
||||||
public static bool IsDirectoryWritable(string dirPath, bool throwIfFails = false)
|
public static bool IsDirectoryWritable(string dirPath, bool throwIfFails = false)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -422,23 +488,6 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void OutlineTextWrapped(string text, Vector4 textcolor, Vector4 outlineColor, float dist = 3)
|
|
||||||
{
|
|
||||||
var cursorPos = ImGui.GetCursorPos();
|
|
||||||
ColorTextWrapped(text, outlineColor);
|
|
||||||
ImGui.SetCursorPos(new(cursorPos.X, cursorPos.Y + dist));
|
|
||||||
ColorTextWrapped(text, outlineColor);
|
|
||||||
ImGui.SetCursorPos(new(cursorPos.X + dist, cursorPos.Y));
|
|
||||||
ColorTextWrapped(text, outlineColor);
|
|
||||||
ImGui.SetCursorPos(new(cursorPos.X + dist, cursorPos.Y + dist));
|
|
||||||
ColorTextWrapped(text, outlineColor);
|
|
||||||
|
|
||||||
ImGui.SetCursorPos(new(cursorPos.X + dist / 2, cursorPos.Y + dist / 2));
|
|
||||||
ColorTextWrapped(text, textcolor);
|
|
||||||
ImGui.SetCursorPos(new(cursorPos.X + dist / 2, cursorPos.Y + dist / 2));
|
|
||||||
ColorTextWrapped(text, textcolor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetScaledWindowSize(float width, bool centerWindow = true)
|
public static void SetScaledWindowSize(float width, bool centerWindow = true)
|
||||||
{
|
{
|
||||||
var newLineHeight = ImGui.GetCursorPosY();
|
var newLineHeight = ImGui.GetCursorPosY();
|
||||||
@@ -510,9 +559,8 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public void BigText(string text)
|
public void BigText(string text)
|
||||||
{
|
{
|
||||||
if (UidFontBuilt) ImGui.PushFont(UidFont);
|
using var font = ImRaii.PushFont(UidFont, UidFontBuilt);
|
||||||
ImGui.TextUnformatted(text);
|
ImGui.TextUnformatted(text);
|
||||||
if (UidFontBuilt) ImGui.PopFont();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DrawCacheDirectorySetting()
|
public void DrawCacheDirectorySetting()
|
||||||
@@ -771,7 +819,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
var text = "Connect";
|
var text = "Connect";
|
||||||
if (_serverSelectionIndex == _serverConfigurationManager.CurrentServerIndex) text = "Reconnect";
|
if (_serverSelectionIndex == _serverConfigurationManager.CurrentServerIndex) text = "Reconnect";
|
||||||
if (IconTextButton(FontAwesomeIcon.Link, text))
|
if (NormalizedIconTextButton(FontAwesomeIcon.Link, text))
|
||||||
{
|
{
|
||||||
_serverConfigurationManager.SelectServer(_serverSelectionIndex);
|
_serverConfigurationManager.SelectServer(_serverSelectionIndex);
|
||||||
_ = _apiController.CreateConnections();
|
_ = _apiController.CreateConnections();
|
||||||
@@ -784,7 +832,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
ImGui.InputText("Custom Service URI", ref _customServerUri, 255);
|
ImGui.InputText("Custom Service URI", ref _customServerUri, 255);
|
||||||
ImGui.SetNextItemWidth(250);
|
ImGui.SetNextItemWidth(250);
|
||||||
ImGui.InputText("Custom Service Name", ref _customServerName, 255);
|
ImGui.InputText("Custom Service Name", ref _customServerName, 255);
|
||||||
if (UiSharedService.IconTextButton(FontAwesomeIcon.Plus, "Add Custom Service")
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Plus, "Add Custom Service")
|
||||||
&& !string.IsNullOrEmpty(_customServerUri)
|
&& !string.IsNullOrEmpty(_customServerUri)
|
||||||
&& !string.IsNullOrEmpty(_customServerName))
|
&& !string.IsNullOrEmpty(_customServerName))
|
||||||
{
|
{
|
||||||
@@ -827,24 +875,6 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
Strings.ToS = new Strings.ToSStrings();
|
Strings.ToS = new Strings.ToSStrings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrintServerState()
|
|
||||||
{
|
|
||||||
if (_apiController.ServerState is ServerState.Connected)
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted("Service " + _serverConfigurationManager.CurrentServer!.ServerName + ":");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextColored(ImGuiColors.ParsedGreen, "Available");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted("(");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextColored(ImGuiColors.ParsedGreen, _apiController.OnlineUsers.ToString(CultureInfo.InvariantCulture));
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted("Users Online");
|
|
||||||
ImGui.SameLine();
|
|
||||||
ImGui.TextUnformatted(")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RecalculateFileCacheSize()
|
public void RecalculateFileCacheSize()
|
||||||
{
|
{
|
||||||
_cacheScanner.InvokeScan(forced: true);
|
_cacheScanner.InvokeScan(forced: true);
|
||||||
@@ -876,7 +906,9 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
|||||||
ImGui.SetWindowPos(new Vector2(center.X - width / 2, center.Y - height / 2), cond);
|
ImGui.SetWindowPos(new Vector2(center.X - width / 2, center.Y - height / 2), cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning disable MA0009 // Add regex evaluation timeout
|
||||||
[GeneratedRegex(@"^(?:[a-zA-Z]:\\[\w\s\-\\]+?|\/(?:[\w\s\-\/])+?)$", RegexOptions.ECMAScript)]
|
[GeneratedRegex(@"^(?:[a-zA-Z]:\\[\w\s\-\\]+?|\/(?:[\w\s\-\/])+?)$", RegexOptions.ECMAScript)]
|
||||||
|
#pragma warning restore MA0009 // Add regex evaluation timeout
|
||||||
private static partial Regex PathRegex();
|
private static partial Regex PathRegex();
|
||||||
|
|
||||||
private void BuildFont()
|
private void BuildFont()
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ public static class VariousExtensions
|
|||||||
cachedPlayer, objectKind, hasNewButNotOldFileReplacements, hasOldButNotNewFileReplacements, hasNewButNotOldGlamourerData, hasOldButNotNewGlamourerData, PlayerChanges.ModFiles, PlayerChanges.Glamourer);
|
cachedPlayer, objectKind, hasNewButNotOldFileReplacements, hasOldButNotNewFileReplacements, hasNewButNotOldGlamourerData, hasOldButNotNewGlamourerData, PlayerChanges.ModFiles, PlayerChanges.Glamourer);
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles);
|
charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles);
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.Glamourer);
|
charaDataToUpdate[objectKind].Add(PlayerChanges.Glamourer);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -69,6 +70,43 @@ public static class VariousExtensions
|
|||||||
{
|
{
|
||||||
logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (FileReplacements not equal) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.ModFiles);
|
logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (FileReplacements not equal) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.ModFiles);
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles);
|
charaDataToUpdate[objectKind].Add(PlayerChanges.ModFiles);
|
||||||
|
if (forceApplyMods || objectKind != ObjectKind.Player)
|
||||||
|
{
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var existingFace = existingFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/face/", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
var existingHair = existingFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/hair/", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
var existingTail = existingFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/tail/", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
var newFace = newFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/face/", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
var newHair = newFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/hair/", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
var newTail = newFileReplacements.Where(g => g.GamePaths.Any(p => p.Contains("/tail/", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
var existingTransients = existingFileReplacements.Where(g => g.GamePaths.Any(g => !g.EndsWith("mdl") && !g.EndsWith("tex") && !g.EndsWith("mtrl")))
|
||||||
|
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
var newTransients = newFileReplacements.Where(g => g.GamePaths.Any(g => !g.EndsWith("mdl") && !g.EndsWith("tex") && !g.EndsWith("mtrl")))
|
||||||
|
.OrderBy(g => string.IsNullOrEmpty(g.Hash) ? g.FileSwapPath : g.Hash, StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
|
||||||
|
logger.LogTrace("[BASE-{appbase}] ExistingFace: {of}, NewFace: {fc}; ExistingHair: {eh}, NewHair: {nh}; ExistingTail: {et}, NewTail: {nt}; ExistingTransient: {etr}, NewTransient: {ntr}", applicationBase,
|
||||||
|
existingFace.Count, newFace.Count, existingHair.Count, newHair.Count, existingTail.Count, newTail.Count, existingTransients.Count, newTransients.Count);
|
||||||
|
|
||||||
|
var differentFace = !existingFace.SequenceEqual(newFace, PlayerData.Data.FileReplacementDataComparer.Instance);
|
||||||
|
var differentHair = !existingHair.SequenceEqual(newHair, PlayerData.Data.FileReplacementDataComparer.Instance);
|
||||||
|
var differentTail = !existingTail.SequenceEqual(newTail, PlayerData.Data.FileReplacementDataComparer.Instance);
|
||||||
|
var differenTransients = !existingTransients.SequenceEqual(newTransients, PlayerData.Data.FileReplacementDataComparer.Instance);
|
||||||
|
if (differentFace || differentHair || differentTail || differenTransients)
|
||||||
|
{
|
||||||
|
logger.LogDebug("[BASE-{appbase}] Different Subparts: Face: {face}, Hair: {hair}, Tail: {tail}, Transients: {transients} => {change}", applicationBase,
|
||||||
|
differentFace, differentHair, differentTail, differenTransients, PlayerChanges.ForcedRedraw);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,6 +138,7 @@ public static class VariousExtensions
|
|||||||
{
|
{
|
||||||
logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff manip data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.ModManip);
|
logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff manip data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.ModManip);
|
||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.ModManip);
|
charaDataToUpdate[objectKind].Add(PlayerChanges.ModManip);
|
||||||
|
charaDataToUpdate[objectKind].Add(PlayerChanges.ForcedRedraw);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool heelsOffsetDifferent = !string.Equals(oldData.HeelsData, newData.HeelsData, StringComparison.Ordinal);
|
bool heelsOffsetDifferent = !string.Equals(oldData.HeelsData, newData.HeelsData, StringComparison.Ordinal);
|
||||||
@@ -109,7 +148,9 @@ public static class VariousExtensions
|
|||||||
charaDataToUpdate[objectKind].Add(PlayerChanges.Heels);
|
charaDataToUpdate[objectKind].Add(PlayerChanges.Heels);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool palettePlusDataDifferent = !string.Equals(oldData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal);
|
bool palettePlusDataDifferent = !string.Equals(oldData.PalettePlusData, newData.PalettePlusData, StringComparison.Ordinal)
|
||||||
|
|| (charaDataToUpdate.TryGetValue(objectKind, out var playerChanges) && playerChanges.Contains(PlayerChanges.Glamourer)
|
||||||
|
&& (!string.IsNullOrEmpty(oldData.PalettePlusData) || !string.IsNullOrEmpty(newData.PalettePlusData)));
|
||||||
if (palettePlusDataDifferent || (forceApplyCustomization && !string.IsNullOrEmpty(newData.PalettePlusData)))
|
if (palettePlusDataDifferent || (forceApplyCustomization && !string.IsNullOrEmpty(newData.PalettePlusData)))
|
||||||
{
|
{
|
||||||
logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff palette data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.Palette);
|
logger.LogDebug("[BASE-{appBase}] Updating {object}/{kind} (Diff palette data) => {change}", applicationBase, cachedPlayer, objectKind, PlayerChanges.Palette);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
private readonly FileCompactor _fileCompactor;
|
private readonly FileCompactor _fileCompactor;
|
||||||
private readonly FileCacheManager _fileDbManager;
|
private readonly FileCacheManager _fileDbManager;
|
||||||
private readonly FileTransferOrchestrator _orchestrator;
|
private readonly FileTransferOrchestrator _orchestrator;
|
||||||
|
private readonly List<ThrottledStream> _activeDownloadStreams;
|
||||||
|
|
||||||
public FileDownloadManager(ILogger<FileDownloadManager> logger, MareMediator mediator,
|
public FileDownloadManager(ILogger<FileDownloadManager> logger, MareMediator mediator,
|
||||||
FileTransferOrchestrator orchestrator,
|
FileTransferOrchestrator orchestrator,
|
||||||
@@ -28,6 +29,18 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
_orchestrator = orchestrator;
|
_orchestrator = orchestrator;
|
||||||
_fileDbManager = fileCacheManager;
|
_fileDbManager = fileCacheManager;
|
||||||
_fileCompactor = fileCompactor;
|
_fileCompactor = fileCompactor;
|
||||||
|
_activeDownloadStreams = [];
|
||||||
|
|
||||||
|
Mediator.Subscribe<DownloadLimitChangedMessage>(this, (msg) =>
|
||||||
|
{
|
||||||
|
if (!_activeDownloadStreams.Any()) return;
|
||||||
|
var newLimit = _orchestrator.DownloadLimitPerSlot();
|
||||||
|
Logger.LogTrace("Setting new Download Speed Limit to {newLimit}", newLimit);
|
||||||
|
foreach (var stream in _activeDownloadStreams)
|
||||||
|
{
|
||||||
|
stream.BandwidthLimit = newLimit;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<DownloadFileTransfer> CurrentDownloads { get; private set; } = [];
|
public List<DownloadFileTransfer> CurrentDownloads { get; private set; } = [];
|
||||||
@@ -71,6 +84,14 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
CancelDownload();
|
CancelDownload();
|
||||||
|
foreach (var stream in _activeDownloadStreams)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stream.Dispose();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,6 +154,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ThrottledStream? stream = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var fileStream = File.Create(tempPath);
|
var fileStream = File.Create(tempPath);
|
||||||
@@ -142,7 +164,10 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
var buffer = new byte[bufferSize];
|
var buffer = new byte[bufferSize];
|
||||||
|
|
||||||
var bytesRead = 0;
|
var bytesRead = 0;
|
||||||
var stream = await response.Content.ReadAsStreamAsync(ct).ConfigureAwait(false);
|
var limit = _orchestrator.DownloadLimitPerSlot();
|
||||||
|
Logger.LogTrace("Starting Download of {id} with a speed limit of {limit}", requestId, limit);
|
||||||
|
stream = new ThrottledStream(await response.Content.ReadAsStreamAsync(ct).ConfigureAwait(false), limit);
|
||||||
|
_activeDownloadStreams.Add(stream);
|
||||||
while ((bytesRead = await stream.ReadAsync(buffer, ct).ConfigureAwait(false)) > 0)
|
while ((bytesRead = await stream.ReadAsync(buffer, ct).ConfigureAwait(false)) > 0)
|
||||||
{
|
{
|
||||||
ct.ThrowIfCancellationRequested();
|
ct.ThrowIfCancellationRequested();
|
||||||
@@ -171,6 +196,14 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (stream != null)
|
||||||
|
{
|
||||||
|
_activeDownloadStreams.Remove(stream);
|
||||||
|
await stream.DisposeAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DownloadFilesInternal(GameObjectHandler gameObjectHandler, List<FileReplacementData> fileReplacement, CancellationToken ct)
|
private async Task DownloadFilesInternal(GameObjectHandler gameObjectHandler, List<FileReplacementData> fileReplacement, CancellationToken ct)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public class FileTransferOrchestrator : DisposableMediatorSubscriberBase
|
|||||||
private readonly TokenProvider _tokenProvider;
|
private readonly TokenProvider _tokenProvider;
|
||||||
private int _availableDownloadSlots;
|
private int _availableDownloadSlots;
|
||||||
private SemaphoreSlim _downloadSemaphore;
|
private SemaphoreSlim _downloadSemaphore;
|
||||||
|
private int CurrentlyUsedDownloadSlots => _availableDownloadSlots - _downloadSemaphore.CurrentCount;
|
||||||
|
|
||||||
public FileTransferOrchestrator(ILogger<FileTransferOrchestrator> logger, MareConfigService mareConfig,
|
public FileTransferOrchestrator(ILogger<FileTransferOrchestrator> logger, MareConfigService mareConfig,
|
||||||
MareMediator mediator, TokenProvider tokenProvider) : base(logger, mediator)
|
MareMediator mediator, TokenProvider tokenProvider) : base(logger, mediator)
|
||||||
@@ -72,6 +73,7 @@ public class FileTransferOrchestrator : DisposableMediatorSubscriberBase
|
|||||||
public void ReleaseDownloadSlot()
|
public void ReleaseDownloadSlot()
|
||||||
{
|
{
|
||||||
_downloadSemaphore.Release();
|
_downloadSemaphore.Release();
|
||||||
|
Mediator.Publish(new DownloadLimitChangedMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<HttpResponseMessage> SendRequestAsync(HttpMethod method, Uri uri,
|
public async Task<HttpResponseMessage> SendRequestAsync(HttpMethod method, Uri uri,
|
||||||
@@ -110,12 +112,28 @@ public class FileTransferOrchestrator : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _downloadSemaphore.WaitAsync(token).ConfigureAwait(false);
|
await _downloadSemaphore.WaitAsync(token).ConfigureAwait(false);
|
||||||
|
Mediator.Publish(new DownloadLimitChangedMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
public long DownloadLimitPerSlot()
|
||||||
|
{
|
||||||
|
var limit = _mareConfig.Current.DownloadSpeedLimitInBytes;
|
||||||
|
if (limit <= 0) return 0;
|
||||||
|
limit = _mareConfig.Current.DownloadSpeedType switch
|
||||||
|
{
|
||||||
|
MareConfiguration.Models.DownloadSpeeds.Bps => limit,
|
||||||
|
MareConfiguration.Models.DownloadSpeeds.KBps => limit * 1024,
|
||||||
|
MareConfiguration.Models.DownloadSpeeds.MBps => limit * 1024 * 1024,
|
||||||
|
_ => limit,
|
||||||
|
};
|
||||||
|
var dividedLimit = limit / (CurrentlyUsedDownloadSlots == 0 ? 1 : CurrentlyUsedDownloadSlots);
|
||||||
|
return dividedLimit == 0 ? 1 : dividedLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<HttpResponseMessage> SendRequestInternalAsync(HttpRequestMessage requestMessage,
|
private async Task<HttpResponseMessage> SendRequestInternalAsync(HttpRequestMessage requestMessage,
|
||||||
CancellationToken? ct = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead)
|
CancellationToken? ct = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead)
|
||||||
{
|
{
|
||||||
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", await _tokenProvider.GetOrUpdateToken(ct!.Value).ConfigureAwait(false));
|
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _tokenProvider.GetToken());
|
||||||
|
|
||||||
if (requestMessage.Content != null && requestMessage.Content is not StreamContent && requestMessage.Content is not ByteArrayContent)
|
if (requestMessage.Content != null && requestMessage.Content is not StreamContent && requestMessage.Content is not ByteArrayContent)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ public sealed class FileUploadManager : DisposableMediatorSubscriberBase
|
|||||||
Logger.LogDebug("Verifying {count} files", unverifiedUploadHashes.Count);
|
Logger.LogDebug("Verifying {count} files", unverifiedUploadHashes.Count);
|
||||||
var filesToUpload = await FilesSend([.. unverifiedUploadHashes], visiblePlayers.Select(p => p.UID).ToList(), uploadToken).ConfigureAwait(false);
|
var filesToUpload = await FilesSend([.. unverifiedUploadHashes], visiblePlayers.Select(p => p.UID).ToList(), uploadToken).ConfigureAwait(false);
|
||||||
|
|
||||||
foreach (var file in filesToUpload.Where(f => !f.IsForbidden))
|
foreach (var file in filesToUpload.Where(f => !f.IsForbidden).DistinctBy(f => f.Hash))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
217
MareSynchronos/WebAPI/Files/ThrottledStream.cs
Normal file
217
MareSynchronos/WebAPI/Files/ThrottledStream.cs
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos.WebAPI.Files
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class for streaming data with throttling support.
|
||||||
|
/// Borrowed from https://github.com/bezzad/Downloader
|
||||||
|
/// </summary>
|
||||||
|
internal class ThrottledStream : Stream
|
||||||
|
{
|
||||||
|
public static long Infinite => long.MaxValue;
|
||||||
|
private readonly Stream _baseStream;
|
||||||
|
private long _bandwidthLimit;
|
||||||
|
private Bandwidth _bandwidth;
|
||||||
|
private CancellationTokenSource _bandwidthChangeTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="T:ThrottledStream" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseStream">The base stream.</param>
|
||||||
|
/// <param name="bandwidthLimit">The maximum bytes per second that can be transferred through the base stream.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <see cref="baseStream" /> is a null reference.</exception>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException">Thrown when <see cref="BandwidthLimit" /> is a negative value.</exception>
|
||||||
|
public ThrottledStream(Stream baseStream, long bandwidthLimit)
|
||||||
|
{
|
||||||
|
if (bandwidthLimit < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(bandwidthLimit),
|
||||||
|
bandwidthLimit, "The maximum number of bytes per second can't be negative.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_baseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream));
|
||||||
|
BandwidthLimit = bandwidthLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bandwidth Limit (in B/s)
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The maximum bytes per second.</value>
|
||||||
|
public long BandwidthLimit
|
||||||
|
{
|
||||||
|
get => _bandwidthLimit;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_bandwidthLimit == value) return;
|
||||||
|
_bandwidthLimit = value <= 0 ? Infinite : value;
|
||||||
|
_bandwidth ??= new Bandwidth();
|
||||||
|
_bandwidth.BandwidthLimit = _bandwidthLimit;
|
||||||
|
_bandwidthChangeTokenSource.Cancel();
|
||||||
|
_bandwidthChangeTokenSource.Dispose();
|
||||||
|
_bandwidthChangeTokenSource = new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanRead => _baseStream.CanRead;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanSeek => _baseStream.CanSeek;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanWrite => _baseStream.CanWrite;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override long Length => _baseStream.Length;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => _baseStream.Position;
|
||||||
|
set => _baseStream.Position = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
_baseStream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
return _baseStream.Seek(offset, origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void SetLength(long value)
|
||||||
|
{
|
||||||
|
_baseStream.SetLength(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
Throttle(count).Wait();
|
||||||
|
return _baseStream.Read(buffer, offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await Throttle(count, cancellationToken).ConfigureAwait(false);
|
||||||
|
return await _baseStream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
Throttle(count).Wait();
|
||||||
|
_baseStream.Write(buffer, offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await Throttle(count, cancellationToken).ConfigureAwait(false);
|
||||||
|
await _baseStream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
_baseStream.Close();
|
||||||
|
base.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Throttle(int transmissionVolume, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
// Make sure the buffer isn't empty.
|
||||||
|
if (BandwidthLimit > 0 && transmissionVolume > 0)
|
||||||
|
{
|
||||||
|
// Calculate the time to sleep.
|
||||||
|
_bandwidth.CalculateSpeed(transmissionVolume);
|
||||||
|
await Sleep(_bandwidth.PopSpeedRetrieveTime(), token).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Sleep(int time, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (time > 0)
|
||||||
|
{
|
||||||
|
var bandWidthtoken = _bandwidthChangeTokenSource.Token;
|
||||||
|
var linked = CancellationTokenSource.CreateLinkedTokenSource(token, bandWidthtoken).Token;
|
||||||
|
await Task.Delay(time, linked).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException)
|
||||||
|
{
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return _baseStream.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Bandwidth
|
||||||
|
{
|
||||||
|
private long _count;
|
||||||
|
private int _lastSecondCheckpoint;
|
||||||
|
private long _lastTransferredBytesCount;
|
||||||
|
private int _speedRetrieveTime;
|
||||||
|
public double Speed { get; private set; }
|
||||||
|
public double AverageSpeed { get; private set; }
|
||||||
|
public long BandwidthLimit { get; set; }
|
||||||
|
|
||||||
|
public Bandwidth()
|
||||||
|
{
|
||||||
|
BandwidthLimit = long.MaxValue;
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CalculateSpeed(long receivedBytesCount)
|
||||||
|
{
|
||||||
|
int elapsedTime = Environment.TickCount - _lastSecondCheckpoint + 1;
|
||||||
|
receivedBytesCount = Interlocked.Add(ref _lastTransferredBytesCount, receivedBytesCount);
|
||||||
|
double momentSpeed = receivedBytesCount * 1000 / elapsedTime; // B/s
|
||||||
|
|
||||||
|
if (1000 < elapsedTime)
|
||||||
|
{
|
||||||
|
Speed = momentSpeed;
|
||||||
|
AverageSpeed = ((AverageSpeed * _count) + Speed) / (_count + 1);
|
||||||
|
_count++;
|
||||||
|
SecondCheckpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (momentSpeed >= BandwidthLimit)
|
||||||
|
{
|
||||||
|
var expectedTime = receivedBytesCount * 1000 / BandwidthLimit;
|
||||||
|
Interlocked.Add(ref _speedRetrieveTime, (int)expectedTime - elapsedTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int PopSpeedRetrieveTime()
|
||||||
|
{
|
||||||
|
return Interlocked.Exchange(ref _speedRetrieveTime, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
SecondCheckpoint();
|
||||||
|
_count = 0;
|
||||||
|
Speed = 0;
|
||||||
|
AverageSpeed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SecondCheckpoint()
|
||||||
|
{
|
||||||
|
Interlocked.Exchange(ref _lastSecondCheckpoint, Environment.TickCount);
|
||||||
|
Interlocked.Exchange(ref _lastTransferredBytesCount, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -103,6 +103,7 @@ public partial class ApiController
|
|||||||
sb.AppendLine($"GlamourerData for {item.Key}: {!string.IsNullOrEmpty(item.Value)}");
|
sb.AppendLine($"GlamourerData for {item.Key}: {!string.IsNullOrEmpty(item.Value)}");
|
||||||
}
|
}
|
||||||
Logger.LogDebug("Chara data contained: {nl} {data}", Environment.NewLine, sb.ToString());
|
Logger.LogDebug("Chara data contained: {nl} {data}", Environment.NewLine, sb.ToString());
|
||||||
|
|
||||||
await UserPushData(new(visibleCharacters, character)).ConfigureAwait(false);
|
await UserPushData(new(visibleCharacters, character)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using MareSynchronos.API.Data;
|
using MareSynchronos.API.Data;
|
||||||
using MareSynchronos.API.Data.Extensions;
|
using MareSynchronos.API.Data.Extensions;
|
||||||
using MareSynchronos.API.Dto;
|
using MareSynchronos.API.Dto;
|
||||||
|
using MareSynchronos.API.Dto.User;
|
||||||
using MareSynchronos.API.SignalR;
|
using MareSynchronos.API.SignalR;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
@@ -33,6 +34,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
private bool _initialized;
|
private bool _initialized;
|
||||||
private HubConnection? _mareHub;
|
private HubConnection? _mareHub;
|
||||||
private ServerState _serverState;
|
private ServerState _serverState;
|
||||||
|
private CensusUpdateMessage? _lastCensus;
|
||||||
|
|
||||||
public ApiController(ILogger<ApiController> logger, HubFactory hubFactory, DalamudUtilService dalamudUtil,
|
public ApiController(ILogger<ApiController> logger, HubFactory hubFactory, DalamudUtilService dalamudUtil,
|
||||||
PairManager pairManager, ServerConfigurationManager serverManager, MareMediator mediator,
|
PairManager pairManager, ServerConfigurationManager serverManager, MareMediator mediator,
|
||||||
@@ -48,9 +50,10 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
Mediator.Subscribe<DalamudLoginMessage>(this, (_) => DalamudUtilOnLogIn());
|
Mediator.Subscribe<DalamudLoginMessage>(this, (_) => DalamudUtilOnLogIn());
|
||||||
Mediator.Subscribe<DalamudLogoutMessage>(this, (_) => DalamudUtilOnLogOut());
|
Mediator.Subscribe<DalamudLogoutMessage>(this, (_) => DalamudUtilOnLogOut());
|
||||||
Mediator.Subscribe<HubClosedMessage>(this, (msg) => MareHubOnClosed(msg.Exception));
|
Mediator.Subscribe<HubClosedMessage>(this, (msg) => MareHubOnClosed(msg.Exception));
|
||||||
Mediator.Subscribe<HubReconnectedMessage>(this, (msg) => _ = Task.Run(MareHubOnReconnected));
|
Mediator.Subscribe<HubReconnectedMessage>(this, async (msg) => await MareHubOnReconnected().ConfigureAwait(false));
|
||||||
Mediator.Subscribe<HubReconnectingMessage>(this, (msg) => MareHubOnReconnecting(msg.Exception));
|
Mediator.Subscribe<HubReconnectingMessage>(this, (msg) => MareHubOnReconnecting(msg.Exception));
|
||||||
Mediator.Subscribe<CyclePauseMessage>(this, (msg) => _ = CyclePause(msg.UserData));
|
Mediator.Subscribe<CyclePauseMessage>(this, (msg) => _ = CyclePause(msg.UserData));
|
||||||
|
Mediator.Subscribe<CensusUpdateMessage>(this, (msg) => _lastCensus = msg);
|
||||||
|
|
||||||
ServerState = ServerState.Offline;
|
ServerState = ServerState.Offline;
|
||||||
|
|
||||||
@@ -185,7 +188,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
$"Your client is outdated ({currentClientVer.Major}.{currentClientVer.Minor}.{currentClientVer.Build}), current is: " +
|
$"Your client is outdated ({currentClientVer.Major}.{currentClientVer.Minor}.{currentClientVer.Build}), current is: " +
|
||||||
$"{_connectionDto.CurrentClientVersion.Major}.{_connectionDto.CurrentClientVersion.Minor}.{_connectionDto.CurrentClientVersion.Build}. " +
|
$"{_connectionDto.CurrentClientVersion.Major}.{_connectionDto.CurrentClientVersion.Minor}.{_connectionDto.CurrentClientVersion.Build}. " +
|
||||||
$"Please keep your Mare Synchronos client up-to-date.",
|
$"Please keep your Mare Synchronos client up-to-date.",
|
||||||
Dalamud.Interface.Internal.Notifications.NotificationType.Error));
|
Dalamud.Interface.Internal.Notifications.NotificationType.Warning));
|
||||||
}
|
}
|
||||||
|
|
||||||
await LoadIninitialPairs().ConfigureAwait(false);
|
await LoadIninitialPairs().ConfigureAwait(false);
|
||||||
@@ -210,6 +213,12 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
Logger.LogInformation("Failed to establish connection, retrying");
|
Logger.LogInformation("Failed to establish connection, retrying");
|
||||||
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token).ConfigureAwait(false);
|
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(5, 20)), token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
Logger.LogWarning(ex, "InvalidOperationException on connection");
|
||||||
|
await StopConnection(ServerState.Disconnected).ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogWarning(ex, "Exception on Connection");
|
Logger.LogWarning(ex, "Exception on Connection");
|
||||||
@@ -369,13 +378,13 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
InitializeApiHooks();
|
InitializeApiHooks();
|
||||||
await LoadIninitialPairs().ConfigureAwait(false);
|
|
||||||
_connectionDto = await GetConnectionDto().ConfigureAwait(false);
|
_connectionDto = await GetConnectionDto().ConfigureAwait(false);
|
||||||
if (_connectionDto.ServerVersion != IMareHub.ApiVersion)
|
if (_connectionDto.ServerVersion != IMareHub.ApiVersion)
|
||||||
{
|
{
|
||||||
await StopConnection(ServerState.VersionMisMatch).ConfigureAwait(false);
|
await StopConnection(ServerState.VersionMisMatch).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
await LoadIninitialPairs().ConfigureAwait(false);
|
||||||
await LoadOnlinePairs().ConfigureAwait(false);
|
await LoadOnlinePairs().ConfigureAwait(false);
|
||||||
ServerState = ServerState.Connected;
|
ServerState = ServerState.Connected;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using Dalamud.Plugin.Services;
|
using MareSynchronos.API.SignalR;
|
||||||
using MareSynchronos.API.SignalR;
|
|
||||||
using MareSynchronos.Interop;
|
|
||||||
using MareSynchronos.MareConfiguration;
|
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Services.ServerConfiguration;
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
using MareSynchronos.WebAPI.SignalR.Utils;
|
using MareSynchronos.WebAPI.SignalR.Utils;
|
||||||
@@ -16,21 +13,19 @@ namespace MareSynchronos.WebAPI.SignalR;
|
|||||||
|
|
||||||
public class HubFactory : MediatorSubscriberBase
|
public class HubFactory : MediatorSubscriberBase
|
||||||
{
|
{
|
||||||
private readonly MareConfigService _configService;
|
private readonly ILoggerProvider _loggingProvider;
|
||||||
private readonly IPluginLog _pluginLog;
|
|
||||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||||
private readonly TokenProvider _tokenProvider;
|
private readonly TokenProvider _tokenProvider;
|
||||||
private HubConnection? _instance;
|
private HubConnection? _instance;
|
||||||
private bool _isDisposed = false;
|
private bool _isDisposed = false;
|
||||||
|
|
||||||
public HubFactory(ILogger<HubFactory> logger, MareMediator mediator,
|
public HubFactory(ILogger<HubFactory> logger, MareMediator mediator,
|
||||||
ServerConfigurationManager serverConfigurationManager, MareConfigService configService,
|
ServerConfigurationManager serverConfigurationManager,
|
||||||
TokenProvider tokenProvider, IPluginLog pluginLog) : base(logger, mediator)
|
TokenProvider tokenProvider, ILoggerProvider pluginLog) : base(logger, mediator)
|
||||||
{
|
{
|
||||||
_serverConfigurationManager = serverConfigurationManager;
|
_serverConfigurationManager = serverConfigurationManager;
|
||||||
_configService = configService;
|
|
||||||
_tokenProvider = tokenProvider;
|
_tokenProvider = tokenProvider;
|
||||||
_pluginLog = pluginLog;
|
_loggingProvider = pluginLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DisposeHubAsync()
|
public async Task DisposeHubAsync()
|
||||||
@@ -92,7 +87,7 @@ public class HubFactory : MediatorSubscriberBase
|
|||||||
.WithAutomaticReconnect(new ForeverRetryPolicy(Mediator))
|
.WithAutomaticReconnect(new ForeverRetryPolicy(Mediator))
|
||||||
.ConfigureLogging(a =>
|
.ConfigureLogging(a =>
|
||||||
{
|
{
|
||||||
a.ClearProviders().AddProvider(new DalamudLoggingProvider(_configService, _pluginLog));
|
a.ClearProviders().AddProvider(_loggingProvider);
|
||||||
a.SetMinimumLevel(LogLevel.Information);
|
a.SetMinimumLevel(LogLevel.Information);
|
||||||
})
|
})
|
||||||
.Build();
|
.Build();
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
namespace MareSynchronos.WebAPI.SignalR;
|
namespace MareSynchronos.WebAPI.SignalR;
|
||||||
|
|
||||||
public record JwtIdentifier(string ApiUrl, string SecretKey);
|
public record JwtIdentifier(string ApiUrl, string CharaHash, string SecretKey)
|
||||||
|
{
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return "{JwtIdentifier; Url: " + ApiUrl + ", Chara: " + CharaHash + ", HasSecretKey: " + !string.IsNullOrEmpty(SecretKey) + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using MareSynchronos.API.Routes;
|
using MareSynchronos.API.Routes;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Services.ServerConfiguration;
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@@ -10,7 +11,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace MareSynchronos.WebAPI.SignalR;
|
namespace MareSynchronos.WebAPI.SignalR;
|
||||||
|
|
||||||
public sealed class TokenProvider : IDisposable
|
public sealed class TokenProvider : IDisposable, IMediatorSubscriber
|
||||||
{
|
{
|
||||||
private readonly DalamudUtilService _dalamudUtil;
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
private readonly HttpClient _httpClient;
|
private readonly HttpClient _httpClient;
|
||||||
@@ -18,24 +19,38 @@ public sealed class TokenProvider : IDisposable
|
|||||||
private readonly ServerConfigurationManager _serverManager;
|
private readonly ServerConfigurationManager _serverManager;
|
||||||
private readonly ConcurrentDictionary<JwtIdentifier, string> _tokenCache = new();
|
private readonly ConcurrentDictionary<JwtIdentifier, string> _tokenCache = new();
|
||||||
|
|
||||||
public TokenProvider(ILogger<TokenProvider> logger, ServerConfigurationManager serverManager, DalamudUtilService dalamudUtil)
|
public TokenProvider(ILogger<TokenProvider> logger, ServerConfigurationManager serverManager, DalamudUtilService dalamudUtil, MareMediator mareMediator)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_serverManager = serverManager;
|
_serverManager = serverManager;
|
||||||
_dalamudUtil = dalamudUtil;
|
_dalamudUtil = dalamudUtil;
|
||||||
_httpClient = new();
|
_httpClient = new();
|
||||||
var ver = Assembly.GetExecutingAssembly().GetName().Version;
|
var ver = Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
|
Mediator = mareMediator;
|
||||||
|
Mediator.Subscribe<DalamudLogoutMessage>(this, (_) =>
|
||||||
|
{
|
||||||
|
_lastJwtIdentifier = null;
|
||||||
|
_tokenCache.Clear();
|
||||||
|
});
|
||||||
|
Mediator.Subscribe<DalamudLoginMessage>(this, (_) =>
|
||||||
|
{
|
||||||
|
_lastJwtIdentifier = null;
|
||||||
|
_tokenCache.Clear();
|
||||||
|
});
|
||||||
_httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MareSynchronos", ver!.Major + "." + ver!.Minor + "." + ver!.Build));
|
_httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MareSynchronos", ver!.Major + "." + ver!.Minor + "." + ver!.Build));
|
||||||
}
|
}
|
||||||
|
|
||||||
private JwtIdentifier CurrentIdentifier => new(_serverManager.CurrentApiUrl, _serverManager.GetSecretKey()!);
|
public MareMediator Mediator { get; }
|
||||||
|
|
||||||
|
private JwtIdentifier? _lastJwtIdentifier;
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
Mediator.UnsubscribeAll(this);
|
||||||
_httpClient.Dispose();
|
_httpClient.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> GetNewToken(CancellationToken token)
|
public async Task<string> GetNewToken(JwtIdentifier identifier, CancellationToken token)
|
||||||
{
|
{
|
||||||
Uri tokenUri;
|
Uri tokenUri;
|
||||||
string response = string.Empty;
|
string response = string.Empty;
|
||||||
@@ -58,16 +73,19 @@ public sealed class TokenProvider : IDisposable
|
|||||||
|
|
||||||
response = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
|
response = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
result.EnsureSuccessStatusCode();
|
result.EnsureSuccessStatusCode();
|
||||||
_tokenCache[CurrentIdentifier] = response;
|
_tokenCache[identifier] = response;
|
||||||
}
|
}
|
||||||
catch (HttpRequestException ex)
|
catch (HttpRequestException ex)
|
||||||
{
|
{
|
||||||
_tokenCache.TryRemove(CurrentIdentifier, out _);
|
_tokenCache.TryRemove(identifier, out _);
|
||||||
|
|
||||||
_logger.LogError(ex, "GetNewToken: Failure to get token");
|
_logger.LogError(ex, "GetNewToken: Failure to get token");
|
||||||
|
|
||||||
if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
||||||
{
|
{
|
||||||
|
Mediator.Publish(new NotificationMessage("Error refreshing token", "Your authentication token could not be renewed. Try reconnecting to Mare manually.",
|
||||||
|
Dalamud.Interface.Internal.Notifications.NotificationType.Error));
|
||||||
|
Mediator.Publish(new DisconnectedMessage());
|
||||||
throw new MareAuthFailureException(response);
|
throw new MareAuthFailureException(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,12 +96,54 @@ public sealed class TokenProvider : IDisposable
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JwtIdentifier? GetIdentifier()
|
||||||
|
{
|
||||||
|
JwtIdentifier jwtIdentifier;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
jwtIdentifier = new(_serverManager.CurrentApiUrl,
|
||||||
|
_dalamudUtil.GetPlayerNameHashedAsync().GetAwaiter().GetResult(),
|
||||||
|
_serverManager.GetSecretKey()!);
|
||||||
|
_lastJwtIdentifier = jwtIdentifier;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (_lastJwtIdentifier == null)
|
||||||
|
{
|
||||||
|
_logger.LogError("GetOrUpdate: No last identifier found, aborting");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogWarning(ex, "GetOrUpdate: Could not get JwtIdentifier for some reason or another, reusing last identifier {identifier}", _lastJwtIdentifier);
|
||||||
|
jwtIdentifier = _lastJwtIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("GetOrUpdate: Using identifier {identifier}", jwtIdentifier);
|
||||||
|
return jwtIdentifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? GetToken()
|
||||||
|
{
|
||||||
|
JwtIdentifier? jwtIdentifier = GetIdentifier();
|
||||||
|
if (jwtIdentifier == null) return null;
|
||||||
|
|
||||||
|
if (_tokenCache.TryGetValue(jwtIdentifier, out var token))
|
||||||
|
{
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("No token present");
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<string?> GetOrUpdateToken(CancellationToken ct)
|
public async Task<string?> GetOrUpdateToken(CancellationToken ct)
|
||||||
{
|
{
|
||||||
if (_tokenCache.TryGetValue(CurrentIdentifier, out var token))
|
JwtIdentifier? jwtIdentifier = GetIdentifier();
|
||||||
|
if (jwtIdentifier == null) return null;
|
||||||
|
|
||||||
|
if (_tokenCache.TryGetValue(jwtIdentifier, out var token))
|
||||||
return token;
|
return token;
|
||||||
|
|
||||||
_logger.LogTrace("GetOrUpdate: Getting new token");
|
_logger.LogTrace("GetOrUpdate: Getting new token");
|
||||||
return await GetNewToken(ct).ConfigureAwait(false);
|
return await GetNewToken(jwtIdentifier, ct).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user