using Dalamud.Bindings.ImGui; using Dalamud.Interface.Colors; using MareSynchronos.MareConfiguration; using MareSynchronos.PlayerData.Handlers; using MareSynchronos.Services; using MareSynchronos.Services.Mediator; using MareSynchronos.WebAPI.Files; using MareSynchronos.WebAPI.Files.Models; using Microsoft.Extensions.Logging; using System.Collections.Concurrent; using System.Numerics; namespace MareSynchronos.UI; public class DownloadUi : WindowMediatorSubscriberBase { private readonly MareConfigService _configService; private readonly ConcurrentDictionary> _currentDownloads = new(); private readonly DalamudUtilService _dalamudUtilService; private readonly FileUploadManager _fileTransferManager; private readonly UiSharedService _uiShared; private readonly ConcurrentDictionary _uploadingPlayers = new(); public DownloadUi(ILogger logger, DalamudUtilService dalamudUtilService, MareConfigService configService, FileUploadManager fileTransferManager, MareMediator mediator, UiSharedService uiShared, PerformanceCollectorService performanceCollectorService) : base(logger, mediator, "Elezen Downloads", performanceCollectorService) { _dalamudUtilService = dalamudUtilService; _configService = configService; _fileTransferManager = fileTransferManager; _uiShared = uiShared; SizeConstraints = new WindowSizeConstraints() { MaximumSize = new Vector2(500, 90), MinimumSize = new Vector2(500, 90), }; Flags |= ImGuiWindowFlags.NoMove; Flags |= ImGuiWindowFlags.NoBackground; Flags |= ImGuiWindowFlags.NoInputs; Flags |= ImGuiWindowFlags.NoNavFocus; Flags |= ImGuiWindowFlags.NoResize; Flags |= ImGuiWindowFlags.NoScrollbar; Flags |= ImGuiWindowFlags.NoTitleBar; Flags |= ImGuiWindowFlags.NoDecoration; Flags |= ImGuiWindowFlags.NoFocusOnAppearing; DisableWindowSounds = true; ForceMainWindow = true; IsOpen = true; Mediator.Subscribe(this, (msg) => _currentDownloads[msg.DownloadId] = msg.DownloadStatus); Mediator.Subscribe(this, (msg) => _currentDownloads.TryRemove(msg.DownloadId, out _)); Mediator.Subscribe(this, (_) => IsOpen = false); Mediator.Subscribe(this, (_) => IsOpen = true); Mediator.Subscribe(this, (msg) => { if (msg.IsUploading) { _uploadingPlayers[msg.Handler] = true; } else { _uploadingPlayers.TryRemove(msg.Handler, out _); } }); } protected override void DrawInternal() { if (_configService.Current.ShowTransferWindow) { try { if (_fileTransferManager.CurrentUploads.Any()) { var currentUploads = _fileTransferManager.CurrentUploads.ToList(); var totalUploads = currentUploads.Count; var doneUploads = currentUploads.Count(c => c.IsTransferred); var totalUploaded = currentUploads.Sum(c => c.Transferred); var totalToUpload = currentUploads.Sum(c => c.Total); UiSharedService.DrawOutlinedFont($"▲", ImGuiColors.DalamudWhite, new Vector4(0, 0, 0, 255), 1); ImGui.SameLine(); var xDistance = ImGui.GetCursorPosX(); UiSharedService.DrawOutlinedFont($"Compressing+Uploading {doneUploads}/{totalUploads}", ImGuiColors.DalamudWhite, new Vector4(0, 0, 0, 255), 1); ImGui.NewLine(); ImGui.SameLine(xDistance); UiSharedService.DrawOutlinedFont( $"{UiSharedService.ByteToString(totalUploaded, addSuffix: false)}/{UiSharedService.ByteToString(totalToUpload)}", ImGuiColors.DalamudWhite, new Vector4(0, 0, 0, 255), 1); if (_currentDownloads.Any()) ImGui.Separator(); } } catch { // ignore errors thrown from UI } try { foreach (var item in _currentDownloads.ToList()) { var dlSlot = item.Value.Count(c => c.Value.DownloadStatus == DownloadStatus.WaitingForSlot); var dlQueue = item.Value.Count(c => c.Value.DownloadStatus == DownloadStatus.WaitingForQueue); var dlProg = item.Value.Count(c => c.Value.DownloadStatus == DownloadStatus.Downloading); var dlDecomp = item.Value.Count(c => c.Value.DownloadStatus == DownloadStatus.Decompressing); var totalFiles = item.Value.Sum(c => c.Value.TotalFiles); var transferredFiles = item.Value.Sum(c => c.Value.TransferredFiles); var totalBytes = item.Value.Sum(c => c.Value.TotalBytes); var transferredBytes = item.Value.Sum(c => c.Value.TransferredBytes); UiSharedService.DrawOutlinedFont($"▼", ImGuiColors.DalamudWhite, new Vector4(0, 0, 0, 255), 1); ImGui.SameLine(); var xDistance = ImGui.GetCursorPosX(); UiSharedService.DrawOutlinedFont( $"{item.Key.Name} [W:{dlSlot}/Q:{dlQueue}/P:{dlProg}/D:{dlDecomp}]", ImGuiColors.DalamudWhite, new Vector4(0, 0, 0, 255), 1); ImGui.NewLine(); ImGui.SameLine(xDistance); UiSharedService.DrawOutlinedFont( $"{transferredFiles}/{totalFiles} ({UiSharedService.ByteToString(transferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(totalBytes)})", ImGuiColors.DalamudWhite, new Vector4(0, 0, 0, 255), 1); } } catch { // ignore errors thrown from UI } } if (_configService.Current.ShowTransferBars) { const int transparency = 100; const int dlBarBorder = 3; foreach (var transfer in _currentDownloads.ToList()) { var screenPos = _dalamudUtilService.WorldToScreen(transfer.Key.GetGameObject()); if (screenPos == Vector2.Zero) continue; var totalBytes = transfer.Value.Sum(c => c.Value.TotalBytes); var transferredBytes = transfer.Value.Sum(c => c.Value.TransferredBytes); var maxDlText = $"{UiSharedService.ByteToString(totalBytes, addSuffix: false)}/{UiSharedService.ByteToString(totalBytes)}"; var textSize = _configService.Current.TransferBarsShowText ? ImGui.CalcTextSize(maxDlText) : new Vector2(10, 10); int dlBarHeight = _configService.Current.TransferBarsHeight > ((int)textSize.Y + 5) ? _configService.Current.TransferBarsHeight : (int)textSize.Y + 5; int dlBarWidth = _configService.Current.TransferBarsWidth > ((int)textSize.X + 10) ? _configService.Current.TransferBarsWidth : (int)textSize.X + 10; var dlBarStart = new Vector2(screenPos.X - dlBarWidth / 2f, screenPos.Y - dlBarHeight / 2f); var dlBarEnd = new Vector2(screenPos.X + dlBarWidth / 2f, screenPos.Y + dlBarHeight / 2f); var drawList = ImGui.GetBackgroundDrawList(); drawList.AddRectFilled( dlBarStart with { X = dlBarStart.X - dlBarBorder - 1, Y = dlBarStart.Y - dlBarBorder - 1 }, dlBarEnd with { X = dlBarEnd.X + dlBarBorder + 1, Y = dlBarEnd.Y + dlBarBorder + 1 }, UiSharedService.Color(0, 0, 0, transparency), 1); drawList.AddRectFilled(dlBarStart with { X = dlBarStart.X - dlBarBorder, Y = dlBarStart.Y - dlBarBorder }, dlBarEnd with { X = dlBarEnd.X + dlBarBorder, Y = dlBarEnd.Y + dlBarBorder }, UiSharedService.Color(220, 220, 220, transparency), 1); drawList.AddRectFilled(dlBarStart, dlBarEnd, UiSharedService.Color(0, 0, 0, transparency), 1); var dlProgressPercent = transferredBytes / (double)totalBytes; drawList.AddRectFilled(dlBarStart, dlBarEnd with { X = dlBarStart.X + (float)(dlProgressPercent * dlBarWidth) }, UiSharedService.Color(50, 205, 50, transparency), 1); if (_configService.Current.TransferBarsShowText) { var downloadText = $"{UiSharedService.ByteToString(transferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(totalBytes)}"; UiSharedService.DrawOutlinedFont(drawList, downloadText, screenPos with { X = screenPos.X - textSize.X / 2f - 1, Y = screenPos.Y - textSize.Y / 2f - 1 }, UiSharedService.Color(255, 255, 255, transparency), UiSharedService.Color(0, 0, 0, transparency), 1); } } if (_configService.Current.ShowUploading) { foreach (var player in _uploadingPlayers.Select(p => p.Key).ToList()) { var screenPos = _dalamudUtilService.WorldToScreen(player.GetGameObject()); if (screenPos == Vector2.Zero) continue; try { using var _ = _uiShared.UidFont.Push(); var uploadText = "Uploading"; var textSize = ImGui.CalcTextSize(uploadText); var drawList = ImGui.GetBackgroundDrawList(); UiSharedService.DrawOutlinedFont(drawList, uploadText, screenPos with { X = screenPos.X - textSize.X / 2f - 1, Y = screenPos.Y - textSize.Y / 2f - 1 }, UiSharedService.Color(255, 255, 0, transparency), UiSharedService.Color(0, 0, 0, transparency), 2); } catch { // ignore errors thrown on UI } } } } } public override bool DrawConditions() { if (_uiShared.EditTrackerPosition) return true; if (!_configService.Current.ShowTransferWindow && !_configService.Current.ShowTransferBars) return false; if (!_currentDownloads.Any() && !_fileTransferManager.CurrentUploads.Any() && !_uploadingPlayers.Any()) return false; if (!IsOpen) return false; return true; } public override void PreDraw() { base.PreDraw(); if (_uiShared.EditTrackerPosition) { Flags &= ~ImGuiWindowFlags.NoMove; Flags &= ~ImGuiWindowFlags.NoBackground; Flags &= ~ImGuiWindowFlags.NoInputs; Flags &= ~ImGuiWindowFlags.NoResize; } else { Flags |= ImGuiWindowFlags.NoMove; Flags |= ImGuiWindowFlags.NoBackground; Flags |= ImGuiWindowFlags.NoInputs; Flags |= ImGuiWindowFlags.NoResize; } var maxHeight = ImGui.GetTextLineHeight() * (_configService.Current.ParallelDownloads + 3); SizeConstraints = new() { MinimumSize = new Vector2(300, maxHeight), MaximumSize = new Vector2(300, maxHeight), }; } }