From 72cd5006dbc09081aae3f063d9c55896e850c7ef Mon Sep 17 00:00:00 2001 From: cake Date: Tue, 2 Dec 2025 06:33:08 +0100 Subject: [PATCH] Force SHA1 hashing on updated hash files --- LightlessSync/FileCache/FileCacheManager.cs | 3 +- .../Configurations/LightlessConfig.cs | 1 + LightlessSync/UI/DownloadUi.cs | 230 +++++++++++++----- LightlessSync/UI/DtrEntry.cs | 2 +- LightlessSync/UI/EditProfileUi.Group.cs | 7 +- LightlessSync/UI/SettingsUi.cs | 8 + LightlessSync/UI/SyncshellFinderUI.cs | 37 ++- 7 files changed, 214 insertions(+), 74 deletions(-) diff --git a/LightlessSync/FileCache/FileCacheManager.cs b/LightlessSync/FileCache/FileCacheManager.cs index e8b0cb8..b9bddda 100644 --- a/LightlessSync/FileCache/FileCacheManager.cs +++ b/LightlessSync/FileCache/FileCacheManager.cs @@ -449,13 +449,12 @@ public sealed class FileCacheManager : IHostedService _logger.LogTrace("Updating hash for {path}", fileCache.ResolvedFilepath); var oldHash = fileCache.Hash; var prefixedPath = fileCache.PrefixedFilePath; - var algo = Crypto.DetectAlgo(fileCache.ResolvedFilepath); if (computeProperties) { var fi = new FileInfo(fileCache.ResolvedFilepath); fileCache.Size = fi.Length; fileCache.CompressedSize = null; - fileCache.Hash = Crypto.ComputeFileHash(fileCache.ResolvedFilepath, algo); + fileCache.Hash = Crypto.ComputeFileHash(fileCache.ResolvedFilepath, Crypto.HashAlgo.Sha1); fileCache.LastModifiedDateTicks = fi.LastWriteTimeUtc.Ticks.ToString(CultureInfo.InvariantCulture); } RemoveHashedFile(oldHash, prefixedPath); diff --git a/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs b/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs index 478db89..e3305a2 100644 --- a/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs +++ b/LightlessSync/LightlessConfiguration/Configurations/LightlessConfig.cs @@ -65,6 +65,7 @@ public class LightlessConfig : ILightlessConfiguration public bool ShowOnlineNotificationsOnlyForNamedPairs { get; set; } = false; public bool ShowTransferBars { get; set; } = true; public bool ShowTransferWindow { get; set; } = false; + public bool ShowPlayerLinesTransferWindow { get; set; } = true; public bool UseNotificationsForDownloads { get; set; } = true; public bool ShowUploading { get; set; } = true; public bool ShowUploadingBigText { get; set; } = true; diff --git a/LightlessSync/UI/DownloadUi.cs b/LightlessSync/UI/DownloadUi.cs index aff23ba..51111ed 100644 --- a/LightlessSync/UI/DownloadUi.cs +++ b/LightlessSync/UI/DownloadUi.cs @@ -24,15 +24,9 @@ public class DownloadUi : WindowMediatorSubscriberBase private readonly PairProcessingLimiter _pairProcessingLimiter; private readonly ConcurrentDictionary _uploadingPlayers = new(); private readonly Dictionary _smoothed = []; - private readonly Dictionary _downloadSpeeds = new(); - - private sealed class DownloadSpeedTracker - { - public long LastBytes; - public double LastTime; - public double SpeedBytesPerSecond; - } + private readonly Dictionary _downloadSpeeds = []; + private byte _transferBoxTransparency = 100; private bool _notificationDismissed = true; private int _lastDownloadStateHash = 0; @@ -160,7 +154,6 @@ public class DownloadUi : WindowMediatorSubscriberBase if (_configService.Current.ShowTransferBars) { - const int transparency = 100; const int dlBarBorder = 3; const float rounding = 6f; var shadowOffset = new Vector2(2, 2); @@ -210,10 +203,10 @@ public class DownloadUi : WindowMediatorSubscriberBase var drawList = ImGui.GetBackgroundDrawList(); //Shadow, background, border, bar background - drawList.AddRectFilled(outerStart + shadowOffset, outerEnd + shadowOffset, UiSharedService.Color(0, 0, 0, transparency / 2), rounding + 2); - drawList.AddRectFilled(outerStart, outerEnd, UiSharedService.Color(0, 0, 0, transparency), rounding + 2); + 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, transparency), rounding); + drawList.AddRectFilled(dlBarStart, dlBarEnd, UiSharedService.Color(0, 0, 0, 100), rounding); var dlProgressPercent = transferredBytes / (double)totalBytes; var progressEndX = dlBarStart.X + (float)(dlProgressPercent * dlBarWidth); @@ -226,7 +219,7 @@ public class DownloadUi : WindowMediatorSubscriberBase 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(ImGuiColors.DalamudGrey), - UiSharedService.Color(0, 0, 0, transparency), + UiSharedService.Color(0, 0, 0, 100), 1 ); } @@ -249,7 +242,7 @@ public class DownloadUi : WindowMediatorSubscriberBase 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, transparency), + UiSharedService.Color(0, 0, 0, 100), 2 ); } @@ -267,7 +260,6 @@ public class DownloadUi : WindowMediatorSubscriberBase if (!_currentDownloads.Any()) return; - const int transparency = 150; const float padding = 6f; const float spacingY = 2f; const float minBoxWidth = 320f; @@ -279,6 +271,7 @@ public class DownloadUi : WindowMediatorSubscriberBase long totalBytes = 0; long transferredBytes = 0; + // (Name, files done, files total, bytes done, bytes total, speed) var perPlayer = new List<(string Name, int TransferredFiles, int TotalFiles, long TransferredBytes, long TotalBytes, double SpeedBytesPerSecond)>(); foreach (var transfer in _currentDownloads.ToList()) @@ -301,29 +294,11 @@ public class DownloadUi : WindowMediatorSubscriberBase { if (!_downloadSpeeds.TryGetValue(handler, out var tracker)) { - tracker = new DownloadSpeedTracker - { - LastBytes = playerTransferredBytes, - LastTime = now, - SpeedBytesPerSecond = 0 - }; + tracker = new DownloadSpeedTracker(windowSeconds: 3.0); _downloadSpeeds[handler] = tracker; } - var dt = now - tracker.LastTime; - var dBytes = playerTransferredBytes - tracker.LastBytes; - - if (dt > 0.1 && dBytes >= 0) - { - var instant = dBytes / dt; - tracker.SpeedBytesPerSecond = tracker.SpeedBytesPerSecond <= 0 - ? instant - : tracker.SpeedBytesPerSecond * 0.8 + instant * 0.2; - } - - tracker.LastTime = now; - tracker.LastBytes = playerTransferredBytes; - speed = tracker.SpeedBytesPerSecond; + speed = tracker.Update(now, playerTransferredBytes); } perPlayer.Add(( @@ -336,6 +311,7 @@ public class DownloadUi : WindowMediatorSubscriberBase )); } + // Clean speed trackers for players with no active downloads foreach (var handler in _downloadSpeeds.Keys.ToList()) { if (!_currentDownloads.ContainsKey(handler)) @@ -345,24 +321,30 @@ public class DownloadUi : WindowMediatorSubscriberBase if (totalFiles == 0 || totalBytes == 0) return; + // max speed for 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"; 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 lightspeed..."; + : "Calculating in lightspeed..."; var headerSize = ImGui.CalcTextSize(headerText); var bytesSize = ImGui.CalcTextSize(bytesText); - var speedSize = ImGui.CalcTextSize(speedText); + var totalSpeedSize = ImGui.CalcTextSize(speedText); float contentWidth = headerSize.X; if (bytesSize.X > contentWidth) contentWidth = bytesSize.X; - if (speedSize.X > contentWidth) contentWidth = speedSize.X; + if (totalSpeedSize.X > contentWidth) contentWidth = totalSpeedSize.X; foreach (var p in perPlayer) { @@ -379,60 +361,114 @@ public class DownloadUi : WindowMediatorSubscriberBase contentWidth = lineSize.X; } - var boxWidth = contentWidth + padding * 2; + 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; - var lineHeight = ImGui.GetTextLineHeight(); - var numTextLines = 3 + perPlayer.Count; - var barHeight = lineHeight * 0.8f; - var boxHeight = padding * 3 + barHeight + numTextLines * (lineHeight + spacingY); + float boxHeight = 0; + boxHeight += padding; + boxHeight += globalBarHeight; + boxHeight += padding; - var origin = windowPos; + boxHeight += lineHeight + spacingY; + boxHeight += lineHeight + spacingY; + boxHeight += lineHeight * 1.4f + spacingY; - var boxMin = origin; - var boxMax = origin + new Vector2(boxWidth, boxHeight); + boxHeight += perPlayer.Count * (lineHeight + perPlayerBarHeight + spacingY * 2); + boxHeight += padding; - drawList.AddRectFilled(boxMin, boxMax, UiSharedService.Color(0, 0, 0, transparency), 5f); + 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); - // Progress bar var cursor = boxMin + new Vector2(padding, padding); + var barMin = cursor; - var barMax = new Vector2(boxMin.X + boxWidth - padding, cursor.Y + barHeight); + var barMax = new Vector2(boxMin.X + boxWidth - padding, cursor.Y + globalBarHeight); var progress = (float)transferredBytes / totalBytes; - drawList.AddRectFilled(barMin, barMax, UiSharedService.Color(40, 40, 40, transparency), 3f); + 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, transparency), 1); + 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, transparency), 1); + UiSharedService.DrawOutlinedFont(drawList, bytesText, cursor, UiSharedService.Color(ImGuiColors.DalamudWhite), UiSharedService.Color(0, 0, 0, _transferBoxTransparency), 1); cursor.Y += lineHeight + spacingY; // Total speed WIP - UiSharedService.DrawOutlinedFont(drawList, speedText, cursor, UiSharedService.Color(UIColors.Get("LightlessPurple")), UiSharedService.Color(0, 0, 0, transparency), 1); - cursor.Y += lineHeight * 1.4f; + UiSharedService.DrawOutlinedFont(drawList, speedText, cursor, UiSharedService.Color(UIColors.Get("LightlessPurple")), UiSharedService.Color(0, 0, 0, _transferBoxTransparency), 1); + cursor.Y += lineHeight * 1.4f + spacingY; - // Per-player lines - foreach (var p in perPlayer.OrderByDescending(p => p.TotalBytes)) + if (_configService.Current.ShowPlayerLinesTransferWindow) { - var playerSpeedText = p.SpeedBytesPerSecond > 0 - ? $"{UiSharedService.ByteToString((long)p.SpeedBytesPerSecond)}/s" - : "-"; + // Per-player lines + var orderedPlayers = perPlayer.OrderByDescending(p => p.TotalBytes).ToList(); - var line = $"{p.Name}: {p.TransferredFiles}/{p.TotalFiles} " + - $"({UiSharedService.ByteToString(p.TransferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(p.TotalBytes)}) " + - $"@ {playerSpeedText}"; + foreach (var p in orderedPlayers) + { + var playerSpeedText = p.SpeedBytesPerSecond > 0 + ? $"{UiSharedService.ByteToString((long)p.SpeedBytesPerSecond)}/s" + : "-"; - UiSharedService.DrawOutlinedFont(drawList, line, cursor, UiSharedService.Color(ImGuiColors.DalamudWhite), UiSharedService.Color(0, 0, 0, transparency), 1); + var line = $"{p.Name}: {p.TransferredFiles}/{p.TotalFiles} " + + $"({UiSharedService.ByteToString(p.TransferredBytes, addSuffix: false)}/{UiSharedService.ByteToString(p.TotalBytes)}) " + + $"@ {playerSpeedText}"; - cursor.Y += lineHeight + spacingY; + UiSharedService.DrawOutlinedFont( + drawList, + line, + cursor, + UiSharedService.Color(255, 255, 255, _transferBoxTransparency), + UiSharedService.Color(0, 0, 0, _transferBoxTransparency), + 1 + ); + cursor.Y += lineHeight + spacingY; + + 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 + ); + + float ratio = 0f; + if (maxSpeed > 0) + ratio = (float)(p.SpeedBytesPerSecond / maxSpeed); + + 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 + ); + + cursor.Y += perPlayerBarHeight + spacingY * 2; + } } } @@ -534,4 +570,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; + } + } } \ No newline at end of file diff --git a/LightlessSync/UI/DtrEntry.cs b/LightlessSync/UI/DtrEntry.cs index 834265f..770331e 100644 --- a/LightlessSync/UI/DtrEntry.cs +++ b/LightlessSync/UI/DtrEntry.cs @@ -348,7 +348,7 @@ public sealed class DtrEntry : IDisposable, IHostedService try { var cid = _dalamudUtilService.GetCIDAsync().GetAwaiter().GetResult(); - var hashedCid = cid.ToString().GetBlake3Hash(); + var hashedCid = cid.ToString().GetHash256(); _localHashedCid = hashedCid; _localHashedCidFetchedAt = now; return hashedCid; diff --git a/LightlessSync/UI/EditProfileUi.Group.cs b/LightlessSync/UI/EditProfileUi.Group.cs index 3d593d8..c2724fb 100644 --- a/LightlessSync/UI/EditProfileUi.Group.cs +++ b/LightlessSync/UI/EditProfileUi.Group.cs @@ -434,8 +434,10 @@ public partial class EditProfileUi try { var fileContent = await File.ReadAllBytesAsync(filePath).ConfigureAwait(false); - await using var stream = new MemoryStream(fileContent); - var format = await Image.DetectFormatAsync(stream).ConfigureAwait(false); + var stream = new MemoryStream(fileContent); + await using (stream.ConfigureAwait(false)) + { + var format = await Image.DetectFormatAsync(stream).ConfigureAwait(false); if (!IsSupportedImageFormat(format)) { _showProfileImageError = true; @@ -461,6 +463,7 @@ public partial class EditProfileUi _showProfileImageError = false; _queuedProfileImage = fileContent; Mediator.Publish(new ClearProfileGroupDataMessage(_groupInfo.Group)); + } } catch (Exception ex) { diff --git a/LightlessSync/UI/SettingsUi.cs b/LightlessSync/UI/SettingsUi.cs index 1820ed0..0f5efac 100644 --- a/LightlessSync/UI/SettingsUi.cs +++ b/LightlessSync/UI/SettingsUi.cs @@ -818,11 +818,19 @@ public class SettingsUi : WindowMediatorSubscriberBase $"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; } + bool showPlayerLinesTransferWindow = _configService.Current.ShowPlayerLinesTransferWindow; + + if (ImGui.Checkbox("Toggle the Player Lines in the Transfer Window", ref showPlayerLinesTransferWindow)) + { + _configService.Current.ShowPlayerLinesTransferWindow = showPlayerLinesTransferWindow; + _configService.Save(); + } ImGui.Unindent(); if (!_configService.Current.ShowTransferWindow) ImGui.EndDisabled(); diff --git a/LightlessSync/UI/SyncshellFinderUI.cs b/LightlessSync/UI/SyncshellFinderUI.cs index 5d67544..b550b41 100644 --- a/LightlessSync/UI/SyncshellFinderUI.cs +++ b/LightlessSync/UI/SyncshellFinderUI.cs @@ -37,7 +37,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase private GroupJoinDto? _joinDto; private GroupJoinInfoDto? _joinInfo; private DefaultPermissionsDto _ownPermissions = null!; - private const bool _useTestSyncshells = false; + private bool _useTestSyncshells = false; private bool _compactView = false; @@ -82,7 +82,15 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase { ImGui.BeginGroup(); _uiSharedService.MediumText("Nearby Syncshells", UIColors.Get("LightlessPurple")); + +#if DEBUG + if (ImGui.SmallButton("Show test syncshells")) + { + _useTestSyncshells = !_useTestSyncshells; + _ = Task.Run(async () => await RefreshSyncshellsAsync().ConfigureAwait(false)); + } ImGui.SameLine(); +#endif string checkboxLabel = "Compact view"; float availWidth = ImGui.GetContentRegionAvail().X; @@ -274,11 +282,30 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase ? shell.Group.Alias : shell.Group.GID; - _uiSharedService.MediumText(displayName, UIColors.Get("LightlessPurple")); - UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault")); + float startX = ImGui.GetCursorPosX(); + float availWidth = ImGui.GetContentRegionAvail().X; + float rightTextW = ImGui.CalcTextSize(broadcasterName).X; - ImGui.TextColored(ImGuiColors.DalamudGrey, "Broadcaster"); + ImGui.BeginGroup(); + + _uiSharedService.MediumText(displayName, UIColors.Get("LightlessPurple")); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip("Click to open profile."); + if (ImGui.IsItemClicked()) + { + //open profile of syncshell + } + + ImGui.SameLine(); + float rightX = startX + availWidth - rightTextW; + var pos = ImGui.GetCursorPos(); + ImGui.SetCursorPos(new Vector2(rightX, pos.Y + 3f * ImGuiHelpers.GlobalScale)); ImGui.TextUnformatted(broadcasterName); + if (ImGui.IsItemHovered()) + ImGui.SetTooltip("Broadcaster of the syncshell."); + + ImGui.EndGroup(); + UiSharedService.ColoredSeparator(UIColors.Get("LightlessPurpleDefault")); ImGui.Dummy(new Vector2(0, 6 * ImGuiHelpers.GlobalScale)); @@ -539,7 +566,7 @@ public class SyncshellFinderUI : WindowMediatorSubscriberBase ClearSelection(); } - private List BuildTestSyncshells() + private static List BuildTestSyncshells() { var testGroup1 = new GroupData("TEST-ALPHA", "Alpha Shell"); var testGroup2 = new GroupData("TEST-BETA", "Beta Shell");