mirror of
https://github.com/RaidMax/IW4M-Admin.git
synced 2025-06-10 15:20:48 -05:00
implement new eventing system
This commit is contained in:
12
SharedLibraryCore/Events/CoreEvent.cs
Normal file
12
SharedLibraryCore/Events/CoreEvent.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace SharedLibraryCore.Events;
|
||||
|
||||
public abstract class CoreEvent
|
||||
{
|
||||
public Guid Id { get; } = Guid.NewGuid();
|
||||
public Guid? CorrelationId { get; init; }
|
||||
public object Source { get; init; }
|
||||
public DateTimeOffset CreatedAt { get; } = DateTimeOffset.UtcNow;
|
||||
public DateTimeOffset? ProcessedAt { get; set; }
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using SharedLibraryCore.Dtos;
|
||||
|
||||
namespace SharedLibraryCore.Events
|
||||
{
|
||||
public class EventApi
|
||||
{
|
||||
private const int MaxEvents = 25;
|
||||
private static readonly ConcurrentQueue<EventInfo> RecentEvents = new ConcurrentQueue<EventInfo>();
|
||||
|
||||
public static IEnumerable<EventInfo> GetEvents(bool shouldConsume)
|
||||
{
|
||||
var eventList = RecentEvents.ToArray();
|
||||
|
||||
// clear queue if events should be consumed
|
||||
if (shouldConsume)
|
||||
{
|
||||
RecentEvents.Clear();
|
||||
}
|
||||
|
||||
return eventList;
|
||||
}
|
||||
|
||||
public static void OnGameEvent(GameEvent gameEvent)
|
||||
{
|
||||
var E = gameEvent;
|
||||
// don't want to clog up the api with unknown events
|
||||
if (E.Type == GameEvent.EventType.Unknown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var apiEvent = new EventInfo
|
||||
{
|
||||
ExtraInfo = E.Extra?.ToString() ?? E.Data,
|
||||
GameInfo = new EntityInfo
|
||||
{
|
||||
Name = E.Owner.GameName.ToString(),
|
||||
Id = (int)E.Owner.GameName
|
||||
},
|
||||
OwnerEntity = new EntityInfo
|
||||
{
|
||||
Name = E.Owner.Hostname,
|
||||
Id = E.Owner.EndPoint
|
||||
},
|
||||
OriginEntity = E.Origin == null
|
||||
? null
|
||||
: new EntityInfo
|
||||
{
|
||||
Id = E.Origin.ClientId,
|
||||
Name = E.Origin.Name
|
||||
},
|
||||
TargetEntity = E.Target == null
|
||||
? null
|
||||
: new EntityInfo
|
||||
{
|
||||
Id = E.Target.ClientId,
|
||||
Name = E.Target.Name
|
||||
},
|
||||
EventType = new EntityInfo
|
||||
{
|
||||
Id = (int)E.Type,
|
||||
Name = E.Type.ToString()
|
||||
},
|
||||
EventTime = E.Time
|
||||
};
|
||||
|
||||
// add the new event to the list
|
||||
AddNewEvent(apiEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds event to the list and removes first added if reached max capacity
|
||||
/// </summary>
|
||||
/// <param name="info">EventInfo to add</param>
|
||||
private static void AddNewEvent(EventInfo info)
|
||||
{
|
||||
// remove the first added event
|
||||
if (RecentEvents.Count >= MaxEvents)
|
||||
{
|
||||
RecentEvents.TryDequeue(out _);
|
||||
}
|
||||
|
||||
RecentEvents.Enqueue(info);
|
||||
}
|
||||
}
|
||||
}
|
44
SharedLibraryCore/Events/EventExtensions.cs
Normal file
44
SharedLibraryCore/Events/EventExtensions.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Events;
|
||||
|
||||
public static class EventExtensions
|
||||
{
|
||||
public static Task InvokeAsync<TEventType>(this Func<TEventType, CancellationToken, Task> function,
|
||||
TEventType eventArgType, CancellationToken token)
|
||||
{
|
||||
if (function is null)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return Task.WhenAll(function.GetInvocationList().Cast<Func<TEventType, CancellationToken, Task>>()
|
||||
.Select(x => RunHandler(x, eventArgType, token)));
|
||||
}
|
||||
|
||||
private static async Task RunHandler<TEventType>(Func<TEventType, CancellationToken, Task> handler,
|
||||
TEventType eventArgType, CancellationToken token)
|
||||
{
|
||||
if (token == CancellationToken.None)
|
||||
{
|
||||
// special case to allow tasks like request after delay to run longer
|
||||
await handler(eventArgType, token);
|
||||
}
|
||||
|
||||
using var timeoutToken = new CancellationTokenSource(Utilities.DefaultCommandTimeout);
|
||||
using var tokenSource =
|
||||
CancellationTokenSource.CreateLinkedTokenSource(token, timeoutToken.Token);
|
||||
|
||||
try
|
||||
{
|
||||
await handler(eventArgType, tokenSource.Token);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
@ -4,5 +4,5 @@ namespace SharedLibraryCore.Events.Game;
|
||||
|
||||
public abstract class GameEventV2 : GameEvent
|
||||
{
|
||||
public IGameServer Server { get; init; }
|
||||
public IGameServer Server => Owner;
|
||||
}
|
||||
|
@ -5,10 +5,11 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog.Context;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Events;
|
||||
|
||||
namespace SharedLibraryCore
|
||||
{
|
||||
public class GameEvent
|
||||
public class GameEvent : CoreEvent
|
||||
{
|
||||
public enum EventFailReason
|
||||
{
|
||||
@ -133,6 +134,8 @@ namespace SharedLibraryCore
|
||||
/// connection was restored to a server (the server began responding again)
|
||||
/// </summary>
|
||||
ConnectionRestored,
|
||||
|
||||
SayTeam = 99,
|
||||
|
||||
// events "generated" by clients
|
||||
/// <summary>
|
||||
@ -246,7 +249,7 @@ namespace SharedLibraryCore
|
||||
/// team info printed out by game script
|
||||
/// </summary>
|
||||
JoinTeam = 304,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// used for community generated plugin events
|
||||
/// </summary>
|
||||
@ -267,7 +270,7 @@ namespace SharedLibraryCore
|
||||
public GameEvent()
|
||||
{
|
||||
Time = DateTime.UtcNow;
|
||||
Id = GetNextEventId();
|
||||
IncrementalId = GetNextEventId();
|
||||
}
|
||||
|
||||
~GameEvent()
|
||||
@ -275,8 +278,6 @@ namespace SharedLibraryCore
|
||||
_eventFinishedWaiter.Dispose();
|
||||
}
|
||||
|
||||
public EventSource Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// suptype of the event for more detailed classification
|
||||
/// </summary>
|
||||
@ -293,11 +294,10 @@ namespace SharedLibraryCore
|
||||
public bool IsRemote { get; set; }
|
||||
public object Extra { get; set; }
|
||||
public DateTime Time { get; set; }
|
||||
public long Id { get; }
|
||||
public long IncrementalId { get; }
|
||||
public EventFailReason FailReason { get; set; }
|
||||
public bool Failed => FailReason != EventFailReason.None;
|
||||
public Guid CorrelationId { get; set; } = Guid.NewGuid();
|
||||
public List<string> Output { get; set; } = new List<string>();
|
||||
public List<string> Output { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the event should block until it is complete
|
||||
@ -328,23 +328,31 @@ namespace SharedLibraryCore
|
||||
/// <returns>waitable task </returns>
|
||||
public async Task<GameEvent> WaitAsync(TimeSpan timeSpan, CancellationToken token)
|
||||
{
|
||||
var processed = false;
|
||||
Utilities.DefaultLogger.LogDebug("Begin wait for event {Id}", Id);
|
||||
try
|
||||
if (FailReason == EventFailReason.Timeout)
|
||||
{
|
||||
processed = await _eventFinishedWaiter.WaitAsync(timeSpan, token);
|
||||
return this;
|
||||
}
|
||||
|
||||
catch (TaskCanceledException)
|
||||
var processed = false;
|
||||
Utilities.DefaultLogger.LogDebug("Begin wait for {Name}, {Type}, {Id}", Type, GetType().Name,
|
||||
IncrementalId);
|
||||
|
||||
try
|
||||
{
|
||||
await _eventFinishedWaiter.WaitAsync(timeSpan, token);
|
||||
processed = true;
|
||||
}
|
||||
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
processed = false;
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
if (_eventFinishedWaiter.CurrentCount == 0)
|
||||
if (processed)
|
||||
{
|
||||
_eventFinishedWaiter.Release();
|
||||
Complete();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,14 @@
|
||||
using SharedLibraryCore.Database.Models;
|
||||
|
||||
namespace SharedLibraryCore.Events.Management;
|
||||
|
||||
public class ClientPersistentIdReceiveEvent : ClientStateEvent
|
||||
{
|
||||
public ClientPersistentIdReceiveEvent(EFClient client, string persistentId)
|
||||
{
|
||||
Client = client;
|
||||
PersistentId = persistentId;
|
||||
}
|
||||
|
||||
public string PersistentId { get; init; }
|
||||
}
|
Reference in New Issue
Block a user