1935 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			1935 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using Dalamud.Game.Text;
 | |
| using Dalamud.Interface;
 | |
| using Dalamud.Interface.Colors;
 | |
| using Dalamud.Interface.Utility;
 | |
| using Dalamud.Interface.Utility.Raii;
 | |
| using Dalamud.Utility;
 | |
| using ImGuiNET;
 | |
| using MareSynchronos.API.Data;
 | |
| using MareSynchronos.API.Data.Comparer;
 | |
| using MareSynchronos.API.Dto.Account;
 | |
| using MareSynchronos.API.Routes;
 | |
| using MareSynchronos.FileCache;
 | |
| using MareSynchronos.Interop.Ipc;
 | |
| using MareSynchronos.MareConfiguration;
 | |
| using MareSynchronos.MareConfiguration.Models;
 | |
| using MareSynchronos.PlayerData.Handlers;
 | |
| using MareSynchronos.PlayerData.Pairs;
 | |
| using MareSynchronos.Services;
 | |
| using MareSynchronos.Services.Mediator;
 | |
| using MareSynchronos.Services.ServerConfiguration;
 | |
| using MareSynchronos.WebAPI;
 | |
| using MareSynchronos.WebAPI.Files;
 | |
| using MareSynchronos.WebAPI.Files.Models;
 | |
| using MareSynchronos.WebAPI.SignalR.Utils;
 | |
| using Microsoft.Extensions.Logging;
 | |
| using System.Collections.Concurrent;
 | |
| using System.Globalization;
 | |
| using System.Numerics;
 | |
| using System.Text.Json;
 | |
| using System.Net.Http.Headers;
 | |
| using System.Net.Http.Json;
 | |
| using System.Reflection;
 | |
| 
 | |
| namespace MareSynchronos.UI;
 | |
| 
 | |
| public class SettingsUi : WindowMediatorSubscriberBase
 | |
| {
 | |
|     private readonly ApiController _apiController;
 | |
|     private readonly IpcManager _ipcManager;
 | |
|     private readonly IpcProvider _ipcProvider;
 | |
|     private readonly CacheMonitor _cacheMonitor;
 | |
|     private readonly DalamudUtilService _dalamudUtilService;
 | |
|     private readonly MareConfigService _configService;
 | |
|     private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
 | |
|     private readonly FileCompactor _fileCompactor;
 | |
|     private readonly FileUploadManager _fileTransferManager;
 | |
|     private readonly FileTransferOrchestrator _fileTransferOrchestrator;
 | |
|     private readonly FileCacheManager _fileCacheManager;
 | |
|     private readonly PairManager _pairManager;
 | |
|     private readonly ChatService _chatService;
 | |
|     private readonly GuiHookService _guiHookService;
 | |
|     private readonly PerformanceCollectorService _performanceCollector;
 | |
|     private readonly ServerConfigurationManager _serverConfigurationManager;
 | |
|     private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
 | |
|     private readonly PlayerPerformanceService _playerPerformanceService;
 | |
|     private readonly UiSharedService _uiShared;
 | |
|     private bool _deleteAccountPopupModalShown = false;
 | |
|     private string _lastTab = string.Empty;
 | |
|     private bool? _notesSuccessfullyApplied = null;
 | |
|     private bool _overwriteExistingLabels = false;
 | |
|     private bool _readClearCache = false;
 | |
|     private CancellationTokenSource? _validationCts;
 | |
|     private Task<List<FileCacheEntity>>? _validationTask;
 | |
|     private bool _wasOpen = false;
 | |
|     private readonly IProgress<(int, int, FileCacheEntity)> _validationProgress;
 | |
|     private (int, int, FileCacheEntity) _currentProgress;
 | |
| 
 | |
|     private bool _registrationInProgress = false;
 | |
|     private bool _registrationSuccess = false;
 | |
|     private string? _registrationMessage;
 | |
| 
 | |
|     public SettingsUi(ILogger<SettingsUi> logger,
 | |
|         UiSharedService uiShared, MareConfigService configService,
 | |
|         PairManager pairManager, ChatService chatService, GuiHookService guiHookService,
 | |
|         ServerConfigurationManager serverConfigurationManager,
 | |
|         PlayerPerformanceConfigService playerPerformanceConfigService, PlayerPerformanceService playerPerformanceService,
 | |
|         MareMediator mediator, PerformanceCollectorService performanceCollector,
 | |
|         FileUploadManager fileTransferManager,
 | |
|         FileTransferOrchestrator fileTransferOrchestrator,
 | |
|         FileCacheManager fileCacheManager,
 | |
|         FileCompactor fileCompactor, ApiController apiController,
 | |
|         IpcManager ipcManager, IpcProvider ipcProvider, CacheMonitor cacheMonitor,
 | |
|         DalamudUtilService dalamudUtilService) : base(logger, mediator, "Loporrit Settings", performanceCollector)
 | |
|     {
 | |
|         _configService = configService;
 | |
|         _pairManager = pairManager;
 | |
|         _chatService = chatService;
 | |
|         _guiHookService = guiHookService;
 | |
|         _serverConfigurationManager = serverConfigurationManager;
 | |
|         _playerPerformanceConfigService = playerPerformanceConfigService;
 | |
|         _playerPerformanceService = playerPerformanceService;
 | |
|         _performanceCollector = performanceCollector;
 | |
|         _fileTransferManager = fileTransferManager;
 | |
|         _fileTransferOrchestrator = fileTransferOrchestrator;
 | |
|         _fileCacheManager = fileCacheManager;
 | |
|         _apiController = apiController;
 | |
|         _ipcManager = ipcManager;
 | |
|         _ipcProvider = ipcProvider;
 | |
|         _cacheMonitor = cacheMonitor;
 | |
|         _dalamudUtilService = dalamudUtilService;
 | |
|         _fileCompactor = fileCompactor;
 | |
|         _uiShared = uiShared;
 | |
|         AllowClickthrough = false;
 | |
|         AllowPinning = false;
 | |
|         _validationProgress = new Progress<(int, int, FileCacheEntity)>(v => _currentProgress = v);
 | |
| 
 | |
|         SizeConstraints = new WindowSizeConstraints()
 | |
|         {
 | |
|             MinimumSize = new Vector2(600, 400),
 | |
|             MaximumSize = new Vector2(600, 2000),
 | |
|         };
 | |
| 
 | |
|         Mediator.Subscribe<OpenSettingsUiMessage>(this, (_) => Toggle());
 | |
|         Mediator.Subscribe<SwitchToIntroUiMessage>(this, (_) => IsOpen = false);
 | |
|         Mediator.Subscribe<CutsceneStartMessage>(this, (_) => UiSharedService_GposeStart());
 | |
|         Mediator.Subscribe<CutsceneEndMessage>(this, (_) => UiSharedService_GposeEnd());
 | |
|         Mediator.Subscribe<CharacterDataCreatedMessage>(this, (msg) => LastCreatedCharacterData = msg.CharacterData);
 | |
|         Mediator.Subscribe<DownloadStartedMessage>(this, (msg) => _currentDownloads[msg.DownloadId] = msg.DownloadStatus);
 | |
|         Mediator.Subscribe<DownloadFinishedMessage>(this, (msg) => _currentDownloads.TryRemove(msg.DownloadId, out _));
 | |
|     }
 | |
| 
 | |
|     public CharacterData? LastCreatedCharacterData { private get; set; }
 | |
|     private ApiController ApiController => _uiShared.ApiController;
 | |
| 
 | |
|     protected override void DrawInternal()
 | |
|     {
 | |
|         _ = _uiShared.DrawOtherPluginState();
 | |
| 
 | |
|         DrawSettingsContent();
 | |
|     }
 | |
| 
 | |
|     public override void OnClose()
 | |
|     {
 | |
|         _uiShared.EditTrackerPosition = false;
 | |
| 
 | |
|         base.OnClose();
 | |
|     }
 | |
| 
 | |
|     private void DrawBlockedTransfers()
 | |
|     {
 | |
|         _lastTab = "BlockedTransfers";
 | |
|         UiSharedService.ColorTextWrapped("Files that you attempted to upload or download that were forbidden to be transferred by their creators will appear here. " +
 | |
|                              "If you see file paths from your drive here, then those files were not allowed to be uploaded. If you see hashes, those files were not allowed to be downloaded. " +
 | |
|                              "Ask your paired friend to send you the mod in question through other means or acquire the mod yourself.",
 | |
|             ImGuiColors.DalamudGrey);
 | |
| 
 | |
|         if (ImGui.BeginTable("TransfersTable", 2, ImGuiTableFlags.SizingStretchProp))
 | |
|         {
 | |
|             ImGui.TableSetupColumn(
 | |
|                 $"Hash/Filename");
 | |
|             ImGui.TableSetupColumn($"Forbidden by");
 | |
| 
 | |
|             ImGui.TableHeadersRow();
 | |
| 
 | |
|             foreach (var item in _fileTransferOrchestrator.ForbiddenTransfers)
 | |
|             {
 | |
|                 ImGui.TableNextColumn();
 | |
|                 if (item is UploadFileTransfer transfer)
 | |
|                 {
 | |
|                     ImGui.TextUnformatted(transfer.LocalFile);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     ImGui.TextUnformatted(item.Hash);
 | |
|                 }
 | |
|                 ImGui.TableNextColumn();
 | |
|                 ImGui.TextUnformatted(item.ForbiddenBy);
 | |
|             }
 | |
|             ImGui.EndTable();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private void DrawCurrentTransfers()
 | |
|     {
 | |
|         _lastTab = "Transfers";
 | |
|         _uiShared.BigText("Transfer Settings");
 | |
| 
 | |
|         int maxParallelDownloads = _configService.Current.ParallelDownloads;
 | |
|         int downloadSpeedLimit = _configService.Current.DownloadSpeedLimitInBytes;
 | |
| 
 | |
|         ImGui.AlignTextToFramePadding();
 | |
|         ImGui.TextUnformatted("Global Download Speed Limit");
 | |
|         ImGui.SameLine();
 | |
|         ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
 | |
|         if (ImGui.InputInt("###speedlimit", ref downloadSpeedLimit))
 | |
|         {
 | |
|             _configService.Current.DownloadSpeedLimitInBytes = downloadSpeedLimit;
 | |
|             _configService.Save();
 | |
|             Mediator.Publish(new DownloadLimitChangedMessage());
 | |
|         }
 | |
|         ImGui.SameLine();
 | |
|         ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
 | |
|         _uiShared.DrawCombo("###speed", [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");
 | |
| 
 | |
|         ImGui.SetNextItemWidth(250 * ImGuiHelpers.GlobalScale);
 | |
|         if (ImGui.SliderInt("Maximum Parallel Downloads", ref maxParallelDownloads, 1, 10))
 | |
|         {
 | |
|             _configService.Current.ParallelDownloads = maxParallelDownloads;
 | |
|             _configService.Save();
 | |
|         }
 | |
| 
 | |
|         ImGui.Separator();
 | |
|         _uiShared.BigText("Transfer UI");
 | |
| 
 | |
|         bool showTransferWindow = _configService.Current.ShowTransferWindow;
 | |
|         if (ImGui.Checkbox("Show separate transfer window", ref showTransferWindow))
 | |
|         {
 | |
|             _configService.Current.ShowTransferWindow = showTransferWindow;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText($"The download window will show the current progress of outstanding downloads.{Environment.NewLine}{Environment.NewLine}" +
 | |
|             $"What do W/Q/P/D stand for?{Environment.NewLine}W = Waiting for Slot (see Maximum Parallel Downloads){Environment.NewLine}" +
 | |
|             $"Q = Queued on Server, waiting for queue ready signal{Environment.NewLine}" +
 | |
|             $"P = Processing download (aka downloading){Environment.NewLine}" +
 | |
|             $"D = Decompressing download");
 | |
|         if (!_configService.Current.ShowTransferWindow) ImGui.BeginDisabled();
 | |
|         ImGui.Indent();
 | |
|         bool editTransferWindowPosition = _uiShared.EditTrackerPosition;
 | |
|         if (ImGui.Checkbox("Edit Transfer Window position", ref editTransferWindowPosition))
 | |
|         {
 | |
|             _uiShared.EditTrackerPosition = editTransferWindowPosition;
 | |
|         }
 | |
|         ImGui.Unindent();
 | |
|         if (!_configService.Current.ShowTransferWindow) ImGui.EndDisabled();
 | |
| 
 | |
|         bool showTransferBars = _configService.Current.ShowTransferBars;
 | |
|         if (ImGui.Checkbox("Show transfer bars rendered below players", ref showTransferBars))
 | |
|         {
 | |
|             _configService.Current.ShowTransferBars = showTransferBars;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("This will render a progress bar during the download at the feet of the player you are downloading from.");
 | |
| 
 | |
|         if (!showTransferBars) ImGui.BeginDisabled();
 | |
|         ImGui.Indent();
 | |
|         bool transferBarShowText = _configService.Current.TransferBarsShowText;
 | |
|         if (ImGui.Checkbox("Show Download Text", ref transferBarShowText))
 | |
|         {
 | |
|             _configService.Current.TransferBarsShowText = transferBarShowText;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Shows download text (amount of MiB downloaded) in the transfer bars");
 | |
|         int transferBarWidth = _configService.Current.TransferBarsWidth;
 | |
|         ImGui.SetNextItemWidth(250 * ImGuiHelpers.GlobalScale);
 | |
|         if (ImGui.SliderInt("Transfer Bar Width", ref transferBarWidth, 0, 500))
 | |
|         {
 | |
|             if (transferBarWidth < 10)
 | |
|                 transferBarWidth = 10;
 | |
|             _configService.Current.TransferBarsWidth = transferBarWidth;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Width of the displayed transfer bars (will never be less wide than the displayed text)");
 | |
|         int transferBarHeight = _configService.Current.TransferBarsHeight;
 | |
|         ImGui.SetNextItemWidth(250 * ImGuiHelpers.GlobalScale);
 | |
|         if (ImGui.SliderInt("Transfer Bar Height", ref transferBarHeight, 0, 50))
 | |
|         {
 | |
|             if (transferBarHeight < 2)
 | |
|                 transferBarHeight = 2;
 | |
|             _configService.Current.TransferBarsHeight = transferBarHeight;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Height of the displayed transfer bars (will never be less tall than the displayed text)");
 | |
|         bool showUploading = _configService.Current.ShowUploading;
 | |
|         if (ImGui.Checkbox("Show 'Uploading' text below players that are currently uploading", ref showUploading))
 | |
|         {
 | |
|             _configService.Current.ShowUploading = showUploading;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("This will render an 'Uploading' text at the feet of the player that is in progress of uploading data.");
 | |
| 
 | |
|         ImGui.Unindent();
 | |
|         if (!showUploading) ImGui.BeginDisabled();
 | |
|         ImGui.Indent();
 | |
|         bool showUploadingBigText = _configService.Current.ShowUploadingBigText;
 | |
|         if (ImGui.Checkbox("Large font for 'Uploading' text", ref showUploadingBigText))
 | |
|         {
 | |
|             _configService.Current.ShowUploadingBigText = showUploadingBigText;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("This will render an 'Uploading' text in a larger font.");
 | |
| 
 | |
|         ImGui.Unindent();
 | |
| 
 | |
|         if (!showUploading) ImGui.EndDisabled();
 | |
|         if (!showTransferBars) ImGui.EndDisabled();
 | |
| 
 | |
|         ImGui.Separator();
 | |
|         _uiShared.BigText("Current Transfers");
 | |
| 
 | |
|         if (ImGui.BeginTabBar("TransfersTabBar"))
 | |
|         {
 | |
|             if (ApiController.ServerState is ServerState.Connected && ImGui.BeginTabItem("Transfers"))
 | |
|             {
 | |
|                 ImGui.TextUnformatted("Uploads");
 | |
|                 if (ImGui.BeginTable("UploadsTable", 3))
 | |
|                 {
 | |
|                     ImGui.TableSetupColumn("File");
 | |
|                     ImGui.TableSetupColumn("Uploaded");
 | |
|                     ImGui.TableSetupColumn("Size");
 | |
|                     ImGui.TableHeadersRow();
 | |
|                     foreach (var transfer in _fileTransferManager.CurrentUploads.ToArray())
 | |
|                     {
 | |
|                         var color = UiSharedService.UploadColor((transfer.Transferred, transfer.Total));
 | |
|                         var col = ImRaii.PushColor(ImGuiCol.Text, color);
 | |
|                         ImGui.TableNextColumn();
 | |
|                         ImGui.TextUnformatted(transfer.Hash);
 | |
|                         ImGui.TableNextColumn();
 | |
|                         ImGui.TextUnformatted(UiSharedService.ByteToString(transfer.Transferred));
 | |
|                         ImGui.TableNextColumn();
 | |
|                         ImGui.TextUnformatted(UiSharedService.ByteToString(transfer.Total));
 | |
|                         col.Dispose();
 | |
|                         ImGui.TableNextRow();
 | |
|                     }
 | |
| 
 | |
|                     ImGui.EndTable();
 | |
|                 }
 | |
|                 ImGui.Separator();
 | |
|                 ImGui.TextUnformatted("Downloads");
 | |
|                 if (ImGui.BeginTable("DownloadsTable", 4))
 | |
|                 {
 | |
|                     ImGui.TableSetupColumn("User");
 | |
|                     ImGui.TableSetupColumn("Server");
 | |
|                     ImGui.TableSetupColumn("Files");
 | |
|                     ImGui.TableSetupColumn("Download");
 | |
|                     ImGui.TableHeadersRow();
 | |
| 
 | |
|                     foreach (var transfer in _currentDownloads.ToArray())
 | |
|                     {
 | |
|                         var userName = transfer.Key.Name;
 | |
|                         foreach (var entry in transfer.Value)
 | |
|                         {
 | |
|                             var color = UiSharedService.UploadColor((entry.Value.TransferredBytes, entry.Value.TotalBytes));
 | |
|                             ImGui.TableNextColumn();
 | |
|                             ImGui.TextUnformatted(userName);
 | |
|                             ImGui.TableNextColumn();
 | |
|                             ImGui.TextUnformatted(entry.Key);
 | |
|                             var col = ImRaii.PushColor(ImGuiCol.Text, color);
 | |
|                             ImGui.TableNextColumn();
 | |
|                             ImGui.TextUnformatted(entry.Value.TransferredFiles + "/" + entry.Value.TotalFiles);
 | |
|                             ImGui.TableNextColumn();
 | |
|                             ImGui.TextUnformatted(UiSharedService.ByteToString(entry.Value.TransferredBytes) + "/" + UiSharedService.ByteToString(entry.Value.TotalBytes));
 | |
|                             ImGui.TableNextColumn();
 | |
|                             col.Dispose();
 | |
|                             ImGui.TableNextRow();
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     ImGui.EndTable();
 | |
|                 }
 | |
| 
 | |
|                 ImGui.EndTabItem();
 | |
|             }
 | |
| 
 | |
|             if (ImGui.BeginTabItem("Blocked Transfers"))
 | |
|             {
 | |
|                 DrawBlockedTransfers();
 | |
|                 ImGui.EndTabItem();
 | |
|             }
 | |
| 
 | |
|             ImGui.EndTabBar();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private static readonly List<(XivChatType, string)> _syncshellChatTypes = [
 | |
|         (XivChatType.None, "(use global setting)"),
 | |
|         (XivChatType.Debug, "Debug"),
 | |
|         (XivChatType.Echo, "Echo"),
 | |
|         (XivChatType.StandardEmote, "Standard Emote"),
 | |
|         (XivChatType.CustomEmote, "Custom Emote"),
 | |
|         (XivChatType.SystemMessage, "System Message"),
 | |
|         (XivChatType.SystemError, "System Error"),
 | |
|         (XivChatType.GatheringSystemMessage, "Gathering Message"),
 | |
|         (XivChatType.ErrorMessage, "Error message"),
 | |
|     ];
 | |
| 
 | |
|     private void DrawChatConfig()
 | |
|     {
 | |
|         _lastTab = "Chat";
 | |
| 
 | |
|         _uiShared.BigText("Chat Settings");
 | |
| 
 | |
|         var disableSyncshellChat = _configService.Current.DisableSyncshellChat;
 | |
| 
 | |
|         if (ImGui.Checkbox("Disable chat globally", ref disableSyncshellChat))
 | |
|         {
 | |
|             _configService.Current.DisableSyncshellChat = disableSyncshellChat;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Global setting to disable chat for all syncshells.");
 | |
| 
 | |
|         using var pushDisableGlobal = ImRaii.Disabled(disableSyncshellChat);
 | |
| 
 | |
|         var uiColors = _dalamudUtilService.UiColors.Value;
 | |
|         int globalChatColor = _configService.Current.ChatColor;
 | |
| 
 | |
|         if (globalChatColor != 0 && !uiColors.ContainsKey(globalChatColor))
 | |
|         {
 | |
|             globalChatColor = 0;
 | |
|             _configService.Current.ChatColor = 0;
 | |
|             _configService.Save();
 | |
|         }
 | |
| 
 | |
|         ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
 | |
|         _uiShared.DrawColorCombo("Chat text color", Enumerable.Concat([0], uiColors.Keys),
 | |
|         i => i switch
 | |
|         {
 | |
|             0 => (uiColors[ChatService.DefaultColor].Dark, "Plugin Default"),
 | |
|             _ => (uiColors[i].Dark, $"[{i}] Sample Text")
 | |
|         },
 | |
|         i => {
 | |
|             _configService.Current.ChatColor = i;
 | |
|             _configService.Save();
 | |
|         }, globalChatColor);
 | |
| 
 | |
|         int globalChatType = _configService.Current.ChatLogKind;
 | |
|         int globalChatTypeIdx = _syncshellChatTypes.FindIndex(x => globalChatType == (int)x.Item1);
 | |
| 
 | |
|         if (globalChatTypeIdx == -1)
 | |
|             globalChatTypeIdx = 0;
 | |
| 
 | |
|         ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
 | |
|         _uiShared.DrawCombo("Chat channel", Enumerable.Range(1, _syncshellChatTypes.Count - 1), i => $"{_syncshellChatTypes[i].Item2}",
 | |
|         i => {
 | |
|             if (_configService.Current.ChatLogKind == (int)_syncshellChatTypes[i].Item1)
 | |
|                 return;
 | |
|             _configService.Current.ChatLogKind = (int)_syncshellChatTypes[i].Item1;
 | |
|             _chatService.PrintChannelExample($"Selected channel: {_syncshellChatTypes[i].Item2}");
 | |
|             _configService.Save();
 | |
|         }, globalChatTypeIdx);
 | |
|         _uiShared.DrawHelpText("FFXIV chat channel to output chat messages on.");
 | |
| 
 | |
|         ImGui.SetWindowFontScale(0.6f);
 | |
|         _uiShared.BigText("\"Chat 2\" Plugin Integration");
 | |
|         ImGui.SetWindowFontScale(1.0f);
 | |
| 
 | |
|         var extraChatTags = _configService.Current.ExtraChatTags;
 | |
|         if (ImGui.Checkbox("Tag messages as ExtraChat", ref extraChatTags))
 | |
|         {
 | |
|             _configService.Current.ExtraChatTags = extraChatTags;
 | |
|             if (!extraChatTags)
 | |
|                 _configService.Current.ExtraChatAPI = false;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("If enabled, messages will be filtered under the category \"ExtraChat channels: All\".\n\nThis works even if ExtraChat is also installed and enabled.");
 | |
| 
 | |
|         ImGui.Separator();
 | |
| 
 | |
|         _uiShared.BigText("Syncshell Settings");
 | |
| 
 | |
|         if (!ApiController.ServerAlive)
 | |
|         {
 | |
|             ImGui.TextUnformatted("Connect to the server to configure individual syncshell settings.");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (_pairManager.Groups.Count == 0)
 | |
|         {
 | |
|             ImGui.TextUnformatted("Once you join a syncshell you can configure its chat settings here.");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         foreach (var group in _pairManager.Groups.OrderBy(k => k.Key.GID, StringComparer.Ordinal))
 | |
|         {
 | |
|             var gid = group.Key.GID;
 | |
|             using var pushId = ImRaii.PushId(gid);
 | |
| 
 | |
|             var shellConfig = _serverConfigurationManager.GetShellConfigForGid(gid);
 | |
|             var shellNumber = shellConfig.ShellNumber;
 | |
|             var shellEnabled = shellConfig.Enabled;
 | |
|             var shellName = _serverConfigurationManager.GetNoteForGid(gid) ?? group.Key.AliasOrGID;
 | |
| 
 | |
|             if (shellEnabled)
 | |
|                 shellName = $"[{shellNumber}] {shellName}";
 | |
| 
 | |
|             ImGui.SetWindowFontScale(0.6f);
 | |
|             _uiShared.BigText(shellName);
 | |
|             ImGui.SetWindowFontScale(1.0f);
 | |
| 
 | |
|             using var pushIndent = ImRaii.PushIndent();
 | |
| 
 | |
|             if (ImGui.Checkbox($"Enable chat for this syncshell##{gid}", ref shellEnabled))
 | |
|             {
 | |
|                 // If there is an active group with the same syncshell number, pick a new one
 | |
|                 int nextNumber = 1;
 | |
|                 bool conflict = false;
 | |
|                 foreach (var otherGroup in _pairManager.Groups)
 | |
|                 {
 | |
|                     if (gid.Equals(otherGroup.Key.GID, StringComparison.Ordinal)) continue;
 | |
|                     var otherShellConfig = _serverConfigurationManager.GetShellConfigForGid(otherGroup.Key.GID);
 | |
|                     if (otherShellConfig.Enabled && otherShellConfig.ShellNumber == shellNumber)
 | |
|                         conflict = true;
 | |
|                     nextNumber = Math.Max(nextNumber, otherShellConfig.ShellNumber) + 1;
 | |
|                 }
 | |
|                 if (conflict)
 | |
|                     shellConfig.ShellNumber = nextNumber;
 | |
|                 shellConfig.Enabled = shellEnabled;
 | |
|                 _serverConfigurationManager.SaveShellConfigForGid(gid, shellConfig);
 | |
|             }
 | |
| 
 | |
|             using var pushDisabled = ImRaii.Disabled(!shellEnabled);
 | |
| 
 | |
|             ImGui.SetNextItemWidth(50 * ImGuiHelpers.GlobalScale);
 | |
| 
 | |
|             // _uiShared.DrawCombo() remembers the selected option -- we don't want that, because the value can change
 | |
|             if (ImGui.BeginCombo("Syncshell number##{gid}", $"{shellNumber}"))
 | |
|             {
 | |
|                 // Same hard-coded number in CommandManagerService
 | |
|                 for (int i = 1; i <= ChatService.CommandMaxNumber; ++i)
 | |
|                 {
 | |
|                     if (ImGui.Selectable($"{i}", i == shellNumber))
 | |
|                     {
 | |
|                         // Find an active group with the same syncshell number as selected, and swap it
 | |
|                         // This logic can leave duplicate IDs present in the config but its not critical
 | |
|                         foreach (var otherGroup in _pairManager.Groups)
 | |
|                         {
 | |
|                             if (gid.Equals(otherGroup.Key.GID, StringComparison.Ordinal)) continue;
 | |
|                             var otherShellConfig = _serverConfigurationManager.GetShellConfigForGid(otherGroup.Key.GID);
 | |
|                             if (otherShellConfig.Enabled && otherShellConfig.ShellNumber == i)
 | |
|                             {
 | |
|                                 otherShellConfig.ShellNumber = shellNumber;
 | |
|                                 _serverConfigurationManager.SaveShellConfigForGid(otherGroup.Key.GID, otherShellConfig);
 | |
|                                 break;
 | |
|                             }
 | |
|                         }
 | |
|                         shellConfig.ShellNumber = i;
 | |
|                         _serverConfigurationManager.SaveShellConfigForGid(gid, shellConfig);
 | |
|                     }
 | |
|                 }
 | |
|                 ImGui.EndCombo();
 | |
|             }
 | |
| 
 | |
|             if (shellConfig.Color != 0 && !uiColors.ContainsKey(shellConfig.Color))
 | |
|             {
 | |
|                 shellConfig.Color = 0;
 | |
|                 _serverConfigurationManager.SaveShellConfigForGid(gid, shellConfig);
 | |
|             }
 | |
| 
 | |
|             ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
 | |
|             _uiShared.DrawColorCombo($"Chat text color##{gid}", Enumerable.Concat([0], uiColors.Keys),
 | |
|             i => i switch
 | |
|             {
 | |
|                 0 => (uiColors[globalChatColor > 0 ? globalChatColor : ChatService.DefaultColor].Dark, "(use global setting)"),
 | |
|                 _ => (uiColors[i].Dark, $"[{i}] Sample Text")
 | |
|             },
 | |
|             i => {
 | |
|                 shellConfig.Color = i;
 | |
|                 _serverConfigurationManager.SaveShellConfigForGid(gid, shellConfig);
 | |
|             }, shellConfig.Color);
 | |
| 
 | |
|             int shellChatTypeIdx = _syncshellChatTypes.FindIndex(x => shellConfig.LogKind == (int)x.Item1);
 | |
| 
 | |
|             if (shellChatTypeIdx == -1)
 | |
|                 shellChatTypeIdx = 0;
 | |
| 
 | |
|             ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
 | |
|             _uiShared.DrawCombo($"Chat channel##{gid}", Enumerable.Range(0, _syncshellChatTypes.Count), i => $"{_syncshellChatTypes[i].Item2}",
 | |
|             i => {
 | |
|                 shellConfig.LogKind = (int)_syncshellChatTypes[i].Item1;
 | |
|                 _serverConfigurationManager.SaveShellConfigForGid(gid, shellConfig);
 | |
|             }, shellChatTypeIdx);
 | |
|             _uiShared.DrawHelpText("Override the FFXIV chat channel used for this syncshell.");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private void DrawAdvanced()
 | |
|     {
 | |
|         _lastTab = "Advanced";
 | |
| 
 | |
|         _uiShared.BigText("Advanced");
 | |
| 
 | |
|         bool mareApi = _configService.Current.MareAPI;
 | |
|         if (ImGui.Checkbox("Enable Mare Synchronos API", ref mareApi))
 | |
|         {
 | |
|             _configService.Current.MareAPI = mareApi;
 | |
|             _configService.Save();
 | |
|             _ipcProvider.HandleMareImpersonation();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Enables handling of the Mare Synchronos API. This currently includes:\n\n" +
 | |
|             " - MCDF loading support for other plugins\n" +
 | |
|             " - Blocking Moodles applications to paired users\n\n" +
 | |
|             "If the Mare Synchronos plugin is loaded while this option is enabled, control of its API will be relinquished.");
 | |
| 
 | |
|         using (_ = ImRaii.PushIndent())
 | |
|         {
 | |
|             ImGui.SameLine(300.0f);
 | |
|             if (_ipcProvider.ImpersonationActive)
 | |
|             {
 | |
|                 UiSharedService.ColorTextWrapped("Mare API active!", ImGuiColors.HealerGreen);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 if (!mareApi)
 | |
|                     UiSharedService.ColorTextWrapped("Mare API inactive: Option is disabled", ImGuiColors.DalamudYellow);
 | |
|                 else if (_ipcProvider.MarePluginEnabled)
 | |
|                     UiSharedService.ColorTextWrapped("Mare API inactive: Mare plugin is loaded", ImGuiColors.DalamudYellow);
 | |
|                 else
 | |
|                     UiSharedService.ColorTextWrapped("Mare API inactive: Unknown reason", ImGuiColors.DalamudRed);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         bool logEvents = _configService.Current.LogEvents;
 | |
|         if (ImGui.Checkbox("Log Event Viewer data to disk", ref logEvents))
 | |
|         {
 | |
|             _configService.Current.LogEvents = logEvents;
 | |
|             _configService.Save();
 | |
|         }
 | |
| 
 | |
|         ImGui.SameLine(300.0f);
 | |
|         if (_uiShared.IconTextButton(FontAwesomeIcon.NotesMedical, "Open Event Viewer"))
 | |
|         {
 | |
|             Mediator.Publish(new UiToggleMessage(typeof(EventViewerUI)));
 | |
|         }
 | |
| 
 | |
|         bool holdCombatApplication = _configService.Current.HoldCombatApplication;
 | |
|         if (ImGui.Checkbox("Hold application during combat", ref holdCombatApplication))
 | |
|         {
 | |
|             if (!holdCombatApplication)
 | |
|                 Mediator.Publish(new CombatOrPerformanceEndMessage());
 | |
|             _configService.Current.HoldCombatApplication = holdCombatApplication;
 | |
|             _configService.Save();
 | |
|         }
 | |
| 
 | |
|         bool serializedApplications = _configService.Current.SerialApplication;
 | |
|         if (ImGui.Checkbox("Serialized player applications", ref serializedApplications))
 | |
|         {
 | |
|             _configService.Current.SerialApplication = serializedApplications;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Experimental - May reduce issues in crowded areas");
 | |
| 
 | |
|         ImGui.Separator();
 | |
|         _uiShared.BigText("Debug");
 | |
| #if DEBUG
 | |
|         if (LastCreatedCharacterData != null && ImGui.TreeNode("Last created character data"))
 | |
|         {
 | |
|             foreach (var l in JsonSerializer.Serialize(LastCreatedCharacterData, new JsonSerializerOptions() { WriteIndented = true }).Split('\n'))
 | |
|             {
 | |
|                 ImGui.TextUnformatted($"{l}");
 | |
|             }
 | |
| 
 | |
|             ImGui.TreePop();
 | |
|         }
 | |
| #endif
 | |
|         if (_uiShared.IconTextButton(FontAwesomeIcon.Copy, "[DEBUG] Copy Last created Character Data to clipboard"))
 | |
|         {
 | |
|             if (LastCreatedCharacterData != null)
 | |
|             {
 | |
|                 ImGui.SetClipboardText(JsonSerializer.Serialize(LastCreatedCharacterData, new JsonSerializerOptions() { WriteIndented = true }));
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 ImGui.SetClipboardText("ERROR: No created character data, cannot copy.");
 | |
|             }
 | |
|         }
 | |
|         UiSharedService.AttachToolTip("Use this when reporting mods being rejected from the server.");
 | |
| 
 | |
|         _uiShared.DrawCombo("Log Level", Enum.GetValues<LogLevel>(), (l) => l.ToString(), (l) =>
 | |
|         {
 | |
|             _configService.Current.LogLevel = l;
 | |
|             _configService.Save();
 | |
|         }, _configService.Current.LogLevel);
 | |
| 
 | |
|         bool logPerformance = _configService.Current.LogPerformance;
 | |
|         if (ImGui.Checkbox("Log Performance Counters", ref logPerformance))
 | |
|         {
 | |
|             _configService.Current.LogPerformance = logPerformance;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Enabling this can incur a (slight) performance impact. Enabling this for extended periods of time is not recommended.");
 | |
| 
 | |
|         using (ImRaii.Disabled(!logPerformance))
 | |
|         {
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.StickyNote, "Print Performance Stats to /xllog"))
 | |
|             {
 | |
|                 _performanceCollector.PrintPerformanceStats();
 | |
|             }
 | |
|             ImGui.SameLine();
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.StickyNote, "Print Performance Stats (last 60s) to /xllog"))
 | |
|             {
 | |
|                 _performanceCollector.PrintPerformanceStats(60);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (ImGui.TreeNode("Active Character Blocks"))
 | |
|         {
 | |
|             var onlinePairs = _pairManager.GetOnlineUserPairs();
 | |
|             foreach (var pair in onlinePairs)
 | |
|             {
 | |
|                 if (pair.IsApplicationBlocked)
 | |
|                 {
 | |
|                     ImGui.TextUnformatted(pair.PlayerName);
 | |
|                     ImGui.SameLine();
 | |
|                     ImGui.TextUnformatted(string.Join(", ", pair.HoldApplicationReasons));
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private void DrawFileStorageSettings()
 | |
|     {
 | |
|         _lastTab = "FileCache";
 | |
| 
 | |
|         _uiShared.BigText("Export MCDF");
 | |
| 
 | |
|         ImGuiHelpers.ScaledDummy(10);
 | |
| 
 | |
|         UiSharedService.ColorTextWrapped("Exporting MCDF has moved.", ImGuiColors.DalamudYellow);
 | |
|         ImGuiHelpers.ScaledDummy(5);
 | |
|         UiSharedService.TextWrapped("It is now found in the Main UI under \"Character Data Hub\"");
 | |
|         if (_uiShared.IconTextButton(FontAwesomeIcon.Running, "Open Character Data Hub"))
 | |
|         {
 | |
|             Mediator.Publish(new UiToggleMessage(typeof(CharaDataHubUi)));
 | |
|         }
 | |
| 
 | |
|         ImGui.Separator();
 | |
| 
 | |
|         _uiShared.BigText("Storage");
 | |
| 
 | |
|         UiSharedService.TextWrapped("Loporrit stores downloaded files from paired people permanently. This is to improve loading performance and requiring less downloads. " +
 | |
|             "The storage governs itself by clearing data beyond the set storage size. Please set the storage size accordingly. It is not necessary to manually clear the storage.");
 | |
| 
 | |
|         _uiShared.DrawFileScanState();
 | |
|         ImGui.AlignTextToFramePadding();
 | |
|         ImGui.TextUnformatted("Monitoring Penumbra Folder: " + (_cacheMonitor.PenumbraWatcher?.Path ?? "Not monitoring"));
 | |
|         if (string.IsNullOrEmpty(_cacheMonitor.PenumbraWatcher?.Path))
 | |
|         {
 | |
|             ImGui.SameLine();
 | |
|             using var id = ImRaii.PushId("penumbraMonitor");
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.ArrowsToCircle, "Try to reinitialize Monitor"))
 | |
|             {
 | |
|                 _cacheMonitor.StartPenumbraWatcher(_ipcManager.Penumbra.ModDirectory);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         ImGui.AlignTextToFramePadding();
 | |
|         ImGui.TextUnformatted("Monitoring Loporrit Storage Folder: " + (_cacheMonitor.MareWatcher?.Path ?? "Not monitoring"));
 | |
|         if (string.IsNullOrEmpty(_cacheMonitor.MareWatcher?.Path))
 | |
|         {
 | |
|             ImGui.SameLine();
 | |
|             using var id = ImRaii.PushId("mareMonitor");
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.ArrowsToCircle, "Try to reinitialize Monitor"))
 | |
|             {
 | |
|                 _cacheMonitor.StartMareWatcher(_configService.Current.CacheFolder);
 | |
|             }
 | |
|         }
 | |
|         if (_cacheMonitor.MareWatcher == null || _cacheMonitor.PenumbraWatcher == null)
 | |
|         {
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.Play, "Resume Monitoring"))
 | |
|             {
 | |
|                 _cacheMonitor.StartMareWatcher(_configService.Current.CacheFolder);
 | |
|                 _cacheMonitor.StartPenumbraWatcher(_ipcManager.Penumbra.ModDirectory);
 | |
|                 _cacheMonitor.InvokeScan();
 | |
|             }
 | |
|             UiSharedService.AttachToolTip("Attempts to resume monitoring for both Penumbra and Loporrit Storage. "
 | |
|                 + "Resuming the monitoring will also force a full scan to run." + Environment.NewLine
 | |
|                 + "If the button remains present after clicking it, consult /xllog for errors");
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             using (ImRaii.Disabled(!UiSharedService.CtrlPressed()))
 | |
|             {
 | |
|                 if (_uiShared.IconTextButton(FontAwesomeIcon.Stop, "Stop Monitoring"))
 | |
|                 {
 | |
|                     _cacheMonitor.StopMonitoring();
 | |
|                 }
 | |
|             }
 | |
|             UiSharedService.AttachToolTip("Stops the monitoring for both Penumbra and Loporrit Storage. "
 | |
|                 + "Do not stop the monitoring, unless you plan to move the Penumbra and Loporrit Storage folders, to ensure correct functionality of Loporrit." + Environment.NewLine
 | |
|                 + "If you stop the monitoring to move folders around, resume it after you are finished moving the files."
 | |
|                 + UiSharedService.TooltipSeparator + "Hold CTRL to enable this button");
 | |
|         }
 | |
| 
 | |
|         _uiShared.DrawCacheDirectorySetting();
 | |
|         ImGui.AlignTextToFramePadding();
 | |
|         if (_cacheMonitor.FileCacheSize >= 0)
 | |
|             ImGui.TextUnformatted($"Currently utilized local storage: {_cacheMonitor.FileCacheSize / 1024.0 / 1024.0 / 1024.0:0.00} GiB");
 | |
|         else
 | |
|             ImGui.TextUnformatted($"Currently utilized local storage: Calculating...");
 | |
|         bool isLinux = _dalamudUtilService.IsWine;
 | |
|         if (!isLinux)
 | |
|             ImGui.TextUnformatted($"Remaining space free on drive: {_cacheMonitor.FileCacheDriveFree / 1024.0 / 1024.0 / 1024.0:0.00} GiB");
 | |
|         bool useFileCompactor = _configService.Current.UseCompactor;
 | |
|         if (!useFileCompactor && !isLinux)
 | |
|         {
 | |
|             UiSharedService.ColorTextWrapped("Hint: To free up space when using Loporrit consider enabling the File Compactor", ImGuiColors.DalamudYellow);
 | |
|         }
 | |
|         if (isLinux || !_cacheMonitor.StorageisNTFS) ImGui.BeginDisabled();
 | |
|         if (ImGui.Checkbox("Use file compactor", ref useFileCompactor))
 | |
|         {
 | |
|             _configService.Current.UseCompactor = useFileCompactor;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("The file compactor can massively reduce your saved files. It might incur a minor penalty on loading files on a slow CPU." + Environment.NewLine
 | |
|             + "It is recommended to leave it enabled to save on space.");
 | |
| 
 | |
|         if (!_fileCompactor.MassCompactRunning)
 | |
|         {
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.FileArchive, "Compact all files in storage"))
 | |
|             {
 | |
|                 _ = Task.Run(() =>
 | |
|                 {
 | |
|                     _fileCompactor.CompactStorage(compress: true);
 | |
|                     _cacheMonitor.RecalculateFileCacheSize(CancellationToken.None);
 | |
|                 });
 | |
|             }
 | |
|             UiSharedService.AttachToolTip("This will run compression on all files in your current storage folder." + Environment.NewLine
 | |
|                 + "You do not need to run this manually if you keep the file compactor enabled.");
 | |
|             ImGui.SameLine();
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.File, "Decompact all files in storage"))
 | |
|             {
 | |
|                 _ = Task.Run(() =>
 | |
|                 {
 | |
|                     _fileCompactor.CompactStorage(compress: false);
 | |
|                     _cacheMonitor.RecalculateFileCacheSize(CancellationToken.None);
 | |
|                 });
 | |
|             }
 | |
|             UiSharedService.AttachToolTip("This will run decompression on all files in your current storage folder.");
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             UiSharedService.ColorText($"File compactor currently running ({_fileCompactor.Progress})", ImGuiColors.DalamudYellow);
 | |
|         }
 | |
|         if (isLinux || !_cacheMonitor.StorageisNTFS)
 | |
|         {
 | |
|             ImGui.EndDisabled();
 | |
|             ImGui.TextUnformatted("The file compactor is only available on Windows and NTFS drives.");
 | |
|         }
 | |
|         ImGuiHelpers.ScaledDummy(new Vector2(10, 10));
 | |
| 
 | |
|         ImGui.Separator();
 | |
|         UiSharedService.TextWrapped("File Storage validation can make sure that all files in your local storage folder are valid. " +
 | |
|             "Run the validation before you clear the Storage for no reason. " + Environment.NewLine +
 | |
|             "This operation, depending on how many files you have in your storage, can take a while and will be CPU and drive intensive.");
 | |
|         using (ImRaii.Disabled(_validationTask != null && !_validationTask.IsCompleted))
 | |
|         {
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.Check, "Start File Storage Validation"))
 | |
|             {
 | |
|                 _validationCts?.Cancel();
 | |
|                 _validationCts?.Dispose();
 | |
|                 _validationCts = new();
 | |
|                 var token = _validationCts.Token;
 | |
|                 _validationTask = Task.Run(() => _fileCacheManager.ValidateLocalIntegrity(_validationProgress, token));
 | |
|             }
 | |
|         }
 | |
|         if (_validationTask != null && !_validationTask.IsCompleted)
 | |
|         {
 | |
|             ImGui.SameLine();
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.Times, "Cancel"))
 | |
|             {
 | |
|                 _validationCts?.Cancel();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (_validationTask != null)
 | |
|         {
 | |
|             using (ImRaii.PushIndent(20f))
 | |
|             {
 | |
|                 if (_validationTask.IsCompleted)
 | |
|                 {
 | |
|                     UiSharedService.TextWrapped($"The storage validation has completed and removed {_validationTask.Result.Count} invalid files from storage.");
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
| 
 | |
|                     UiSharedService.TextWrapped($"Storage validation is running: {_currentProgress.Item1}/{_currentProgress.Item2}");
 | |
|                     UiSharedService.TextWrapped($"Current item: {_currentProgress.Item3.ResolvedFilepath}");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         ImGui.Separator();
 | |
| 
 | |
|         ImGuiHelpers.ScaledDummy(new Vector2(10, 10));
 | |
|         ImGui.TextUnformatted("To clear the local storage accept the following disclaimer");
 | |
|         ImGui.Indent();
 | |
|         ImGui.Checkbox("##readClearCache", ref _readClearCache);
 | |
|         ImGui.SameLine();
 | |
|         UiSharedService.TextWrapped("I understand that: " + Environment.NewLine + "- By clearing the local storage I put the file servers of my connected service under extra strain by having to redownload all data."
 | |
|             + Environment.NewLine + "- This is not a step to try to fix sync issues."
 | |
|             + Environment.NewLine + "- This can make the situation of not getting other players data worse in situations of heavy file server load.");
 | |
|         if (!_readClearCache)
 | |
|             ImGui.BeginDisabled();
 | |
|         if (_uiShared.IconTextButton(FontAwesomeIcon.Trash, "Clear local storage") && UiSharedService.CtrlPressed() && _readClearCache)
 | |
|         {
 | |
|             _ = Task.Run(() =>
 | |
|             {
 | |
|                 foreach (var file in Directory.GetFiles(_configService.Current.CacheFolder))
 | |
|                 {
 | |
|                     File.Delete(file);
 | |
|                 }
 | |
|             });
 | |
|         }
 | |
|         UiSharedService.AttachToolTip("You normally do not need to do this. THIS IS NOT SOMETHING YOU SHOULD BE DOING TO TRY TO FIX SYNC ISSUES." + Environment.NewLine
 | |
|             + "This will solely remove all downloaded data from all players and will require you to re-download everything again." + Environment.NewLine
 | |
|             + "Loporrit's storage is self-clearing and will not surpass the limit you have set it to." + Environment.NewLine
 | |
|             + "If you still think you need to do this hold CTRL while pressing the button.");
 | |
|         if (!_readClearCache)
 | |
|             ImGui.EndDisabled();
 | |
|         ImGui.Unindent();
 | |
|     }
 | |
| 
 | |
|     private void DrawGeneral()
 | |
|     {
 | |
|         if (!string.Equals(_lastTab, "General", StringComparison.OrdinalIgnoreCase))
 | |
|         {
 | |
|             _notesSuccessfullyApplied = null;
 | |
|         }
 | |
| 
 | |
|         _lastTab = "General";
 | |
| 
 | |
|         _uiShared.BigText("Notes");
 | |
|         if (_uiShared.IconTextButton(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()));
 | |
|         }
 | |
|         if (_uiShared.IconTextButton(FontAwesomeIcon.FileImport, "Import notes from clipboard"))
 | |
|         {
 | |
|             _notesSuccessfullyApplied = null;
 | |
|             var notes = ImGui.GetClipboardText();
 | |
|             _notesSuccessfullyApplied = _uiShared.ApplyNotesFromClipboard(notes, _overwriteExistingLabels);
 | |
|         }
 | |
| 
 | |
|         ImGui.SameLine();
 | |
|         ImGui.Checkbox("Overwrite existing notes", ref _overwriteExistingLabels);
 | |
|         _uiShared.DrawHelpText("If this option is selected all already existing notes for UIDs will be overwritten by the imported notes.");
 | |
|         if (_notesSuccessfullyApplied.HasValue && _notesSuccessfullyApplied.Value)
 | |
|         {
 | |
|             UiSharedService.ColorTextWrapped("User Notes successfully imported", ImGuiColors.HealerGreen);
 | |
|         }
 | |
|         else if (_notesSuccessfullyApplied.HasValue && !_notesSuccessfullyApplied.Value)
 | |
|         {
 | |
|             UiSharedService.ColorTextWrapped("Attempt to import notes from clipboard failed. Check formatting and try again", ImGuiColors.DalamudRed);
 | |
|         }
 | |
| 
 | |
|         var openPopupOnAddition = _configService.Current.OpenPopupOnAdd;
 | |
| 
 | |
|         if (ImGui.Checkbox("Open Notes Popup on user addition", ref openPopupOnAddition))
 | |
|         {
 | |
|             _configService.Current.OpenPopupOnAdd = openPopupOnAddition;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("This will open a popup that allows you to set the notes for a user after successfully adding them to your individual pairs.");
 | |
| 
 | |
|         ImGui.Separator();
 | |
|         _uiShared.BigText("UI");
 | |
|         var showCharacterNames = _configService.Current.ShowCharacterNames;
 | |
|         var showVisibleSeparate = _configService.Current.ShowVisibleUsersSeparately;
 | |
|         var showOfflineSeparate = _configService.Current.ShowOfflineUsersSeparately;
 | |
|         var showProfiles = _configService.Current.ProfilesShow;
 | |
|         var showNsfwProfiles = _configService.Current.ProfilesAllowNsfw;
 | |
|         var profileDelay = _configService.Current.ProfileDelay;
 | |
|         var profileOnRight = _configService.Current.ProfilePopoutRight;
 | |
|         var enableRightClickMenu = _configService.Current.EnableRightClickMenus;
 | |
|         var enableDtrEntry = _configService.Current.EnableDtrEntry;
 | |
|         var showUidInDtrTooltip = _configService.Current.ShowUidInDtrTooltip;
 | |
|         var preferNoteInDtrTooltip = _configService.Current.PreferNoteInDtrTooltip;
 | |
|         var useColorsInDtr = _configService.Current.UseColorsInDtr;
 | |
|         var dtrColorsDefault = _configService.Current.DtrColorsDefault;
 | |
|         var dtrColorsNotConnected = _configService.Current.DtrColorsNotConnected;
 | |
|         var dtrColorsPairsInRange = _configService.Current.DtrColorsPairsInRange;
 | |
| 
 | |
|         if (ImGui.Checkbox("Enable Game Right Click Menu Entries", ref enableRightClickMenu))
 | |
|         {
 | |
|             _configService.Current.EnableRightClickMenus = enableRightClickMenu;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("This will add Loporrit related right click menu entries in the game UI on paired players.");
 | |
| 
 | |
|         if (ImGui.Checkbox("Display status and visible pair count in Server Info Bar", ref enableDtrEntry))
 | |
|         {
 | |
|             _configService.Current.EnableDtrEntry = enableDtrEntry;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("This will add Loporrit 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();
 | |
|             }
 | |
| 
 | |
|             ImGui.SetNextItemWidth(250 * ImGuiHelpers.GlobalScale);
 | |
|             _uiShared.DrawCombo("Server Info Bar style", Enumerable.Range(0, DtrEntry.NumStyles), (i) => DtrEntry.RenderDtrStyle(i, "123"),
 | |
|             (i) =>
 | |
|             {
 | |
|                 _configService.Current.DtrStyle = i;
 | |
|                 _configService.Save();
 | |
|             }, _configService.Current.DtrStyle);
 | |
| 
 | |
|             if (ImGui.Checkbox("Color-code the Server Info Bar entry according to status", ref useColorsInDtr))
 | |
|             {
 | |
|                 _configService.Current.UseColorsInDtr = useColorsInDtr;
 | |
|                 _configService.Save();
 | |
|             }
 | |
| 
 | |
|             using (ImRaii.Disabled(!useColorsInDtr))
 | |
|             {
 | |
|                 using var indent2 = ImRaii.PushIndent();
 | |
|                 if (InputDtrColors("Default", ref dtrColorsDefault))
 | |
|                 {
 | |
|                     _configService.Current.DtrColorsDefault = dtrColorsDefault;
 | |
|                     _configService.Save();
 | |
|                 }
 | |
| 
 | |
|                 ImGui.SameLine();
 | |
|                 if (InputDtrColors("Not Connected", ref dtrColorsNotConnected))
 | |
|                 {
 | |
|                     _configService.Current.DtrColorsNotConnected = dtrColorsNotConnected;
 | |
|                     _configService.Save();
 | |
|                 }
 | |
| 
 | |
|                 ImGui.SameLine();
 | |
|                 if (InputDtrColors("Pairs in Range", ref dtrColorsPairsInRange))
 | |
|                 {
 | |
|                     _configService.Current.DtrColorsPairsInRange = dtrColorsPairsInRange;
 | |
|                     _configService.Save();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         var useNameColors = _configService.Current.UseNameColors;
 | |
|         var nameColors = _configService.Current.NameColors;
 | |
|         var autoPausedNameColors = _configService.Current.BlockedNameColors;
 | |
|         if (ImGui.Checkbox("Color nameplates of paired players", ref useNameColors))
 | |
|         {
 | |
|             _configService.Current.UseNameColors = useNameColors;
 | |
|             _configService.Save();
 | |
|             _guiHookService.RequestRedraw();
 | |
|         }
 | |
| 
 | |
|         using (ImRaii.Disabled(!useNameColors))
 | |
|         {
 | |
|             using var indent = ImRaii.PushIndent();
 | |
|             if (InputDtrColors("Character Name Color", ref nameColors))
 | |
|             {
 | |
|                 _configService.Current.NameColors = nameColors;
 | |
|                 _configService.Save();
 | |
|                 _guiHookService.RequestRedraw();
 | |
|             }
 | |
| 
 | |
|             ImGui.SameLine();
 | |
| 
 | |
|             if (InputDtrColors("Blocked Character Color", ref autoPausedNameColors))
 | |
|             {
 | |
|                 _configService.Current.BlockedNameColors = autoPausedNameColors;
 | |
|                 _configService.Save();
 | |
|                 _guiHookService.RequestRedraw();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (ImGui.Checkbox("Show separate Visible group", ref showVisibleSeparate))
 | |
|         {
 | |
|             _configService.Current.ShowVisibleUsersSeparately = showVisibleSeparate;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("This will show all currently visible users in a special 'Visible' group in the main UI.");
 | |
| 
 | |
|         if (ImGui.Checkbox("Show separate Offline group", ref showOfflineSeparate))
 | |
|         {
 | |
|             _configService.Current.ShowOfflineUsersSeparately = showOfflineSeparate;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("This will show all currently offline users in a special 'Offline' group in the main UI.");
 | |
| 
 | |
|         if (ImGui.Checkbox("Show player names", ref showCharacterNames))
 | |
|         {
 | |
|             _configService.Current.ShowCharacterNames = showCharacterNames;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("This will show character names instead of UIDs when possible");
 | |
| 
 | |
|         if (ImGui.Checkbox("Show Profiles on Hover", ref showProfiles))
 | |
|         {
 | |
|             Mediator.Publish(new ClearProfileDataMessage());
 | |
|             _configService.Current.ProfilesShow = showProfiles;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("This will show the configured user profile after a set delay");
 | |
|         ImGui.Indent();
 | |
|         if (!showProfiles) ImGui.BeginDisabled();
 | |
|         if (ImGui.Checkbox("Popout profiles on the right", ref profileOnRight))
 | |
|         {
 | |
|             _configService.Current.ProfilePopoutRight = profileOnRight;
 | |
|             _configService.Save();
 | |
|             Mediator.Publish(new CompactUiChange(Vector2.Zero, Vector2.Zero));
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Will show profiles on the right side of the main UI");
 | |
|         ImGui.SetNextItemWidth(250 * ImGuiHelpers.GlobalScale);
 | |
|         if (ImGui.SliderFloat("Hover Delay", ref profileDelay, 1, 10))
 | |
|         {
 | |
|             _configService.Current.ProfileDelay = profileDelay;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Delay until the profile should be displayed");
 | |
|         if (!showProfiles) ImGui.EndDisabled();
 | |
|         ImGui.Unindent();
 | |
|         if (ImGui.Checkbox("Show profiles marked as NSFW", ref showNsfwProfiles))
 | |
|         {
 | |
|             Mediator.Publish(new ClearProfileDataMessage());
 | |
|             _configService.Current.ProfilesAllowNsfw = showNsfwProfiles;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Will show profiles that have the NSFW tag enabled");
 | |
| 
 | |
|         ImGui.Separator();
 | |
| 
 | |
|         var disableOptionalPluginWarnings = _configService.Current.DisableOptionalPluginWarnings;
 | |
|         var onlineNotifs = _configService.Current.ShowOnlineNotifications;
 | |
|         var onlineNotifsPairsOnly = _configService.Current.ShowOnlineNotificationsOnlyForIndividualPairs;
 | |
|         var onlineNotifsNamedOnly = _configService.Current.ShowOnlineNotificationsOnlyForNamedPairs;
 | |
|         _uiShared.BigText("Notifications");
 | |
| 
 | |
|         ImGui.SetNextItemWidth(250 * ImGuiHelpers.GlobalScale);
 | |
|         _uiShared.DrawCombo("Info Notification Display##settingsUi", (NotificationLocation[])Enum.GetValues(typeof(NotificationLocation)), (i) => i.ToString(),
 | |
|         (i) =>
 | |
|         {
 | |
|             _configService.Current.InfoNotification = i;
 | |
|             _configService.Save();
 | |
|         }, _configService.Current.InfoNotification);
 | |
|         _uiShared.DrawHelpText("The location where \"Info\" notifications will display."
 | |
|                       + Environment.NewLine + "'Nowhere' will not show any Info notifications"
 | |
|                       + Environment.NewLine + "'Chat' will print Info notifications in chat"
 | |
|                       + Environment.NewLine + "'Toast' will show Warning toast notifications in the bottom right corner"
 | |
|                       + Environment.NewLine + "'Both' will show chat as well as the toast notification");
 | |
| 
 | |
|         ImGui.SetNextItemWidth(250 * ImGuiHelpers.GlobalScale);
 | |
|         _uiShared.DrawCombo("Warning Notification Display##settingsUi", (NotificationLocation[])Enum.GetValues(typeof(NotificationLocation)), (i) => i.ToString(),
 | |
|         (i) =>
 | |
|         {
 | |
|             _configService.Current.WarningNotification = i;
 | |
|             _configService.Save();
 | |
|         }, _configService.Current.WarningNotification);
 | |
|         _uiShared.DrawHelpText("The location where \"Warning\" notifications will display."
 | |
|                               + Environment.NewLine + "'Nowhere' will not show any Warning notifications"
 | |
|                               + Environment.NewLine + "'Chat' will print Warning notifications in chat"
 | |
|                               + Environment.NewLine + "'Toast' will show Warning toast notifications in the bottom right corner"
 | |
|                               + Environment.NewLine + "'Both' will show chat as well as the toast notification");
 | |
| 
 | |
|         ImGui.SetNextItemWidth(250 * ImGuiHelpers.GlobalScale);
 | |
|         _uiShared.DrawCombo("Error Notification Display##settingsUi", (NotificationLocation[])Enum.GetValues(typeof(NotificationLocation)), (i) => i.ToString(),
 | |
|         (i) =>
 | |
|         {
 | |
|             _configService.Current.ErrorNotification = i;
 | |
|             _configService.Save();
 | |
|         }, _configService.Current.ErrorNotification);
 | |
|         _uiShared.DrawHelpText("The location where \"Error\" notifications will display."
 | |
|                               + Environment.NewLine + "'Nowhere' will not show any Error notifications"
 | |
|                               + Environment.NewLine + "'Chat' will print Error notifications in chat"
 | |
|                               + Environment.NewLine + "'Toast' will show Error toast notifications in the bottom right corner"
 | |
|                               + Environment.NewLine + "'Both' will show chat as well as the toast notification");
 | |
| 
 | |
|         if (ImGui.Checkbox("Disable optional plugin warnings", ref disableOptionalPluginWarnings))
 | |
|         {
 | |
|             _configService.Current.DisableOptionalPluginWarnings = disableOptionalPluginWarnings;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Enabling this will not show any \"Warning\" labeled messages for missing optional plugins.");
 | |
|         if (ImGui.Checkbox("Enable online notifications", ref onlineNotifs))
 | |
|         {
 | |
|             _configService.Current.ShowOnlineNotifications = onlineNotifs;
 | |
|             _configService.Save();
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Enabling this will show a small notification (type: Info) in the bottom right corner when pairs go online.");
 | |
| 
 | |
|         using (ImRaii.Disabled(!onlineNotifs))
 | |
|         {
 | |
|             using var indent = ImRaii.PushIndent();
 | |
|             if (ImGui.Checkbox("Notify only for individual pairs", ref onlineNotifsPairsOnly))
 | |
|             {
 | |
|                 _configService.Current.ShowOnlineNotificationsOnlyForIndividualPairs = onlineNotifsPairsOnly;
 | |
|                 _configService.Save();
 | |
|             }
 | |
|             _uiShared.DrawHelpText("Enabling this will only show online notifications (type: Info) for individual pairs.");
 | |
|             if (ImGui.Checkbox("Notify only for named pairs", ref onlineNotifsNamedOnly))
 | |
|             {
 | |
|                 _configService.Current.ShowOnlineNotificationsOnlyForNamedPairs = onlineNotifsNamedOnly;
 | |
|                 _configService.Save();
 | |
|             }
 | |
|             _uiShared.DrawHelpText("Enabling this will only show online notifications (type: Info) for pairs where you have set an individual note.");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private bool _perfUnapplied = false;
 | |
| 
 | |
|     private void DrawPerformance()
 | |
|     {
 | |
|         _uiShared.BigText("Performance Settings");
 | |
|         UiSharedService.TextWrapped("The configuration options here are to give you more informed warnings and automation when it comes to other performance-intensive synced players.");
 | |
|         ImGui.Separator();
 | |
|         bool recalculatePerformance = false;
 | |
|         string? recalculatePerformanceUID = null;
 | |
| 
 | |
|         _uiShared.BigText("Global Configuration");
 | |
| 
 | |
|         bool alwaysShrinkTextures = _playerPerformanceConfigService.Current.TextureShrinkMode == TextureShrinkMode.Always;
 | |
|         bool deleteOriginalTextures = _playerPerformanceConfigService.Current.TextureShrinkDeleteOriginal;
 | |
| 
 | |
|         using (ImRaii.Disabled(deleteOriginalTextures))
 | |
|         {
 | |
|             if (ImGui.Checkbox("Shrink downloaded textures", ref alwaysShrinkTextures))
 | |
|             {
 | |
|                 if (alwaysShrinkTextures)
 | |
|                     _playerPerformanceConfigService.Current.TextureShrinkMode = TextureShrinkMode.Always;
 | |
|                 else
 | |
|                     _playerPerformanceConfigService.Current.TextureShrinkMode = TextureShrinkMode.Never;
 | |
|                 _playerPerformanceConfigService.Save();
 | |
|                 recalculatePerformance = true;
 | |
|                 _cacheMonitor.ClearSubstStorage();
 | |
|             }
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Automatically shrinks texture resolution of synced players to reduce VRAM utilization." + UiSharedService.TooltipSeparator
 | |
|             + "Texture Size Limit (DXT/BC5/BC7 Compressed): 2048x2048" + Environment.NewLine
 | |
|             + "Texture Size Limit (A8R8G8B8 Uncompressed): 1024x1024" + UiSharedService.TooltipSeparator
 | |
|             + "Enable to reduce lag in large crowds." + Environment.NewLine
 | |
|             + "Disable this for higher quality during GPose.");
 | |
| 
 | |
|         using (ImRaii.Disabled(!alwaysShrinkTextures || _cacheMonitor.FileCacheSize < 0))
 | |
|         {
 | |
|             using var indent = ImRaii.PushIndent();
 | |
|             if (ImGui.Checkbox("Delete original textures from disk", ref deleteOriginalTextures))
 | |
|             {
 | |
|                 _playerPerformanceConfigService.Current.TextureShrinkDeleteOriginal = deleteOriginalTextures;
 | |
|                 _playerPerformanceConfigService.Save();
 | |
|                 _ = Task.Run(() =>
 | |
|                 {
 | |
|                     _cacheMonitor.DeleteSubstOriginals();
 | |
|                     _cacheMonitor.RecalculateFileCacheSize(CancellationToken.None);
 | |
|                 });
 | |
|             }
 | |
|             _uiShared.DrawHelpText("Deletes original, full-sized, textures from disk after downloading and shrinking." + UiSharedService.TooltipSeparator
 | |
|                 + "Caution!!! This will cause a re-download of all textures when the shrink option is disabled.");
 | |
|         }
 | |
| 
 | |
|         var totalVramBytes = _pairManager.GetOnlineUserPairs().Where(p => p.IsVisible && p.LastAppliedApproximateVRAMBytes > 0).Sum(p => p.LastAppliedApproximateVRAMBytes);
 | |
| 
 | |
|         ImGui.TextUnformatted("Current VRAM utilization by all nearby players:");
 | |
|         ImGui.SameLine();
 | |
|         using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.HealerGreen, totalVramBytes < 2.0 * 1024.0 * 1024.0 * 1024.0))
 | |
|             using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudYellow, totalVramBytes >= 4.0 * 1024.0 * 1024.0 * 1024.0))
 | |
|                 using (ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudRed, totalVramBytes >= 6.0 * 1024.0 * 1024.0 * 1024.0))
 | |
|                     ImGui.TextUnformatted($"{totalVramBytes / 1024.0 / 1024.0 / 1024.0:0.00} GiB");
 | |
| 
 | |
|         ImGui.Separator();
 | |
|         _uiShared.BigText("Individual Limits");
 | |
|         bool autoPause = _playerPerformanceConfigService.Current.AutoPausePlayersExceedingThresholds;
 | |
|         if (ImGui.Checkbox("Automatically block players exceeding thresholds", ref autoPause))
 | |
|         {
 | |
|             _playerPerformanceConfigService.Current.AutoPausePlayersExceedingThresholds = autoPause;
 | |
|             _playerPerformanceConfigService.Save();
 | |
|             recalculatePerformance = true;
 | |
|         }
 | |
|         _uiShared.DrawHelpText("When enabled, it will automatically block the modded appearance of all players that exceed the thresholds defined below." + Environment.NewLine
 | |
|             + "Will print a warning in chat when a player is blocked automatically.");
 | |
|         using (ImRaii.Disabled(!autoPause))
 | |
|         {
 | |
|             using var indent = ImRaii.PushIndent();
 | |
|             var notifyDirectPairs = _playerPerformanceConfigService.Current.NotifyAutoPauseDirectPairs;
 | |
|             var notifyGroupPairs = _playerPerformanceConfigService.Current.NotifyAutoPauseGroupPairs;
 | |
|             if (ImGui.Checkbox("Display auto-block warnings for individual pairs", ref notifyDirectPairs))
 | |
|             {
 | |
|                 _playerPerformanceConfigService.Current.NotifyAutoPauseDirectPairs = notifyDirectPairs;
 | |
|                 _playerPerformanceConfigService.Save();
 | |
|             }
 | |
|             if (ImGui.Checkbox("Display auto-block warnings for syncshell pairs", ref notifyGroupPairs))
 | |
|             {
 | |
|                 _playerPerformanceConfigService.Current.NotifyAutoPauseGroupPairs = notifyGroupPairs;
 | |
|                 _playerPerformanceConfigService.Save();
 | |
|             }
 | |
|             var vramAuto = _playerPerformanceConfigService.Current.VRAMSizeAutoPauseThresholdMiB;
 | |
|             var trisAuto = _playerPerformanceConfigService.Current.TrisAutoPauseThresholdThousands;
 | |
|             ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
 | |
|             if (ImGui.InputInt("Auto Block VRAM threshold", ref vramAuto))
 | |
|             {
 | |
|                 _playerPerformanceConfigService.Current.VRAMSizeAutoPauseThresholdMiB = vramAuto;
 | |
|                 _playerPerformanceConfigService.Save();
 | |
|                 _perfUnapplied = true;
 | |
|             }
 | |
|             ImGui.SameLine();
 | |
|             ImGui.Text("(MiB)");
 | |
|             _uiShared.DrawHelpText("When a loading in player and their VRAM usage exceeds this amount, automatically blocks the synced player." + UiSharedService.TooltipSeparator
 | |
|                 + "Default: 550 MiB");
 | |
|             ImGui.SetNextItemWidth(100 * ImGuiHelpers.GlobalScale);
 | |
|             if (ImGui.InputInt("Auto Block Triangle threshold", ref trisAuto))
 | |
|             {
 | |
|                 _playerPerformanceConfigService.Current.TrisAutoPauseThresholdThousands = trisAuto;
 | |
|                 _playerPerformanceConfigService.Save();
 | |
|                 _perfUnapplied = true;
 | |
|             }
 | |
|             ImGui.SameLine();
 | |
|             ImGui.Text("(thousand triangles)");
 | |
|             _uiShared.DrawHelpText("When a loading in player and their triangle count exceeds this amount, automatically blocks the synced player." + UiSharedService.TooltipSeparator
 | |
|                 + "Default: 375 thousand");
 | |
|             using (ImRaii.Disabled(!_perfUnapplied))
 | |
|             {
 | |
|                 if (ImGui.Button("Apply Changes Now"))
 | |
|                 {
 | |
|                     recalculatePerformance = true;
 | |
|                     _perfUnapplied = false;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
| #region Whitelist
 | |
|         ImGui.Separator();
 | |
|         _uiShared.BigText("Whitelisted UIDs");
 | |
|         bool ignoreDirectPairs = _playerPerformanceConfigService.Current.IgnoreDirectPairs;
 | |
|         if (ImGui.Checkbox("Whitelist all individual pairs", ref ignoreDirectPairs))
 | |
|         {
 | |
|             _playerPerformanceConfigService.Current.IgnoreDirectPairs = ignoreDirectPairs;
 | |
|             _playerPerformanceConfigService.Save();
 | |
|             recalculatePerformance = true;
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Individual pairs will never be affected by auto blocks.");
 | |
|         ImGui.Dummy(new Vector2(5));
 | |
|         UiSharedService.TextWrapped("The entries in the list below will be not have auto block thresholds enforced.");
 | |
|         ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
 | |
|         var whitelistPos = ImGui.GetCursorPos();
 | |
|         ImGui.SetCursorPosX(240 * ImGuiHelpers.GlobalScale);
 | |
|         ImGui.InputText("##whitelistuid", ref _uidToAddForIgnore, 20);
 | |
|         using (ImRaii.Disabled(string.IsNullOrEmpty(_uidToAddForIgnore)))
 | |
|         {
 | |
|             ImGui.SetCursorPosX(240 * ImGuiHelpers.GlobalScale);
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.Plus, "Add UID/Vanity ID to whitelist"))
 | |
|             {
 | |
|                 if (!_serverConfigurationManager.IsUidWhitelisted(_uidToAddForIgnore))
 | |
|                 {
 | |
|                     _serverConfigurationManager.AddWhitelistUid(_uidToAddForIgnore);
 | |
|                     recalculatePerformance = true;
 | |
|                     recalculatePerformanceUID = _uidToAddForIgnore;
 | |
|                 }
 | |
|                 _uidToAddForIgnore = string.Empty;
 | |
|             }
 | |
|         }
 | |
|         ImGui.SetCursorPosX(240 * ImGuiHelpers.GlobalScale);
 | |
|         _uiShared.DrawHelpText("Hint: UIDs are case sensitive.\nVanity IDs are also acceptable.");
 | |
|         ImGui.Dummy(new Vector2(10));
 | |
|         var playerList = _serverConfigurationManager.Whitelist;
 | |
|         if (_selectedEntry > playerList.Count - 1)
 | |
|             _selectedEntry = -1;
 | |
|         ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
 | |
|         ImGui.SetCursorPosY(whitelistPos.Y);
 | |
|         using (var lb = ImRaii.ListBox("##whitelist"))
 | |
|         {
 | |
|             if (lb)
 | |
|             {
 | |
|                 for (int i = 0; i < playerList.Count; i++)
 | |
|                 {
 | |
|                     bool shouldBeSelected = _selectedEntry == i;
 | |
|                     if (ImGui.Selectable(playerList[i] + "##" + i, shouldBeSelected))
 | |
|                     {
 | |
|                         _selectedEntry = i;
 | |
|                     }
 | |
|                     string? lastSeenName = _serverConfigurationManager.GetNameForUid(playerList[i]);
 | |
|                     if (lastSeenName != null)
 | |
|                     {
 | |
|                         ImGui.SameLine();
 | |
|                         _uiShared.IconText(FontAwesomeIcon.InfoCircle);
 | |
|                         UiSharedService.AttachToolTip($"Last seen name: {lastSeenName}");
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         using (ImRaii.Disabled(_selectedEntry == -1))
 | |
|         {
 | |
|             using var pushId = ImRaii.PushId("deleteSelectedWhitelist");
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.Trash, "Delete selected UID"))
 | |
|             {
 | |
|                 _serverConfigurationManager.RemoveWhitelistUid(_serverConfigurationManager.Whitelist[_selectedEntry]);
 | |
|                 if (_selectedEntry > playerList.Count - 1)
 | |
|                     --_selectedEntry;
 | |
|                 _playerPerformanceConfigService.Save();
 | |
|                 recalculatePerformance = true;
 | |
|             }
 | |
|         }
 | |
| #endregion Whitelist
 | |
| 
 | |
| #region Blacklist
 | |
|         ImGui.Separator();
 | |
|         _uiShared.BigText("Blacklisted UIDs");
 | |
|         UiSharedService.TextWrapped("The entries in the list below will never have their characters displayed.");
 | |
|         ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
 | |
|         var blacklistPos = ImGui.GetCursorPos();
 | |
|         ImGui.SetCursorPosX(240 * ImGuiHelpers.GlobalScale);
 | |
|         ImGui.InputText("##uid", ref _uidToAddForIgnoreBlacklist, 20);
 | |
|         using (ImRaii.Disabled(string.IsNullOrEmpty(_uidToAddForIgnoreBlacklist)))
 | |
|         {
 | |
|             ImGui.SetCursorPosX(240 * ImGuiHelpers.GlobalScale);
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.Plus, "Add UID/Vanity ID to blacklist"))
 | |
|             {
 | |
|                 if (!_serverConfigurationManager.IsUidBlacklisted(_uidToAddForIgnoreBlacklist))
 | |
|                 {
 | |
|                     _serverConfigurationManager.AddBlacklistUid(_uidToAddForIgnoreBlacklist);
 | |
|                     recalculatePerformance = true;
 | |
|                     recalculatePerformanceUID = _uidToAddForIgnoreBlacklist;
 | |
|                 }
 | |
|                 _uidToAddForIgnoreBlacklist = string.Empty;
 | |
|             }
 | |
|         }
 | |
|         _uiShared.DrawHelpText("Hint: UIDs are case sensitive.\nVanity IDs are also acceptable.");
 | |
|         ImGui.Dummy(new Vector2(10));
 | |
|         var blacklist = _serverConfigurationManager.Blacklist;
 | |
|         if (_selectedEntryBlacklist > blacklist.Count - 1)
 | |
|             _selectedEntryBlacklist = -1;
 | |
|         ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
 | |
|         ImGui.SetCursorPosY(blacklistPos.Y);
 | |
|         using (var lb = ImRaii.ListBox("##blacklist"))
 | |
|         {
 | |
|             if (lb)
 | |
|             {
 | |
|                 for (int i = 0; i < blacklist.Count; i++)
 | |
|                 {
 | |
|                     bool shouldBeSelected = _selectedEntryBlacklist == i;
 | |
|                     if (ImGui.Selectable(blacklist[i] + "##BL" + i, shouldBeSelected))
 | |
|                     {
 | |
|                         _selectedEntryBlacklist = i;
 | |
|                     }
 | |
|                     string? lastSeenName = _serverConfigurationManager.GetNameForUid(blacklist[i]);
 | |
|                     if (lastSeenName != null)
 | |
|                     {
 | |
|                         ImGui.SameLine();
 | |
|                         _uiShared.IconText(FontAwesomeIcon.InfoCircle);
 | |
|                         UiSharedService.AttachToolTip($"Last seen name: {lastSeenName}");
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         using (ImRaii.Disabled(_selectedEntryBlacklist == -1))
 | |
|         {
 | |
|             using var pushId = ImRaii.PushId("deleteSelectedBlacklist");
 | |
|             if (_uiShared.IconTextButton(FontAwesomeIcon.Trash, "Delete selected UID"))
 | |
|             {
 | |
|                 _serverConfigurationManager.RemoveBlacklistUid(_serverConfigurationManager.Blacklist[_selectedEntryBlacklist]);
 | |
|                 if (_selectedEntryBlacklist > blacklist.Count - 1)
 | |
|                     --_selectedEntryBlacklist;
 | |
|                 _playerPerformanceConfigService.Save();
 | |
|                 recalculatePerformance = true;
 | |
|             }
 | |
|         }
 | |
| #endregion Blacklist
 | |
| 
 | |
|         if (recalculatePerformance)
 | |
|             Mediator.Publish(new RecalculatePerformanceMessage(recalculatePerformanceUID));
 | |
|     }
 | |
| 
 | |
|     private static bool InputDtrColors(string label, ref DtrEntry.Colors colors)
 | |
|     {
 | |
|         using var id = ImRaii.PushId(label);
 | |
|         var innerSpacing = ImGui.GetStyle().ItemInnerSpacing.X;
 | |
|         var foregroundColor = ConvertColor(colors.Foreground);
 | |
|         var glowColor = ConvertColor(colors.Glow);
 | |
| 
 | |
|         var ret = ImGui.ColorEdit3("###foreground", ref foregroundColor, ImGuiColorEditFlags.NoInputs | ImGuiColorEditFlags.NoLabel | ImGuiColorEditFlags.Uint8);
 | |
|         if (ImGui.IsItemHovered())
 | |
|             ImGui.SetTooltip("Foreground Color - Set to pure black (#000000) to use the default color");
 | |
| 
 | |
|         ImGui.SameLine(0.0f, innerSpacing);
 | |
|         ret |= ImGui.ColorEdit3("###glow", ref glowColor, ImGuiColorEditFlags.NoInputs | ImGuiColorEditFlags.NoLabel | ImGuiColorEditFlags.Uint8);
 | |
|         if (ImGui.IsItemHovered())
 | |
|             ImGui.SetTooltip("Glow Color - Set to pure black (#000000) to use the default color");
 | |
| 
 | |
|         ImGui.SameLine(0.0f, innerSpacing);
 | |
|         ImGui.TextUnformatted(label);
 | |
| 
 | |
|         if (ret)
 | |
|             colors = new(ConvertBackColor(foregroundColor), ConvertBackColor(glowColor));
 | |
| 
 | |
|         return ret;
 | |
| 
 | |
|         static Vector3 ConvertColor(uint color)
 | |
|             => unchecked(new((byte)color / 255.0f, (byte)(color >> 8) / 255.0f, (byte)(color >> 16) / 255.0f));
 | |
| 
 | |
|         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);
 | |
|     }
 | |
| 
 | |
|     private void DrawServerConfiguration()
 | |
|     {
 | |
|         _lastTab = "Service Settings";
 | |
|         if (ApiController.ServerAlive)
 | |
|         {
 | |
|             _uiShared.BigText("Service Actions");
 | |
|             ImGuiHelpers.ScaledDummy(new Vector2(5, 5));
 | |
|             if (ImGui.Button("Delete account"))
 | |
|             {
 | |
|                 _deleteAccountPopupModalShown = true;
 | |
|                 ImGui.OpenPopup("Delete your account?");
 | |
|             }
 | |
| 
 | |
|             _uiShared.DrawHelpText("Completely deletes your currently connected account.");
 | |
| 
 | |
|             if (ImGui.BeginPopupModal("Delete your account?", ref _deleteAccountPopupModalShown, UiSharedService.PopupWindowFlags))
 | |
|             {
 | |
|                 UiSharedService.TextWrapped(
 | |
|                     "Your account and all associated files and data on the service will be deleted.");
 | |
|                 UiSharedService.TextWrapped("Your UID will be removed from all pairing lists.");
 | |
|                 ImGui.TextUnformatted("Are you sure you want to continue?");
 | |
|                 ImGui.Separator();
 | |
|                 ImGui.Spacing();
 | |
| 
 | |
|                 var buttonSize = (ImGui.GetWindowContentRegionMax().X - ImGui.GetWindowContentRegionMin().X -
 | |
|                                   ImGui.GetStyle().ItemSpacing.X) / 2;
 | |
| 
 | |
|                 if (ImGui.Button("Delete account", new Vector2(buttonSize, 0)))
 | |
|                 {
 | |
|                     _ = Task.Run(ApiController.UserDelete);
 | |
|                     _deleteAccountPopupModalShown = false;
 | |
|                     Mediator.Publish(new SwitchToIntroUiMessage());
 | |
|                 }
 | |
| 
 | |
|                 ImGui.SameLine();
 | |
| 
 | |
|                 if (ImGui.Button("Cancel##cancelDelete", new Vector2(buttonSize, 0)))
 | |
|                 {
 | |
|                     _deleteAccountPopupModalShown = false;
 | |
|                 }
 | |
| 
 | |
|                 UiSharedService.SetScaledWindowSize(325);
 | |
|                 ImGui.EndPopup();
 | |
|             }
 | |
|             ImGui.Separator();
 | |
|         }
 | |
| 
 | |
|         _uiShared.BigText("Service & Character Settings");
 | |
| 
 | |
|         var idx = _uiShared.DrawServiceSelection();
 | |
|         var playerName = _dalamudUtilService.GetPlayerName();
 | |
|         var playerWorldId = _dalamudUtilService.GetHomeWorldId();
 | |
|         var worldData = _uiShared.WorldData.OrderBy(u => u.Value, StringComparer.Ordinal).ToDictionary(k => k.Key, k => k.Value);
 | |
|         string playerWorldName = worldData.GetValueOrDefault((ushort)playerWorldId, $"{playerWorldId}");
 | |
| 
 | |
|         ImGuiHelpers.ScaledDummy(new Vector2(10, 10));
 | |
| 
 | |
|         var selectedServer = _serverConfigurationManager.GetServerByIndex(idx);
 | |
|         if (selectedServer == _serverConfigurationManager.CurrentServer)
 | |
|         {
 | |
|             if (_apiController.IsConnected)
 | |
|                 UiSharedService.ColorTextWrapped("For any changes to be applied to the current service you need to reconnect to the service.", ImGuiColors.DalamudYellow);
 | |
|         }
 | |
| 
 | |
|         if (ImGui.BeginTabBar("serverTabBar"))
 | |
|         {
 | |
|             if (ImGui.BeginTabItem("Character Assignments"))
 | |
|             {
 | |
|                 if (selectedServer.SecretKeys.Count > 0)
 | |
|                 {
 | |
|                     float windowPadding = ImGui.GetStyle().WindowPadding.X;
 | |
|                     float itemSpacing = ImGui.GetStyle().ItemSpacing.X;
 | |
|                     float longestName = 0.0f;
 | |
|                     if (selectedServer.Authentications.Count > 0)
 | |
|                         longestName = selectedServer.Authentications.Max(p => ImGui.CalcTextSize($"{p.CharacterName} @ Pandaemonium  ").X);
 | |
|                     float iconWidth;
 | |
| 
 | |
|                     using (_ = _uiShared.IconFont.Push())
 | |
|                         iconWidth = ImGui.CalcTextSize(FontAwesomeIcon.Trash.ToIconString()).X;
 | |
| 
 | |
|                     UiSharedService.ColorTextWrapped("Characters listed here will connect with the specified secret key.", ImGuiColors.DalamudYellow);
 | |
|                     int i = 0;
 | |
|                     foreach (var item in selectedServer.Authentications.ToList())
 | |
|                     {
 | |
|                         using var charaId = ImRaii.PushId("selectedChara" + i);
 | |
| 
 | |
|                         bool thisIsYou = string.Equals(playerName, item.CharacterName, StringComparison.OrdinalIgnoreCase)
 | |
|                             && playerWorldId == item.WorldId;
 | |
| 
 | |
|                         if (!worldData.TryGetValue((ushort)item.WorldId, out string? worldPreview))
 | |
|                             worldPreview = worldData.First().Value;
 | |
| 
 | |
|                         _uiShared.IconText(thisIsYou ? FontAwesomeIcon.Star : FontAwesomeIcon.None);
 | |
| 
 | |
|                         if (thisIsYou)
 | |
|                             UiSharedService.AttachToolTip("Current character");
 | |
| 
 | |
|                         ImGui.SameLine(windowPadding + iconWidth + itemSpacing);
 | |
|                         float beforeName = ImGui.GetCursorPosX();
 | |
|                         ImGui.TextUnformatted($"{item.CharacterName} @ {worldPreview}");
 | |
|                         float afterName = ImGui.GetCursorPosX();
 | |
| 
 | |
|                         ImGui.SameLine(afterName + (afterName - beforeName) + longestName + itemSpacing);
 | |
| 
 | |
|                         var secretKeyIdx = item.SecretKeyIdx;
 | |
|                         var keys = selectedServer.SecretKeys;
 | |
|                         if (!keys.TryGetValue(secretKeyIdx, out var secretKey))
 | |
|                         {
 | |
|                             secretKey = new();
 | |
|                         }
 | |
|                         var friendlyName = secretKey.FriendlyName;
 | |
| 
 | |
|                         ImGui.SetNextItemWidth(afterName - iconWidth - itemSpacing * 2 - windowPadding);
 | |
| 
 | |
|                         string selectedKeyName = string.Empty;
 | |
|                         if (selectedServer.SecretKeys.TryGetValue(item.SecretKeyIdx, out var selectedKey))
 | |
|                             selectedKeyName = selectedKey.FriendlyName;
 | |
| 
 | |
|                         // _uiShared.DrawCombo() remembers the selected option -- we don't want that, because the value can change
 | |
|                         if (ImGui.BeginCombo($"##{item.CharacterName}{i}", selectedKeyName))
 | |
|                         {
 | |
|                             foreach (var key in selectedServer.SecretKeys)
 | |
|                             {
 | |
|                                 if (ImGui.Selectable($"{key.Value.FriendlyName}##{i}", key.Key == item.SecretKeyIdx)
 | |
|                                     && key.Key != item.SecretKeyIdx)
 | |
|                                 {
 | |
|                                     item.SecretKeyIdx = key.Key;
 | |
|                                     _serverConfigurationManager.Save();
 | |
|                                 }
 | |
|                             }
 | |
|                             ImGui.EndCombo();
 | |
|                         }
 | |
| 
 | |
|                         ImGui.SameLine();
 | |
| 
 | |
|                         if (_uiShared.IconButton(FontAwesomeIcon.Trash))
 | |
|                             _serverConfigurationManager.RemoveCharacterFromServer(idx, item);
 | |
|                         UiSharedService.AttachToolTip("Delete character assignment");
 | |
| 
 | |
|                         i++;
 | |
|                     }
 | |
| 
 | |
|                     ImGui.Separator();
 | |
|                     using (_ = ImRaii.Disabled(selectedServer.Authentications.Exists(c =>
 | |
|                             string.Equals(c.CharacterName, _uiShared.PlayerName, StringComparison.Ordinal)
 | |
|                                 && c.WorldId == _uiShared.WorldId
 | |
|                     )))
 | |
|                     {
 | |
|                         if (_uiShared.IconTextButton(FontAwesomeIcon.User, "Add current character"))
 | |
|                         {
 | |
|                             _serverConfigurationManager.AddCurrentCharacterToServer(idx);
 | |
|                         }
 | |
|                         ImGui.SameLine();
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     UiSharedService.ColorTextWrapped("You need to add a Secret Key first before adding Characters.", ImGuiColors.DalamudYellow);
 | |
|                 }
 | |
| 
 | |
|                 ImGui.EndTabItem();
 | |
|             }
 | |
| 
 | |
|             if (ImGui.BeginTabItem("Secret Key Management"))
 | |
|             {
 | |
|                 foreach (var item in selectedServer.SecretKeys.ToList())
 | |
|                 {
 | |
|                     using var id = ImRaii.PushId("key" + item.Key);
 | |
|                     var friendlyName = item.Value.FriendlyName;
 | |
|                     if (ImGui.InputText("Secret Key Display Name", ref friendlyName, 255))
 | |
|                     {
 | |
|                         item.Value.FriendlyName = friendlyName;
 | |
|                         _serverConfigurationManager.Save();
 | |
|                     }
 | |
|                     var key = item.Value.Key;
 | |
|                     var keyInUse = selectedServer.Authentications.Exists(p => p.SecretKeyIdx == item.Key);
 | |
|                     if (keyInUse) ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudGrey3);
 | |
|                     if (ImGui.InputText("Secret Key", ref key, 64, keyInUse ? ImGuiInputTextFlags.ReadOnly : default))
 | |
|                     {
 | |
|                         item.Value.Key = key;
 | |
|                         _serverConfigurationManager.Save();
 | |
|                     }
 | |
|                     if (keyInUse) ImGui.PopStyleColor();
 | |
| 
 | |
|                     bool thisIsYou = selectedServer.Authentications.Any(a =>
 | |
|                         a.SecretKeyIdx == item.Key
 | |
|                             && string.Equals(a.CharacterName, _uiShared.PlayerName, StringComparison.OrdinalIgnoreCase)
 | |
|                             && a.WorldId == playerWorldId
 | |
|                     );
 | |
| 
 | |
|                     bool disableAssignment = thisIsYou || item.Value.Key.IsNullOrEmpty();
 | |
| 
 | |
|                     using (_ = ImRaii.Disabled(disableAssignment))
 | |
|                     {
 | |
|                         if (_uiShared.IconTextButton(FontAwesomeIcon.User, "Assign current character"))
 | |
|                         {
 | |
|                             var currentAssignment = selectedServer.Authentications.Find(a =>
 | |
|                                 string.Equals(a.CharacterName, _uiShared.PlayerName, StringComparison.OrdinalIgnoreCase)
 | |
|                                     && a.WorldId == playerWorldId
 | |
|                             );
 | |
| 
 | |
|                             if (currentAssignment == null)
 | |
|                             {
 | |
|                                 selectedServer.Authentications.Add(new Authentication()
 | |
|                                 {
 | |
|                                     CharacterName = playerName,
 | |
|                                     WorldId = playerWorldId,
 | |
|                                     SecretKeyIdx = item.Key
 | |
|                                 });
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 currentAssignment.SecretKeyIdx = item.Key;
 | |
|                             }
 | |
|                         }
 | |
|                         if (!disableAssignment)
 | |
|                             UiSharedService.AttachToolTip($"Use this secret key for {playerName} @ {playerWorldName}");
 | |
|                     }
 | |
| 
 | |
|                     ImGui.SameLine();
 | |
|                     using var disableDelete = ImRaii.Disabled(keyInUse);
 | |
|                     if (_uiShared.IconTextButton(FontAwesomeIcon.Trash, "Delete Secret Key") && UiSharedService.CtrlPressed())
 | |
|                     {
 | |
|                         selectedServer.SecretKeys.Remove(item.Key);
 | |
|                         _serverConfigurationManager.Save();
 | |
|                     }
 | |
|                     if (!keyInUse)
 | |
|                         UiSharedService.AttachToolTip("Hold CTRL to delete this secret key entry");
 | |
| 
 | |
|                     if (keyInUse)
 | |
|                     {
 | |
|                         UiSharedService.ColorTextWrapped("This key is currently assigned to a character and cannot be edited or deleted.", ImGuiColors.DalamudYellow);
 | |
|                     }
 | |
| 
 | |
|                     if (item.Key != selectedServer.SecretKeys.Keys.LastOrDefault())
 | |
|                         ImGui.Separator();
 | |
|                 }
 | |
| 
 | |
|                 ImGui.Separator();
 | |
|                 if (_uiShared.IconTextButton(FontAwesomeIcon.Plus, "Add new Secret Key"))
 | |
|                 {
 | |
|                     selectedServer.SecretKeys.Add(selectedServer.SecretKeys.Any() ? selectedServer.SecretKeys.Max(p => p.Key) + 1 : 0, new SecretKey()
 | |
|                     {
 | |
|                         FriendlyName = "New Secret Key",
 | |
|                     });
 | |
|                     _serverConfigurationManager.Save();
 | |
|                 }
 | |
| 
 | |
|                 if (true) // Enable registration button for all servers
 | |
|                 {
 | |
|                     ImGui.SameLine();
 | |
|                     if (_uiShared.IconTextButton(FontAwesomeIcon.Plus, "Register a new Loporrit account"))
 | |
|                     {
 | |
|                         _registrationInProgress = true;
 | |
|                         _ = Task.Run(async () => {
 | |
|                             try
 | |
|                             {
 | |
|                                 using HttpClient httpClient = new();
 | |
|                                 var ver = Assembly.GetExecutingAssembly().GetName().Version;
 | |
|                                 httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MareSynchronos", ver!.Major + "." + ver!.Minor + "." + ver!.Build));
 | |
|                                 var postUri = MareAuth.AuthRegisterFullPath(new Uri(selectedServer.ServerUri
 | |
|                                     .Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase)
 | |
|                                     .Replace("ws://", "http://", StringComparison.OrdinalIgnoreCase)));
 | |
|                                 _logger.LogInformation("Registering new account: {uri}", postUri.ToString());
 | |
|                                 var result = await httpClient.PostAsync(postUri, null).ConfigureAwait(false);
 | |
|                                 result.EnsureSuccessStatusCode();
 | |
|                                 var reply = await result.Content.ReadFromJsonAsync<RegisterReplyDto>().ConfigureAwait(false) ?? new();
 | |
|                                 if (!reply.Success)
 | |
|                                 {
 | |
|                                     _logger.LogWarning("Registration failed: {err}", reply.ErrorMessage);
 | |
|                                     _registrationMessage = reply.ErrorMessage;
 | |
|                                     if (_registrationMessage.IsNullOrEmpty())
 | |
|                                         _registrationMessage = "An unknown error occured. Please try again later.";
 | |
|                                     return;
 | |
|                                 }
 | |
|                                 _registrationMessage = "New account registered.\nPlease keep a copy of your secret key in case you need to reset your plugins, or to use it on another PC.";
 | |
|                                 _registrationSuccess = true;
 | |
|                                 selectedServer.SecretKeys.Add(selectedServer.SecretKeys.Any() ? selectedServer.SecretKeys.Max(p => p.Key) + 1 : 0, new SecretKey()
 | |
|                                 {
 | |
|                                     FriendlyName = reply.UID + $" (registered {DateTime.Now:yyyy-MM-dd})",
 | |
|                                     Key = reply.SecretKey ?? ""
 | |
|                                 });
 | |
|                                 _serverConfigurationManager.Save();
 | |
|                             }
 | |
|                             catch (Exception ex)
 | |
|                             {
 | |
|                                 _logger.LogWarning(ex, "Registration failed");
 | |
|                                 _registrationSuccess = false;
 | |
|                                 _registrationMessage = "An unknown error occured. Please try again later.";
 | |
|                             }
 | |
|                             finally
 | |
|                             {
 | |
|                                 _registrationInProgress = false;
 | |
|                             }
 | |
|                         }, CancellationToken.None);
 | |
|                     }
 | |
|                     if (_registrationInProgress)
 | |
|                     {
 | |
|                         ImGui.TextUnformatted("Sending request...");
 | |
|                     }
 | |
|                     else if (!_registrationMessage.IsNullOrEmpty())
 | |
|                     {
 | |
|                         if (!_registrationSuccess)
 | |
|                             ImGui.TextColored(ImGuiColors.DalamudYellow, _registrationMessage);
 | |
|                         else
 | |
|                             ImGui.TextWrapped(_registrationMessage);
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 ImGui.EndTabItem();
 | |
|             }
 | |
| 
 | |
|             if (ImGui.BeginTabItem("Service Settings"))
 | |
|             {
 | |
|                 var serverName = selectedServer.ServerName;
 | |
|                 var serverUri = selectedServer.ServerUri;
 | |
|                 var isMain = string.Equals(serverName, ApiController.LoporritServer, StringComparison.OrdinalIgnoreCase);
 | |
|                 var flags = isMain ? ImGuiInputTextFlags.ReadOnly : ImGuiInputTextFlags.None;
 | |
| 
 | |
|                 if (ImGui.InputText("Service URI", ref serverUri, 255, flags))
 | |
|                 {
 | |
|                     selectedServer.ServerUri = serverUri;
 | |
|                 }
 | |
|                 if (isMain)
 | |
|                 {
 | |
|                     _uiShared.DrawHelpText("You cannot edit the URI of the main service.");
 | |
|                 }
 | |
| 
 | |
|                 if (ImGui.InputText("Service Name", ref serverName, 255, flags))
 | |
|                 {
 | |
|                     selectedServer.ServerName = serverName;
 | |
|                     _serverConfigurationManager.Save();
 | |
|                 }
 | |
|                 if (isMain)
 | |
|                 {
 | |
|                     _uiShared.DrawHelpText("You cannot edit the name of the main service.");
 | |
|                 }
 | |
| 
 | |
|                 if (!isMain && selectedServer != _serverConfigurationManager.CurrentServer)
 | |
|                 {
 | |
|                     if (_uiShared.IconTextButton(FontAwesomeIcon.Trash, "Delete Service") && UiSharedService.CtrlPressed())
 | |
|                     {
 | |
|                         _serverConfigurationManager.DeleteServer(selectedServer);
 | |
|                     }
 | |
|                     _uiShared.DrawHelpText("Hold CTRL to delete this service");
 | |
|                 }
 | |
|                 ImGui.EndTabItem();
 | |
|             }
 | |
|             ImGui.EndTabBar();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private string _uidToAddForIgnore = string.Empty;
 | |
|     private int _selectedEntry = -1;
 | |
| 
 | |
|     private string _uidToAddForIgnoreBlacklist = string.Empty;
 | |
|     private int _selectedEntryBlacklist = -1;
 | |
| 
 | |
|     private void DrawSettingsContent()
 | |
|     {
 | |
|         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.Separator();
 | |
|         if (ImGui.BeginTabBar("mainTabBar"))
 | |
|         {
 | |
|             if (ImGui.BeginTabItem("General"))
 | |
|             {
 | |
|                 DrawGeneral();
 | |
|                 ImGui.EndTabItem();
 | |
|             }
 | |
| 
 | |
|             if (ImGui.BeginTabItem("Performance"))
 | |
|             {
 | |
|                 DrawPerformance();
 | |
|                 ImGui.EndTabItem();
 | |
|             }
 | |
| 
 | |
|             if (ImGui.BeginTabItem("Storage"))
 | |
|             {
 | |
|                 DrawFileStorageSettings();
 | |
|                 ImGui.EndTabItem();
 | |
|             }
 | |
| 
 | |
|             if (ImGui.BeginTabItem("Transfers"))
 | |
|             {
 | |
|                 DrawCurrentTransfers();
 | |
|                 ImGui.EndTabItem();
 | |
|             }
 | |
| 
 | |
|             if (ImGui.BeginTabItem("Service Settings"))
 | |
|             {
 | |
|                 ImGui.BeginDisabled(_registrationInProgress);
 | |
|                 DrawServerConfiguration();
 | |
|                 ImGui.EndTabItem();
 | |
|                 ImGui.EndDisabled(); // _registrationInProgress
 | |
|             }
 | |
| 
 | |
|             if (ImGui.BeginTabItem("Chat"))
 | |
|             {
 | |
|                 DrawChatConfig();
 | |
|                 ImGui.EndTabItem();
 | |
|             }
 | |
| 
 | |
|             if (ImGui.BeginTabItem("Advanced"))
 | |
|             {
 | |
|                 DrawAdvanced();
 | |
|                 ImGui.EndTabItem();
 | |
|             }
 | |
| 
 | |
|             ImGui.EndTabBar();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private void UiSharedService_GposeEnd()
 | |
|     {
 | |
|         IsOpen = _wasOpen;
 | |
|     }
 | |
| 
 | |
|     private void UiSharedService_GposeStart()
 | |
|     {
 | |
|         _wasOpen = IsOpen;
 | |
|         IsOpen = false;
 | |
|     }
 | |
| }
 | 
