diff --git a/LightlessAPI b/LightlessAPI
index fdd492a..c3caa7e 160000
--- a/LightlessAPI
+++ b/LightlessAPI
@@ -1 +1 @@
-Subproject commit fdd492a8f478949d910ed0efd3e4a3ca3312ed9c
+Subproject commit c3caa7e25cf17fd52c4765bf051ec37c8fd92082
diff --git a/LightlessSync/LightlessSync.csproj b/LightlessSync/LightlessSync.csproj
index 707d2a3..96efb14 100644
--- a/LightlessSync/LightlessSync.csproj
+++ b/LightlessSync/LightlessSync.csproj
@@ -37,6 +37,7 @@
+
diff --git a/LightlessSync/Services/ContextMenuService.cs b/LightlessSync/Services/ContextMenuService.cs
index 3fe893c..024e17b 100644
--- a/LightlessSync/Services/ContextMenuService.cs
+++ b/LightlessSync/Services/ContextMenuService.cs
@@ -10,7 +10,6 @@ using LightlessSync.UI;
using LightlessSync.UI.Services;
using LightlessSync.Utils;
using LightlessSync.WebAPI;
-using Lumina.Excel.Sheets;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
@@ -172,9 +171,8 @@ internal class ContextMenuService : IHostedService
_logger.LogTrace("Cannot send pair request to {TargetName}@{World} while in PvP or GPose.", target.TargetName, target.TargetHomeWorld.RowId);
return;
}
-
- var world = GetWorld(target.TargetHomeWorld.RowId);
- if (!IsWorldValid(world))
+
+ if (!IsWorldValid(target.TargetHomeWorld.RowId))
{
_logger.LogTrace("Target player {TargetName}@{World} is on an invalid world.", target.TargetName, target.TargetHomeWorld.RowId);
return;
@@ -226,9 +224,8 @@ internal class ContextMenuService : IHostedService
{
if (args.Target is not MenuTargetDefault target)
return;
-
- var world = GetWorld(target.TargetHomeWorld.RowId);
- if (!IsWorldValid(world))
+
+ if (!target.TargetHomeWorld.IsValid || !IsWorldValid(target.TargetHomeWorld.RowId))
return;
try
@@ -237,7 +234,7 @@ internal class ContextMenuService : IHostedService
if (targetData == null || targetData.Address == nint.Zero)
{
- _logger.LogWarning("Target player {TargetName}@{World} not found in object table.", target.TargetName, world.Name);
+ _logger.LogWarning("Target player {TargetName}@{World} not found in object table.", target.TargetName, target.TargetHomeWorld.Value.Name);
return;
}
@@ -252,7 +249,7 @@ internal class ContextMenuService : IHostedService
}
// Notify in chat when NotificationService is disabled
- NotifyInChat($"Pair request sent to {target.TargetName}@{world.Name}.", NotificationType.Info);
+ NotifyInChat($"Pair request sent to {target.TargetName}@{target.TargetHomeWorld.Value.Name}.", NotificationType.Info);
}
catch (Exception ex)
{
@@ -312,37 +309,8 @@ internal class ContextMenuService : IHostedService
p.HomeWorld.RowId == target.TargetHomeWorld.RowId);
}
- private World GetWorld(uint worldId)
+ private bool IsWorldValid(uint worldId)
{
- var sheet = _gameData.GetExcelSheet()!;
- var luminaWorlds = sheet.Where(x =>
- {
- var dc = x.DataCenter.ValueNullable;
- var name = x.Name.ExtractText();
- var internalName = x.InternalName.ExtractText();
-
- if (dc == null || dc.Value.Region == 0 || string.IsNullOrWhiteSpace(dc.Value.Name.ExtractText()))
- return false;
-
- if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(internalName))
- return false;
-
- if (name.Contains('-', StringComparison.Ordinal) || name.Contains('_', StringComparison.Ordinal))
- return false;
-
- return x.DataCenter.Value.Region != 5 || x.RowId > 3001 && x.RowId != 1200 && IsChineseJapaneseKoreanString(name);
- });
-
- return luminaWorlds.FirstOrDefault(x => x.RowId == worldId);
- }
-
- private static bool IsChineseJapaneseKoreanString(string text) => text.All(IsChineseJapaneseKoreanCharacter);
-
- private static bool IsChineseJapaneseKoreanCharacter(char c) => c >= 0x4E00 && c <= 0x9FFF;
-
- public static bool IsWorldValid(World world)
- {
- var name = world.Name.ToString();
- return !string.IsNullOrWhiteSpace(name) && char.IsUpper(name[0]);
+ return _dalamudUtil.WorldData.Value.ContainsKey((ushort)worldId);
}
}
diff --git a/LightlessSync/Services/DalamudUtilService.cs b/LightlessSync/Services/DalamudUtilService.cs
index ca59296..71bdace 100644
--- a/LightlessSync/Services/DalamudUtilService.cs
+++ b/LightlessSync/Services/DalamudUtilService.cs
@@ -1,11 +1,13 @@
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
+using Dalamud.Game.Text;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Control;
+using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using LightlessSync.API.Dto.CharaData;
@@ -26,6 +28,7 @@ using System.Text;
using BattleNpcSubKind = FFXIVClientStructs.FFXIV.Client.Game.Object.BattleNpcSubKind;
using DalamudObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject;
+using Map = Lumina.Excel.Sheets.Map;
using VisibilityFlags = FFXIVClientStructs.FFXIV.Client.Game.Object.VisibilityFlags;
namespace LightlessSync.Services;
@@ -57,6 +60,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
private string _lastGlobalBlockReason = string.Empty;
private ushort _lastZone = 0;
private ushort _lastWorldId = 0;
+ private uint _lastMapId = 0;
private bool _sentBetweenAreas = false;
private Lazy _cid;
@@ -86,7 +90,8 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
WorldData = new(() =>
{
return gameData.GetExcelSheet(clientLanguage)!
- .Where(w => !w.Name.IsEmpty && w.DataCenter.RowId != 0 && (w.IsPublic || char.IsUpper(w.Name.ToString()[0])))
+ .Where(w => !w.Name.IsEmpty && w.DataCenter.RowId != 0 && (w.IsPublic || char.IsUpper(w.Name.ToString()[0])
+ || w is { RowId: > 1000, Region: 101 or 201 }))
.ToDictionary(w => (ushort)w.RowId, w => w.Name.ToString());
});
JobData = new(() =>
@@ -659,7 +664,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
var location = new LocationInfo();
location.ServerId = _playerState.CurrentWorld.RowId;
- //location.InstanceId = UIState.Instance()->PublicInstance.InstanceId; //TODO:Need API update first
+ location.InstanceId = UIState.Instance()->PublicInstance.InstanceId;
location.TerritoryId = _clientState.TerritoryType;
location.MapId = _clientState.MapId;
if (houseMan != null)
@@ -685,7 +690,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
var outside = houseMan->OutdoorTerritory;
var house = outside->HouseId;
location.WardId = house.WardIndex + 1u;
- location.HouseId = (uint)houseMan->GetCurrentPlot() + 1;
+ //location.HouseId = (uint)houseMan->GetCurrentPlot() + 1;
location.DivisionId = houseMan->GetCurrentDivision();
}
//_logger.LogWarning(LocationToString(location));
@@ -713,10 +718,10 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
str += $" - {MapData.Value[(ushort)location.MapId].MapName}";
}
- // if (location.InstanceId is not 0)
- // {
- // str += ((SeIconChar)(57520 + location.InstanceId)).ToIconString();
- // }
+ if (location.InstanceId is not 0)
+ {
+ str += ((SeIconChar)(57520 + location.InstanceId)).ToIconString();
+ }
if (location.WardId is not 0)
{
@@ -1157,6 +1162,18 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
Mediator.Publish(new ZoneSwitchEndMessage());
Mediator.Publish(new ResumeScanMessage(nameof(ConditionFlag.BetweenAreas)));
}
+
+ //Map
+ if (!_sentBetweenAreas)
+ {
+ var mapid = _clientState.MapId;
+ if (mapid != _lastMapId)
+ {
+ _lastMapId = mapid;
+ Mediator.Publish(new MapChangedMessage(mapid));
+ }
+ }
+
var localPlayer = _objectTable.LocalPlayer;
if (localPlayer != null)
diff --git a/LightlessSync/Services/LocationShareService.cs b/LightlessSync/Services/LocationShareService.cs
index 0d3f5dc..38b2834 100644
--- a/LightlessSync/Services/LocationShareService.cs
+++ b/LightlessSync/Services/LocationShareService.cs
@@ -3,7 +3,9 @@ using LightlessSync.API.Dto.CharaData;
using LightlessSync.API.Dto.User;
using LightlessSync.Services.Mediator;
using LightlessSync.WebAPI;
+using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Primitives;
namespace LightlessSync.Services
{
@@ -11,7 +13,9 @@ namespace LightlessSync.Services
{
private readonly DalamudUtilService _dalamudUtilService;
private readonly ApiController _apiController;
- private Dictionary _locations = [];
+ private IMemoryCache _locations = new MemoryCache(new MemoryCacheOptions());
+ private IMemoryCache _sharingStatus = new MemoryCache(new MemoryCacheOptions());
+ private CancellationTokenSource _resetToken = new CancellationTokenSource();
public LocationShareService(ILogger logger, LightlessMediator mediator, DalamudUtilService dalamudUtilService, ApiController apiController) : base(logger, mediator)
{
@@ -19,37 +23,58 @@ namespace LightlessSync.Services
_apiController = apiController;
- Mediator.Subscribe(this, (msg) => _locations.Clear());
+ Mediator.Subscribe(this, (msg) =>
+ {
+ _resetToken.Cancel();
+ _resetToken.Dispose();
+ _resetToken = new CancellationTokenSource();
+ });
Mediator.Subscribe(this, (msg) =>
{
_ = _apiController.UpdateLocation(new LocationDto(new UserData(_apiController.UID, apiController.DisplayName), _dalamudUtilService.GetMapData()));
_ = RequestAllLocation();
} );
- Mediator.Subscribe(this, UpdateLocationList);
- Mediator.Subscribe(this,
+ Mediator.Subscribe(this, UpdateLocationList);
+ Mediator.Subscribe(this,
msg => _ = _apiController.UpdateLocation(new LocationDto(new UserData(_apiController.UID, _apiController.DisplayName), _dalamudUtilService.GetMapData())));
}
- private void UpdateLocationList(LocationMessage msg)
+ private void UpdateLocationList(LocationSharingMessage msg)
{
- if (_locations.ContainsKey(msg.Uid) && msg.LocationInfo.ServerId is 0)
+ if (_locations.TryGetValue(msg.User.UID, out _) && msg.LocationInfo.ServerId is 0)
{
- _locations.Remove(msg.Uid);
+ _locations.Remove(msg.User.UID);
return;
}
- if ( msg.LocationInfo.ServerId is not 0 && !_locations.TryAdd(msg.Uid, msg.LocationInfo))
+ if ( msg.LocationInfo.ServerId is not 0 && msg.ExpireAt > DateTime.UtcNow)
{
- _locations[msg.Uid] = msg.LocationInfo;
+ AddLocationInfo(msg.User.UID, msg.LocationInfo, msg.ExpireAt);
}
}
+ private void AddLocationInfo(string uid, LocationInfo location, DateTimeOffset expireAt)
+ {
+ var options = new MemoryCacheEntryOptions()
+ .SetAbsoluteExpiration(expireAt)
+ .AddExpirationToken(new CancellationChangeToken(_resetToken.Token));
+ _locations.Set(uid, location, options);
+ }
+
private async Task RequestAllLocation()
{
try
{
- var data = await _apiController.RequestAllLocationInfo().ConfigureAwait(false);
- _locations = data.ToDictionary(x => x.user.UID, x => x.location, StringComparer.Ordinal);
+ var (data, status) = await _apiController.RequestAllLocationInfo().ConfigureAwait(false);
+ foreach (var dto in data)
+ {
+ AddLocationInfo(dto.LocationDto.User.UID, dto.LocationDto.Location, dto.ExpireAt);
+ }
+
+ foreach (var dto in status)
+ {
+ AddStatus(dto.User.UID, dto.ExpireAt);
+ }
}
catch (Exception e)
{
@@ -58,11 +83,19 @@ namespace LightlessSync.Services
}
}
+ private void AddStatus(string uid, DateTimeOffset expireAt)
+ {
+ var options = new MemoryCacheEntryOptions()
+ .SetAbsoluteExpiration(expireAt)
+ .AddExpirationToken(new CancellationChangeToken(_resetToken.Token));
+ _sharingStatus.Set(uid, expireAt, options);
+ }
+
public string GetUserLocation(string uid)
{
try
{
- if (_locations.TryGetValue(uid, out var location))
+ if (_locations.TryGetValue(uid, out var location))
{
return _dalamudUtilService.LocationToString(location);
}
@@ -74,5 +107,31 @@ namespace LightlessSync.Services
throw;
}
}
+
+ public DateTimeOffset GetSharingStatus(string uid)
+ {
+ try
+ {
+ if (_sharingStatus.TryGetValue(uid, out var expireAt))
+ {
+ return expireAt;
+ }
+ return DateTimeOffset.MinValue;
+ }
+ catch (Exception e)
+ {
+ Logger.LogError(e,"GetSharingStatus error : ");
+ throw;
+ }
+ }
+
+ public void UpdateSharingStatus(List users, DateTimeOffset expireAt)
+ {
+ foreach (var user in users)
+ {
+ AddStatus(user, expireAt);
+ }
+ }
+
}
}
\ No newline at end of file
diff --git a/LightlessSync/Services/Mediator/Messages.cs b/LightlessSync/Services/Mediator/Messages.cs
index be0c06b..efe3341 100644
--- a/LightlessSync/Services/Mediator/Messages.cs
+++ b/LightlessSync/Services/Mediator/Messages.cs
@@ -135,7 +135,7 @@ public record ChatChannelsUpdated : MessageBase;
public record ChatChannelMessageAdded(string ChannelKey, ChatMessageEntry Message) : MessageBase;
public record GroupCollectionChangedMessage : MessageBase;
public record OpenUserProfileMessage(UserData User) : MessageBase;
-public record LocationMessage(string Uid, LocationInfo LocationInfo) : MessageBase;
-
+public record LocationSharingMessage(UserData User, LocationInfo LocationInfo, DateTimeOffset ExpireAt) : MessageBase;
+public record MapChangedMessage(uint MapId) : MessageBase;
#pragma warning restore S2094
#pragma warning restore MA0048 // File name must match type name
\ No newline at end of file
diff --git a/LightlessSync/UI/Components/DrawFolderGroup.cs b/LightlessSync/UI/Components/DrawFolderGroup.cs
index 277115a..c39326c 100644
--- a/LightlessSync/UI/Components/DrawFolderGroup.cs
+++ b/LightlessSync/UI/Components/DrawFolderGroup.cs
@@ -131,7 +131,6 @@ public class DrawFolderGroup : DrawFolderBase
bool disableSounds = perm.IsDisableSounds();
bool disableAnims = perm.IsDisableAnimations();
bool disableVfx = perm.IsDisableVFX();
- bool shareLocation = perm.IsSharingLocation();
if ((_groupFullInfoDto.GroupPermissions.IsPreferDisableAnimations() != disableAnims
|| _groupFullInfoDto.GroupPermissions.IsPreferDisableSounds() != disableSounds
@@ -165,13 +164,6 @@ public class DrawFolderGroup : DrawFolderBase
_ = _apiController.GroupChangeIndividualPermissionState(new(_groupFullInfoDto.Group, new(_apiController.UID), perm));
ImGui.CloseCurrentPopup();
}
-
- if (_uiSharedService.IconTextButton(!shareLocation ? FontAwesomeIcon.Globe : FontAwesomeIcon.StopCircle, !shareLocation ? "Share your location to all users in Syncshell" : "STOP Share your location to all users in Syncshell", menuWidth, true))
- {
- perm.SetShareLocation(!shareLocation);
- _ = _apiController.GroupChangeIndividualPermissionState(new(_groupFullInfoDto.Group, new(_apiController.UID), perm));
- ImGui.CloseCurrentPopup();
- }
if (IsModerator || IsOwner)
{
diff --git a/LightlessSync/UI/Components/DrawUserPair.cs b/LightlessSync/UI/Components/DrawUserPair.cs
index 785fb21..c8725e2 100644
--- a/LightlessSync/UI/Components/DrawUserPair.cs
+++ b/LightlessSync/UI/Components/DrawUserPair.cs
@@ -220,16 +220,47 @@ public class DrawUserPair
}
UiSharedService.AttachToolTip("Changes VFX sync permissions with this user." + (individual ? individualText : string.Empty));
- var isShareingLocation = _pair.UserPair!.OwnPermissions.IsSharingLocation();
- string isShareingLocationText = isShareingLocation ? "Disable location sharing" : "Enable location sharing";
- var isShareingLocationIcon = isShareingLocation ? FontAwesomeIcon.StopCircle : FontAwesomeIcon.Globe;
- if (_uiSharedService.IconTextButton(isShareingLocationIcon, isShareingLocationText, _menuWidth, true))
+ ImGui.SetCursorPosX(10f);
+ _uiSharedService.IconText(FontAwesomeIcon.Globe);
+ ImGui.SameLine();
+ if (ImGui.BeginMenu("Toggle Location sharing"))
{
- var permissions = _pair.UserPair.OwnPermissions;
- permissions.SetShareLocation(!isShareingLocation);
- _ = _apiController.UserSetPairPermissions(new UserPermissionsDto(_pair.UserData, permissions));
+ if (ImGui.MenuItem("Share for 30 Mins"))
+ {
+ _ = ToggleLocationSharing([_pair.UserData.UID], DateTimeOffset.UtcNow.AddMinutes(30));
+ }
+
+ if (ImGui.MenuItem("Share for 1 Hour"))
+ {
+ _ = ToggleLocationSharing([_pair.UserData.UID], DateTimeOffset.UtcNow.AddHours(1));
+ }
+
+ if (ImGui.MenuItem("Share for 3 Hours"))
+ {
+ _ = ToggleLocationSharing([_pair.UserData.UID], DateTimeOffset.UtcNow.AddHours(3));
+ }
+
+ if (ImGui.MenuItem("Share until manually stop"))
+ {
+ _ = ToggleLocationSharing([_pair.UserData.UID], DateTimeOffset.MaxValue);
+ }
+
+ ImGui.Separator();
+ if (ImGui.MenuItem("Stop Sharing"))
+ {
+ _ = ToggleLocationSharing([_pair.UserData.UID], DateTimeOffset.MinValue);
+ }
+ ImGui.EndMenu();
+ }
+ }
+
+ private async Task ToggleLocationSharing(List users, DateTimeOffset expireAt)
+ {
+ var updated = await _apiController.ToggleLocationSharing(new LocationSharingToggleDto(users, expireAt)).ConfigureAwait(false);
+ if (updated)
+ {
+ _locationShareService.UpdateSharingStatus(users, expireAt);
}
- UiSharedService.AttachToolTip("Changes location sharing permissions with this user." + (individual ? individualText : string.Empty));
}
private void DrawIndividualMenu()
@@ -581,7 +612,6 @@ public class DrawUserPair
: UiSharedService.TooltipSeparator + "Hold CTRL to enable preferred permissions while pausing." + Environment.NewLine + "This will leave this pair paused even if unpausing syncshells including this pair."))
: "Resume pairing with " + _pair.UserData.AliasOrUID);
- //Location sharing
if (_pair.IsPaired)
{
var individualSoundsDisabled = (_pair.UserPair?.OwnPermissions.IsDisableSounds() ?? false) || (_pair.UserPair?.OtherPermissions.IsDisableSounds() ?? false);
@@ -590,31 +620,32 @@ public class DrawUserPair
var individualIsSticky = _pair.UserPair!.OwnPermissions.IsSticky();
var individualIcon = individualIsSticky ? FontAwesomeIcon.ArrowCircleUp : FontAwesomeIcon.InfoCircle;
-
var shareLocationIcon = FontAwesomeIcon.Globe;
- var shareLocation = _pair.UserPair?.OwnPermissions.IsSharingLocation() ?? false;
- var shareLocationOther = _pair.UserPair?.OtherPermissions.IsSharingLocation() ?? false;
+ var location = _locationShareService.GetUserLocation(_pair.UserPair!.User.UID);
+ var shareLocation = !string.IsNullOrEmpty(location);
+ var expireAt = _locationShareService.GetSharingStatus(_pair.UserPair!.User.UID);
+ var shareLocationToOther = expireAt > DateTimeOffset.UtcNow;
var shareColor = shareLocation switch
{
- true when shareLocationOther => UIColors.Get("LightlessGreen"),
- false when shareLocationOther => UIColors.Get("LightlessBlue"),
+ true when shareLocationToOther => UIColors.Get("LightlessGreen"),
+ true when !shareLocationToOther => UIColors.Get("LightlessBlue"),
_ => UIColors.Get("LightlessYellow"),
};
- if (shareLocation || shareLocationOther)
+ if (shareLocation || shareLocationToOther)
{
currentRightSide -= (_uiSharedService.GetIconSize(shareLocationIcon).X + spacingX);
ImGui.SameLine(currentRightSide);
- using (ImRaii.PushColor(ImGuiCol.Text, shareColor, shareLocation || shareLocationOther))
+ using (ImRaii.PushColor(ImGuiCol.Text, shareColor, shareLocation || shareLocationToOther))
_uiSharedService.IconText(shareLocationIcon);
if (ImGui.IsItemHovered())
{
ImGui.BeginTooltip();
- if (shareLocationOther)
+
+ if (_pair.IsOnline)
{
- var location = _locationShareService.GetUserLocation(_pair.UserPair!.User.UID);
- if (_pair.IsOnline)
+ if (shareLocation)
{
if (!string.IsNullOrEmpty(location))
{
@@ -624,27 +655,31 @@ public class DrawUserPair
}
else
{
- ImGui.TextUnformatted("Location info not updated, reconnect or waiting for zone-changing.");
+ ImGui.TextUnformatted("Location info not updated, reconnect or wait for update.");
}
}
else
{
- ImGui.TextUnformatted("User not onlineㄟ( ▔, ▔ )ㄏ");
+ ImGui.TextUnformatted("NOT Sharing location with you. o(TヘTo)");
}
}
else
{
- ImGui.TextUnformatted("NOT Sharing location with you.(⊙x⊙;)");
+ ImGui.TextUnformatted("User not online. (´・ω・`)?");
}
ImGui.Separator();
- if (shareLocation)
+ if (shareLocationToOther)
{
- ImGui.TextUnformatted("Sharing your location.ヾ(•ω•`)o");
+ ImGui.TextUnformatted("Sharing your location. ヾ(•ω•`)o");
+ if (expireAt != DateTimeOffset.MaxValue)
+ {
+ ImGui.TextUnformatted("Expires at " + expireAt.ToLocalTime().ToString("g"));
+ }
}
else
{
- ImGui.TextUnformatted("NOT sharing your location.(´。_。`)");
+ ImGui.TextUnformatted("NOT sharing your location.  ̄へ ̄");
}
ImGui.EndTooltip();
}
diff --git a/LightlessSync/UI/DataAnalysisUi.cs b/LightlessSync/UI/DataAnalysisUi.cs
index 32245d2..a3061a7 100644
--- a/LightlessSync/UI/DataAnalysisUi.cs
+++ b/LightlessSync/UI/DataAnalysisUi.cs
@@ -2183,7 +2183,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
bool toggleClicked = false;
if (showToggle)
{
- var icon = isCollapsed ? FontAwesomeIcon.ChevronRight : FontAwesomeIcon.ChevronLeft;
+ var icon = !isCollapsed ? FontAwesomeIcon.ChevronRight : FontAwesomeIcon.ChevronLeft;
Vector2 iconSize;
using (_uiSharedService.IconFont.Push())
{
diff --git a/LightlessSync/UI/DrawEntityFactory.cs b/LightlessSync/UI/DrawEntityFactory.cs
index e810a29..e7bcc87 100644
--- a/LightlessSync/UI/DrawEntityFactory.cs
+++ b/LightlessSync/UI/DrawEntityFactory.cs
@@ -28,8 +28,8 @@ public class DrawEntityFactory
private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly LightlessConfigService _configService;
private readonly UiSharedService _uiSharedService;
- private readonly LocationShareService _locationShareService;
private readonly PlayerPerformanceConfigService _playerPerformanceConfigService;
+ private readonly LocationShareService _locationShareService;
private readonly CharaDataManager _charaDataManager;
private readonly SelectTagForPairUi _selectTagForPairUi;
private readonly RenamePairTagUi _renamePairTagUi;
@@ -53,8 +53,8 @@ public class DrawEntityFactory
ServerConfigurationManager serverConfigurationManager,
LightlessConfigService configService,
UiSharedService uiSharedService,
- LocationShareService locationShareService,
PlayerPerformanceConfigService playerPerformanceConfigService,
+ LocationShareService locationShareService,
CharaDataManager charaDataManager,
SelectTagForSyncshellUi selectTagForSyncshellUi,
RenameSyncshellTagUi renameSyncshellTagUi,
@@ -73,8 +73,8 @@ public class DrawEntityFactory
_serverConfigurationManager = serverConfigurationManager;
_configService = configService;
_uiSharedService = uiSharedService;
- _locationShareService = locationShareService;
_playerPerformanceConfigService = playerPerformanceConfigService;
+ _locationShareService = locationShareService;
_charaDataManager = charaDataManager;
_selectTagForSyncshellUi = selectTagForSyncshellUi;
_renameSyncshellTagUi = renameSyncshellTagUi;
diff --git a/LightlessSync/UI/PermissionWindowUI.cs b/LightlessSync/UI/PermissionWindowUI.cs
index 9d88547..5dee098 100644
--- a/LightlessSync/UI/PermissionWindowUI.cs
+++ b/LightlessSync/UI/PermissionWindowUI.cs
@@ -43,7 +43,6 @@ public class PermissionWindowUI : WindowMediatorSubscriberBase
var disableSounds = _ownPermissions.IsDisableSounds();
var disableAnimations = _ownPermissions.IsDisableAnimations();
var disableVfx = _ownPermissions.IsDisableVFX();
- var shareLocation = _ownPermissions.IsSharingLocation();
var style = ImGui.GetStyle();
var indentSize = ImGui.GetFrameHeight() + style.ItemSpacing.X;
@@ -71,7 +70,6 @@ public class PermissionWindowUI : WindowMediatorSubscriberBase
var otherDisableSounds = otherPerms.IsDisableSounds();
var otherDisableAnimations = otherPerms.IsDisableAnimations();
var otherDisableVFX = otherPerms.IsDisableVFX();
- var otherShareLocation = otherPerms.IsSharingLocation();
using (ImRaii.PushIndent(indentSize, false))
{
@@ -126,24 +124,6 @@ public class PermissionWindowUI : WindowMediatorSubscriberBase
ImGui.AlignTextToFramePadding();
ImGui.Text(Pair.UserData.AliasOrUID + " has " + (!otherDisableVFX ? "not " : string.Empty) + "disabled VFX sync with you");
}
-
- if (ImGui.Checkbox("Enable location Sharing", ref shareLocation))
- {
- _ownPermissions.SetShareLocation(shareLocation);
- }
- _uiSharedService.DrawHelpText("Enable location sharing will only effect your side." + UiSharedService.TooltipSeparator
- + "Note: this is NOT bidirectional, you can choose to share even others dont share with you.");
- using (ImRaii.PushIndent(indentSize, false))
- {
- _uiSharedService.BooleanToColoredIcon(shareLocation, false);
- ImGui.SameLine();
- ImGui.AlignTextToFramePadding();
- ImGui.Text((!shareLocation ? "Not" : string.Empty) + "sharing location with " + Pair.UserData.AliasOrUID + " .");
-
-#if DEBUG
- _uiSharedService.BooleanToColoredIcon(otherShareLocation, true);
-#endif
- }
ImGuiHelpers.ScaledDummy(0.5f);
ImGui.Separator();
diff --git a/LightlessSync/WebAPI/Files/FileDownloadManager.cs b/LightlessSync/WebAPI/Files/FileDownloadManager.cs
index 47774f7..2731619 100644
--- a/LightlessSync/WebAPI/Files/FileDownloadManager.cs
+++ b/LightlessSync/WebAPI/Files/FileDownloadManager.cs
@@ -28,6 +28,8 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
private readonly TextureMetadataHelper _textureMetadataHelper;
private readonly ConcurrentDictionary _activeDownloadStreams;
+ private readonly SemaphoreSlim _decompressGate =
+ new(Math.Max(1, Environment.ProcessorCount / 2), Math.Max(1, Environment.ProcessorCount / 2));
private volatile bool _disableDirectDownloads;
private int _consecutiveDirectDownloadFailures;
@@ -522,32 +524,57 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
try
{
+ // sanity check length
if (fileLengthBytes < 0 || fileLengthBytes > int.MaxValue)
throw new InvalidDataException($"Invalid block entry length: {fileLengthBytes}");
+ // safe cast after check
+ var len = checked((int)fileLengthBytes);
+
if (!replacementLookup.TryGetValue(fileHash, out var repl))
{
Logger.LogWarning("{dlName}: No replacement mapping for {fileHash}", downloadLabel, fileHash);
- // still need to skip bytes:
- var skip = checked((int)fileLengthBytes);
- fileBlockStream.Position += skip;
+ fileBlockStream.Seek(len, SeekOrigin.Current);
continue;
}
+ // decompress
var filePath = _fileDbManager.GetCacheFilePath(fileHash, repl.Extension);
+ Logger.LogTrace("{dlName}: Decompressing {file}:{len} => {dest}", downloadLabel, fileHash, fileLengthBytes, filePath);
- Logger.LogDebug("{dlName}: Decompressing {file}:{len} => {dest}", downloadLabel, fileHash, fileLengthBytes, filePath);
-
- var len = checked((int)fileLengthBytes);
+ // read compressed data
var compressed = new byte[len];
-
await ReadExactlyAsync(fileBlockStream, compressed.AsMemory(0, len), ct).ConfigureAwait(false);
- MungeBuffer(compressed);
- var decompressed = LZ4Wrapper.Unwrap(compressed);
+ if (len == 0)
+ {
+ await _fileCompactor.WriteAllBytesAsync(filePath, Array.Empty(), ct).ConfigureAwait(false);
+ PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale);
+ continue;
+ }
- await _fileCompactor.WriteAllBytesAsync(filePath, decompressed, ct).ConfigureAwait(false);
- PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale);
+ MungeBuffer(compressed);
+
+ // limit concurrent decompressions
+ await _decompressGate.WaitAsync(ct).ConfigureAwait(false);
+ try
+ {
+ var sw = System.Diagnostics.Stopwatch.StartNew();
+
+ // decompress
+ var decompressed = LZ4Wrapper.Unwrap(compressed);
+
+ Logger.LogTrace("{dlName}: Unwrap {fileHash} took {ms}ms (compressed {c} bytes, decompressed {d} bytes)",
+ downloadLabel, fileHash, sw.ElapsedMilliseconds, compressed.Length, decompressed?.Length ?? -1);
+
+ // write to file
+ await _fileCompactor.WriteAllBytesAsync(filePath, decompressed, ct).ConfigureAwait(false);
+ PersistFileToStorage(fileHash, filePath, repl.GamePath, skipDownscale);
+ }
+ finally
+ {
+ _decompressGate.Release();
+ }
}
catch (EndOfStreamException)
{
@@ -605,20 +632,16 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
.. await FilesGetSizes(hashes, ct).ConfigureAwait(false),
];
- Logger.LogDebug("Files with size 0 or less: {files}",
- string.Join(", ", downloadFileInfoFromService.Where(f => f.Size <= 0).Select(f => f.Hash)));
-
foreach (var dto in downloadFileInfoFromService.Where(c => c.IsForbidden))
{
if (!_orchestrator.ForbiddenTransfers.Exists(f => string.Equals(f.Hash, dto.Hash, StringComparison.Ordinal)))
_orchestrator.ForbiddenTransfers.Add(new DownloadFileTransfer(dto));
}
- CurrentDownloads = downloadFileInfoFromService
+ CurrentDownloads = [.. downloadFileInfoFromService
.Distinct()
.Select(d => new DownloadFileTransfer(d))
- .Where(d => d.CanBeTransferred)
- .ToList();
+ .Where(d => d.CanBeTransferred)];
return CurrentDownloads;
}
diff --git a/LightlessSync/WebAPI/SignalR/ApIController.Functions.Users.cs b/LightlessSync/WebAPI/SignalR/ApIController.Functions.Users.cs
index 6cd704f..37b91f9 100644
--- a/LightlessSync/WebAPI/SignalR/ApIController.Functions.Users.cs
+++ b/LightlessSync/WebAPI/SignalR/ApIController.Functions.Users.cs
@@ -206,11 +206,15 @@ public partial class ApiController
if (!IsConnected) return;
await _lightlessHub!.SendAsync(nameof(UpdateLocation), locationDto, offline).ConfigureAwait(false);
}
-
- public async Task> RequestAllLocationInfo()
+ public async Task<(List, List)> RequestAllLocationInfo()
{
- if (!IsConnected) return [];
- return await _lightlessHub!.InvokeAsync>(nameof(RequestAllLocationInfo)).ConfigureAwait(false);
+ if (!IsConnected) return ([],[]);
+ return await _lightlessHub!.InvokeAsync<(List, List)>(nameof(RequestAllLocationInfo)).ConfigureAwait(false);
+ }
+ public async Task ToggleLocationSharing(LocationSharingToggleDto dto)
+ {
+ if (!IsConnected) return false;
+ return await _lightlessHub!.InvokeAsync(nameof(ToggleLocationSharing), dto).ConfigureAwait(false);
}
}
#pragma warning restore MA0040
\ No newline at end of file
diff --git a/LightlessSync/WebAPI/SignalR/ApiController.Functions.Callbacks.cs b/LightlessSync/WebAPI/SignalR/ApiController.Functions.Callbacks.cs
index 01abc88..d0a05f7 100644
--- a/LightlessSync/WebAPI/SignalR/ApiController.Functions.Callbacks.cs
+++ b/LightlessSync/WebAPI/SignalR/ApiController.Functions.Callbacks.cs
@@ -260,10 +260,10 @@ public partial class ApiController
return Task.CompletedTask;
}
- public Task Client_SendLocationToClient(LocationDto locationDto)
+ public Task Client_SendLocationToClient(LocationDto locationDto, DateTimeOffset expireAt)
{
- Logger.LogDebug($"{nameof(Client_SendLocationToClient)}: {locationDto.user}");
- ExecuteSafely(() => Mediator.Publish(new LocationMessage(locationDto.user.UID, locationDto.location)));
+ Logger.LogDebug($"{nameof(Client_SendLocationToClient)}: {locationDto.User} {expireAt}");
+ ExecuteSafely(() => Mediator.Publish(new LocationSharingMessage(locationDto.User, locationDto.Location, expireAt)));
return Task.CompletedTask;
}
@@ -447,8 +447,8 @@ public partial class ApiController
if (_initialized) return;
_lightlessHub!.On(nameof(Client_GposeLobbyPushWorldData), act);
}
-
- public void OnReciveLocation(Action act)
+
+ public void OnReceiveLocation(Action act)
{
if (_initialized) return;
_lightlessHub!.On(nameof(Client_SendLocationToClient), act);
diff --git a/LightlessSync/WebAPI/SignalR/ApiController.cs b/LightlessSync/WebAPI/SignalR/ApiController.cs
index a814758..45705bf 100644
--- a/LightlessSync/WebAPI/SignalR/ApiController.cs
+++ b/LightlessSync/WebAPI/SignalR/ApiController.cs
@@ -606,8 +606,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IL
OnGposeLobbyPushCharacterData((dto) => _ = Client_GposeLobbyPushCharacterData(dto));
OnGposeLobbyPushPoseData((dto, data) => _ = Client_GposeLobbyPushPoseData(dto, data));
OnGposeLobbyPushWorldData((dto, data) => _ = Client_GposeLobbyPushWorldData(dto, data));
- OnReciveLocation(dto => _ = Client_SendLocationToClient(dto));
-
+ OnReceiveLocation((dto, time) => _ = Client_SendLocationToClient(dto, time));
_healthCheckTokenSource?.Cancel();
_healthCheckTokenSource?.Dispose();
@@ -776,5 +775,6 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IL
ServerState = state;
}
+
}
#pragma warning restore MA0040