watafak
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Colors;
|
||||
@@ -8,6 +7,7 @@ using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Utility;
|
||||
using LightlessSync.API.Data;
|
||||
using LightlessSync.API.Data.Comparer;
|
||||
using LightlessSync.API.Data.Enum;
|
||||
using LightlessSync.API.Routes;
|
||||
using LightlessSync.FileCache;
|
||||
using LightlessSync.Interop.Ipc;
|
||||
@@ -18,9 +18,11 @@ using LightlessSync.PlayerData.Handlers;
|
||||
using LightlessSync.PlayerData.Pairs;
|
||||
using LightlessSync.Services;
|
||||
using LightlessSync.Services.ActorTracking;
|
||||
using LightlessSync.Services.Events;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.PairProcessing;
|
||||
using LightlessSync.Services.ServerConfiguration;
|
||||
using LightlessSync.UI.Models;
|
||||
using LightlessSync.UI.Services;
|
||||
using LightlessSync.UI.Style;
|
||||
using LightlessSync.Utils;
|
||||
@@ -39,7 +41,6 @@ using System.Net.Http.Json;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using static Penumbra.GameData.Files.ShpkFile;
|
||||
|
||||
namespace LightlessSync.UI;
|
||||
|
||||
@@ -62,6 +63,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
private readonly PerformanceCollectorService _performanceCollector;
|
||||
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
|
||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly UiSharedService _uiShared;
|
||||
private readonly IProgress<(int, int, FileCacheEntity)> _validationProgress;
|
||||
@@ -75,11 +77,13 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
private bool _readClearCache = false;
|
||||
private int _selectedEntry = -1;
|
||||
private string _uidToAddForIgnore = string.Empty;
|
||||
private string _lightfinderIconInput = string.Empty;
|
||||
private bool _lightfinderIconInputInitialized = false;
|
||||
private int _lightfinderIconPresetIndex = -1;
|
||||
private bool _selectGeneralTabOnNextDraw = false;
|
||||
private string _pairDebugFilter = string.Empty;
|
||||
private bool _pairDebugVisibleOnly = true;
|
||||
private bool _pairDiagnosticsEnabled;
|
||||
private string? _selectedPairDebugUid = null;
|
||||
private static readonly LightlessConfig DefaultConfig = new();
|
||||
private static readonly JsonSerializerOptions DebugJsonOptions = new() { WriteIndented = true };
|
||||
private MainSettingsTab _selectedMainTab = MainSettingsTab.General;
|
||||
private TransferSettingsTab _selectedTransferTab = TransferSettingsTab.Transfers;
|
||||
private ServerSettingsTab _selectedServerTab = ServerSettingsTab.CharacterManagement;
|
||||
@@ -143,15 +147,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
PermissionSettings,
|
||||
}
|
||||
|
||||
private static readonly (string Label, SeIconChar Icon)[] LightfinderIconPresets = new[]
|
||||
{
|
||||
("Link Marker", SeIconChar.LinkMarker), ("Hyadelyn", SeIconChar.Hyadelyn), ("Gil", SeIconChar.Gil),
|
||||
("Quest Sync", SeIconChar.QuestSync), ("Glamoured", SeIconChar.Glamoured),
|
||||
("Glamoured (Dyed)", SeIconChar.GlamouredDyed), ("Auto-Translate Open", SeIconChar.AutoTranslateOpen),
|
||||
("Auto-Translate Close", SeIconChar.AutoTranslateClose), ("Boxed Star", SeIconChar.BoxedStar),
|
||||
("Boxed Plus", SeIconChar.BoxedPlus)
|
||||
};
|
||||
|
||||
private CancellationTokenSource? _validationCts;
|
||||
private Task<List<FileCacheEntity>>? _validationTask;
|
||||
private bool _wasOpen = false;
|
||||
@@ -162,6 +157,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
ServerConfigurationManager serverConfigurationManager,
|
||||
PlayerPerformanceConfigService playerPerformanceConfigService,
|
||||
PairProcessingLimiter pairProcessingLimiter,
|
||||
EventAggregator eventAggregator,
|
||||
LightlessMediator mediator, PerformanceCollectorService performanceCollector,
|
||||
FileUploadManager fileTransferManager,
|
||||
FileTransferOrchestrator fileTransferOrchestrator,
|
||||
@@ -179,6 +175,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_playerPerformanceConfigService = playerPerformanceConfigService;
|
||||
_pairProcessingLimiter = pairProcessingLimiter;
|
||||
_eventAggregator = eventAggregator;
|
||||
_performanceCollector = performanceCollector;
|
||||
_fileTransferManager = fileTransferManager;
|
||||
_fileTransferOrchestrator = fileTransferOrchestrator;
|
||||
@@ -192,34 +189,14 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
_uiShared = uiShared;
|
||||
_nameplateService = nameplateService;
|
||||
_actorObjectService = actorObjectService;
|
||||
AllowClickthrough = false;
|
||||
AllowPinning = true;
|
||||
_validationProgress = new Progress<(int, int, FileCacheEntity)>(v => _currentProgress = v);
|
||||
|
||||
SizeConstraints = new WindowSizeConstraints()
|
||||
{
|
||||
MinimumSize = new Vector2(900f, 400f),
|
||||
MaximumSize = new Vector2(900f, 2000f),
|
||||
};
|
||||
|
||||
TitleBarButtons = new()
|
||||
{
|
||||
new TitleBarButton()
|
||||
{
|
||||
Icon = FontAwesomeIcon.FileAlt,
|
||||
Click = (msg) =>
|
||||
{
|
||||
Mediator.Publish(new UiToggleMessage(typeof(UpdateNotesUi)));
|
||||
},
|
||||
IconOffset = new(2, 1),
|
||||
ShowTooltip = () =>
|
||||
{
|
||||
ImGui.BeginTooltip();
|
||||
ImGui.Text("View Update Notes");
|
||||
ImGui.EndTooltip();
|
||||
}
|
||||
}
|
||||
};
|
||||
WindowBuilder.For(this)
|
||||
.AllowPinning(true)
|
||||
.AllowClickthrough(false)
|
||||
.SetSizeConstraints(new Vector2(900f, 400f), new Vector2(900f, 2000f))
|
||||
.AddTitleBarButton(FontAwesomeIcon.FileAlt, "View Update Notes", () => Mediator.Publish(new UiToggleMessage(typeof(UpdateNotesUi))))
|
||||
.Apply();
|
||||
|
||||
Mediator.Subscribe<OpenSettingsUiMessage>(this, (_) => Toggle());
|
||||
Mediator.Subscribe<OpenLightfinderSettingsMessage>(this, (_) =>
|
||||
@@ -1309,9 +1286,425 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
UiSharedService.TooltipSeparator
|
||||
+ "Keeping LOD enabled can lead to more crashes. Use at your own risk.");
|
||||
|
||||
ImGuiHelpers.ScaledDummy(10f);
|
||||
DrawPairDebugPanel();
|
||||
|
||||
UiSharedService.ColoredSeparator(UIColors.Get("LightlessYellow"), 2f);
|
||||
}
|
||||
|
||||
private void DrawPairDebugPanel()
|
||||
{
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessYellow"));
|
||||
ImGui.TextUnformatted("Pair Diagnostics");
|
||||
ImGui.PopStyleColor();
|
||||
ImGuiHelpers.ScaledDummy(3f);
|
||||
|
||||
ImGui.Checkbox("Enable Pair Diagnostics", ref _pairDiagnosticsEnabled);
|
||||
UiSharedService.AttachToolTip("When disabled the UI stops querying pair handlers and no diagnostics are processed.");
|
||||
|
||||
if (!_pairDiagnosticsEnabled)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("Diagnostics are disabled. Enable the toggle above to inspect active pairs.", UIColors.Get("LightlessYellow"));
|
||||
return;
|
||||
}
|
||||
|
||||
var snapshot = _pairUiService.GetSnapshot();
|
||||
if (snapshot.PairsByUid.Count == 0)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("No pairs are currently tracked. Connect to the service and re-open this panel.", UIColors.Get("LightlessYellow"));
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth(280f * ImGuiHelpers.GlobalScale);
|
||||
ImGui.InputTextWithHint("##pairDebugFilter", "Search by UID, alias, or player name...", ref _pairDebugFilter, 96);
|
||||
UiSharedService.AttachToolTip("Filters the list by UID, aliases, or currently cached player name.");
|
||||
ImGui.SameLine();
|
||||
ImGui.Checkbox("Visible pairs only", ref _pairDebugVisibleOnly);
|
||||
UiSharedService.AttachToolTip("When enabled only currently visible pairs remain in the list.");
|
||||
|
||||
var pairs = snapshot.PairsByUid.Values;
|
||||
var filteredPairs = pairs
|
||||
.Where(p => !_pairDebugVisibleOnly || p.IsVisible)
|
||||
.Where(p => PairMatchesFilter(p, _pairDebugFilter))
|
||||
.OrderByDescending(p => p.IsVisible)
|
||||
.ThenByDescending(p => p.IsOnline)
|
||||
.ThenBy(p => p.UserData.AliasOrUID, StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
|
||||
if (filteredPairs.Count == 0)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped("No pairs match the current filters.", UIColors.Get("LightlessYellow"));
|
||||
_selectedPairDebugUid = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectedPairDebugUid is null || !filteredPairs.Any(p => string.Equals(p.UserData.UID, _selectedPairDebugUid, StringComparison.Ordinal)))
|
||||
{
|
||||
_selectedPairDebugUid = filteredPairs[0].UserData.UID;
|
||||
}
|
||||
|
||||
if (_selectedPairDebugUid is null || !snapshot.PairsByUid.TryGetValue(_selectedPairDebugUid, out var selectedPair))
|
||||
{
|
||||
selectedPair = filteredPairs[0];
|
||||
}
|
||||
|
||||
var visibleCount = pairs.Count(p => p.IsVisible);
|
||||
var onlineCount = pairs.Count(p => p.IsOnline);
|
||||
var totalPairs = snapshot.PairsByUid.Count;
|
||||
ImGui.TextUnformatted($"Visible: {visibleCount} / {totalPairs}; Online: {onlineCount}");
|
||||
|
||||
var mainChildHeight = MathF.Max(ImGui.GetTextLineHeightWithSpacing() * 12f, ImGui.GetContentRegionAvail().Y * 0.95f);
|
||||
if (ImGui.BeginChild("##pairDebugPanel", new Vector2(-1, mainChildHeight), true, ImGuiWindowFlags.HorizontalScrollbar))
|
||||
{
|
||||
var childAvail = ImGui.GetContentRegionAvail();
|
||||
var leftWidth = MathF.Max(220f * ImGuiHelpers.GlobalScale, childAvail.X * 0.35f);
|
||||
leftWidth = MathF.Min(leftWidth, childAvail.X * 0.6f);
|
||||
if (ImGui.BeginChild("##pairDebugList", new Vector2(leftWidth, 0), true, ImGuiWindowFlags.HorizontalScrollbar))
|
||||
{
|
||||
if (ImGui.BeginTable("##pairDebugTable", 3, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollY | ImGuiTableFlags.ScrollX))
|
||||
{
|
||||
ImGui.TableSetupScrollFreeze(0, 1);
|
||||
ImGui.TableSetupColumn("", ImGuiTableColumnFlags.WidthFixed, 20f * ImGuiHelpers.GlobalScale);
|
||||
ImGui.TableSetupColumn("Pair");
|
||||
ImGui.TableSetupColumn("State", ImGuiTableColumnFlags.WidthFixed, 90f * ImGuiHelpers.GlobalScale);
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var entry in filteredPairs)
|
||||
{
|
||||
var isSelected = string.Equals(entry.UserData.UID, _selectedPairDebugUid, StringComparison.Ordinal);
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
DrawPairStateIndicator(entry);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (ImGui.Selectable($"{entry.UserData.AliasOrUID}##pairDebugSelect_{entry.UserData.UID}", isSelected, ImGuiSelectableFlags.SpanAllColumns))
|
||||
{
|
||||
_selectedPairDebugUid = entry.UserData.UID;
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
ImGui.SetTooltip($"UID: {entry.UserData.UID}\nVisible: {entry.IsVisible}\nOnline: {entry.IsOnline}\nDirect pair: {entry.IsDirectlyPaired}");
|
||||
}
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(entry.IsVisible ? "Visible" : entry.IsOnline ? "Online" : "Offline");
|
||||
}
|
||||
|
||||
ImGui.EndTable();
|
||||
}
|
||||
}
|
||||
ImGui.EndChild();
|
||||
|
||||
ImGui.SameLine();
|
||||
|
||||
if (ImGui.BeginChild("##pairDebugDetails", new Vector2(0, 0), true, ImGuiWindowFlags.HorizontalScrollbar))
|
||||
{
|
||||
DrawPairDebugDetails(selectedPair, snapshot);
|
||||
}
|
||||
ImGui.EndChild();
|
||||
}
|
||||
ImGui.EndChild();
|
||||
}
|
||||
|
||||
private static bool PairMatchesFilter(Pair pair, string filter)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filter))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return pair.UserData.UID.Contains(filter, StringComparison.OrdinalIgnoreCase)
|
||||
|| pair.UserData.AliasOrUID.Contains(filter, StringComparison.OrdinalIgnoreCase)
|
||||
|| (!string.IsNullOrEmpty(pair.PlayerName) && pair.PlayerName.Contains(filter, StringComparison.OrdinalIgnoreCase))
|
||||
|| (!string.IsNullOrEmpty(pair.Ident) && pair.Ident.Contains(filter, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static void DrawPairStateIndicator(Pair pair)
|
||||
{
|
||||
var color = pair.IsVisible
|
||||
? UIColors.Get("LightlessGreen")
|
||||
: pair.IsOnline ? UIColors.Get("LightlessYellow")
|
||||
: UIColors.Get("DimRed");
|
||||
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
var cursor = ImGui.GetCursorScreenPos();
|
||||
var radius = ImGui.GetTextLineHeight() * 0.35f;
|
||||
var center = cursor + new Vector2(radius, radius);
|
||||
drawList.AddCircleFilled(center, radius, ImGui.ColorConvertFloat4ToU32(color));
|
||||
ImGui.Dummy(new Vector2(radius * 2f, radius * 2f));
|
||||
}
|
||||
|
||||
private void DrawPairDebugDetails(Pair pair, PairUiSnapshot snapshot)
|
||||
{
|
||||
var debugInfo = pair.GetDebugInfo();
|
||||
var statusColor = pair.IsVisible
|
||||
? UIColors.Get("LightlessGreen")
|
||||
: pair.IsOnline ? UIColors.Get("LightlessYellow")
|
||||
: UIColors.Get("DimRed");
|
||||
|
||||
ImGui.TextColored(statusColor, pair.UserData.AliasOrUID);
|
||||
ImGui.SameLine();
|
||||
ImGui.TextColored(statusColor, $"[{(pair.IsVisible ? "Visible" : pair.IsOnline ? "Online" : "Offline")}]");
|
||||
|
||||
if (ImGui.BeginTable("##pairDebugProperties", 2, ImGuiTableFlags.SizingStretchProp))
|
||||
{
|
||||
DrawPairPropertyRow("UID", pair.UserData.UID);
|
||||
DrawPairPropertyRow("Alias", string.IsNullOrEmpty(pair.UserData.Alias) ? "(none)" : pair.UserData.Alias!);
|
||||
DrawPairPropertyRow("Player Name", pair.PlayerName ?? "(not cached)");
|
||||
DrawPairPropertyRow("Handler Ident", string.IsNullOrEmpty(pair.Ident) ? "(not bound)" : pair.Ident);
|
||||
DrawPairPropertyRow("Character Id", FormatCharacterId(pair.PlayerCharacterId));
|
||||
DrawPairPropertyRow("Direct Pair", FormatBool(pair.IsDirectlyPaired));
|
||||
DrawPairPropertyRow("Individual Status", pair.IndividualPairStatus.ToString());
|
||||
DrawPairPropertyRow("Any Connection", FormatBool(pair.HasAnyConnection()));
|
||||
DrawPairPropertyRow("Paused", FormatBool(pair.IsPaused));
|
||||
DrawPairPropertyRow("Visible", FormatBool(pair.IsVisible), statusColor);
|
||||
DrawPairPropertyRow("Online", FormatBool(pair.IsOnline));
|
||||
DrawPairPropertyRow("Has Handler", FormatBool(debugInfo.HasHandler));
|
||||
DrawPairPropertyRow("Handler Initialized", FormatBool(debugInfo.HandlerInitialized));
|
||||
DrawPairPropertyRow("Handler Visible", FormatBool(debugInfo.HandlerVisible));
|
||||
DrawPairPropertyRow("Handler Scheduled For Deletion", FormatBool(debugInfo.HandlerScheduledForDeletion));
|
||||
DrawPairPropertyRow("Note", pair.GetNote() ?? "(none)");
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.TextUnformatted("Applied Data");
|
||||
if (ImGui.BeginTable("##pairDebugDataStats", 2, ImGuiTableFlags.SizingStretchProp))
|
||||
{
|
||||
DrawPairPropertyRow("Last Data Size", FormatBytes(pair.LastAppliedDataBytes));
|
||||
DrawPairPropertyRow("Approx. VRAM", FormatBytes(pair.LastAppliedApproximateVRAMBytes));
|
||||
DrawPairPropertyRow("Effective VRAM", FormatBytes(pair.LastAppliedApproximateEffectiveVRAMBytes));
|
||||
DrawPairPropertyRow("Last Triangles", pair.LastAppliedDataTris < 0 ? "n/a" : pair.LastAppliedDataTris.ToString(CultureInfo.InvariantCulture));
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
var lastData = pair.LastReceivedCharacterData;
|
||||
if (lastData is null)
|
||||
{
|
||||
ImGui.TextDisabled("No character data has been received for this pair.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var fileReplacementCount = lastData.FileReplacements.Values.Sum(list => list?.Count ?? 0);
|
||||
var totalGamePaths = lastData.FileReplacements.Values.Sum(list => list?.Sum(replacement => replacement.GamePaths.Length) ?? 0);
|
||||
ImGui.BulletText($"File replacements: {fileReplacementCount} entries across {totalGamePaths} game paths.");
|
||||
ImGui.BulletText($"Customize+: {lastData.CustomizePlusData.Count}, Glamourer entries: {lastData.GlamourerData.Count}");
|
||||
ImGui.BulletText($"Manipulation length: {lastData.ManipulationData.Length}, Heels set: {FormatBool(!string.IsNullOrEmpty(lastData.HeelsData))}");
|
||||
|
||||
if (ImGui.TreeNode("Last Received Character Data (JSON)"))
|
||||
{
|
||||
DrawJsonBlob(lastData);
|
||||
ImGui.TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.TextUnformatted("Application Timeline");
|
||||
if (ImGui.BeginTable("##pairDebugTimeline", 2, ImGuiTableFlags.SizingStretchProp))
|
||||
{
|
||||
DrawPairPropertyRow("Last Data Received", FormatTimestamp(debugInfo.LastDataReceivedAt));
|
||||
DrawPairPropertyRow("Last Apply Attempt", FormatTimestamp(debugInfo.LastApplyAttemptAt));
|
||||
DrawPairPropertyRow("Last Successful Apply", FormatTimestamp(debugInfo.LastSuccessfulApplyAt));
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(debugInfo.LastFailureReason))
|
||||
{
|
||||
UiSharedService.ColorTextWrapped($"Last failure: {debugInfo.LastFailureReason}", UIColors.Get("DimRed"));
|
||||
if (debugInfo.BlockingConditions.Count > 0)
|
||||
{
|
||||
ImGui.TextUnformatted("Blocking conditions:");
|
||||
foreach (var condition in debugInfo.BlockingConditions)
|
||||
{
|
||||
ImGui.BulletText(condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.TextUnformatted("Application & Download State");
|
||||
if (ImGui.BeginTable("##pairDebugProcessing", 2, ImGuiTableFlags.SizingStretchProp))
|
||||
{
|
||||
DrawPairPropertyRow("Applying Data", FormatBool(debugInfo.IsApplying));
|
||||
DrawPairPropertyRow("Downloading", FormatBool(debugInfo.IsDownloading));
|
||||
DrawPairPropertyRow("Pending Downloads", debugInfo.PendingDownloadCount.ToString(CultureInfo.InvariantCulture));
|
||||
DrawPairPropertyRow("Forbidden Downloads", debugInfo.ForbiddenDownloadCount.ToString(CultureInfo.InvariantCulture));
|
||||
ImGui.EndTable();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
ImGui.TextUnformatted("Syncshell Memberships");
|
||||
if (snapshot.PairsWithGroups.TryGetValue(pair, out var groups) && groups.Count > 0)
|
||||
{
|
||||
foreach (var group in groups.OrderBy(g => g.Group.AliasOrGID, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
var flags = group.GroupPairUserInfos.TryGetValue(pair.UserData.UID, out var info) ? info : GroupPairUserInfo.None;
|
||||
var flagLabel = flags switch
|
||||
{
|
||||
GroupPairUserInfo.None => string.Empty,
|
||||
_ => $" ({string.Join(", ", GetGroupInfoFlags(flags))})"
|
||||
};
|
||||
ImGui.BulletText($"{group.Group.AliasOrGID} [{group.Group.GID}]{flagLabel}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextDisabled("Not a member of any syncshells.");
|
||||
}
|
||||
|
||||
if (pair.UserPair is null)
|
||||
{
|
||||
ImGui.TextDisabled("Pair DTO snapshot unavailable.");
|
||||
}
|
||||
else if (ImGui.TreeNode("Pair DTO Snapshot"))
|
||||
{
|
||||
DrawJsonBlob(pair.UserPair);
|
||||
ImGui.TreePop();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
DrawPairEventLog(pair);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetGroupInfoFlags(GroupPairUserInfo info)
|
||||
{
|
||||
if (info.HasFlag(GroupPairUserInfo.IsModerator))
|
||||
{
|
||||
yield return "Moderator";
|
||||
}
|
||||
|
||||
if (info.HasFlag(GroupPairUserInfo.IsPinned))
|
||||
{
|
||||
yield return "Pinned";
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawPairEventLog(Pair pair)
|
||||
{
|
||||
ImGui.TextUnformatted("Recent Events");
|
||||
var events = _eventAggregator.EventList.Value;
|
||||
var alias = pair.UserData.Alias;
|
||||
var aliasOrUid = pair.UserData.AliasOrUID;
|
||||
var rawUid = pair.UserData.UID;
|
||||
var playerName = pair.PlayerName;
|
||||
|
||||
var relevantEvents = events.Where(e =>
|
||||
EventMatchesIdentifier(e, rawUid)
|
||||
|| EventMatchesIdentifier(e, aliasOrUid)
|
||||
|| EventMatchesIdentifier(e, alias)
|
||||
|| (!string.IsNullOrEmpty(playerName) && string.Equals(e.Character, playerName, StringComparison.OrdinalIgnoreCase)))
|
||||
.OrderByDescending(e => e.EventTime)
|
||||
.Take(40)
|
||||
.ToList();
|
||||
|
||||
if (relevantEvents.Count == 0)
|
||||
{
|
||||
ImGui.TextDisabled("No recent events were logged for this pair.");
|
||||
return;
|
||||
}
|
||||
|
||||
var baseTableHeight = 300f * ImGuiHelpers.GlobalScale;
|
||||
var tableHeight = MathF.Max(baseTableHeight, ImGui.GetContentRegionAvail().Y);
|
||||
if (ImGui.BeginTable("##pairDebugEvents", 3,
|
||||
ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.ScrollX | ImGuiTableFlags.ScrollY,
|
||||
new Vector2(0f, tableHeight)))
|
||||
{
|
||||
ImGui.TableSetupScrollFreeze(0, 1);
|
||||
ImGui.TableSetupColumn("Time", ImGuiTableColumnFlags.WidthFixed, 110f * ImGuiHelpers.GlobalScale);
|
||||
ImGui.TableSetupColumn("Type", ImGuiTableColumnFlags.WidthFixed, 60f * ImGuiHelpers.GlobalScale);
|
||||
ImGui.TableSetupColumn("Details");
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
foreach (var ev in relevantEvents)
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(ev.EventTime.ToString("T", CultureInfo.CurrentCulture));
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
var (icon, color) = ev.EventSeverity switch
|
||||
{
|
||||
EventSeverity.Informational => (FontAwesomeIcon.InfoCircle, UIColors.Get("LightlessGreen")),
|
||||
EventSeverity.Warning => (FontAwesomeIcon.ExclamationTriangle, UIColors.Get("LightlessYellow")),
|
||||
EventSeverity.Error => (FontAwesomeIcon.ExclamationCircle, UIColors.Get("DimRed")),
|
||||
_ => (FontAwesomeIcon.QuestionCircle, UIColors.Get("LightlessGrey"))
|
||||
};
|
||||
_uiShared.IconText(icon, color);
|
||||
UiSharedService.AttachToolTip(ev.EventSeverity.ToString());
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextWrapped($"[{ev.EventSource}] {ev.Message}");
|
||||
}
|
||||
|
||||
ImGui.EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool EventMatchesIdentifier(Event evt, string? identifier)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(identifier))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (!string.IsNullOrEmpty(evt.UserId) && string.Equals(evt.UserId, identifier, StringComparison.OrdinalIgnoreCase))
|
||||
|| (!string.IsNullOrEmpty(evt.AliasOrUid) && string.Equals(evt.AliasOrUid, identifier, StringComparison.OrdinalIgnoreCase))
|
||||
|| (!string.IsNullOrEmpty(evt.UID) && string.Equals(evt.UID, identifier, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
private static void DrawJsonBlob(object? value)
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
ImGui.TextDisabled("(null)");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var json = JsonSerializer.Serialize(value, DebugJsonOptions);
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, UIColors.Get("LightlessGrey"));
|
||||
foreach (var line in json.Split('\n'))
|
||||
{
|
||||
ImGui.TextUnformatted(line);
|
||||
}
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UiSharedService.ColorTextWrapped($"Failed to serialize data: {ex.Message}", UIColors.Get("DimRed"));
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawPairPropertyRow(string label, string value, Vector4? colorOverride = null)
|
||||
{
|
||||
ImGui.TableNextRow();
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TextUnformatted(label);
|
||||
ImGui.TableNextColumn();
|
||||
if (colorOverride is { } color)
|
||||
{
|
||||
ImGui.TextColored(color, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.TextUnformatted(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static string FormatTimestamp(DateTime? value)
|
||||
{
|
||||
return value is null ? "n/a" : value.Value.ToLocalTime().ToString("G", CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
private static string FormatBytes(long value) => value < 0 ? "n/a" : UiSharedService.ByteToString(value);
|
||||
|
||||
private static string FormatCharacterId(uint id) => id == uint.MaxValue ? "n/a" : $"{id} (0x{id:X8})";
|
||||
|
||||
private static string FormatBool(bool value) => value ? "Yes" : "No";
|
||||
|
||||
|
||||
private void DrawFileStorageSettings()
|
||||
{
|
||||
_lastTab = "FileCache";
|
||||
@@ -2092,11 +2485,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
// redo
|
||||
}
|
||||
else
|
||||
{
|
||||
_lightfinderIconInputInitialized = false;
|
||||
_lightfinderIconPresetIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
_uiShared.DrawHelpText("Switch between the Lightfinder text label and an icon on nameplates.");
|
||||
@@ -2105,11 +2493,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
//redo
|
||||
}
|
||||
else
|
||||
{
|
||||
_lightfinderIconInputInitialized = false;
|
||||
_lightfinderIconPresetIndex = -1;
|
||||
}
|
||||
|
||||
UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurple"), 1.5f);
|
||||
ImGui.TreePop();
|
||||
|
||||
Reference in New Issue
Block a user