1
0
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:
RaidMax
2023-04-05 09:54:57 -05:00
parent 53a6ef2ec3
commit ebe69a94ad
39 changed files with 1410 additions and 526 deletions

View 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; }
}

View File

@ -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);
}
}
}

View 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
}
}
}

View File

@ -4,5 +4,5 @@ namespace SharedLibraryCore.Events.Game;
public abstract class GameEventV2 : GameEvent
{
public IGameServer Server { get; init; }
public IGameServer Server => Owner;
}

View File

@ -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();
}
}

View File

@ -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; }
}