service cleanups, containing logic directly now
This commit is contained in:
1086
CONTRIBUTING.md
Normal file
1086
CONTRIBUTING.md
Normal file
File diff suppressed because it is too large
Load Diff
111
DEVELOPMENT.md
Normal file
111
DEVELOPMENT.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Development Setup for macOS
|
||||
|
||||
This document explains how to set up the Lightless Sync development environment on macOS.
|
||||
|
||||
## Problem: "Cannot resolve symbol 'Dalamud'"
|
||||
|
||||
When developing Dalamud plugins on macOS, you may encounter the error:
|
||||
```
|
||||
Cannot resolve symbol 'Dalamud'
|
||||
Dalamud.NET.Sdk: Dalamud installation not found at /Users/.../Library/Application Support/XIV on Mac/dalamud/Hooks/dev/
|
||||
```
|
||||
|
||||
This happens because the Dalamud.NET.Sdk expects to find Dalamud assemblies at a specific path, but they don't exist if you don't have XIV on Mac or Dalamud installed.
|
||||
|
||||
## Solution
|
||||
|
||||
### Automated Setup (Recommended)
|
||||
|
||||
Run the setup script to download the required Dalamud assemblies:
|
||||
|
||||
```bash
|
||||
./setup-dalamud.sh
|
||||
```
|
||||
|
||||
This script will:
|
||||
1. Create a development directory at `~/.dalamud/dev`
|
||||
2. Download the latest Dalamud assemblies from the official distribution
|
||||
3. Extract them to the development directory
|
||||
|
||||
### Manual Setup
|
||||
|
||||
If you prefer to set up manually:
|
||||
|
||||
1. **Create the Dalamud directory:**
|
||||
```bash
|
||||
mkdir -p ~/.dalamud/dev
|
||||
```
|
||||
|
||||
2. **Download Dalamud assemblies:**
|
||||
```bash
|
||||
curl -L -o /tmp/dalamud.zip https://goatcorp.github.io/dalamud-distrib/latest.zip
|
||||
unzip /tmp/dalamud.zip -d ~/.dalamud/dev
|
||||
```
|
||||
|
||||
3. **Set the DALAMUD_HOME environment variable (optional):**
|
||||
```bash
|
||||
export DALAMUD_HOME="$HOME/.dalamud/dev"
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
The project includes a `Directory.Build.props` file that automatically configures the `DALAMUD_HOME` path to use `~/.dalamud/dev` if it exists. This overrides the default XIV on Mac path.
|
||||
|
||||
The Dalamud.NET.Sdk will then use this path to find the required assemblies for compilation and IntelliSense.
|
||||
|
||||
## Building the Project
|
||||
|
||||
After setup, you can build the project normally:
|
||||
|
||||
```bash
|
||||
dotnet restore
|
||||
dotnet build
|
||||
```
|
||||
|
||||
## IDE Configuration
|
||||
|
||||
### JetBrains Rider / IntelliJ IDEA
|
||||
|
||||
After running the setup script, you may need to:
|
||||
1. Invalidate caches and restart: **File → Invalidate Caches → Invalidate and Restart**
|
||||
2. Reload the solution: **Right-click on solution → Reload All Projects**
|
||||
|
||||
The IDE should now resolve all Dalamud symbols correctly.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Build still fails with "Dalamud installation not found"
|
||||
|
||||
1. Verify the assemblies were downloaded:
|
||||
```bash
|
||||
ls -la ~/.dalamud/dev/Dalamud.dll
|
||||
```
|
||||
|
||||
2. Check that `Directory.Build.props` exists in the project root
|
||||
|
||||
3. Try cleaning and rebuilding:
|
||||
```bash
|
||||
dotnet clean
|
||||
dotnet build
|
||||
```
|
||||
|
||||
### IDE still shows "Cannot resolve symbol 'Dalamud'"
|
||||
|
||||
1. Ensure the build succeeds first (run `dotnet build`)
|
||||
2. Restart your IDE
|
||||
3. Try invalidating caches (Rider/IntelliJ)
|
||||
4. Check that the project references are loaded correctly
|
||||
|
||||
## Files Modified
|
||||
|
||||
- `Directory.Build.props` - Configures DALAMUD_HOME path
|
||||
- `LightlessSync/LightlessSync.csproj` - Removed duplicate DalamudPackager reference
|
||||
- `PenumbraAPI/Penumbra.Api.csproj` - Added DalamudLibPath configuration
|
||||
- `setup-dalamud.sh` - Setup script to download Dalamud assemblies
|
||||
|
||||
## Additional Notes
|
||||
|
||||
- The Dalamud assemblies are only needed for development/compilation
|
||||
- You don't need a running FFXIV or XIV on Mac installation to develop plugins
|
||||
- The assemblies are downloaded from the official Dalamud distribution
|
||||
- Updates to Dalamud may require re-running the setup script
|
||||
@@ -269,8 +269,7 @@ public sealed class Plugin : IDalamudPlugin
|
||||
s.GetRequiredService<WindowSystem>(), s.GetServices<WindowMediatorSubscriberBase>(),
|
||||
s.GetRequiredService<UiFactory>(),
|
||||
s.GetRequiredService<FileDialogManager>(),
|
||||
s.GetRequiredService<LightlessMediator>(),
|
||||
s.GetRequiredService<NotificationService>()));
|
||||
s.GetRequiredService<LightlessMediator>()));
|
||||
collection.AddScoped((s) => new CommandManagerService(commandManager, s.GetRequiredService<PerformanceCollectorService>(),
|
||||
s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<CacheMonitor>(), s.GetRequiredService<ApiController>(),
|
||||
s.GetRequiredService<LightlessMediator>(), s.GetRequiredService<LightlessConfigService>()));
|
||||
|
||||
@@ -108,7 +108,9 @@ public record OpenCharaDataHubWithFilterMessage(UserData UserData) : MessageBase
|
||||
public record EnableBroadcastMessage(string HashedCid, bool Enabled) : MessageBase;
|
||||
public record BroadcastStatusChangedMessage(bool Enabled, TimeSpan? Ttl) : MessageBase;
|
||||
public record SyncshellBroadcastsUpdatedMessage : MessageBase;
|
||||
public record PairRequestReceivedMessage(string HashedCid, string Message) : MessageBase;
|
||||
public record PairRequestsUpdatedMessage : MessageBase;
|
||||
public record PairDownloadStatusMessage(List<(string PlayerName, float Progress, string Status)> DownloadStatus, int QueueWaiting) : MessageBase;
|
||||
public record VisibilityChange : MessageBase;
|
||||
#pragma warning restore S2094
|
||||
#pragma warning restore MA0048 // File name must match type name
|
||||
@@ -45,7 +45,9 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
Mediator.Subscribe<NotificationMessage>(this, HandleNotificationMessage);
|
||||
Mediator.Subscribe<PairRequestReceivedMessage>(this, HandlePairRequestReceived);
|
||||
Mediator.Subscribe<PairRequestsUpdatedMessage>(this, HandlePairRequestsUpdated);
|
||||
Mediator.Subscribe<PairDownloadStatusMessage>(this, HandlePairDownloadStatus);
|
||||
Mediator.Subscribe<PerformanceNotificationMessage>(this, HandlePerformanceNotification);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
@@ -293,33 +295,8 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
return actions;
|
||||
}
|
||||
|
||||
public void ShowPairDownloadNotification(List<(string playerName, float progress, string status)> downloadStatus,
|
||||
int queueWaiting = 0)
|
||||
{
|
||||
var userDownloads = downloadStatus.Where(x => x.playerName != "Pair Queue").ToList();
|
||||
var totalProgress = userDownloads.Count > 0 ? userDownloads.Average(x => x.progress) : 0f;
|
||||
var message = BuildPairDownloadMessage(userDownloads, queueWaiting);
|
||||
|
||||
var notification = new LightlessNotification
|
||||
{
|
||||
Id = "pair_download_progress",
|
||||
Title = "Downloading Pair Data",
|
||||
Message = message,
|
||||
Type = NotificationType.Download,
|
||||
Duration = TimeSpan.FromSeconds(_configService.Current.DownloadNotificationDurationSeconds),
|
||||
ShowProgress = true,
|
||||
Progress = totalProgress
|
||||
};
|
||||
|
||||
Mediator.Publish(new LightlessNotificationMessage(notification));
|
||||
|
||||
if (AreAllDownloadsCompleted(userDownloads))
|
||||
{
|
||||
DismissPairDownloadNotification();
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildPairDownloadMessage(List<(string playerName, float progress, string status)> userDownloads,
|
||||
private string BuildPairDownloadMessage(List<(string PlayerName, float Progress, string Status)> userDownloads,
|
||||
int queueWaiting)
|
||||
{
|
||||
var messageParts = new List<string>();
|
||||
@@ -331,7 +308,7 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
|
||||
if (userDownloads.Count > 0)
|
||||
{
|
||||
var completedCount = userDownloads.Count(x => x.progress >= 1.0f);
|
||||
var completedCount = userDownloads.Count(x => x.Progress >= 1.0f);
|
||||
messageParts.Add($"Progress: {completedCount}/{userDownloads.Count} completed");
|
||||
}
|
||||
|
||||
@@ -344,29 +321,29 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
return string.Join("\n", messageParts);
|
||||
}
|
||||
|
||||
private string BuildActiveDownloadLines(List<(string playerName, float progress, string status)> userDownloads)
|
||||
private string BuildActiveDownloadLines(List<(string PlayerName, float Progress, string Status)> userDownloads)
|
||||
{
|
||||
var activeDownloads = userDownloads
|
||||
.Where(x => x.progress < 1.0f)
|
||||
.Where(x => x.Progress < 1.0f)
|
||||
.Take(_configService.Current.MaxConcurrentPairApplications);
|
||||
|
||||
if (!activeDownloads.Any()) return string.Empty;
|
||||
|
||||
return string.Join("\n", activeDownloads.Select(x => $"• {x.playerName}: {FormatDownloadStatus(x)}"));
|
||||
return string.Join("\n", activeDownloads.Select(x => $"• {x.PlayerName}: {FormatDownloadStatus(x)}"));
|
||||
}
|
||||
|
||||
private string FormatDownloadStatus((string playerName, float progress, string status) download) =>
|
||||
download.status switch
|
||||
private string FormatDownloadStatus((string PlayerName, float Progress, string Status) download) =>
|
||||
download.Status switch
|
||||
{
|
||||
"downloading" => $"{download.progress:P0}",
|
||||
"downloading" => $"{download.Progress:P0}",
|
||||
"decompressing" => "decompressing",
|
||||
"queued" => "queued",
|
||||
"waiting" => "waiting for slot",
|
||||
_ => download.status
|
||||
_ => download.Status
|
||||
};
|
||||
|
||||
private bool AreAllDownloadsCompleted(List<(string playerName, float progress, string status)> userDownloads) =>
|
||||
userDownloads.Any() && userDownloads.All(x => x.progress >= 1.0f);
|
||||
private bool AreAllDownloadsCompleted(List<(string PlayerName, float Progress, string Status)> userDownloads) =>
|
||||
userDownloads.Any() && userDownloads.All(x => x.Progress >= 1.0f);
|
||||
|
||||
public void DismissPairDownloadNotification() =>
|
||||
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
||||
@@ -581,12 +558,25 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
_chatGui.Print(se.BuiltString);
|
||||
}
|
||||
|
||||
private void HandlePairRequestReceived(PairRequestReceivedMessage msg)
|
||||
{
|
||||
var request = _pairRequestService.RegisterIncomingRequest(msg.HashedCid, msg.Message);
|
||||
var senderName = string.IsNullOrEmpty(request.DisplayName) ? "Unknown User" : request.DisplayName;
|
||||
|
||||
_shownPairRequestNotifications.Add(request.HashedCid);
|
||||
ShowPairRequestNotification(
|
||||
senderName,
|
||||
request.HashedCid,
|
||||
onAccept: () => _pairRequestService.AcceptPairRequest(request.HashedCid, senderName),
|
||||
onDecline: () => _pairRequestService.DeclinePairRequest(request.HashedCid));
|
||||
}
|
||||
|
||||
private void HandlePairRequestsUpdated(PairRequestsUpdatedMessage _)
|
||||
{
|
||||
var activeRequests = _pairRequestService.GetActiveRequests();
|
||||
var activeRequestIds = activeRequests.Select(r => r.HashedCid).ToHashSet();
|
||||
|
||||
// Dismiss notifications for requests that are no longer active
|
||||
// Dismiss notifications for requests that are no longer active (expired)
|
||||
var notificationsToRemove = _shownPairRequestNotifications
|
||||
.Where(hashedCid => !activeRequestIds.Contains(hashedCid))
|
||||
.ToList();
|
||||
@@ -597,17 +587,30 @@ public class NotificationService : DisposableMediatorSubscriberBase, IHostedServ
|
||||
Mediator.Publish(new LightlessNotificationDismissMessage(notificationId));
|
||||
_shownPairRequestNotifications.Remove(hashedCid);
|
||||
}
|
||||
}
|
||||
|
||||
// Show/update notifications for all active requests
|
||||
foreach (var request in activeRequests)
|
||||
private void HandlePairDownloadStatus(PairDownloadStatusMessage msg)
|
||||
{
|
||||
var userDownloads = msg.DownloadStatus.Where(x => x.PlayerName != "Pair Queue").ToList();
|
||||
var totalProgress = userDownloads.Count > 0 ? userDownloads.Average(x => x.Progress) : 0f;
|
||||
var message = BuildPairDownloadMessage(userDownloads, msg.QueueWaiting);
|
||||
|
||||
var notification = new LightlessNotification
|
||||
{
|
||||
_shownPairRequestNotifications.Add(request.HashedCid);
|
||||
ShowPairRequestNotification(
|
||||
request.DisplayName,
|
||||
request.HashedCid,
|
||||
() => _pairRequestService.AcceptPairRequest(request.HashedCid, request.DisplayName),
|
||||
() => _pairRequestService.DeclinePairRequest(request.HashedCid)
|
||||
);
|
||||
Id = "pair_download_progress",
|
||||
Title = "Downloading Pair Data",
|
||||
Message = message,
|
||||
Type = NotificationType.Download,
|
||||
Duration = TimeSpan.FromSeconds(_configService.Current.DownloadNotificationDurationSeconds),
|
||||
ShowProgress = true,
|
||||
Progress = totalProgress
|
||||
};
|
||||
|
||||
Mediator.Publish(new LightlessNotificationMessage(notification));
|
||||
|
||||
if (AreAllDownloadsCompleted(userDownloads))
|
||||
{
|
||||
Mediator.Publish(new LightlessNotificationDismissMessage("pair_download_progress"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,12 @@ public sealed class PairRequestService : DisposableMediatorSubscriberBase
|
||||
|
||||
private static readonly TimeSpan Expiration = TimeSpan.FromMinutes(5);
|
||||
|
||||
public PairRequestService(ILogger<PairRequestService> logger, LightlessMediator mediator, DalamudUtilService dalamudUtil, PairManager pairManager, Lazy<WebAPI.ApiController> apiController)
|
||||
public PairRequestService(
|
||||
ILogger<PairRequestService> logger,
|
||||
LightlessMediator mediator,
|
||||
DalamudUtilService dalamudUtil,
|
||||
PairManager pairManager,
|
||||
Lazy<WebAPI.ApiController> apiController)
|
||||
: base(logger, mediator)
|
||||
{
|
||||
_dalamudUtil = dalamudUtil;
|
||||
|
||||
@@ -23,8 +23,7 @@ public sealed class UiService : DisposableMediatorSubscriberBase
|
||||
LightlessConfigService lightlessConfigService, WindowSystem windowSystem,
|
||||
IEnumerable<WindowMediatorSubscriberBase> windows,
|
||||
UiFactory uiFactory, FileDialogManager fileDialogManager,
|
||||
LightlessMediator lightlessMediator,
|
||||
NotificationService notificationService) : base(logger, lightlessMediator)
|
||||
LightlessMediator lightlessMediator) : base(logger, lightlessMediator)
|
||||
{
|
||||
_logger = logger;
|
||||
_logger.LogTrace("Creating {type}", GetType().Name);
|
||||
|
||||
@@ -22,13 +22,12 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
private readonly UiSharedService _uiShared;
|
||||
private readonly PairProcessingLimiter _pairProcessingLimiter;
|
||||
private readonly ConcurrentDictionary<GameObjectHandler, bool> _uploadingPlayers = new();
|
||||
private readonly NotificationService _notificationService;
|
||||
private bool _notificationDismissed = true;
|
||||
private int _lastDownloadStateHash = 0;
|
||||
|
||||
public DownloadUi(ILogger<DownloadUi> logger, DalamudUtilService dalamudUtilService, LightlessConfigService configService,
|
||||
PairProcessingLimiter pairProcessingLimiter, FileUploadManager fileTransferManager, LightlessMediator mediator, UiSharedService uiShared,
|
||||
PerformanceCollectorService performanceCollectorService, NotificationService notificationService)
|
||||
PerformanceCollectorService performanceCollectorService)
|
||||
: base(logger, mediator, "Lightless Sync Downloads", performanceCollectorService)
|
||||
{
|
||||
_dalamudUtilService = dalamudUtilService;
|
||||
@@ -36,7 +35,6 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
_pairProcessingLimiter = pairProcessingLimiter;
|
||||
_fileTransferManager = fileTransferManager;
|
||||
_uiShared = uiShared;
|
||||
_notificationService = notificationService;
|
||||
|
||||
SizeConstraints = new WindowSizeConstraints()
|
||||
{
|
||||
@@ -359,7 +357,7 @@ public class DownloadUi : WindowMediatorSubscriberBase
|
||||
_lastDownloadStateHash = currentHash;
|
||||
if (downloadStatus.Count > 0 || queueWaiting > 0)
|
||||
{
|
||||
_notificationService.ShowPairDownloadNotification(downloadStatus, queueWaiting);
|
||||
Mediator.Publish(new PairDownloadStatusMessage(downloadStatus, queueWaiting));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
private readonly IProgress<(int, int, FileCacheEntity)> _validationProgress;
|
||||
private readonly NameplateService _nameplateService;
|
||||
private readonly NameplateHandler _nameplateHandler;
|
||||
private readonly NotificationService _lightlessNotificationService;
|
||||
private (int, int, FileCacheEntity) _currentProgress;
|
||||
private bool _deleteAccountPopupModalShown = false;
|
||||
private bool _deleteFilesPopupModalShown = false;
|
||||
@@ -107,8 +106,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
IpcManager ipcManager, CacheMonitor cacheMonitor,
|
||||
DalamudUtilService dalamudUtilService, HttpClient httpClient,
|
||||
NameplateService nameplateService,
|
||||
NameplateHandler nameplateHandler,
|
||||
NotificationService lightlessNotificationService) : base(logger, mediator, "Lightless Sync Settings",
|
||||
NameplateHandler nameplateHandler) : base(logger, mediator, "Lightless Sync Settings",
|
||||
performanceCollector)
|
||||
{
|
||||
_configService = configService;
|
||||
@@ -130,7 +128,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
_uiShared = uiShared;
|
||||
_nameplateService = nameplateService;
|
||||
_nameplateHandler = nameplateHandler;
|
||||
_lightlessNotificationService = lightlessNotificationService;
|
||||
AllowClickthrough = false;
|
||||
AllowPinning = true;
|
||||
_validationProgress = new Progress<(int, int, FileCacheEntity)>(v => _currentProgress = v);
|
||||
@@ -3616,20 +3613,7 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_pair", new Vector2(availableWidth, 0)))
|
||||
{
|
||||
_lightlessNotificationService.ShowPairRequestNotification(
|
||||
"Test User",
|
||||
"test-uid-123",
|
||||
() =>
|
||||
{
|
||||
Mediator.Publish(new NotificationMessage("Accepted", "You accepted the test pair request.",
|
||||
NotificationType.Info));
|
||||
},
|
||||
() =>
|
||||
{
|
||||
Mediator.Publish(new NotificationMessage("Declined", "You declined the test pair request.",
|
||||
NotificationType.Info));
|
||||
}
|
||||
);
|
||||
Mediator.Publish(new PairRequestReceivedMessage("test-uid-123", "Test User wants to pair with you."));
|
||||
}
|
||||
}
|
||||
UiSharedService.AttachToolTip("Test pair request notification");
|
||||
@@ -3652,15 +3636,14 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
{
|
||||
if (ImGui.Button($"{FontAwesomeIcon.Play.ToIconString()}##test_download", new Vector2(availableWidth, 0)))
|
||||
{
|
||||
_lightlessNotificationService.ShowPairDownloadNotification(
|
||||
new List<(string playerName, float progress, string status)>
|
||||
{
|
||||
Mediator.Publish(new PairDownloadStatusMessage(
|
||||
[
|
||||
("Player One", 0.35f, "downloading"),
|
||||
("Player Two", 0.75f, "downloading"),
|
||||
("Player Three", 1.0f, "downloading")
|
||||
},
|
||||
queueWaiting: 2
|
||||
);
|
||||
],
|
||||
2
|
||||
));
|
||||
}
|
||||
}
|
||||
UiSharedService.AttachToolTip("Test download progress notification");
|
||||
|
||||
@@ -107,17 +107,17 @@ public partial class ApiController
|
||||
}
|
||||
public Task Client_ReceiveBroadcastPairRequest(UserPairNotificationDto dto)
|
||||
{
|
||||
if (dto == null)
|
||||
Logger.LogDebug("Client_ReceiveBroadcastPairRequest: {dto}", dto);
|
||||
|
||||
if (dto is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
var request = _pairRequestService.RegisterIncomingRequest(dto.myHashedCid, dto.message ?? string.Empty);
|
||||
var senderName = string.IsNullOrEmpty(request.DisplayName) ? "Unknown User" : request.DisplayName;
|
||||
|
||||
_lightlessNotificationService.ShowPairRequestNotification(
|
||||
senderName,
|
||||
request.HashedCid,
|
||||
onAccept: () => _pairRequestService.AcceptPairRequest(request.HashedCid, senderName),
|
||||
onDecline: () => _pairRequestService.DeclinePairRequest(request.HashedCid));
|
||||
ExecuteSafely(() =>
|
||||
{
|
||||
Mediator.Publish(new PairRequestReceivedMessage(dto.myHashedCid, dto.message ?? string.Empty));
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user