rework lightfinder for new api
This commit is contained in:
Submodule LightlessAPI updated: 6c542c0ccc...f3c6064892
@@ -22,6 +22,12 @@ public class LightlessConfig : ILightlessConfiguration
|
|||||||
public DtrEntry.Colors DtrColorsDefault { get; set; } = default;
|
public DtrEntry.Colors DtrColorsDefault { get; set; } = default;
|
||||||
public DtrEntry.Colors DtrColorsNotConnected { get; set; } = new(Glow: 0x0428FFu);
|
public DtrEntry.Colors DtrColorsNotConnected { get; set; } = new(Glow: 0x0428FFu);
|
||||||
public DtrEntry.Colors DtrColorsPairsInRange { get; set; } = new(Glow: 0xFFBA47u);
|
public DtrEntry.Colors DtrColorsPairsInRange { get; set; } = new(Glow: 0xFFBA47u);
|
||||||
|
public bool ShowLightfinderInDtr { get; set; } = false;
|
||||||
|
public bool UseLightfinderColorsInDtr { get; set; } = true;
|
||||||
|
public DtrEntry.Colors DtrColorsLightfinderEnabled { get; set; } = new(Foreground: 0xB590FFu, Glow: 0x4F406Eu);
|
||||||
|
public DtrEntry.Colors DtrColorsLightfinderDisabled { get; set; } = new(Foreground: 0xD44444u, Glow: 0x642222u);
|
||||||
|
public DtrEntry.Colors DtrColorsLightfinderCooldown { get; set; } = new(Foreground: 0xFFE97Au, Glow: 0x766C3Au);
|
||||||
|
public DtrEntry.Colors DtrColorsLightfinderUnavailable { get; set; } = new(Foreground: 0x000000u, Glow: 0x000000u);
|
||||||
public bool UseLightlessRedesign { get; set; } = true;
|
public bool UseLightlessRedesign { get; set; } = true;
|
||||||
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;
|
||||||
@@ -75,6 +81,7 @@ public class LightlessConfig : ILightlessConfiguration
|
|||||||
public bool overrideFcTagColor { get; set; } = false;
|
public bool overrideFcTagColor { get; set; } = false;
|
||||||
public bool useColoredUIDs { get; set; } = true;
|
public bool useColoredUIDs { get; set; } = true;
|
||||||
public bool BroadcastEnabled { get; set; } = false;
|
public bool BroadcastEnabled { get; set; } = false;
|
||||||
|
public bool LightfinderAutoEnableOnConnect { get; set; } = false;
|
||||||
public short LightfinderLabelOffsetX { get; set; } = 0;
|
public short LightfinderLabelOffsetX { get; set; } = 0;
|
||||||
public short LightfinderLabelOffsetY { get; set; } = 0;
|
public short LightfinderLabelOffsetY { get; set; } = 0;
|
||||||
public bool LightfinderLabelUseIcon { get; set; } = false;
|
public bool LightfinderLabelUseIcon { get; set; } = false;
|
||||||
|
|||||||
@@ -142,8 +142,15 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
clientState, objectTable, framework, gameGui, condition, gameData, targetManager, gameConfig,
|
clientState, objectTable, framework, gameGui, condition, gameData, targetManager, gameConfig,
|
||||||
s.GetRequiredService<BlockedCharacterHandler>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PerformanceCollectorService>(),
|
s.GetRequiredService<BlockedCharacterHandler>(), s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PerformanceCollectorService>(),
|
||||||
s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<PlayerPerformanceConfigService>()));
|
s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<PlayerPerformanceConfigService>()));
|
||||||
collection.AddSingleton((s) => new DtrEntry(s.GetRequiredService<ILogger<DtrEntry>>(), dtrBar, s.GetRequiredService<LightlessConfigService>(),
|
collection.AddSingleton((s) => new DtrEntry(
|
||||||
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<PairManager>(), s.GetRequiredService<ApiController>(), s.GetRequiredService<ServerConfigurationManager>()));
|
s.GetRequiredService<ILogger<DtrEntry>>(),
|
||||||
|
dtrBar,
|
||||||
|
s.GetRequiredService<LightlessConfigService>(),
|
||||||
|
s.GetRequiredService<LightlessMediator>(),
|
||||||
|
s.GetRequiredService<PairManager>(),
|
||||||
|
s.GetRequiredService<ApiController>(),
|
||||||
|
s.GetRequiredService<ServerConfigurationManager>(),
|
||||||
|
s.GetRequiredService<BroadcastService>()));
|
||||||
collection.AddSingleton(s => new PairManager(s.GetRequiredService<ILogger<PairManager>>(), s.GetRequiredService<PairFactory>(),
|
collection.AddSingleton(s => new PairManager(s.GetRequiredService<ILogger<PairManager>>(), s.GetRequiredService<PairFactory>(),
|
||||||
s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<LightlessMediator>(), contextMenu, s.GetRequiredService<PairProcessingLimiter>()));
|
s.GetRequiredService<LightlessConfigService>(), s.GetRequiredService<LightlessMediator>(), contextMenu, s.GetRequiredService<PairProcessingLimiter>()));
|
||||||
collection.AddSingleton<RedrawManager>();
|
collection.AddSingleton<RedrawManager>();
|
||||||
@@ -279,4 +286,4 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
_host.StopAsync().GetAwaiter().GetResult();
|
_host.StopAsync().GetAwaiter().GetResult();
|
||||||
_host.Dispose();
|
_host.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using LightlessSync.WebAPI;
|
|||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace LightlessSync.Services;
|
namespace LightlessSync.Services;
|
||||||
public class BroadcastService : IHostedService, IMediatorSubscriber
|
public class BroadcastService : IHostedService, IMediatorSubscriber
|
||||||
@@ -16,9 +17,11 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
|
|||||||
private readonly LightlessMediator _mediator;
|
private readonly LightlessMediator _mediator;
|
||||||
private readonly LightlessConfigService _config;
|
private readonly LightlessConfigService _config;
|
||||||
private readonly DalamudUtilService _dalamudUtil;
|
private readonly DalamudUtilService _dalamudUtil;
|
||||||
|
private CancellationTokenSource? _lightfinderCancelTokens;
|
||||||
|
private Action? _connectedHandler;
|
||||||
public LightlessMediator Mediator => _mediator;
|
public LightlessMediator Mediator => _mediator;
|
||||||
|
|
||||||
public bool IsLightFinderAvailable { get; private set; } = true;
|
public bool IsLightFinderAvailable { get; private set; } = false;
|
||||||
|
|
||||||
public bool IsBroadcasting => _config.Current.BroadcastEnabled;
|
public bool IsBroadcasting => _config.Current.BroadcastEnabled;
|
||||||
private bool _syncedOnStartup = false;
|
private bool _syncedOnStartup = false;
|
||||||
@@ -57,24 +60,125 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
|
|||||||
await action().ConfigureAwait(false);
|
await action().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
private async Task<string?> GetLocalHashedCidAsync(string context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cid = await _dalamudUtil.GetCIDAsync().ConfigureAwait(false);
|
||||||
|
return cid.ToString().GetHash256();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Failed to resolve CID for {Context}", context);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyBroadcastDisabled(bool forcePublish = false)
|
||||||
|
{
|
||||||
|
bool wasEnabled = _config.Current.BroadcastEnabled;
|
||||||
|
bool hadExpiry = _config.Current.BroadcastTtl != DateTime.MinValue;
|
||||||
|
bool hadRemaining = _remainingTtl.HasValue;
|
||||||
|
|
||||||
|
_config.Current.BroadcastEnabled = false;
|
||||||
|
_config.Current.BroadcastTtl = DateTime.MinValue;
|
||||||
|
|
||||||
|
if (wasEnabled || hadExpiry)
|
||||||
|
_config.Save();
|
||||||
|
|
||||||
|
_remainingTtl = null;
|
||||||
|
_waitingForTtlFetch = false;
|
||||||
|
_syncedOnStartup = false;
|
||||||
|
|
||||||
|
if (forcePublish || wasEnabled || hadRemaining)
|
||||||
|
_mediator.Publish(new BroadcastStatusChangedMessage(false, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryApplyBroadcastEnabled(TimeSpan? ttl, string context)
|
||||||
|
{
|
||||||
|
if (ttl is not { } validTtl || validTtl <= TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Lightfinder enable skipped ({Context}): invalid TTL ({TTL})", context, ttl);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wasEnabled = _config.Current.BroadcastEnabled;
|
||||||
|
TimeSpan? previousRemaining = _remainingTtl;
|
||||||
|
DateTime previousExpiry = _config.Current.BroadcastTtl;
|
||||||
|
|
||||||
|
var newExpiry = DateTime.UtcNow + validTtl;
|
||||||
|
|
||||||
|
_config.Current.BroadcastEnabled = true;
|
||||||
|
_config.Current.BroadcastTtl = newExpiry;
|
||||||
|
|
||||||
|
if (!wasEnabled || previousExpiry != newExpiry)
|
||||||
|
_config.Save();
|
||||||
|
|
||||||
|
_remainingTtl = validTtl;
|
||||||
|
_waitingForTtlFetch = false;
|
||||||
|
|
||||||
|
if (!wasEnabled || previousRemaining != validTtl)
|
||||||
|
_mediator.Publish(new BroadcastStatusChangedMessage(true, validTtl));
|
||||||
|
|
||||||
|
_logger.LogInformation("Lightfinder broadcast enabled ({Context}), TTL: {TTL}", context, validTtl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleLightfinderUnavailable(string message, Exception? ex = null)
|
||||||
|
{
|
||||||
|
if (ex != null)
|
||||||
|
_logger.LogWarning(ex, message);
|
||||||
|
else
|
||||||
|
_logger.LogWarning(message);
|
||||||
|
|
||||||
|
IsLightFinderAvailable = false;
|
||||||
|
ApplyBroadcastDisabled(forcePublish: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisconnected()
|
||||||
|
{
|
||||||
|
IsLightFinderAvailable = false;
|
||||||
|
ApplyBroadcastDisabled(forcePublish: true);
|
||||||
|
_logger.LogDebug("Cleared Lightfinder state due to disconnect.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_mediator.Subscribe<EnableBroadcastMessage>(this, OnEnableBroadcast);
|
_mediator.Subscribe<EnableBroadcastMessage>(this, OnEnableBroadcast);
|
||||||
_mediator.Subscribe<BroadcastStatusChangedMessage>(this, OnBroadcastStatusChanged);
|
_mediator.Subscribe<BroadcastStatusChangedMessage>(this, OnBroadcastStatusChanged);
|
||||||
_mediator.Subscribe<PriorityFrameworkUpdateMessage>(this, OnTick);
|
_mediator.Subscribe<PriorityFrameworkUpdateMessage>(this, OnTick);
|
||||||
|
_mediator.Subscribe<DisconnectedMessage>(this, _ => OnDisconnected());
|
||||||
|
|
||||||
_apiController.OnConnected += () => _ = CheckLightfinderSupportAsync(cancellationToken);
|
IsLightFinderAvailable = false;
|
||||||
//_ = CheckLightfinderSupportAsync(cancellationToken);
|
|
||||||
|
_lightfinderCancelTokens?.Cancel();
|
||||||
|
_lightfinderCancelTokens?.Dispose();
|
||||||
|
_lightfinderCancelTokens = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||||
|
_connectedHandler = () => _ = CheckLightfinderSupportAsync(_lightfinderCancelTokens.Token);
|
||||||
|
_apiController.OnConnected += _connectedHandler;
|
||||||
|
|
||||||
|
if (_apiController.IsConnected)
|
||||||
|
_ = CheckLightfinderSupportAsync(_lightfinderCancelTokens.Token);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
|
_lightfinderCancelTokens?.Cancel();
|
||||||
|
_lightfinderCancelTokens?.Dispose();
|
||||||
|
_lightfinderCancelTokens = null;
|
||||||
|
|
||||||
|
if (_connectedHandler is not null)
|
||||||
|
{
|
||||||
|
_apiController.OnConnected -= _connectedHandler;
|
||||||
|
_connectedHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
_mediator.UnsubscribeAll(this);
|
_mediator.UnsubscribeAll(this);
|
||||||
_apiController.OnConnected -= () => _ = CheckLightfinderSupportAsync(cancellationToken);
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
// need to rework this, this is cooked
|
|
||||||
private async Task CheckLightfinderSupportAsync(CancellationToken cancellationToken)
|
private async Task CheckLightfinderSupportAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -85,25 +189,54 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
|
|||||||
if (cancellationToken.IsCancellationRequested)
|
if (cancellationToken.IsCancellationRequested)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var dummy = "0".PadLeft(64, '0');
|
var hashedCid = await GetLocalHashedCidAsync("Lightfinder state check").ConfigureAwait(false);
|
||||||
|
if (string.IsNullOrEmpty(hashedCid))
|
||||||
|
return;
|
||||||
|
|
||||||
await _apiController.IsUserBroadcasting(dummy).ConfigureAwait(false);
|
BroadcastStatusInfoDto? status = null;
|
||||||
await _apiController.SetBroadcastStatus(dummy, true, null).ConfigureAwait(false);
|
try
|
||||||
await _apiController.GetBroadcastTtl(dummy).ConfigureAwait(false);
|
{
|
||||||
await _apiController.AreUsersBroadcasting([dummy]).ConfigureAwait(false);
|
status = await _apiController.IsUserBroadcasting(hashedCid).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (HubException ex) when (ex.Message.Contains("Method does not exist", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
HandleLightfinderUnavailable("Lightfinder unavailable on server (required method missing).", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsLightFinderAvailable)
|
||||||
|
_logger.LogInformation("Lightfinder is available.");
|
||||||
|
|
||||||
IsLightFinderAvailable = true;
|
IsLightFinderAvailable = true;
|
||||||
_logger.LogInformation("Lightfinder is available.");
|
|
||||||
}
|
|
||||||
catch (HubException ex) when (ex.Message.Contains("Method does not exist"))
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Lightfinder unavailable: required method missing.");
|
|
||||||
IsLightFinderAvailable = false;
|
|
||||||
|
|
||||||
_config.Current.BroadcastEnabled = false;
|
bool isBroadcasting = status?.IsBroadcasting == true;
|
||||||
_config.Current.BroadcastTtl = DateTime.MinValue;
|
TimeSpan? ttl = status?.TTL;
|
||||||
_config.Save();
|
|
||||||
_mediator.Publish(new BroadcastStatusChangedMessage(false, null));
|
if (isBroadcasting)
|
||||||
|
{
|
||||||
|
if (ttl is not { } remaining || remaining <= TimeSpan.Zero)
|
||||||
|
ttl = await GetBroadcastTtlAsync(hashedCid).ConfigureAwait(false);
|
||||||
|
|
||||||
|
if (TryApplyBroadcastEnabled(ttl, "server handshake"))
|
||||||
|
{
|
||||||
|
_syncedOnStartup = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
isBroadcasting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isBroadcasting)
|
||||||
|
{
|
||||||
|
ApplyBroadcastDisabled(forcePublish: true);
|
||||||
|
_logger.LogInformation("Lightfinder is available but no active broadcast was found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_config.Current.LightfinderAutoEnableOnConnect && !isBroadcasting)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Auto-enabling Lightfinder broadcast after reconnect.");
|
||||||
|
_mediator.Publish(new EnableBroadcastMessage(hashedCid, true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
@@ -111,14 +244,7 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(ex, "Lightfinder check failed.");
|
HandleLightfinderUnavailable("Lightfinder check failed.", ex);
|
||||||
IsLightFinderAvailable = false;
|
|
||||||
|
|
||||||
_config.Current.BroadcastEnabled = false;
|
|
||||||
_config.Current.BroadcastTtl = DateTime.MinValue;
|
|
||||||
_config.Save();
|
|
||||||
|
|
||||||
_mediator.Publish(new BroadcastStatusChangedMessage(false, null));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,46 +265,38 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
await _apiController.SetBroadcastStatus(msg.HashedCid, msg.Enabled, groupDto).ConfigureAwait(false);
|
await _apiController.SetBroadcastStatus(msg.Enabled, groupDto).ConfigureAwait(false);
|
||||||
|
|
||||||
_logger.LogDebug("Broadcast {Status} for {Cid}", msg.Enabled ? "enabled" : "disabled", msg.HashedCid);
|
_logger.LogDebug("Broadcast {Status} for {Cid}", msg.Enabled ? "enabled" : "disabled", msg.HashedCid);
|
||||||
|
|
||||||
if (!msg.Enabled)
|
if (!msg.Enabled)
|
||||||
{
|
{
|
||||||
_config.Current.BroadcastEnabled = false;
|
ApplyBroadcastDisabled(forcePublish: true);
|
||||||
_config.Current.BroadcastTtl = DateTime.MinValue;
|
|
||||||
_config.Save();
|
|
||||||
|
|
||||||
_mediator.Publish(new BroadcastStatusChangedMessage(false, null));
|
|
||||||
Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(BroadcastService), Services.Events.EventSeverity.Informational, $"Disabled Lightfinder for Player: {msg.HashedCid}")));
|
Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(BroadcastService), Services.Events.EventSeverity.Informational, $"Disabled Lightfinder for Player: {msg.HashedCid}")));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_waitingForTtlFetch = true;
|
_waitingForTtlFetch = true;
|
||||||
|
|
||||||
TimeSpan? ttl = await GetBroadcastTtlAsync(msg.HashedCid).ConfigureAwait(false);
|
try
|
||||||
|
|
||||||
if (ttl is { } remaining && remaining > TimeSpan.Zero)
|
|
||||||
{
|
{
|
||||||
_config.Current.BroadcastTtl = DateTime.UtcNow + remaining;
|
TimeSpan? ttl = await GetBroadcastTtlAsync(msg.HashedCid).ConfigureAwait(false);
|
||||||
_config.Current.BroadcastEnabled = true;
|
|
||||||
_config.Save();
|
|
||||||
|
|
||||||
_logger.LogDebug("Fetched TTL from server: {TTL}", remaining);
|
if (TryApplyBroadcastEnabled(ttl, "client request"))
|
||||||
_mediator.Publish(new BroadcastStatusChangedMessage(true, remaining));
|
{
|
||||||
Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(BroadcastService), Services.Events.EventSeverity.Informational, $"Enabled Lightfinder for Player: {msg.HashedCid}")));
|
_logger.LogDebug("Fetched TTL from server: {TTL}", ttl);
|
||||||
|
Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(BroadcastService), Services.Events.EventSeverity.Informational, $"Enabled Lightfinder for Player: {msg.HashedCid}")));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ApplyBroadcastDisabled(forcePublish: true);
|
||||||
|
_logger.LogWarning("No valid TTL returned after enabling broadcast. Disabling.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
finally
|
||||||
{
|
{
|
||||||
_logger.LogWarning("No valid TTL returned after enabling broadcast. Disabling.");
|
_waitingForTtlFetch = false;
|
||||||
_config.Current.BroadcastEnabled = false;
|
|
||||||
_config.Current.BroadcastTtl = DateTime.MinValue;
|
|
||||||
_config.Save();
|
|
||||||
|
|
||||||
_mediator.Publish(new BroadcastStatusChangedMessage(false, null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_waitingForTtlFetch = false;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -219,17 +337,24 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<TimeSpan?> GetBroadcastTtlAsync(string cid)
|
public async Task<TimeSpan?> GetBroadcastTtlAsync(string? cidForLog = null)
|
||||||
{
|
{
|
||||||
TimeSpan? ttl = null;
|
TimeSpan? ttl = null;
|
||||||
await RequireConnectionAsync(nameof(GetBroadcastTtlAsync), async () => {
|
await RequireConnectionAsync(nameof(GetBroadcastTtlAsync), async () => {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ttl = await _apiController.GetBroadcastTtl(cid).ConfigureAwait(false);
|
ttl = await _apiController.GetBroadcastTtl().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(ex, "Failed to fetch broadcast TTL for {cid}", cid);
|
if (cidForLog is { Length: > 0 })
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Failed to fetch broadcast TTL for {Cid}", cidForLog);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Failed to fetch broadcast TTL");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
return ttl;
|
return ttl;
|
||||||
@@ -281,7 +406,12 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hashedCid = (await _dalamudUtil.GetCIDAsync().ConfigureAwait(false)).ToString().GetHash256();
|
var hashedCid = await GetLocalHashedCidAsync(nameof(ToggleBroadcast)).ConfigureAwait(false);
|
||||||
|
if (string.IsNullOrEmpty(hashedCid))
|
||||||
|
{
|
||||||
|
_logger.LogWarning("ToggleBroadcast - unable to resolve CID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -321,31 +451,31 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
|
|||||||
await RequireConnectionAsync(nameof(OnTick), async () => {
|
await RequireConnectionAsync(nameof(OnTick), async () => {
|
||||||
if (!_syncedOnStartup && _config.Current.BroadcastEnabled)
|
if (!_syncedOnStartup && _config.Current.BroadcastEnabled)
|
||||||
{
|
{
|
||||||
_syncedOnStartup = true;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string hashedCid = (await _dalamudUtil.GetCIDAsync().ConfigureAwait(false)).ToString().GetHash256();
|
var hashedCid = await GetLocalHashedCidAsync("startup TTL refresh").ConfigureAwait(false);
|
||||||
TimeSpan? ttl = await GetBroadcastTtlAsync(hashedCid).ConfigureAwait(false);
|
if (string.IsNullOrEmpty(hashedCid))
|
||||||
if (ttl is { }
|
|
||||||
remaining && remaining > TimeSpan.Zero)
|
|
||||||
{
|
{
|
||||||
_config.Current.BroadcastTtl = DateTime.UtcNow + remaining;
|
_logger.LogDebug("Skipping TTL refresh; hashed CID unavailable.");
|
||||||
_config.Current.BroadcastEnabled = true;
|
return;
|
||||||
_config.Save();
|
}
|
||||||
_logger.LogDebug("Refreshed broadcast TTL from server on first OnTick: {TTL}", remaining);
|
|
||||||
|
TimeSpan? ttl = await GetBroadcastTtlAsync(hashedCid).ConfigureAwait(false);
|
||||||
|
if (TryApplyBroadcastEnabled(ttl, "startup TTL refresh"))
|
||||||
|
{
|
||||||
|
_syncedOnStartup = true;
|
||||||
|
_logger.LogDebug("Refreshed broadcast TTL from server on first OnTick: {TTL}", ttl);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogWarning("No valid TTL found on OnTick. Disabling broadcast state.");
|
_logger.LogWarning("No valid TTL found on OnTick. Disabling broadcast state.");
|
||||||
_config.Current.BroadcastEnabled = false;
|
ApplyBroadcastDisabled(forcePublish: true);
|
||||||
_config.Current.BroadcastTtl = DateTime.MinValue;
|
|
||||||
_config.Save();
|
|
||||||
_mediator.Publish(new BroadcastStatusChangedMessage(false, null));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Failed to refresh TTL in OnTick");
|
_logger.LogError(ex, "Failed to refresh TTL in OnTick");
|
||||||
|
_syncedOnStartup = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_config.Current.BroadcastEnabled)
|
if (_config.Current.BroadcastEnabled)
|
||||||
@@ -362,10 +492,7 @@ public class BroadcastService : IHostedService, IMediatorSubscriber
|
|||||||
if (_remainingTtl == null)
|
if (_remainingTtl == null)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Broadcast TTL expired. Disabling broadcast locally.");
|
_logger.LogDebug("Broadcast TTL expired. Disabling broadcast locally.");
|
||||||
_config.Current.BroadcastEnabled = false;
|
ApplyBroadcastDisabled(forcePublish: true);
|
||||||
_config.Current.BroadcastTtl = DateTime.MinValue;
|
|
||||||
_config.Save();
|
|
||||||
_mediator.Publish(new BroadcastStatusChangedMessage(false, null));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ internal class ContextMenuService : IHostedService
|
|||||||
var receiverCid = DalamudUtilService.GetHashedCIDFromPlayerPointer(targetData.Address);
|
var receiverCid = DalamudUtilService.GetHashedCIDFromPlayerPointer(targetData.Address);
|
||||||
|
|
||||||
_logger.LogInformation("Sending pair request: sender {SenderCid}, receiver {ReceiverCid}", senderCid, receiverCid);
|
_logger.LogInformation("Sending pair request: sender {SenderCid}, receiver {ReceiverCid}", senderCid, receiverCid);
|
||||||
await _apiController.TryPairWithContentId(receiverCid, senderCid).ConfigureAwait(false);
|
await _apiController.TryPairWithContentId(receiverCid).ConfigureAwait(false);
|
||||||
if (!string.IsNullOrWhiteSpace(receiverCid))
|
if (!string.IsNullOrWhiteSpace(receiverCid))
|
||||||
{
|
{
|
||||||
_pairRequestService.RemoveRequest(receiverCid);
|
_pairRequestService.RemoveRequest(receiverCid);
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ namespace LightlessSync.UI
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("DimRed"));
|
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("DimRed"));
|
||||||
ImGui.Text("The Lightfinder<EFBFBD>s light wanes, but not in vain."); // cringe..
|
ImGui.Text("The Lightfinder’s light wanes, but not in vain."); // cringe..
|
||||||
ImGui.PopStyleColor();
|
ImGui.PopStyleColor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using Dalamud.Game.Gui.Dtr;
|
using Dalamud.Game.Gui.Dtr;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using LightlessSync.LightlessConfiguration;
|
using LightlessSync.LightlessConfiguration;
|
||||||
using LightlessSync.LightlessConfiguration.Configurations;
|
using LightlessSync.LightlessConfiguration.Configurations;
|
||||||
using LightlessSync.PlayerData.Pairs;
|
using LightlessSync.PlayerData.Pairs;
|
||||||
|
using LightlessSync.Services;
|
||||||
using LightlessSync.Services.Mediator;
|
using LightlessSync.Services.Mediator;
|
||||||
using LightlessSync.Services.ServerConfiguration;
|
using LightlessSync.Services.ServerConfiguration;
|
||||||
using LightlessSync.WebAPI;
|
using LightlessSync.WebAPI;
|
||||||
@@ -12,6 +13,7 @@ using LightlessSync.WebAPI.SignalR.Utils;
|
|||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace LightlessSync.UI;
|
namespace LightlessSync.UI;
|
||||||
|
|
||||||
@@ -22,35 +24,52 @@ public sealed class DtrEntry : IDisposable, IHostedService
|
|||||||
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
private readonly CancellationTokenSource _cancellationTokenSource = new();
|
||||||
private readonly ConfigurationServiceBase<LightlessConfig> _configService;
|
private readonly ConfigurationServiceBase<LightlessConfig> _configService;
|
||||||
private readonly IDtrBar _dtrBar;
|
private readonly IDtrBar _dtrBar;
|
||||||
private readonly Lazy<IDtrBarEntry> _entry;
|
private readonly Lazy<IDtrBarEntry> _statusEntry;
|
||||||
|
private readonly Lazy<IDtrBarEntry> _lightfinderEntry;
|
||||||
private readonly ILogger<DtrEntry> _logger;
|
private readonly ILogger<DtrEntry> _logger;
|
||||||
|
private readonly BroadcastService _broadcastService;
|
||||||
private readonly LightlessMediator _lightlessMediator;
|
private readonly LightlessMediator _lightlessMediator;
|
||||||
private readonly PairManager _pairManager;
|
private readonly PairManager _pairManager;
|
||||||
private Task? _runTask;
|
private Task? _runTask;
|
||||||
private string? _text;
|
private string? _statusText;
|
||||||
private string? _tooltip;
|
private string? _statusTooltip;
|
||||||
private Colors _colors;
|
private Colors _statusColors;
|
||||||
|
private string? _lightfinderText;
|
||||||
|
private string? _lightfinderTooltip;
|
||||||
|
private Colors _lightfinderColors;
|
||||||
|
|
||||||
public DtrEntry(ILogger<DtrEntry> logger, IDtrBar dtrBar, ConfigurationServiceBase<LightlessConfig> configService, LightlessMediator lightlessMediator, PairManager pairManager, ApiController apiController, ServerConfigurationManager serverManager)
|
public DtrEntry(
|
||||||
|
ILogger<DtrEntry> logger,
|
||||||
|
IDtrBar dtrBar,
|
||||||
|
ConfigurationServiceBase<LightlessConfig> configService,
|
||||||
|
LightlessMediator lightlessMediator,
|
||||||
|
PairManager pairManager,
|
||||||
|
ApiController apiController,
|
||||||
|
ServerConfigurationManager serverManager,
|
||||||
|
BroadcastService broadcastService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_dtrBar = dtrBar;
|
_dtrBar = dtrBar;
|
||||||
_entry = new(CreateEntry);
|
_statusEntry = new(CreateStatusEntry);
|
||||||
|
_lightfinderEntry = new(CreateLightfinderEntry);
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
_lightlessMediator = lightlessMediator;
|
_lightlessMediator = lightlessMediator;
|
||||||
_pairManager = pairManager;
|
_pairManager = pairManager;
|
||||||
_apiController = apiController;
|
_apiController = apiController;
|
||||||
_serverManager = serverManager;
|
_serverManager = serverManager;
|
||||||
|
_broadcastService = broadcastService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_entry.IsValueCreated)
|
if (_statusEntry.IsValueCreated)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Disposing DtrEntry");
|
_logger.LogDebug("Disposing DtrEntry");
|
||||||
Clear();
|
Clear();
|
||||||
_entry.Value.Remove();
|
_statusEntry.Value.Remove();
|
||||||
}
|
}
|
||||||
|
if (_lightfinderEntry.IsValueCreated)
|
||||||
|
_lightfinderEntry.Value.Remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
@@ -70,7 +89,7 @@ public sealed class DtrEntry : IDisposable, IHostedService
|
|||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
// ignore cancelled
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -80,33 +99,66 @@ public sealed class DtrEntry : IDisposable, IHostedService
|
|||||||
|
|
||||||
private void Clear()
|
private void Clear()
|
||||||
{
|
{
|
||||||
if (!_entry.IsValueCreated) return;
|
HideStatusEntry();
|
||||||
_logger.LogInformation("Clearing entry");
|
HideLightfinderEntry();
|
||||||
_text = null;
|
|
||||||
_tooltip = null;
|
|
||||||
_colors = default;
|
|
||||||
|
|
||||||
_entry.Value.Shown = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IDtrBarEntry CreateEntry()
|
private void HideStatusEntry()
|
||||||
{
|
{
|
||||||
_logger.LogTrace("Creating new DtrBar entry");
|
if (_statusEntry.IsValueCreated && _statusEntry.Value.Shown)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Hiding status entry");
|
||||||
|
_statusEntry.Value.Shown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_statusText = null;
|
||||||
|
_statusTooltip = null;
|
||||||
|
_statusColors = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HideLightfinderEntry()
|
||||||
|
{
|
||||||
|
if (_lightfinderEntry.IsValueCreated && _lightfinderEntry.Value.Shown)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Hiding Lightfinder entry");
|
||||||
|
_lightfinderEntry.Value.Shown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lightfinderText = null;
|
||||||
|
_lightfinderTooltip = null;
|
||||||
|
_lightfinderColors = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDtrBarEntry CreateStatusEntry()
|
||||||
|
{
|
||||||
|
_logger.LogTrace("Creating status DtrBar entry");
|
||||||
var entry = _dtrBar.Get("Lightless Sync");
|
var entry = _dtrBar.Get("Lightless Sync");
|
||||||
entry.OnClick = interactionEvent => OnClickEvent(interactionEvent);
|
entry.OnClick = interactionEvent => OnStatusEntryClick(interactionEvent);
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClickEvent(DtrInteractionEvent interactionEvent)
|
private IDtrBarEntry CreateLightfinderEntry()
|
||||||
{
|
{
|
||||||
if (interactionEvent.ClickType.Equals(MouseClickType.Left) && !interactionEvent.ModifierKeys.Equals(ClickModifierKeys.Shift))
|
_logger.LogTrace("Creating Lightfinder DtrBar entry");
|
||||||
|
var entry = _dtrBar.Get("Lightfinder");
|
||||||
|
entry.OnClick = interactionEvent => OnLightfinderEntryClick(interactionEvent);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStatusEntryClick(DtrInteractionEvent interactionEvent)
|
||||||
|
{
|
||||||
|
if (interactionEvent.ClickType.Equals(MouseClickType.Left))
|
||||||
{
|
{
|
||||||
_lightlessMediator.Publish(new UiToggleMessage(typeof(CompactUi)));
|
if (interactionEvent.ModifierKeys.HasFlag(ClickModifierKeys.Shift))
|
||||||
}
|
{
|
||||||
else if (interactionEvent.ClickType.Equals(MouseClickType.Left) && interactionEvent.ModifierKeys.Equals(ClickModifierKeys.Shift))
|
_lightlessMediator.Publish(new UiToggleMessage(typeof(SettingsUi)));
|
||||||
{
|
}
|
||||||
_lightlessMediator.Publish(new UiToggleMessage(typeof(SettingsUi)));
|
else
|
||||||
|
{
|
||||||
|
_lightlessMediator.Publish(new UiToggleMessage(typeof(CompactUi)));
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interactionEvent.ClickType.Equals(MouseClickType.Right))
|
if (interactionEvent.ClickType.Equals(MouseClickType.Right))
|
||||||
@@ -131,6 +183,17 @@ public sealed class DtrEntry : IDisposable, IHostedService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnLightfinderEntryClick(DtrInteractionEvent interactionEvent)
|
||||||
|
{
|
||||||
|
if (!_configService.Current.ShowLightfinderInDtr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (interactionEvent.ClickType.Equals(MouseClickType.Left))
|
||||||
|
{
|
||||||
|
_broadcastService.ToggleBroadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task RunAsync()
|
private async Task RunAsync()
|
||||||
{
|
{
|
||||||
while (!_cancellationTokenSource.IsCancellationRequested)
|
while (!_cancellationTokenSource.IsCancellationRequested)
|
||||||
@@ -143,73 +206,171 @@ public sealed class DtrEntry : IDisposable, IHostedService
|
|||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
if (!_configService.Current.EnableDtrEntry || !_configService.Current.HasValidSetup())
|
var config = _configService.Current;
|
||||||
{
|
|
||||||
if (_entry.IsValueCreated && _entry.Value.Shown)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Disabling entry");
|
|
||||||
|
|
||||||
Clear();
|
if (!config.HasValidSetup())
|
||||||
}
|
{
|
||||||
|
HideStatusEntry();
|
||||||
|
HideLightfinderEntry();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_entry.Value.Shown)
|
if (config.EnableDtrEntry)
|
||||||
{
|
UpdateStatusEntry(config);
|
||||||
_logger.LogInformation("Showing entry");
|
else
|
||||||
_entry.Value.Shown = true;
|
HideStatusEntry();
|
||||||
}
|
|
||||||
|
|
||||||
|
if (config.ShowLightfinderInDtr)
|
||||||
|
UpdateLightfinderEntry(config);
|
||||||
|
else
|
||||||
|
HideLightfinderEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStatusEntry(LightlessConfig config)
|
||||||
|
{
|
||||||
string text;
|
string text;
|
||||||
string tooltip;
|
string tooltip;
|
||||||
Colors colors;
|
Colors colors;
|
||||||
|
|
||||||
if (_apiController.IsConnected)
|
if (_apiController.IsConnected)
|
||||||
{
|
{
|
||||||
var pairCount = _pairManager.GetVisibleUserCount();
|
var pairCount = _pairManager.GetVisibleUserCount();
|
||||||
text = $"\uE044 {pairCount}";
|
text = $"\uE044 {pairCount}";
|
||||||
if (pairCount > 0)
|
if (pairCount > 0)
|
||||||
{
|
{
|
||||||
IEnumerable<string> visiblePairs;
|
var preferNote = config.PreferNoteInDtrTooltip;
|
||||||
if (_configService.Current.ShowUidInDtrTooltip)
|
var showUid = config.ShowUidInDtrTooltip;
|
||||||
{
|
|
||||||
visiblePairs = _pairManager.GetOnlineUserPairs()
|
var visiblePairsQuery = _pairManager.GetOnlineUserPairs()
|
||||||
.Where(x => x.IsVisible)
|
.Where(x => x.IsVisible);
|
||||||
.Select(x => string.Format("{0} ({1})", _configService.Current.PreferNoteInDtrTooltip ? x.GetNote() ?? x.PlayerName : x.PlayerName, x.UserData.AliasOrUID));
|
|
||||||
}
|
IEnumerable<string> visiblePairs = showUid
|
||||||
else
|
? visiblePairsQuery.Select(x => string.Format("{0} ({1})", preferNote ? x.GetNote() ?? x.PlayerName : x.PlayerName, x.UserData.AliasOrUID))
|
||||||
{
|
: visiblePairsQuery.Select(x => string.Format("{0}", preferNote ? x.GetNote() ?? x.PlayerName : x.PlayerName));
|
||||||
visiblePairs = _pairManager.GetOnlineUserPairs()
|
|
||||||
.Where(x => x.IsVisible)
|
|
||||||
.Select(x => string.Format("{0}", _configService.Current.PreferNoteInDtrTooltip ? x.GetNote() ?? x.PlayerName : x.PlayerName));
|
|
||||||
}
|
|
||||||
|
|
||||||
tooltip = $"Lightless Sync: Connected{Environment.NewLine}----------{Environment.NewLine}{string.Join(Environment.NewLine, visiblePairs)}";
|
tooltip = $"Lightless Sync: Connected{Environment.NewLine}----------{Environment.NewLine}{string.Join(Environment.NewLine, visiblePairs)}";
|
||||||
colors = _configService.Current.DtrColorsPairsInRange;
|
colors = config.DtrColorsPairsInRange;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tooltip = "Lightless Sync: Connected";
|
tooltip = "Lightless Sync: Connected";
|
||||||
colors = _configService.Current.DtrColorsDefault;
|
colors = config.DtrColorsDefault;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
text = "\uE044 \uE04C";
|
text = "\uE044 \uE04C";
|
||||||
tooltip = "Lightless Sync: Not Connected";
|
tooltip = "Lightless Sync: Not Connected";
|
||||||
colors = _configService.Current.DtrColorsNotConnected;
|
colors = config.DtrColorsNotConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_configService.Current.UseColorsInDtr)
|
if (!config.UseColorsInDtr)
|
||||||
colors = default;
|
colors = default;
|
||||||
|
|
||||||
if (!string.Equals(text, _text, StringComparison.Ordinal) || !string.Equals(tooltip, _tooltip, StringComparison.Ordinal) || colors != _colors)
|
var statusEntry = _statusEntry.Value;
|
||||||
|
if (!statusEntry.Shown)
|
||||||
{
|
{
|
||||||
_text = text;
|
_logger.LogInformation("Showing status entry");
|
||||||
_tooltip = tooltip;
|
statusEntry.Shown = true;
|
||||||
_colors = colors;
|
|
||||||
_entry.Value.Text = BuildColoredSeString(text, colors);
|
|
||||||
_entry.Value.Tooltip = tooltip;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool statusNeedsUpdate =
|
||||||
|
!string.Equals(text, _statusText, StringComparison.Ordinal) ||
|
||||||
|
!string.Equals(tooltip, _statusTooltip, StringComparison.Ordinal) ||
|
||||||
|
colors != _statusColors;
|
||||||
|
|
||||||
|
if (statusNeedsUpdate)
|
||||||
|
{
|
||||||
|
statusEntry.Text = BuildColoredSeString(text, colors);
|
||||||
|
statusEntry.Tooltip = tooltip;
|
||||||
|
_statusText = text;
|
||||||
|
_statusTooltip = tooltip;
|
||||||
|
_statusColors = colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLightfinderEntry(LightlessConfig config)
|
||||||
|
{
|
||||||
|
var lightfinderEntry = _lightfinderEntry.Value;
|
||||||
|
if (!lightfinderEntry.Shown)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Showing Lightfinder entry");
|
||||||
|
lightfinderEntry.Shown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var indicator = BuildLightfinderIndicator();
|
||||||
|
var lightfinderText = indicator.Text ?? string.Empty;
|
||||||
|
var lightfinderColors = config.UseLightfinderColorsInDtr ? indicator.Colors : default;
|
||||||
|
var lightfinderTooltip = BuildLightfinderTooltip(indicator.Tooltip);
|
||||||
|
|
||||||
|
bool lightfinderNeedsUpdate =
|
||||||
|
!string.Equals(lightfinderText, _lightfinderText, StringComparison.Ordinal) ||
|
||||||
|
!string.Equals(lightfinderTooltip, _lightfinderTooltip, StringComparison.Ordinal) ||
|
||||||
|
lightfinderColors != _lightfinderColors;
|
||||||
|
|
||||||
|
if (lightfinderNeedsUpdate)
|
||||||
|
{
|
||||||
|
lightfinderEntry.Text = BuildColoredSeString(lightfinderText, lightfinderColors);
|
||||||
|
lightfinderEntry.Tooltip = lightfinderTooltip;
|
||||||
|
_lightfinderText = lightfinderText;
|
||||||
|
_lightfinderTooltip = lightfinderTooltip;
|
||||||
|
_lightfinderColors = lightfinderColors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private (string Text, Colors Colors, string Tooltip) BuildLightfinderIndicator()
|
||||||
|
{
|
||||||
|
var config = _configService.Current;
|
||||||
|
const string icon = "\uE048";
|
||||||
|
if (!_broadcastService.IsLightFinderAvailable)
|
||||||
|
{
|
||||||
|
return ($"{icon} --", config.DtrColorsLightfinderUnavailable, "Lightfinder - Unavailable on this server.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_broadcastService.IsBroadcasting)
|
||||||
|
{
|
||||||
|
return ($"{icon} ON", config.DtrColorsLightfinderEnabled, "Lightfinder - Enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
var tooltip = new StringBuilder("Lightfinder - Disabled");
|
||||||
|
var colors = config.DtrColorsLightfinderDisabled;
|
||||||
|
if (_broadcastService.RemainingCooldown is { } cooldown && cooldown > TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
tooltip.AppendLine();
|
||||||
|
tooltip.Append("Cooldown: ").Append(Math.Ceiling(cooldown.TotalSeconds)).Append("s");
|
||||||
|
colors = config.DtrColorsLightfinderCooldown;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($"{icon} OFF", colors, tooltip.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildLightfinderTooltip(string baseTooltip)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
if (!string.IsNullOrWhiteSpace(baseTooltip))
|
||||||
|
builder.Append(baseTooltip.TrimEnd());
|
||||||
|
else
|
||||||
|
builder.Append("Lightfinder status unavailable.");
|
||||||
|
|
||||||
|
return builder.ToString().TrimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AppendColoredSegment(SeStringBuilder builder, string? text, Colors colors)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (colors.Foreground != default)
|
||||||
|
builder.Add(BuildColorStartPayload(_colorTypeForeground, colors.Foreground));
|
||||||
|
if (colors.Glow != default)
|
||||||
|
builder.Add(BuildColorStartPayload(_colorTypeGlow, colors.Glow));
|
||||||
|
|
||||||
|
builder.AddText(text);
|
||||||
|
|
||||||
|
if (colors.Glow != default)
|
||||||
|
builder.Add(BuildColorEndPayload(_colorTypeGlow));
|
||||||
|
if (colors.Foreground != default)
|
||||||
|
builder.Add(BuildColorEndPayload(_colorTypeForeground));
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Colored SeString
|
#region Colored SeString
|
||||||
@@ -219,15 +380,7 @@ public sealed class DtrEntry : IDisposable, IHostedService
|
|||||||
private static SeString BuildColoredSeString(string text, Colors colors)
|
private static SeString BuildColoredSeString(string text, Colors colors)
|
||||||
{
|
{
|
||||||
var ssb = new SeStringBuilder();
|
var ssb = new SeStringBuilder();
|
||||||
if (colors.Foreground != default)
|
AppendColoredSegment(ssb, text, colors);
|
||||||
ssb.Add(BuildColorStartPayload(_colorTypeForeground, colors.Foreground));
|
|
||||||
if (colors.Glow != default)
|
|
||||||
ssb.Add(BuildColorStartPayload(_colorTypeGlow, colors.Glow));
|
|
||||||
ssb.AddText(text);
|
|
||||||
if (colors.Glow != default)
|
|
||||||
ssb.Add(BuildColorEndPayload(_colorTypeGlow));
|
|
||||||
if (colors.Foreground != default)
|
|
||||||
ssb.Add(BuildColorEndPayload(_colorTypeForeground));
|
|
||||||
return ssb.Build();
|
return ssb.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -181,12 +181,13 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
var foregroundColor = ConvertColor(colors.Foreground);
|
var foregroundColor = ConvertColor(colors.Foreground);
|
||||||
var glowColor = ConvertColor(colors.Glow);
|
var glowColor = ConvertColor(colors.Glow);
|
||||||
|
|
||||||
var ret = ImGui.ColorEdit3("###foreground", ref foregroundColor, ImGuiColorEditFlags.NoInputs | ImGuiColorEditFlags.NoLabel | ImGuiColorEditFlags.Uint8);
|
const ImGuiColorEditFlags colorFlags = ImGuiColorEditFlags.NoInputs | ImGuiColorEditFlags.NoLabel;
|
||||||
|
var ret = ImGui.ColorEdit3("###foreground", ref foregroundColor, colorFlags);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip("Foreground Color - Set to pure black (#000000) to use the default color");
|
ImGui.SetTooltip("Foreground Color - Set to pure black (#000000) to use the default color");
|
||||||
|
|
||||||
ImGui.SameLine(0.0f, innerSpacing);
|
ImGui.SameLine(0.0f, innerSpacing);
|
||||||
ret |= ImGui.ColorEdit3("###glow", ref glowColor, ImGuiColorEditFlags.NoInputs | ImGuiColorEditFlags.NoLabel | ImGuiColorEditFlags.Uint8);
|
ret |= ImGui.ColorEdit3("###glow", ref glowColor, colorFlags);
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip("Glow Color - Set to pure black (#000000) to use the default color");
|
ImGui.SetTooltip("Glow Color - Set to pure black (#000000) to use the default color");
|
||||||
|
|
||||||
@@ -199,10 +200,26 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
static Vector3 ConvertColor(uint color)
|
static Vector3 ConvertColor(uint color)
|
||||||
=> unchecked(new((byte)color / 255.0f, (byte)(color >> 8) / 255.0f, (byte)(color >> 16) / 255.0f));
|
{
|
||||||
|
var r = (color & 0xFF) / 255.0f;
|
||||||
|
var g = ((color >> 8) & 0xFF) / 255.0f;
|
||||||
|
var b = ((color >> 16) & 0xFF) / 255.0f;
|
||||||
|
return new(r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
static uint ConvertBackColor(Vector3 color)
|
static uint ConvertBackColor(Vector3 color)
|
||||||
=> byte.CreateSaturating(color.X * 255.0f) | ((uint)byte.CreateSaturating(color.Y * 255.0f) << 8) | ((uint)byte.CreateSaturating(color.Z * 255.0f) << 16);
|
{
|
||||||
|
static byte ToByte(float channel)
|
||||||
|
{
|
||||||
|
var scaled = MathF.Round(Math.Clamp(channel, 0f, 1f) * 255.0f);
|
||||||
|
return (byte)Math.Clamp((int)scaled, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
var r = ToByte(color.X);
|
||||||
|
var g = ToByte(color.Y);
|
||||||
|
var b = ToByte(color.Z);
|
||||||
|
return (uint)(r | (g << 8) | (b << 16));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawBlockedTransfers()
|
private void DrawBlockedTransfers()
|
||||||
@@ -1066,10 +1083,66 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
|||||||
|
|
||||||
if (_uiShared.MediumTreeNode("Lightfinder", UIColors.Get("LightlessPurple")))
|
if (_uiShared.MediumTreeNode("Lightfinder", UIColors.Get("LightlessPurple")))
|
||||||
{
|
{
|
||||||
|
bool autoEnable = _configService.Current.LightfinderAutoEnableOnConnect;
|
||||||
var autoAlign = _configService.Current.LightfinderAutoAlign;
|
var autoAlign = _configService.Current.LightfinderAutoAlign;
|
||||||
var offsetX = (int)_configService.Current.LightfinderLabelOffsetX;
|
var offsetX = (int)_configService.Current.LightfinderLabelOffsetX;
|
||||||
var offsetY = (int)_configService.Current.LightfinderLabelOffsetY;
|
var offsetY = (int)_configService.Current.LightfinderLabelOffsetY;
|
||||||
var labelScale = _configService.Current.LightfinderLabelScale;
|
var labelScale = _configService.Current.LightfinderLabelScale;
|
||||||
|
bool showLightfinderInDtr = _configService.Current.ShowLightfinderInDtr;
|
||||||
|
var dtrLightfinderEnabled = _configService.Current.DtrColorsLightfinderEnabled;
|
||||||
|
var dtrLightfinderDisabled = _configService.Current.DtrColorsLightfinderDisabled;
|
||||||
|
var dtrLightfinderCooldown = _configService.Current.DtrColorsLightfinderCooldown;
|
||||||
|
var dtrLightfinderUnavailable = _configService.Current.DtrColorsLightfinderUnavailable;
|
||||||
|
|
||||||
|
ImGui.TextUnformatted("Connection");
|
||||||
|
if (ImGui.Checkbox("Auto-enable Lightfinder on server connection", ref autoEnable))
|
||||||
|
{
|
||||||
|
_configService.Current.LightfinderAutoEnableOnConnect = autoEnable;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
_uiShared.DrawHelpText("When enabled, Lightfinder will automatically turn on after reconnecting to the Lightless server.");
|
||||||
|
|
||||||
|
_uiShared.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"), 1.5f);
|
||||||
|
|
||||||
|
ImGui.TextUnformatted("Lightfinder Info Bar");
|
||||||
|
if (ImGui.Checkbox("Show Lightfinder status in Server info bar", ref showLightfinderInDtr))
|
||||||
|
{
|
||||||
|
_configService.Current.ShowLightfinderInDtr = showLightfinderInDtr;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
_uiShared.DrawHelpText("Adds a Lightfinder status to the Server info bar. Left click toggles Lightfinder when visible.");
|
||||||
|
|
||||||
|
bool useLightfinderColors = _configService.Current.UseLightfinderColorsInDtr;
|
||||||
|
if (ImGui.Checkbox("Color-code the Lightfinder info bar according to status", ref useLightfinderColors))
|
||||||
|
{
|
||||||
|
_configService.Current.UseLightfinderColorsInDtr = useLightfinderColors;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.BeginDisabled(!showLightfinderInDtr || !useLightfinderColors);
|
||||||
|
if (InputDtrColors("Enables", ref dtrLightfinderEnabled))
|
||||||
|
{
|
||||||
|
_configService.Current.DtrColorsLightfinderEnabled = dtrLightfinderEnabled;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
if (InputDtrColors("Disabled", ref dtrLightfinderDisabled))
|
||||||
|
{
|
||||||
|
_configService.Current.DtrColorsLightfinderDisabled = dtrLightfinderDisabled;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
if (InputDtrColors("Cooldown", ref dtrLightfinderCooldown))
|
||||||
|
{
|
||||||
|
_configService.Current.DtrColorsLightfinderCooldown = dtrLightfinderCooldown;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
if (InputDtrColors("Unavailable", ref dtrLightfinderUnavailable))
|
||||||
|
{
|
||||||
|
_configService.Current.DtrColorsLightfinderUnavailable = dtrLightfinderUnavailable;
|
||||||
|
_configService.Save();
|
||||||
|
}
|
||||||
|
ImGui.EndDisabled();
|
||||||
|
|
||||||
|
_uiShared.ColoredSeparator(UIColors.Get("LightlessPurpleDefault"), 1.5f);
|
||||||
|
|
||||||
ImGui.TextUnformatted("Alignment");
|
ImGui.TextUnformatted("Alignment");
|
||||||
ImGui.BeginDisabled(autoAlign);
|
ImGui.BeginDisabled(autoAlign);
|
||||||
|
|||||||
@@ -401,7 +401,7 @@ public class TopTabMenu
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var myCidHash = (await _dalamudUtilService.GetCIDAsync().ConfigureAwait(false)).ToString().GetHash256();
|
var myCidHash = (await _dalamudUtilService.GetCIDAsync().ConfigureAwait(false)).ToString().GetHash256();
|
||||||
await _apiController.TryPairWithContentId(request.HashedCid, myCidHash).ConfigureAwait(false);
|
await _apiController.TryPairWithContentId(request.HashedCid).ConfigureAwait(false);
|
||||||
_pairRequestService.RemoveRequest(request.HashedCid);
|
_pairRequestService.RemoveRequest(request.HashedCid);
|
||||||
|
|
||||||
var display = string.IsNullOrEmpty(request.DisplayName) ? request.HashedCid : request.DisplayName;
|
var display = string.IsNullOrEmpty(request.DisplayName) ? request.HashedCid : request.DisplayName;
|
||||||
|
|||||||
@@ -35,16 +35,16 @@ public partial class ApiController
|
|||||||
await _lightlessHub!.SendAsync(nameof(UserAddPair), user).ConfigureAwait(false);
|
await _lightlessHub!.SendAsync(nameof(UserAddPair), user).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task TryPairWithContentId(string otherCid, string myCid)
|
public async Task TryPairWithContentId(string otherCid)
|
||||||
{
|
{
|
||||||
if (!IsConnected) return;
|
if (!IsConnected) return;
|
||||||
await _lightlessHub!.SendAsync(nameof(TryPairWithContentId), otherCid, myCid).ConfigureAwait(false);
|
await _lightlessHub!.SendAsync(nameof(TryPairWithContentId), otherCid).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetBroadcastStatus(string hashedCid, bool enabled, GroupBroadcastRequestDto? groupDto = null)
|
public async Task SetBroadcastStatus(bool enabled, GroupBroadcastRequestDto? groupDto = null)
|
||||||
{
|
{
|
||||||
CheckConnection();
|
CheckConnection();
|
||||||
await _lightlessHub!.InvokeAsync(nameof(SetBroadcastStatus), hashedCid, enabled, groupDto).ConfigureAwait(false);
|
await _lightlessHub!.InvokeAsync(nameof(SetBroadcastStatus), enabled, groupDto).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<BroadcastStatusInfoDto?> IsUserBroadcasting(string hashedCid)
|
public async Task<BroadcastStatusInfoDto?> IsUserBroadcasting(string hashedCid)
|
||||||
@@ -59,10 +59,10 @@ public partial class ApiController
|
|||||||
return await _lightlessHub!.InvokeAsync<BroadcastStatusBatchDto>(nameof(AreUsersBroadcasting), hashedCids).ConfigureAwait(false);
|
return await _lightlessHub!.InvokeAsync<BroadcastStatusBatchDto>(nameof(AreUsersBroadcasting), hashedCids).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<TimeSpan?> GetBroadcastTtl(string hashedCid)
|
public async Task<TimeSpan?> GetBroadcastTtl()
|
||||||
{
|
{
|
||||||
CheckConnection();
|
CheckConnection();
|
||||||
return await _lightlessHub!.InvokeAsync<TimeSpan?>(nameof(GetBroadcastTtl), hashedCid).ConfigureAwait(false);
|
return await _lightlessHub!.InvokeAsync<TimeSpan?>(nameof(GetBroadcastTtl)).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UserDelete()
|
public async Task UserDelete()
|
||||||
|
|||||||
Submodule PenumbraAPI updated: dd14131793...648b6fc2ce
Reference in New Issue
Block a user