2.0.0 (#92)
All checks were successful
Tag and Release Lightless / tag-and-release (push) Successful in 2m27s
All checks were successful
Tag and Release Lightless / tag-and-release (push) Successful in 2m27s
2.0.0 Changes: - Reworked shell finder UI with compact or list view with profile tags showing with the listing, allowing moderators to broadcast the syncshell as well to have it be used more. - Reworked user list in syncshell admin screen to have filter visible and moved away from table to its own thing, allowing to copy uid/note/alias when clicking on the name. - Reworked download bars and download box to make it look more modern, removed the jitter around, so it shouldn't vibrate around much. - Chat has been added to the top menu, working in Zone or in Syncshells to be used there. - Paired system has been revamped to make pausing and unpausing faster, and loading people should be faster as well. - Moved to the internal object table to have faster load times for users; people should load in faster - Compactor is running on a multi-threaded level instead of single-threaded; this should increase the speed of compacting files - Nameplate Service has been reworked so it wouldn't use the nameplate handler anymore. - Files can be resized when downloading to reduce load on users if they aren't compressed. (can be toggled to resize all). - Penumbra Collections are now only made when people are visible, reducing the load on boot-up when having many syncshells in your list. - Lightfinder plates have been moved away from using Nameplates, but will use an overlay. - Main UI has been changed a bit with a gradient, and on hover will glow up now. - Reworked Profile UI for Syncshell and Users to be more user-facing with more customizable items. - Reworked Settings UI to look more modern. - Performance should be better due to new systems that would dispose of the collections and better caching of items. Co-authored-by: defnotken <itsdefnotken@gmail.com> Co-authored-by: azyges <aaaaaa@aaa.aaa> Co-authored-by: choco <choco@patat.nl> Co-authored-by: cake <admin@cakeandbanana.nl> Co-authored-by: Minmoose <KennethBohr@outlook.com> Reviewed-on: #92
This commit was merged in pull request #92.
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface.Colors;
|
||||
using LightlessSync.LightlessConfiguration;
|
||||
using LightlessSync.LightlessConfiguration.Models;
|
||||
using LightlessSync.PlayerData.Handlers;
|
||||
using LightlessSync.Services;
|
||||
using LightlessSync.Services.Mediator;
|
||||
using LightlessSync.Services.PairProcessing;
|
||||
using LightlessSync.WebAPI.Files;
|
||||
using LightlessSync.WebAPI.Files.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -22,6 +23,10 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
private readonly UiSharedService _uiShared;
|
||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
||||
private readonly ConcurrentDictionary<GameObjectHandler, bool> _uploadingPlayers = new();
|
||||
private readonly Dictionary<GameObjectHandler, Vector2> _smoothed = [];
|
||||
private readonly Dictionary<GameObjectHandler, DownloadSpeedTracker> _downloadSpeeds = [];
|
||||
|
||||
private byte _transferBoxTransparency = 100;
|
||||
private bool _notificationDismissed = true;
|
||||
private int _lastDownloadStateHash = 0;
|
||||
|
||||
@@ -95,155 +100,32 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
if (_configService.Current.ShowTransferWindow)
|
||||
{
|
||||
var limiterSnapshot = _pairProcessingLimiter.GetSnapshot();
|
||||
|
||||
try
|
||||
|
||||
// Check if download notifications are enabled (not set to TextOverlay)
|
||||
var useNotifications =
|
||||
_configService.Current.UseLightlessNotifications &&
|
||||
_configService.Current.LightlessDownloadNotification == NotificationLocation.LightlessUi;
|
||||
|
||||
if (useNotifications)
|
||||
{
|
||||
if (_fileTransferManager.IsUploading)
|
||||
if (!_currentDownloads.IsEmpty)
|
||||
{
|
||||
var currentUploads = _fileTransferManager.GetCurrentUploadsSnapshot();
|
||||
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
|
||||
{
|
||||
_logger.LogDebug("Error drawing upload progress");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Check if download notifications are enabled (not set to TextOverlay)
|
||||
var useNotifications = _configService.Current.UseLightlessNotifications
|
||||
? _configService.Current.LightlessDownloadNotification != NotificationLocation.TextOverlay
|
||||
: _configService.Current.UseNotificationsForDownloads;
|
||||
|
||||
if (useNotifications)
|
||||
{
|
||||
// Use notification system
|
||||
if (_currentDownloads.Any())
|
||||
{
|
||||
UpdateDownloadNotificationIfChanged(limiterSnapshot);
|
||||
_notificationDismissed = false;
|
||||
}
|
||||
else if (!_notificationDismissed)
|
||||
{
|
||||
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
||||
_notificationDismissed = true;
|
||||
_lastDownloadStateHash = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use text overlay
|
||||
if (limiterSnapshot.IsEnabled)
|
||||
{
|
||||
var queueColor = limiterSnapshot.Waiting > 0 ? ImGuiColors.DalamudYellow : ImGuiColors.DalamudGrey;
|
||||
var queueText = $"Pair queue {limiterSnapshot.InFlight}/{limiterSnapshot.Limit}";
|
||||
queueText += limiterSnapshot.Waiting > 0 ? $" ({limiterSnapshot.Waiting} waiting, {limiterSnapshot.Remaining} free)" : $" ({limiterSnapshot.Remaining} free)";
|
||||
UiSharedService.DrawOutlinedFont(queueText, queueColor, new Vector4(0, 0, 0, 255), 1);
|
||||
ImGui.NewLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
UiSharedService.DrawOutlinedFont("Pair apply limiter disabled", ImGuiColors.DalamudGrey, new Vector4(0, 0, 0, 255), 1);
|
||||
ImGui.NewLine();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
_logger.LogDebug("Error drawing download progress");
|
||||
}
|
||||
}
|
||||
|
||||
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(UIColors.Get("LightlessPurple")));
|
||||
|
||||
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);
|
||||
UpdateDownloadNotificationIfChanged(limiterSnapshot);
|
||||
_notificationDismissed = false;
|
||||
}
|
||||
else if (!_notificationDismissed)
|
||||
{
|
||||
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
||||
_notificationDismissed = true;
|
||||
_lastDownloadStateHash = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DrawDownloadSummaryBox();
|
||||
|
||||
if (_configService.Current.ShowUploading)
|
||||
{
|
||||
const int transparency = 100;
|
||||
foreach (var player in _uploadingPlayers.Select(p => p.Key).ToList())
|
||||
{
|
||||
var screenPos = _dalamudUtilService.WorldToScreen(player.GetGameObject());
|
||||
@@ -253,29 +135,621 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
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.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);
|
||||
UiSharedService.Color(0, 0, 0, transparency),
|
||||
2
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.LogDebug("Error drawing upload progress");
|
||||
_logger.LogDebug("Error drawing upload progress");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_configService.Current.ShowTransferBars)
|
||||
{
|
||||
DrawTransferBar();
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTransferBar()
|
||||
{
|
||||
const int dlBarBorder = 3;
|
||||
const float rounding = 6f;
|
||||
var shadowOffset = new Vector2(2, 2);
|
||||
|
||||
foreach (var transfer in _currentDownloads.ToList())
|
||||
{
|
||||
var transferKey = transfer.Key;
|
||||
var rawPos = _dalamudUtilService.WorldToScreen(transferKey.GetGameObject());
|
||||
|
||||
// If RawPos is zero, remove it from smoothed dictionary
|
||||
if (rawPos == Vector2.Zero)
|
||||
{
|
||||
_smoothed.Remove(transferKey);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Smoothing out the movement and fix jitter around the position.
|
||||
Vector2 screenPos = _smoothed.TryGetValue(transferKey, out var lastPos)
|
||||
? (rawPos - lastPos).Length() < 4f ? lastPos : rawPos
|
||||
: rawPos;
|
||||
_smoothed[transferKey] = screenPos;
|
||||
|
||||
var totalBytes = transfer.Value.Sum(c => c.Value.TotalBytes);
|
||||
var transferredBytes = transfer.Value.Sum(c => c.Value.TransferredBytes);
|
||||
|
||||
// Per-player state counts
|
||||
var dlSlot = 0;
|
||||
var dlQueue = 0;
|
||||
var dlProg = 0;
|
||||
var dlDecomp = 0;
|
||||
|
||||
foreach (var entry in transfer.Value)
|
||||
{
|
||||
var fileStatus = entry.Value;
|
||||
switch (fileStatus.DownloadStatus)
|
||||
{
|
||||
case DownloadStatus.WaitingForSlot:
|
||||
dlSlot++;
|
||||
break;
|
||||
case DownloadStatus.WaitingForQueue:
|
||||
dlQueue++;
|
||||
break;
|
||||
case DownloadStatus.Downloading:
|
||||
dlProg++;
|
||||
break;
|
||||
case DownloadStatus.Decompressing:
|
||||
dlDecomp++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string statusText;
|
||||
if (dlProg > 0)
|
||||
{
|
||||
statusText = "Downloading";
|
||||
}
|
||||
else if (dlDecomp > 0 || (totalBytes > 0 && transferredBytes >= totalBytes))
|
||||
{
|
||||
statusText = "Decompressing";
|
||||
}
|
||||
else if (dlQueue > 0)
|
||||
{
|
||||
statusText = "Waiting for queue";
|
||||
}
|
||||
else if (dlSlot > 0)
|
||||
{
|
||||
statusText = "Waiting for slot";
|
||||
}
|
||||
else
|
||||
{
|
||||
statusText = "Waiting";
|
||||
}
|
||||
|
||||
var hasValidSize = totalBytes > 0;
|
||||
|
||||
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);
|
||||
|
||||
// Precompute rects
|
||||
var outerStart = new Vector2(dlBarStart.X - dlBarBorder - 1, dlBarStart.Y - dlBarBorder - 1);
|
||||
var outerEnd = new Vector2(dlBarEnd.X + dlBarBorder + 1, dlBarEnd.Y + dlBarBorder + 1);
|
||||
var borderStart = new Vector2(dlBarStart.X - dlBarBorder, dlBarStart.Y - dlBarBorder);
|
||||
var borderEnd = new Vector2(dlBarEnd.X + dlBarBorder, dlBarEnd.Y + dlBarBorder);
|
||||
|
||||
var drawList = ImGui.GetBackgroundDrawList();
|
||||
|
||||
drawList.AddRectFilled(
|
||||
outerStart + shadowOffset,
|
||||
outerEnd + shadowOffset,
|
||||
UiSharedService.Color(0, 0, 0, 100 / 2),
|
||||
rounding + 2
|
||||
);
|
||||
drawList.AddRectFilled(
|
||||
outerStart,
|
||||
outerEnd,
|
||||
UiSharedService.Color(0, 0, 0, 100),
|
||||
rounding + 2
|
||||
);
|
||||
drawList.AddRectFilled(
|
||||
borderStart,
|
||||
borderEnd,
|
||||
UiSharedService.Color(ImGuiColors.DalamudGrey),
|
||||
rounding
|
||||
);
|
||||
drawList.AddRectFilled(
|
||||
dlBarStart,
|
||||
dlBarEnd,
|
||||
UiSharedService.Color(0, 0, 0, 100),
|
||||
rounding
|
||||
);
|
||||
|
||||
bool showFill = false;
|
||||
double fillPercent = 0.0;
|
||||
|
||||
if (hasValidSize)
|
||||
{
|
||||
if (dlProg > 0)
|
||||
{
|
||||
fillPercent = transferredBytes / (double)totalBytes;
|
||||
showFill = true;
|
||||
}
|
||||
else if (dlDecomp > 0 || transferredBytes >= totalBytes)
|
||||
{
|
||||
fillPercent = 1.0;
|
||||
showFill = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (showFill)
|
||||
{
|
||||
if (fillPercent < 0) fillPercent = 0;
|
||||
if (fillPercent > 1) fillPercent = 1;
|
||||
|
||||
var progressEndX = dlBarStart.X + (float)(fillPercent * dlBarWidth);
|
||||
var progressEnd = new Vector2(progressEndX, dlBarEnd.Y);
|
||||
|
||||
drawList.AddRectFilled(
|
||||
dlBarStart,
|
||||
progressEnd,
|
||||
UiSharedService.Color(UIColors.Get("LightlessPurple")),
|
||||
rounding
|
||||
);
|
||||
}
|
||||
|
||||
if (_configService.Current.TransferBarsShowText)
|
||||
{
|
||||
string downloadText;
|
||||
|
||||
if (dlProg > 0 && hasValidSize)
|
||||
{
|
||||
downloadText =
|
||||
$"{statusText} {UiSharedService.ByteToString(transferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(totalBytes)}";
|
||||
}
|
||||
else if ((dlDecomp > 0 || transferredBytes >= totalBytes) && hasValidSize)
|
||||
{
|
||||
downloadText = "Decompressing";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Waiting states
|
||||
downloadText = statusText;
|
||||
}
|
||||
|
||||
var actualTextSize = ImGui.CalcTextSize(downloadText);
|
||||
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
downloadText,
|
||||
screenPos with
|
||||
{
|
||||
X = screenPos.X - actualTextSize.X / 2f - 1,
|
||||
Y = screenPos.Y - actualTextSize.Y / 2f - 1
|
||||
},
|
||||
UiSharedService.Color(ImGuiColors.DalamudGrey),
|
||||
UiSharedService.Color(0, 0, 0, 100),
|
||||
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(ImGuiColors.DalamudYellow),
|
||||
UiSharedService.Color(0, 0, 0, 100),
|
||||
2
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.LogDebug("Error drawing upload progress");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDownloadSummaryBox()
|
||||
{
|
||||
if (_currentDownloads.IsEmpty)
|
||||
return;
|
||||
|
||||
const float padding = 6f;
|
||||
const float spacingY = 2f;
|
||||
const float minBoxWidth = 320f;
|
||||
|
||||
var now = ImGui.GetTime();
|
||||
|
||||
int totalFiles = 0;
|
||||
int transferredFiles = 0;
|
||||
long totalBytes = 0;
|
||||
long transferredBytes = 0;
|
||||
|
||||
var totalDlSlot = 0;
|
||||
var totalDlQueue = 0;
|
||||
var totalDlProg = 0;
|
||||
var totalDlDecomp = 0;
|
||||
|
||||
var perPlayer = new List<(
|
||||
string Name,
|
||||
int TransferredFiles,
|
||||
int TotalFiles,
|
||||
long TransferredBytes,
|
||||
long TotalBytes,
|
||||
double SpeedBytesPerSecond,
|
||||
int DlSlot,
|
||||
int DlQueue,
|
||||
int DlProg,
|
||||
int DlDecomp)>();
|
||||
|
||||
foreach (var transfer in _currentDownloads)
|
||||
{
|
||||
var handler = transfer.Key;
|
||||
var statuses = transfer.Value.Values;
|
||||
|
||||
var playerTotalFiles = statuses.Sum(s => s.TotalFiles);
|
||||
var playerTransferredFiles = statuses.Sum(s => s.TransferredFiles);
|
||||
var playerTotalBytes = statuses.Sum(s => s.TotalBytes);
|
||||
var playerTransferredBytes = statuses.Sum(s => s.TransferredBytes);
|
||||
|
||||
totalFiles += playerTotalFiles;
|
||||
transferredFiles += playerTransferredFiles;
|
||||
totalBytes += playerTotalBytes;
|
||||
transferredBytes += playerTransferredBytes;
|
||||
|
||||
// per-player W/Q/P/D
|
||||
var playerDlSlot = 0;
|
||||
var playerDlQueue = 0;
|
||||
var playerDlProg = 0;
|
||||
var playerDlDecomp = 0;
|
||||
|
||||
foreach (var entry in transfer.Value)
|
||||
{
|
||||
var fileStatus = entry.Value;
|
||||
switch (fileStatus.DownloadStatus)
|
||||
{
|
||||
case DownloadStatus.WaitingForSlot:
|
||||
playerDlSlot++;
|
||||
totalDlSlot++;
|
||||
break;
|
||||
case DownloadStatus.WaitingForQueue:
|
||||
playerDlQueue++;
|
||||
totalDlQueue++;
|
||||
break;
|
||||
case DownloadStatus.Downloading:
|
||||
playerDlProg++;
|
||||
totalDlProg++;
|
||||
break;
|
||||
case DownloadStatus.Decompressing:
|
||||
playerDlDecomp++;
|
||||
totalDlDecomp++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double speed = 0;
|
||||
if (playerTotalBytes > 0)
|
||||
{
|
||||
if (!_downloadSpeeds.TryGetValue(handler, out var tracker))
|
||||
{
|
||||
tracker = new DownloadSpeedTracker(windowSeconds: 3.0);
|
||||
_downloadSpeeds[handler] = tracker;
|
||||
}
|
||||
|
||||
speed = tracker.Update(now, playerTransferredBytes);
|
||||
}
|
||||
|
||||
perPlayer.Add((
|
||||
handler.Name,
|
||||
playerTransferredFiles,
|
||||
playerTotalFiles,
|
||||
playerTransferredBytes,
|
||||
playerTotalBytes,
|
||||
speed,
|
||||
playerDlSlot,
|
||||
playerDlQueue,
|
||||
playerDlProg,
|
||||
playerDlDecomp
|
||||
));
|
||||
}
|
||||
|
||||
// Clean speed trackers for players with no active downloads
|
||||
foreach (var handler in _downloadSpeeds.Keys.ToList())
|
||||
{
|
||||
if (!_currentDownloads.ContainsKey(handler))
|
||||
_downloadSpeeds.Remove(handler);
|
||||
}
|
||||
|
||||
if (totalFiles == 0 || totalBytes == 0)
|
||||
return;
|
||||
|
||||
// max speed for per-player bar scale (clamped)
|
||||
double maxSpeed = perPlayer.Count > 0 ? perPlayer.Max(p => p.SpeedBytesPerSecond) : 0;
|
||||
if (maxSpeed <= 0)
|
||||
maxSpeed = 1;
|
||||
|
||||
var drawList = ImGui.GetBackgroundDrawList();
|
||||
var windowPos = ImGui.GetWindowPos();
|
||||
|
||||
// Overall texts
|
||||
var headerText =
|
||||
$"Downloading {transferredFiles}/{totalFiles} files [W:{totalDlSlot}/Q:{totalDlQueue}/P:{totalDlProg}/D:{totalDlDecomp}]";
|
||||
|
||||
var bytesText =
|
||||
$"{UiSharedService.ByteToString(transferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(totalBytes)}";
|
||||
|
||||
var totalSpeed = perPlayer.Sum(p => p.SpeedBytesPerSecond);
|
||||
var speedText = totalSpeed > 0
|
||||
? $"{UiSharedService.ByteToString((long)totalSpeed)}/s"
|
||||
: "Calculating in lightspeed...";
|
||||
|
||||
var headerSize = ImGui.CalcTextSize(headerText);
|
||||
var bytesSize = ImGui.CalcTextSize(bytesText);
|
||||
var totalSpeedSize = ImGui.CalcTextSize(speedText);
|
||||
|
||||
float contentWidth = headerSize.X;
|
||||
if (bytesSize.X > contentWidth) contentWidth = bytesSize.X;
|
||||
if (totalSpeedSize.X > contentWidth) contentWidth = totalSpeedSize.X;
|
||||
|
||||
if (_configService.Current.ShowPlayerLinesTransferWindow)
|
||||
{
|
||||
foreach (var p in perPlayer)
|
||||
{
|
||||
var line =
|
||||
$"{p.Name} [W:{p.DlSlot}/Q:{p.DlQueue}/P:{p.DlProg}/D:{p.DlDecomp}] {p.TransferredFiles}/{p.TotalFiles}";
|
||||
|
||||
var lineSize = ImGui.CalcTextSize(line);
|
||||
if (lineSize.X > contentWidth)
|
||||
contentWidth = lineSize.X;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var lineHeight = ImGui.GetTextLineHeight();
|
||||
var globalBarHeight = lineHeight * 0.8f;
|
||||
var perPlayerBarHeight = lineHeight * 0.4f;
|
||||
|
||||
// Box width
|
||||
float boxWidth = contentWidth + padding * 2;
|
||||
if (boxWidth < minBoxWidth)
|
||||
boxWidth = minBoxWidth;
|
||||
|
||||
// Box height
|
||||
float boxHeight = 0;
|
||||
boxHeight += padding;
|
||||
boxHeight += globalBarHeight;
|
||||
boxHeight += padding;
|
||||
|
||||
boxHeight += lineHeight + spacingY;
|
||||
boxHeight += lineHeight + spacingY;
|
||||
boxHeight += lineHeight * 1.4f + spacingY;
|
||||
|
||||
if (_configService.Current.ShowPlayerLinesTransferWindow)
|
||||
{
|
||||
foreach (var p in perPlayer)
|
||||
{
|
||||
boxHeight += lineHeight + spacingY;
|
||||
|
||||
var showBar = _configService.Current.ShowPlayerSpeedBarsTransferWindow
|
||||
&& p.TransferredBytes > 0;
|
||||
|
||||
if (showBar)
|
||||
{
|
||||
boxHeight += perPlayerBarHeight + spacingY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boxHeight += padding;
|
||||
|
||||
var boxMin = windowPos;
|
||||
var boxMax = new Vector2(windowPos.X + boxWidth, windowPos.Y + boxHeight);
|
||||
|
||||
// Background + border
|
||||
drawList.AddRectFilled(boxMin, boxMax, UiSharedService.Color(0, 0, 0, _transferBoxTransparency), 5f);
|
||||
drawList.AddRect(boxMin, boxMax, UiSharedService.Color(ImGuiColors.DalamudGrey), 5f);
|
||||
|
||||
var cursor = boxMin + new Vector2(padding, padding);
|
||||
|
||||
var barMin = cursor;
|
||||
var barMax = new Vector2(boxMin.X + boxWidth - padding, cursor.Y + globalBarHeight);
|
||||
|
||||
var progress = (float)transferredBytes / totalBytes;
|
||||
if (progress < 0f) progress = 0f;
|
||||
if (progress > 1f) progress = 1f;
|
||||
|
||||
drawList.AddRectFilled(barMin, barMax, UiSharedService.Color(40, 40, 40, _transferBoxTransparency), 3f);
|
||||
drawList.AddRectFilled(
|
||||
barMin,
|
||||
new Vector2(barMin.X + (barMax.X - barMin.X) * progress, barMax.Y),
|
||||
UiSharedService.Color(UIColors.Get("LightlessPurple")),
|
||||
3f
|
||||
);
|
||||
|
||||
cursor.Y = barMax.Y + padding;
|
||||
|
||||
// Header
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
headerText,
|
||||
cursor,
|
||||
UiSharedService.Color(ImGuiColors.DalamudWhite),
|
||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||
1
|
||||
);
|
||||
cursor.Y += lineHeight + spacingY;
|
||||
|
||||
// Bytes
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
bytesText,
|
||||
cursor,
|
||||
UiSharedService.Color(ImGuiColors.DalamudWhite),
|
||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||
1
|
||||
);
|
||||
cursor.Y += lineHeight + spacingY;
|
||||
|
||||
// Total speed
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
speedText,
|
||||
cursor,
|
||||
UiSharedService.Color(UIColors.Get("LightlessPurple")),
|
||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||
1
|
||||
);
|
||||
cursor.Y += lineHeight * 1.4f + spacingY;
|
||||
|
||||
var orderedPlayers = perPlayer.OrderByDescending(p => p.TotalBytes).ToList();
|
||||
|
||||
foreach (var p in orderedPlayers)
|
||||
{
|
||||
var hasSpeed = p.SpeedBytesPerSecond > 0;
|
||||
var playerSpeedText = hasSpeed
|
||||
? $"{UiSharedService.ByteToString((long)p.SpeedBytesPerSecond)}/s"
|
||||
: "-";
|
||||
|
||||
var showBar = _configService.Current.ShowPlayerSpeedBarsTransferWindow
|
||||
&& p.TransferredBytes > 0;
|
||||
|
||||
var labelLine =
|
||||
$"{p.Name} [W:{p.DlSlot}/Q:{p.DlQueue}/P:{p.DlProg}/D:{p.DlDecomp}] {p.TransferredFiles}/{p.TotalFiles}";
|
||||
|
||||
if (!showBar)
|
||||
{
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
labelLine,
|
||||
cursor,
|
||||
UiSharedService.Color(255, 255, 255, _transferBoxTransparency),
|
||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||
1
|
||||
);
|
||||
|
||||
cursor.Y += lineHeight + spacingY;
|
||||
continue;
|
||||
}
|
||||
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
labelLine,
|
||||
cursor,
|
||||
UiSharedService.Color(255, 255, 255, _transferBoxTransparency),
|
||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||
1
|
||||
);
|
||||
cursor.Y += lineHeight + spacingY;
|
||||
|
||||
// Bar background
|
||||
var barBgMin = new Vector2(boxMin.X + padding, cursor.Y);
|
||||
var barBgMax = new Vector2(boxMax.X - padding, cursor.Y + perPlayerBarHeight);
|
||||
|
||||
drawList.AddRectFilled(
|
||||
barBgMin,
|
||||
barBgMax,
|
||||
UiSharedService.Color(40, 40, 40, _transferBoxTransparency),
|
||||
3f
|
||||
);
|
||||
|
||||
// Fill based on Progress of download
|
||||
float ratio = 0f;
|
||||
if (p.TotalBytes > 0)
|
||||
ratio = (float)p.TransferredBytes / p.TotalBytes;
|
||||
|
||||
if (ratio < 0f) ratio = 0f;
|
||||
if (ratio > 1f) ratio = 1f;
|
||||
|
||||
var fillX = barBgMin.X + (barBgMax.X - barBgMin.X) * ratio;
|
||||
var barFillMax = new Vector2(fillX, barBgMax.Y);
|
||||
|
||||
drawList.AddRectFilled(
|
||||
barBgMin,
|
||||
barFillMax,
|
||||
UiSharedService.Color(UIColors.Get("LightlessPurple")),
|
||||
3f
|
||||
);
|
||||
|
||||
// Text inside bar: downloading vs decompressing
|
||||
string barText;
|
||||
|
||||
var isDecompressing = p.DlDecomp > 0 && p.TransferredBytes >= p.TotalBytes && p.TotalBytes > 0;
|
||||
|
||||
if (isDecompressing)
|
||||
{
|
||||
// Keep bar full, static text showing decompressing
|
||||
barText = "Decompressing...";
|
||||
}
|
||||
else
|
||||
{
|
||||
var bytesInside =
|
||||
$"{UiSharedService.ByteToString(p.TransferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(p.TotalBytes)}";
|
||||
|
||||
barText = hasSpeed
|
||||
? $"{bytesInside} @ {playerSpeedText}"
|
||||
: bytesInside;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(barText))
|
||||
{
|
||||
var barTextSize = ImGui.CalcTextSize(barText);
|
||||
|
||||
var barTextPos = new Vector2(
|
||||
barBgMin.X + ((barBgMax.X - barBgMin.X) - barTextSize.X) / 2f - 1,
|
||||
barBgMin.Y + ((perPlayerBarHeight - barTextSize.Y) / 2f) - 1
|
||||
);
|
||||
|
||||
UiSharedService.DrawOutlinedFont(
|
||||
drawList,
|
||||
barText,
|
||||
barTextPos,
|
||||
UiSharedService.Color(255, 255, 255, _transferBoxTransparency),
|
||||
UiSharedService.Color(0, 0, 0, _transferBoxTransparency),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
cursor.Y += perPlayerBarHeight + spacingY;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool DrawConditions()
|
||||
{
|
||||
if (_uiShared.EditTrackerPosition) return true;
|
||||
if (!_configService.Current.ShowTransferWindow && !_configService.Current.ShowTransferBars) return false;
|
||||
if (!_currentDownloads.Any() && !_fileTransferManager.IsUploading && !_uploadingPlayers.Any()) return false;
|
||||
if (_currentDownloads.IsEmpty && !_fileTransferManager.IsUploading && _uploadingPlayers.IsEmpty) return false;
|
||||
if (!IsOpen) return false;
|
||||
return true;
|
||||
}
|
||||
@@ -369,4 +843,70 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class DownloadSpeedTracker
|
||||
{
|
||||
private readonly Queue<(double Time, long Bytes)> _samples = new();
|
||||
private readonly double _windowSeconds;
|
||||
|
||||
public double SpeedBytesPerSecond { get; private set; }
|
||||
|
||||
public DownloadSpeedTracker(double windowSeconds = 3.0)
|
||||
{
|
||||
_windowSeconds = windowSeconds;
|
||||
}
|
||||
|
||||
public double Update(double now, long totalBytes)
|
||||
{
|
||||
if (_samples.Count > 0 && totalBytes < _samples.Last().Bytes)
|
||||
{
|
||||
_samples.Clear();
|
||||
}
|
||||
|
||||
_samples.Enqueue((now, totalBytes));
|
||||
|
||||
while (_samples.Count > 0 && now - _samples.Peek().Time > _windowSeconds)
|
||||
_samples.Dequeue();
|
||||
|
||||
if (_samples.Count < 2)
|
||||
{
|
||||
SpeedBytesPerSecond = 0;
|
||||
return SpeedBytesPerSecond;
|
||||
}
|
||||
|
||||
var oldest = _samples.Peek();
|
||||
var newest = _samples.Last();
|
||||
|
||||
var dt = newest.Time - oldest.Time;
|
||||
if (dt <= 0.0001)
|
||||
{
|
||||
SpeedBytesPerSecond = 0;
|
||||
return SpeedBytesPerSecond;
|
||||
}
|
||||
|
||||
var dBytes = newest.Bytes - oldest.Bytes;
|
||||
if (dBytes <= 0)
|
||||
{
|
||||
SpeedBytesPerSecond = 0;
|
||||
return SpeedBytesPerSecond;
|
||||
}
|
||||
|
||||
|
||||
const long minBytesForSpeed = 32 * 1024;
|
||||
if (dBytes < minBytesForSpeed)
|
||||
{
|
||||
|
||||
return SpeedBytesPerSecond;
|
||||
}
|
||||
|
||||
var avg = dBytes / dt;
|
||||
|
||||
const double alpha = 0.3;
|
||||
SpeedBytesPerSecond = SpeedBytesPerSecond <= 0
|
||||
? avg
|
||||
: SpeedBytesPerSecond * (1 - alpha) + avg * alpha;
|
||||
|
||||
return SpeedBytesPerSecond;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user