diff --git a/Moonlight/App/Events/EventSystem.cs b/Moonlight/App/Events/EventSystem.cs new file mode 100644 index 00000000..eb5b12da --- /dev/null +++ b/Moonlight/App/Events/EventSystem.cs @@ -0,0 +1,132 @@ +using System.Diagnostics; +using Logging.Net; + +namespace Moonlight.App.Events; + +public class EventSystem +{ + private Dictionary Storage = new(); + private List Subscribers = new(); + + private readonly bool Debug = true; + private readonly bool DisableWarning = false; + private readonly TimeSpan TookToLongTime = TimeSpan.FromSeconds(1); + + public Task On(string id, object handle, Func action) + { + if(Debug) + Logger.Debug($"{handle} subscribed to '{id}'"); + + lock (Subscribers) + { + if (!Subscribers.Any(x => x.Id == id && x.Handle == handle)) + { + Subscribers.Add(new () + { + Action = action, + Handle = handle, + Id = id + }); + } + } + + return Task.CompletedTask; + } + + public Task Emit(string id, object? d = null) + { + int hashCode = -1; + + if (d != null) + { + hashCode = d.GetHashCode(); + Storage.TryAdd(hashCode, d); + } + + Subscriber[] subscribers; + + lock (Subscribers) + { + subscribers = Subscribers + .Where(x => x.Id == id) + .ToArray(); + } + + var tasks = new List(); + + foreach (var subscriber in subscribers) + { + tasks.Add(new Task(() => + { + int storageId = hashCode + 0; // To create a copy of the hash code + + object? data = null; + + if (storageId != -1) + { + if (Storage.TryGetValue(storageId, out var value)) + { + data = value; + } + else + { + Logger.Warn($"Object with the hash '{storageId}' was not present in the storage"); + return; + } + } + + var stopWatch = new Stopwatch(); + stopWatch.Start(); + + var del = (Delegate)subscriber.Action; + + ((Task)del.DynamicInvoke(data)!).Wait(); + + stopWatch.Stop(); + + if (!DisableWarning) + { + if (stopWatch.Elapsed.TotalMilliseconds > TookToLongTime.TotalMilliseconds) + { + Logger.Warn($"Subscriber {subscriber.Handle} for event '{subscriber.Id}' took long to process. {stopWatch.Elapsed.TotalMilliseconds}ms"); + } + } + + if (Debug) + { + Logger.Debug($"Subscriber {subscriber.Handle} for event '{subscriber.Id}' took {stopWatch.Elapsed.TotalMilliseconds}ms"); + } + })); + } + + foreach (var task in tasks) + { + task.Start(); + } + + Task.Run(() => + { + Task.WaitAll(tasks.ToArray()); + Storage.Remove(hashCode); + Logger.Debug($"Completed all event tasks for '{id}' and removed object from storage"); + }); + + if(Debug) + Logger.Debug($"Completed event emit '{id}'"); + + return Task.CompletedTask; + } + + public Task Off(string id, object handle) + { + if(Debug) + Logger.Debug($"{handle} unsubscribed to '{id}'"); + + lock (Subscribers) + { + Subscribers.RemoveAll(x => x.Id == id && x.Handle == handle); + } + + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Moonlight/App/Events/Subscriber.cs b/Moonlight/App/Events/Subscriber.cs new file mode 100644 index 00000000..0d64012c --- /dev/null +++ b/Moonlight/App/Events/Subscriber.cs @@ -0,0 +1,8 @@ +namespace Moonlight.App.Events; + +public class Subscriber +{ + public string Id { get; set; } + public object Action { get; set; } + public object Handle { get; set; } +} \ No newline at end of file diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index 33c9acc5..a8a470b9 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -4,6 +4,7 @@ using CurrieTechnologies.Razor.SweetAlert2; using Logging.Net; using Moonlight.App.ApiClients.CloudPanel; using Moonlight.App.Database; +using Moonlight.App.Events; using Moonlight.App.Helpers; using Moonlight.App.LogMigrator; using Moonlight.App.Repositories; @@ -105,6 +106,7 @@ namespace Moonlight builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddScoped();