add events
This commit is contained in:
@@ -89,6 +89,8 @@ public class MarePlugin : MediatorSubscriberBase, IHostedService
|
|||||||
{
|
{
|
||||||
var version = Assembly.GetExecutingAssembly().GetName().Version!;
|
var version = Assembly.GetExecutingAssembly().GetName().Version!;
|
||||||
Logger.LogInformation("Launching {name} {major}.{minor}.{build}-lop{rev}", "Loporrit Sync", version.Major, version.Minor, version.Build, version.Revision);
|
Logger.LogInformation("Launching {name} {major}.{minor}.{build}-lop{rev}", "Loporrit Sync", version.Major, version.Minor, version.Build, version.Revision);
|
||||||
|
Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(MarePlugin), Services.Events.EventSeverity.Informational,
|
||||||
|
$"Starting Loporrit Sync {version.Major}.{version.Minor}.{version.Build}-lop{version.Revision}")));
|
||||||
|
|
||||||
Mediator.Subscribe<SwitchToMainUiMessage>(this, (msg) => _ = Task.Run(WaitForPlayerAndLaunchCharacterManager));
|
Mediator.Subscribe<SwitchToMainUiMessage>(this, (msg) => _ = Task.Run(WaitForPlayerAndLaunchCharacterManager));
|
||||||
Mediator.Subscribe<DalamudLoginMessage>(this, (_) => DalamudUtilOnLogIn());
|
Mediator.Subscribe<DalamudLoginMessage>(this, (_) => DalamudUtilOnLogIn());
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using MareSynchronos.Interop;
|
|||||||
using MareSynchronos.PlayerData.Factories;
|
using MareSynchronos.PlayerData.Factories;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
|
using MareSynchronos.Services.Events;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Services.ServerConfiguration;
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
using MareSynchronos.Utils;
|
using MareSynchronos.Utils;
|
||||||
@@ -111,6 +112,9 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
if (_isVisible != value)
|
if (_isVisible != value)
|
||||||
{
|
{
|
||||||
_isVisible = value;
|
_isVisible = value;
|
||||||
|
string text = "User Visibility Changed, now: " + (_isVisible ? "Is Visible" : "Is not Visible");
|
||||||
|
Mediator.Publish(new EventMessage(new Event(PlayerName, OnlineUser.User, nameof(PairHandler),
|
||||||
|
EventSeverity.Informational, text)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,6 +130,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
{
|
{
|
||||||
if (_dalamudUtil.IsInCombat)
|
if (_dalamudUtil.IsInCombat)
|
||||||
{
|
{
|
||||||
|
Mediator.Publish(new EventMessage(new Event(PlayerName, OnlineUser.User, nameof(PairHandler), EventSeverity.Warning,
|
||||||
|
"Cannot apply character data: you are in combat, deferring application")));
|
||||||
Logger.LogDebug("[BASE-{appBase}] Received data but player is in combat", applicationBase);
|
Logger.LogDebug("[BASE-{appBase}] Received data but player is in combat", applicationBase);
|
||||||
_dataReceivedInCombat = new(applicationBase, characterData, forceApplyCustomization);
|
_dataReceivedInCombat = new(applicationBase, characterData, forceApplyCustomization);
|
||||||
SetUploading(isUploading: false);
|
SetUploading(isUploading: false);
|
||||||
@@ -134,6 +140,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
if (_charaHandler == null || (PlayerCharacter == IntPtr.Zero))
|
if (_charaHandler == null || (PlayerCharacter == IntPtr.Zero))
|
||||||
{
|
{
|
||||||
|
Mediator.Publish(new EventMessage(new Event(PlayerName, OnlineUser.User, nameof(PairHandler), EventSeverity.Warning,
|
||||||
|
"Cannot apply character data: Receiving Player is in an invalid state, deferring application")));
|
||||||
Logger.LogDebug("[BASE-{appBase}] Received data but player was in invalid state, charaHandlerIsNull: {charaIsNull}, playerPointerIsNull: {ptrIsNull}",
|
Logger.LogDebug("[BASE-{appBase}] Received data but player was in invalid state, charaHandlerIsNull: {charaIsNull}, playerPointerIsNull: {ptrIsNull}",
|
||||||
applicationBase, _charaHandler == null, PlayerCharacter == IntPtr.Zero);
|
applicationBase, _charaHandler == null, PlayerCharacter == IntPtr.Zero);
|
||||||
var hasDiffMods = characterData.CheckUpdatedData(applicationBase, _cachedData, Logger,
|
var hasDiffMods = characterData.CheckUpdatedData(applicationBase, _cachedData, Logger,
|
||||||
@@ -154,10 +162,15 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
if (_dalamudUtil.IsInCutscene || _dalamudUtil.IsInGpose || !_ipcManager.CheckPenumbraApi() || !_ipcManager.CheckGlamourerApi())
|
if (_dalamudUtil.IsInCutscene || _dalamudUtil.IsInGpose || !_ipcManager.CheckPenumbraApi() || !_ipcManager.CheckGlamourerApi())
|
||||||
{
|
{
|
||||||
|
Mediator.Publish(new EventMessage(new Event(PlayerName, OnlineUser.User, nameof(PairHandler), EventSeverity.Warning,
|
||||||
|
"Cannot apply character data: you are in GPose, a Cutscene or Penumbra/Glamourer is not available")));
|
||||||
Logger.LogInformation("[BASE-{appbase}] Application of data for {player} while in cutscene/gpose or Penumbra/Glamourer unavailable, returning", applicationBase, this);
|
Logger.LogInformation("[BASE-{appbase}] Application of data for {player} while in cutscene/gpose or Penumbra/Glamourer unavailable, returning", applicationBase, this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mediator.Publish(new EventMessage(new Event(PlayerName, OnlineUser.User, nameof(PairHandler), EventSeverity.Informational,
|
||||||
|
"Applying Character Data")));
|
||||||
|
|
||||||
_forceApplyMods |= forceApplyCustomization;
|
_forceApplyMods |= forceApplyCustomization;
|
||||||
|
|
||||||
var charaDataToUpdate = characterData.CheckUpdatedData(applicationBase, _cachedData?.DeepClone() ?? new(), Logger, this, forceApplyCustomization, _forceApplyMods);
|
var charaDataToUpdate = characterData.CheckUpdatedData(applicationBase, _cachedData?.DeepClone() ?? new(), Logger, this, forceApplyCustomization, _forceApplyMods);
|
||||||
@@ -217,6 +230,11 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
_charaHandler?.Dispose();
|
_charaHandler?.Dispose();
|
||||||
_charaHandler = null;
|
_charaHandler = null;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(name))
|
||||||
|
{
|
||||||
|
Mediator.Publish(new EventMessage(new Event(name, OnlineUser.User, nameof(PairHandler), EventSeverity.Informational, "Disposing User")));
|
||||||
|
}
|
||||||
|
|
||||||
if (_lifetime.IsCancellationRequested) return;
|
if (_lifetime.IsCancellationRequested) return;
|
||||||
|
|
||||||
Logger.LogDebug("[{applicationId}] Removing Temp Collection for {name} ({user})", applicationId, name, OnlineUser);
|
Logger.LogDebug("[{applicationId}] Removing Temp Collection for {name} ({user})", applicationId, name, OnlineUser);
|
||||||
@@ -365,6 +383,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
Logger.LogDebug("[BASE-{appBase}] Downloading missing files for player {name}, {kind}", applicationBase, PlayerName, updatedData);
|
Logger.LogDebug("[BASE-{appBase}] Downloading missing files for player {name}, {kind}", applicationBase, PlayerName, updatedData);
|
||||||
if (toDownloadReplacements.Any())
|
if (toDownloadReplacements.Any())
|
||||||
{
|
{
|
||||||
|
Mediator.Publish(new EventMessage(new Event(PlayerName, OnlineUser.User, nameof(PairHandler), EventSeverity.Informational,
|
||||||
|
$"Starting download for {toDownloadReplacements.Count} files")));
|
||||||
await _downloadManager.DownloadFiles(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
|
await _downloadManager.DownloadFiles(_charaHandler!, toDownloadReplacements, downloadToken).ConfigureAwait(false);
|
||||||
_downloadManager.CancelDownload();
|
_downloadManager.CancelDownload();
|
||||||
}
|
}
|
||||||
@@ -469,6 +489,8 @@ public sealed class PairHandler : DisposableMediatorSubscriberBase
|
|||||||
Logger.LogDebug("One-Time Initializing {this}", this);
|
Logger.LogDebug("One-Time Initializing {this}", this);
|
||||||
Initialize(pc.Name);
|
Initialize(pc.Name);
|
||||||
Logger.LogDebug("One-Time Initialized {this}", this);
|
Logger.LogDebug("One-Time Initialized {this}", this);
|
||||||
|
Mediator.Publish(new EventMessage(new Event(PlayerName, OnlineUser.User, nameof(PairHandler), EventSeverity.Informational,
|
||||||
|
$"Initializing User For Character {pc.Name}")));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_charaHandler?.Address != nint.Zero && !IsVisible)
|
if (_charaHandler?.Address != nint.Zero && !IsVisible)
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ public class Pair
|
|||||||
}
|
}
|
||||||
|
|
||||||
CachedPlayer?.Dispose();
|
CachedPlayer?.Dispose();
|
||||||
CachedPlayer = _cachedPlayerFactory.Create(_onlineUserIdentDto!);
|
CachedPlayer = _cachedPlayerFactory.Create(new OnlineUserIdentDto(UserData, _onlineUserIdentDto!.Ident));
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ using MareSynchronos.API.Dto.Group;
|
|||||||
using MareSynchronos.API.Dto.User;
|
using MareSynchronos.API.Dto.User;
|
||||||
using MareSynchronos.MareConfiguration;
|
using MareSynchronos.MareConfiguration;
|
||||||
using MareSynchronos.PlayerData.Factories;
|
using MareSynchronos.PlayerData.Factories;
|
||||||
|
using MareSynchronos.Services.Events;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@@ -138,8 +139,9 @@ public sealed class PairManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
public void ReceiveCharaData(OnlineUserCharaDataDto dto)
|
public void ReceiveCharaData(OnlineUserCharaDataDto dto)
|
||||||
{
|
{
|
||||||
if (!_allClientPairs.ContainsKey(dto.User)) throw new InvalidOperationException("No user found for " + dto.User);
|
if (!_allClientPairs.TryGetValue(dto.User, out var pair)) throw new InvalidOperationException("No user found for " + dto.User);
|
||||||
|
|
||||||
|
Mediator.Publish(new EventMessage(new Event(pair.UserData, nameof(PairManager), EventSeverity.Informational, "Received Character Data")));
|
||||||
_allClientPairs[dto.User].ApplyData(dto);
|
_allClientPairs[dto.User].ApplyData(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using MareSynchronos.PlayerData.Factories;
|
|||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
using MareSynchronos.PlayerData.Services;
|
using MareSynchronos.PlayerData.Services;
|
||||||
using MareSynchronos.Services;
|
using MareSynchronos.Services;
|
||||||
|
using MareSynchronos.Services.Events;
|
||||||
using MareSynchronos.Services.Mediator;
|
using MareSynchronos.Services.Mediator;
|
||||||
using MareSynchronos.Services.ServerConfiguration;
|
using MareSynchronos.Services.ServerConfiguration;
|
||||||
using MareSynchronos.UI;
|
using MareSynchronos.UI;
|
||||||
@@ -91,6 +92,8 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddSingleton<FileCompactor>();
|
collection.AddSingleton<FileCompactor>();
|
||||||
collection.AddSingleton<TagHandler>();
|
collection.AddSingleton<TagHandler>();
|
||||||
collection.AddSingleton<UidDisplayHandler>();
|
collection.AddSingleton<UidDisplayHandler>();
|
||||||
|
collection.AddSingleton((s) => new EventAggregator(pluginInterface.ConfigDirectory.FullName,
|
||||||
|
s.GetRequiredService<ILogger<EventAggregator>>(), s.GetRequiredService<MareMediator>()));
|
||||||
collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(),
|
collection.AddSingleton((s) => new DalamudUtilService(s.GetRequiredService<ILogger<DalamudUtilService>>(),
|
||||||
clientState, objectTable, framework, gameGui, toastGui, condition, gameData, targetManager,
|
clientState, objectTable, framework, gameGui, toastGui, condition, gameData, targetManager,
|
||||||
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>()));
|
s.GetRequiredService<MareMediator>(), s.GetRequiredService<PerformanceCollectorService>()));
|
||||||
@@ -118,6 +121,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>();
|
collection.AddScoped<WindowMediatorSubscriberBase, DownloadUi>();
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, PopoutProfileUi>();
|
collection.AddScoped<WindowMediatorSubscriberBase, PopoutProfileUi>();
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, DataAnalysisUi>();
|
collection.AddScoped<WindowMediatorSubscriberBase, DataAnalysisUi>();
|
||||||
|
collection.AddScoped<WindowMediatorSubscriberBase, EventViewerUI>();
|
||||||
|
|
||||||
collection.AddScoped<WindowMediatorSubscriberBase, EditProfileUi>((s) => new EditProfileUi(s.GetRequiredService<ILogger<EditProfileUi>>(),
|
collection.AddScoped<WindowMediatorSubscriberBase, EditProfileUi>((s) => new EditProfileUi(s.GetRequiredService<ILogger<EditProfileUi>>(),
|
||||||
s.GetRequiredService<MareMediator>(), s.GetRequiredService<ApiController>(), pluginInterface.UiBuilder, s.GetRequiredService<UiSharedService>(),
|
s.GetRequiredService<MareMediator>(), s.GetRequiredService<ApiController>(), pluginInterface.UiBuilder, s.GetRequiredService<UiSharedService>(),
|
||||||
@@ -153,6 +157,7 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
collection.AddHostedService(p => p.GetRequiredService<PerformanceCollectorService>());
|
collection.AddHostedService(p => p.GetRequiredService<PerformanceCollectorService>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<DtrEntry>());
|
collection.AddHostedService(p => p.GetRequiredService<DtrEntry>());
|
||||||
collection.AddHostedService(p => p.GetRequiredService<MarePlugin>());
|
collection.AddHostedService(p => p.GetRequiredService<MarePlugin>());
|
||||||
|
collection.AddHostedService(p => p.GetRequiredService<EventAggregator>());
|
||||||
})
|
})
|
||||||
.Build()
|
.Build()
|
||||||
.RunAsync(_pluginCts.Token);
|
.RunAsync(_pluginCts.Token);
|
||||||
|
|||||||
45
MareSynchronos/Services/Events/Event.cs
Normal file
45
MareSynchronos/Services/Events/Event.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using MareSynchronos.API.Data;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Services.Events;
|
||||||
|
|
||||||
|
public record Event
|
||||||
|
{
|
||||||
|
public DateTime EventTime { get; }
|
||||||
|
public string UID { get; }
|
||||||
|
public string Character { get; }
|
||||||
|
public string EventSource { get; }
|
||||||
|
public EventSeverity EventSeverity { get; }
|
||||||
|
public string Message { get; }
|
||||||
|
|
||||||
|
public Event(string? Character, UserData UserData, string EventSource, EventSeverity EventSeverity, string Message)
|
||||||
|
{
|
||||||
|
EventTime = DateTime.Now;
|
||||||
|
this.UID = UserData.AliasOrUID;
|
||||||
|
this.Character = Character ?? string.Empty;
|
||||||
|
this.EventSource = EventSource;
|
||||||
|
this.EventSeverity = EventSeverity;
|
||||||
|
this.Message = Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event(UserData UserData, string EventSource, EventSeverity EventSeverity, string Message) : this(null, UserData, EventSource, EventSeverity, Message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Event(string EventSource, EventSeverity EventSeverity, string Message)
|
||||||
|
: this(new UserData(string.Empty), EventSource, EventSeverity, Message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(UID))
|
||||||
|
return $"{EventTime:HH:mm:ss.fff}\t[{EventSource}]{{{(int)EventSeverity}}}\t{Message}";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Character))
|
||||||
|
return $"{EventTime:HH:mm:ss.fff}\t[{EventSource}]{{{(int)EventSeverity}}}\t<{UID}> {Message}";
|
||||||
|
else
|
||||||
|
return $"{EventTime:HH:mm:ss.fff}\t[{EventSource}]{{{(int)EventSeverity}}}\t<{UID}\\{Character}> {Message}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
112
MareSynchronos/Services/Events/EventAggregator.cs
Normal file
112
MareSynchronos/Services/Events/EventAggregator.cs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using MareSynchronos.Utils;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace MareSynchronos.Services.Events;
|
||||||
|
|
||||||
|
public class EventAggregator : MediatorSubscriberBase, IHostedService
|
||||||
|
{
|
||||||
|
private readonly RollingList<Event> _events = new(500);
|
||||||
|
private readonly SemaphoreSlim _lock = new(1);
|
||||||
|
private readonly string _configDirectory;
|
||||||
|
private readonly ILogger<EventAggregator> _logger;
|
||||||
|
|
||||||
|
public Lazy<List<Event>> EventList { get; private set; }
|
||||||
|
public bool NewEventsAvailable => !EventList.IsValueCreated;
|
||||||
|
public string EventLogFolder => Path.Combine(_configDirectory, "eventlog");
|
||||||
|
private string CurrentLogName => $"{DateTime.Now:yyyy-MM-dd}-events.log";
|
||||||
|
private DateTime _currentTime;
|
||||||
|
|
||||||
|
public EventAggregator(string configDirectory, ILogger<EventAggregator> logger, MareMediator mareMediator) : base(logger, mareMediator)
|
||||||
|
{
|
||||||
|
Logger.LogInformation("Starting EventAggregatorService");
|
||||||
|
Logger.LogInformation("Started EventAggregatorService");
|
||||||
|
|
||||||
|
Mediator.Subscribe<EventMessage>(this, (msg) =>
|
||||||
|
{
|
||||||
|
_lock.Wait();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.LogTrace("Received Event: {evt}", msg.Event.ToString());
|
||||||
|
_events.Add(msg.Event);
|
||||||
|
WriteToFile(msg.Event);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
RecreateLazy();
|
||||||
|
});
|
||||||
|
|
||||||
|
EventList = CreateEventLazy();
|
||||||
|
_configDirectory = configDirectory;
|
||||||
|
_logger = logger;
|
||||||
|
_currentTime = DateTime.Now - TimeSpan.FromDays(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecreateLazy()
|
||||||
|
{
|
||||||
|
if (!EventList.IsValueCreated) return;
|
||||||
|
|
||||||
|
EventList = CreateEventLazy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Lazy<List<Event>> CreateEventLazy()
|
||||||
|
{
|
||||||
|
return new Lazy<List<Event>>(() =>
|
||||||
|
{
|
||||||
|
_lock.Wait();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return [.. _events];
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_lock.Release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteToFile(Event receivedEvent)
|
||||||
|
{
|
||||||
|
if (DateTime.Now.Day != _currentTime.Day)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_currentTime = DateTime.Now;
|
||||||
|
var filesInDirectory = Directory.EnumerateFiles(EventLogFolder, "*.log");
|
||||||
|
if (filesInDirectory.Skip(10).Any())
|
||||||
|
{
|
||||||
|
File.Delete(filesInDirectory.OrderBy(f => new FileInfo(f).LastWriteTimeUtc).First());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Could not delete last events");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventLogFile = Path.Combine(EventLogFolder, CurrentLogName);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(EventLogFolder)) Directory.CreateDirectory(EventLogFolder);
|
||||||
|
File.AppendAllLines(eventLogFile, [receivedEvent.ToString()]);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, $"Could not write to event file {eventLogFile}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
MareSynchronos/Services/Events/EventSeverity.cs
Normal file
8
MareSynchronos/Services/Events/EventSeverity.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace MareSynchronos.Services.Events;
|
||||||
|
|
||||||
|
public enum EventSeverity
|
||||||
|
{
|
||||||
|
Informational = 0,
|
||||||
|
Warning = 1,
|
||||||
|
Error = 2
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ using MareSynchronos.API.Dto;
|
|||||||
using MareSynchronos.API.Dto.Group;
|
using MareSynchronos.API.Dto.Group;
|
||||||
using MareSynchronos.PlayerData.Handlers;
|
using MareSynchronos.PlayerData.Handlers;
|
||||||
using MareSynchronos.PlayerData.Pairs;
|
using MareSynchronos.PlayerData.Pairs;
|
||||||
|
using MareSynchronos.Services.Events;
|
||||||
using MareSynchronos.WebAPI.Files.Models;
|
using MareSynchronos.WebAPI.Files.Models;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
@@ -78,6 +79,7 @@ public record CensusUpdateMessage(byte Gender, byte RaceId, byte TribeId) : Mess
|
|||||||
public record TargetPairMessage(Pair Pair) : MessageBase;
|
public record TargetPairMessage(Pair Pair) : MessageBase;
|
||||||
public record CombatStartMessage : MessageBase;
|
public record CombatStartMessage : MessageBase;
|
||||||
public record CombatEndMessage : MessageBase;
|
public record CombatEndMessage : MessageBase;
|
||||||
|
public record EventMessage(Event Event) : MessageBase;
|
||||||
public record PenumbraDirectoryChangedMessage(string? ModDirectory) : MessageBase;
|
public record PenumbraDirectoryChangedMessage(string? ModDirectory) : MessageBase;
|
||||||
|
|
||||||
public record UserChatMsgMessage(SignedChatMessage ChatMsg) : MessageBase;
|
public record UserChatMsgMessage(SignedChatMessage ChatMsg) : MessageBase;
|
||||||
|
|||||||
@@ -502,12 +502,21 @@ public class CompactUi : WindowMediatorSubscriberBase
|
|||||||
ImGui.TextUnformatted(downloadText);
|
ImGui.TextUnformatted(downloadText);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Mare Character Data Analysis", WindowContentWidth))
|
var bottomButtonWidth = (WindowContentWidth - ImGui.GetStyle().ItemSpacing.X) / 2;
|
||||||
|
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.PersonCircleQuestion, "Character Analysis", bottomButtonWidth))
|
||||||
{
|
{
|
||||||
Mediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
|
Mediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.NotesMedical, "Event Viewer", bottomButtonWidth))
|
||||||
|
{
|
||||||
|
Mediator.Publish(new UiToggleMessage(typeof(EventViewerUI)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawUIDHeader()
|
private void DrawUIDHeader()
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class DataAnalysisUi : WindowMediatorSubscriberBase
|
|||||||
private bool _showModal = false;
|
private bool _showModal = false;
|
||||||
|
|
||||||
public DataAnalysisUi(ILogger<DataAnalysisUi> logger, MareMediator mediator, CharacterAnalyzer characterAnalyzer, IpcManager ipcManager, PerformanceCollectorService performanceCollectorService)
|
public DataAnalysisUi(ILogger<DataAnalysisUi> logger, MareMediator mediator, CharacterAnalyzer characterAnalyzer, IpcManager ipcManager, PerformanceCollectorService performanceCollectorService)
|
||||||
: base(logger, mediator, "Mare Character Data Analysis", performanceCollectorService)
|
: base(logger, mediator, "Character Data Analysis", performanceCollectorService)
|
||||||
{
|
{
|
||||||
_characterAnalyzer = characterAnalyzer;
|
_characterAnalyzer = characterAnalyzer;
|
||||||
_ipcManager = ipcManager;
|
_ipcManager = ipcManager;
|
||||||
|
|||||||
223
MareSynchronos/UI/EventViewerUI.cs
Normal file
223
MareSynchronos/UI/EventViewerUI.cs
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Colors;
|
||||||
|
using Dalamud.Interface.Utility;
|
||||||
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
using ImGuiNET;
|
||||||
|
using MareSynchronos.Services;
|
||||||
|
using MareSynchronos.Services.Events;
|
||||||
|
using MareSynchronos.Services.Mediator;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
namespace MareSynchronos.UI;
|
||||||
|
|
||||||
|
internal class EventViewerUI : WindowMediatorSubscriberBase
|
||||||
|
{
|
||||||
|
private readonly EventAggregator _eventAggregator;
|
||||||
|
private readonly UiSharedService _uiSharedService;
|
||||||
|
private List<Event> _currentEvents = new();
|
||||||
|
private Lazy<List<Event>> _filteredEvents;
|
||||||
|
private string _filterFreeText = string.Empty;
|
||||||
|
private string _filterCharacter = string.Empty;
|
||||||
|
private string _filterUid = string.Empty;
|
||||||
|
private string _filterSource = string.Empty;
|
||||||
|
private string _filterEvent = string.Empty;
|
||||||
|
|
||||||
|
private List<Event> CurrentEvents
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _currentEvents;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_currentEvents = value;
|
||||||
|
_filteredEvents = RecreateFilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EventViewerUI(ILogger<EventViewerUI> logger, MareMediator mediator,
|
||||||
|
EventAggregator eventAggregator, UiSharedService uiSharedService,
|
||||||
|
PerformanceCollectorService performanceCollectorService) : base(logger, mediator, "Event Viewer", performanceCollectorService)
|
||||||
|
{
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
|
_uiSharedService = uiSharedService;
|
||||||
|
SizeConstraints = new()
|
||||||
|
{
|
||||||
|
MinimumSize = new(600, 500),
|
||||||
|
MaximumSize = new(1000, 2000)
|
||||||
|
};
|
||||||
|
_filteredEvents = RecreateFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Lazy<List<Event>> RecreateFilter()
|
||||||
|
{
|
||||||
|
return new(() =>
|
||||||
|
CurrentEvents.Where(f =>
|
||||||
|
(string.IsNullOrEmpty(_filterFreeText)
|
||||||
|
|| (f.EventSource.Contains(_filterFreeText, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| f.Character.Contains(_filterFreeText, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| f.UID.Contains(_filterFreeText, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| f.Message.Contains(_filterFreeText, StringComparison.OrdinalIgnoreCase)
|
||||||
|
))
|
||||||
|
&&
|
||||||
|
(string.IsNullOrEmpty(_filterUid)
|
||||||
|
|| (f.UID.Contains(_filterUid, StringComparison.OrdinalIgnoreCase))
|
||||||
|
)
|
||||||
|
&&
|
||||||
|
(string.IsNullOrEmpty(_filterSource)
|
||||||
|
|| (f.EventSource.Contains(_filterSource, StringComparison.OrdinalIgnoreCase))
|
||||||
|
)
|
||||||
|
&&
|
||||||
|
(string.IsNullOrEmpty(_filterCharacter)
|
||||||
|
|| (f.Character.Contains(_filterCharacter, StringComparison.OrdinalIgnoreCase))
|
||||||
|
)
|
||||||
|
&&
|
||||||
|
(string.IsNullOrEmpty(_filterEvent)
|
||||||
|
|| (f.Message.Contains(_filterEvent, StringComparison.OrdinalIgnoreCase))
|
||||||
|
)
|
||||||
|
).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearFilters()
|
||||||
|
{
|
||||||
|
_filterFreeText = string.Empty;
|
||||||
|
_filterCharacter = string.Empty;
|
||||||
|
_filterUid = string.Empty;
|
||||||
|
_filterSource = string.Empty;
|
||||||
|
_filterEvent = string.Empty;
|
||||||
|
_filteredEvents = RecreateFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnOpen()
|
||||||
|
{
|
||||||
|
CurrentEvents = _eventAggregator.EventList.Value.OrderByDescending(f => f.EventTime).ToList();
|
||||||
|
ClearFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DrawInternal()
|
||||||
|
{
|
||||||
|
using (ImRaii.Disabled(!_eventAggregator.NewEventsAvailable))
|
||||||
|
{
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.ArrowsToCircle, "Refresh events"))
|
||||||
|
{
|
||||||
|
CurrentEvents = _eventAggregator.EventList.Value.OrderByDescending(f => f.EventTime).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_eventAggregator.NewEventsAvailable)
|
||||||
|
{
|
||||||
|
ImGui.SameLine();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
UiSharedService.ColorTextWrapped("New events are available, press refresh to update", ImGuiColors.DalamudYellow);
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttonSize = UiSharedService.GetNormalizedIconTextButtonSize(FontAwesomeIcon.FolderOpen, "Open EventLog Folder");
|
||||||
|
var dist = ImGui.GetWindowContentRegionMax().X - buttonSize.X;
|
||||||
|
ImGui.SameLine(dist);
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.FolderOpen, "Open EventLog folder"))
|
||||||
|
{
|
||||||
|
ProcessStartInfo ps = new()
|
||||||
|
{
|
||||||
|
FileName = _eventAggregator.EventLogFolder,
|
||||||
|
UseShellExecute = true,
|
||||||
|
WindowStyle = ProcessWindowStyle.Normal
|
||||||
|
};
|
||||||
|
Process.Start(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
UiSharedService.FontText("Last Events", _uiSharedService.UidFont);
|
||||||
|
var foldOut = ImRaii.TreeNode("Filter");
|
||||||
|
if (foldOut)
|
||||||
|
{
|
||||||
|
if (UiSharedService.NormalizedIconTextButton(FontAwesomeIcon.Ban, "Clear Filters"))
|
||||||
|
{
|
||||||
|
ClearFilters();
|
||||||
|
}
|
||||||
|
bool changedFilter = false;
|
||||||
|
ImGui.SetNextItemWidth(200);
|
||||||
|
changedFilter |= ImGui.InputText("Search all columns", ref _filterFreeText, 50);
|
||||||
|
ImGui.SetNextItemWidth(200);
|
||||||
|
changedFilter |= ImGui.InputText("Filter by Source", ref _filterSource, 50);
|
||||||
|
ImGui.SetNextItemWidth(200);
|
||||||
|
changedFilter |= ImGui.InputText("Filter by UID", ref _filterUid, 50);
|
||||||
|
ImGui.SetNextItemWidth(200);
|
||||||
|
changedFilter |= ImGui.InputText("Filter by Character", ref _filterCharacter, 50);
|
||||||
|
ImGui.SetNextItemWidth(200);
|
||||||
|
changedFilter |= ImGui.InputText("Filter by Event", ref _filterEvent, 50);
|
||||||
|
if (changedFilter) _filteredEvents = RecreateFilter();
|
||||||
|
}
|
||||||
|
foldOut.Dispose();
|
||||||
|
|
||||||
|
var cursorPos = ImGui.GetCursorPosY();
|
||||||
|
var max = ImGui.GetWindowContentRegionMax();
|
||||||
|
var min = ImGui.GetWindowContentRegionMin();
|
||||||
|
var width = max.X - min.X;
|
||||||
|
var height = max.Y - cursorPos;
|
||||||
|
using var table = ImRaii.Table("eventTable", 6, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg,
|
||||||
|
new Vector2(width, height));
|
||||||
|
if (table)
|
||||||
|
{
|
||||||
|
ImGui.TableSetupScrollFreeze(0, 1);
|
||||||
|
ImGui.TableSetupColumn(string.Empty, ImGuiTableColumnFlags.NoSort);
|
||||||
|
ImGui.TableSetupColumn("Time");
|
||||||
|
ImGui.TableSetupColumn("Source");
|
||||||
|
ImGui.TableSetupColumn("UID");
|
||||||
|
ImGui.TableSetupColumn("Character");
|
||||||
|
ImGui.TableSetupColumn("Event");
|
||||||
|
ImGui.TableHeadersRow();
|
||||||
|
foreach (var ev in _filteredEvents.Value)
|
||||||
|
{
|
||||||
|
var icon = ev.EventSeverity switch
|
||||||
|
{
|
||||||
|
EventSeverity.Informational => FontAwesomeIcon.InfoCircle,
|
||||||
|
EventSeverity.Warning => FontAwesomeIcon.ExclamationTriangle,
|
||||||
|
EventSeverity.Error => FontAwesomeIcon.Cross,
|
||||||
|
_ => FontAwesomeIcon.QuestionCircle
|
||||||
|
};
|
||||||
|
|
||||||
|
var iconColor = ev.EventSeverity switch
|
||||||
|
{
|
||||||
|
EventSeverity.Informational => new Vector4(),
|
||||||
|
EventSeverity.Warning => ImGuiColors.DalamudYellow,
|
||||||
|
EventSeverity.Error => ImGuiColors.DalamudRed,
|
||||||
|
_ => new Vector4()
|
||||||
|
};
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
UiSharedService.NormalizedIcon(icon, iconColor == new Vector4() ? null : iconColor);
|
||||||
|
UiSharedService.AttachToolTip(ev.EventSeverity.ToString());
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted(ev.EventTime.ToString("G", CultureInfo.CurrentCulture));
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted(ev.EventSource);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted(string.IsNullOrEmpty(ev.UID) ? "--" : ev.UID);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
ImGui.TextUnformatted(string.IsNullOrEmpty(ev.Character) ? "--" : ev.Character);
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.AlignTextToFramePadding();
|
||||||
|
var posX = ImGui.GetCursorPosX();
|
||||||
|
var maxTextLength = ImGui.GetWindowContentRegionMax().X - posX;
|
||||||
|
var textSize = ImGui.CalcTextSize(ev.Message).X;
|
||||||
|
var msg = ev.Message;
|
||||||
|
while (textSize > maxTextLength)
|
||||||
|
{
|
||||||
|
msg = msg[..^5] + "...";
|
||||||
|
textSize = ImGui.CalcTextSize(msg).X;
|
||||||
|
}
|
||||||
|
ImGui.TextUnformatted(msg);
|
||||||
|
if (!string.Equals(msg, ev.Message, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
UiSharedService.AttachToolTip(ev.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -128,6 +128,8 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
await StopConnection(ServerState.Disconnected).ConfigureAwait(false);
|
await StopConnection(ServerState.Disconnected).ConfigureAwait(false);
|
||||||
|
|
||||||
Logger.LogInformation("Recreating Connection");
|
Logger.LogInformation("Recreating Connection");
|
||||||
|
Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(ApiController), Services.Events.EventSeverity.Informational,
|
||||||
|
$"Starting Connection to {_serverManager.CurrentServer.ServerName}")));
|
||||||
|
|
||||||
_connectionCancellationTokenSource.Cancel();
|
_connectionCancellationTokenSource.Cancel();
|
||||||
_connectionCancellationTokenSource = new CancellationTokenSource();
|
_connectionCancellationTokenSource = new CancellationTokenSource();
|
||||||
@@ -411,6 +413,9 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
_healthCheckTokenSource?.Cancel();
|
_healthCheckTokenSource?.Cancel();
|
||||||
ServerState = ServerState.Reconnecting;
|
ServerState = ServerState.Reconnecting;
|
||||||
Logger.LogWarning(arg, "Connection closed... Reconnecting");
|
Logger.LogWarning(arg, "Connection closed... Reconnecting");
|
||||||
|
Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(ApiController), Services.Events.EventSeverity.Warning,
|
||||||
|
$"Connection interrupted, reconnecting to {_serverManager.CurrentServer.ServerName}")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StopConnection(ServerState state)
|
private async Task StopConnection(ServerState state)
|
||||||
@@ -422,6 +427,9 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
|
|||||||
|
|
||||||
if (_mareHub is not null)
|
if (_mareHub is not null)
|
||||||
{
|
{
|
||||||
|
Mediator.Publish(new EventMessage(new Services.Events.Event(nameof(ApiController), Services.Events.EventSeverity.Informational,
|
||||||
|
$"Stopping existing connection to {_serverManager.CurrentServer.ServerName}")));
|
||||||
|
|
||||||
_initialized = false;
|
_initialized = false;
|
||||||
_healthCheckTokenSource?.Cancel();
|
_healthCheckTokenSource?.Cancel();
|
||||||
Mediator.Publish(new DisconnectedMessage());
|
Mediator.Publish(new DisconnectedMessage());
|
||||||
|
|||||||
Reference in New Issue
Block a user