diff options
Diffstat (limited to 'Impostor-dev/src/Impostor.Server/Events')
29 files changed, 971 insertions, 0 deletions
diff --git a/Impostor-dev/src/Impostor.Server/Events/EventHandler.cs b/Impostor-dev/src/Impostor.Server/Events/EventHandler.cs new file mode 100644 index 0000000..190f7f3 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/EventHandler.cs @@ -0,0 +1,24 @@ +using Impostor.Api.Events; +using Impostor.Server.Events.Register; + +namespace Impostor.Server.Events +{ + internal readonly struct EventHandler + { + public EventHandler(IEventListener o, IRegisteredEventListener listener) + { + Object = o; + Listener = listener; + } + + public IEventListener Object { get; } + + public IRegisteredEventListener Listener { get; } + + public void Deconstruct(out IEventListener o, out IRegisteredEventListener listener) + { + o = Object; + listener = Listener; + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Server/Events/EventManager.cs b/Impostor-dev/src/Impostor.Server/Events/EventManager.cs new file mode 100644 index 0000000..5625c4d --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/EventManager.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Impostor.Api; +using Impostor.Api.Events; +using Impostor.Api.Events.Managers; +using Impostor.Server.Events.Register; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Impostor.Server.Events +{ + internal class EventManager : IEventManager + { + private readonly ConcurrentDictionary<Type, TemporaryEventRegister> _temporaryEventListeners; + private readonly ConcurrentDictionary<Type, List<EventHandler>> _cachedEventHandlers; + private readonly ILogger<EventManager> _logger; + private readonly IServiceProvider _serviceProvider; + + public EventManager(ILogger<EventManager> logger, IServiceProvider serviceProvider) + { + _logger = logger; + _serviceProvider = serviceProvider; + _temporaryEventListeners = new ConcurrentDictionary<Type, TemporaryEventRegister>(); + _cachedEventHandlers = new ConcurrentDictionary<Type, List<EventHandler>>(); + } + + /// <inheritdoc /> + public IDisposable RegisterListener<TListener>(TListener listener, Func<Func<Task>, Task> invoker = null) + where TListener : IEventListener + { + if (listener == null) + { + throw new ArgumentNullException(nameof(listener)); + } + + var eventListeners = RegisteredEventListener.FromType(listener.GetType()); + var disposes = new IDisposable[eventListeners.Count]; + + foreach (var eventListener in eventListeners) + { + IRegisteredEventListener wrappedEventListener = new WrappedRegisteredEventListener(eventListener, listener); + + if (invoker != null) + { + wrappedEventListener = new InvokedRegisteredEventListener(wrappedEventListener, invoker); + } + + var register = _temporaryEventListeners.GetOrAdd( + wrappedEventListener.EventType, + _ => new TemporaryEventRegister()); + + register.Add(wrappedEventListener); + } + + if (eventListeners.Count > 0) + { + _cachedEventHandlers.TryRemove(typeof(TListener), out _); + } + + return new MultiDisposable(disposes); + } + + /// <inheritdoc /> + public bool IsRegistered<TEvent>() + where TEvent : IEvent + { + if (_cachedEventHandlers.TryGetValue(typeof(TEvent), out var handlers)) + { + return handlers.Count > 0; + } + + return GetHandlers<TEvent>().Any(); + } + + /// <inheritdoc /> + public async ValueTask CallAsync<T>(T @event) + where T : IEvent + { + try + { + if (!_cachedEventHandlers.TryGetValue(typeof(T), out var handlers)) + { + handlers = CacheEventHandlers<T>(); + } + + foreach (var (handler, eventListener) in handlers) + { + await eventListener.InvokeAsync(handler, @event, _serviceProvider); + } + } + catch (ImpostorCheatException) + { + throw; + } + catch (Exception e) + { + _logger.LogError(e, "Invocation of event {0} threw an exception.", @event.GetType().Name); + } + } + + private List<EventHandler> CacheEventHandlers<TEvent>() + where TEvent : IEvent + { + var handlers = GetHandlers<TEvent>() + .OrderByDescending(e => e.Listener.Priority) + .ToList(); + + _cachedEventHandlers[typeof(TEvent)] = handlers; + + return handlers; + } + + /// <summary> + /// Get all the event listeners for the given event type. + /// </summary> + /// <returns>The event listeners.</returns> + private IEnumerable<EventHandler> GetHandlers<TEvent>() + where TEvent : IEvent + { + var eventType = typeof(TEvent); + var interfaces = eventType.GetInterfaces(); + + foreach (var @interface in interfaces) + { + if (_temporaryEventListeners.TryGetValue(@interface, out var cb)) + { + foreach (var eventListener in cb.GetEventListeners()) + { + yield return new EventHandler(null, eventListener); + } + } + } + + foreach (var handler in _serviceProvider.GetServices<IEventListener>()) + { + if (handler is IManualEventListener manualEventListener && manualEventListener.CanExecute<TEvent>()) + { + yield return new EventHandler(handler, new ManualRegisteredEventListener(manualEventListener)); + continue; + } + + var events = RegisteredEventListener.FromType(handler.GetType()); + + foreach (var eventHandler in events) + { + if (eventHandler.EventType != typeof(TEvent) && !interfaces.Contains(eventHandler.EventType)) + { + continue; + } + + yield return new EventHandler(handler, eventHandler); + } + } + + if (_temporaryEventListeners.TryGetValue(eventType, out var cb2)) + { + foreach (var eventListener in cb2.GetEventListeners()) + { + yield return new EventHandler(null, eventListener); + } + } + } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/GameAlterEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/GameAlterEvent.cs new file mode 100644 index 0000000..3fb2368 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/GameAlterEvent.cs @@ -0,0 +1,18 @@ +using Impostor.Api.Events; +using Impostor.Api.Games; + +namespace Impostor.Server.Events +{ + public class GameAlterEvent : IGameAlterEvent + { + public GameAlterEvent(IGame game, bool isPublic) + { + Game = game; + IsPublic = isPublic; + } + + public IGame Game { get; } + + public bool IsPublic { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/GameCreatedEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/GameCreatedEvent.cs new file mode 100644 index 0000000..57e7a20 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/GameCreatedEvent.cs @@ -0,0 +1,15 @@ +using Impostor.Api.Events; +using Impostor.Api.Games; + +namespace Impostor.Server.Events +{ + public class GameCreatedEvent : IGameCreatedEvent + { + public GameCreatedEvent(IGame game) + { + Game = game; + } + + public IGame Game { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/GameDestroyedEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/GameDestroyedEvent.cs new file mode 100644 index 0000000..5ee1b11 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/GameDestroyedEvent.cs @@ -0,0 +1,15 @@ +using Impostor.Api.Events; +using Impostor.Api.Games; + +namespace Impostor.Server.Events +{ + public class GameDestroyedEvent : IGameDestroyedEvent + { + public GameDestroyedEvent(IGame game) + { + Game = game; + } + + public IGame Game { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/GameEndedEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/GameEndedEvent.cs new file mode 100644 index 0000000..4ec4dcf --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/GameEndedEvent.cs @@ -0,0 +1,19 @@ +using Impostor.Api.Events; +using Impostor.Api.Games; +using Impostor.Api.Innersloth; + +namespace Impostor.Server.Events +{ + public class GameEndedEvent : IGameEndedEvent + { + public GameEndedEvent(IGame game, GameOverReason gameOverReason) + { + Game = game; + GameOverReason = gameOverReason; + } + + public IGame Game { get; } + + public GameOverReason GameOverReason { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/GamePlayerJoinedEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/GamePlayerJoinedEvent.cs new file mode 100644 index 0000000..d728c59 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/GamePlayerJoinedEvent.cs @@ -0,0 +1,19 @@ +using Impostor.Api.Events; +using Impostor.Api.Games; +using Impostor.Api.Net; + +namespace Impostor.Server.Events +{ + public class GamePlayerJoinedEvent : IGamePlayerJoinedEvent + { + public GamePlayerJoinedEvent(IGame game, IClientPlayer player) + { + Game = game; + Player = player; + } + + public IGame Game { get; } + + public IClientPlayer Player { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/GamePlayerLeftEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/GamePlayerLeftEvent.cs new file mode 100644 index 0000000..d295103 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/GamePlayerLeftEvent.cs @@ -0,0 +1,22 @@ +using Impostor.Api.Events; +using Impostor.Api.Games; +using Impostor.Api.Net; + +namespace Impostor.Server.Events +{ + public class GamePlayerLeftEvent : IGamePlayerLeftEvent + { + public GamePlayerLeftEvent(IGame game, IClientPlayer player, bool isBan) + { + Game = game; + Player = player; + IsBan = isBan; + } + + public IGame Game { get; } + + public IClientPlayer Player { get; } + + public bool IsBan { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/GameStartedEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/GameStartedEvent.cs new file mode 100644 index 0000000..d21f9ec --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/GameStartedEvent.cs @@ -0,0 +1,15 @@ +using Impostor.Api.Events; +using Impostor.Api.Games; + +namespace Impostor.Server.Events +{ + public class GameStartedEvent : IGameStartedEvent + { + public GameStartedEvent(IGame game) + { + Game = game; + } + + public IGame Game { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/GameStartingEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/GameStartingEvent.cs new file mode 100644 index 0000000..a0763e8 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/GameStartingEvent.cs @@ -0,0 +1,15 @@ +using Impostor.Api.Events; +using Impostor.Api.Games; + +namespace Impostor.Server.Events +{ + public class GameStartingEvent : IGameStartingEvent + { + public GameStartingEvent(IGame game) + { + Game = game; + } + + public IGame Game { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Meeting/MeetingEndedEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Meeting/MeetingEndedEvent.cs new file mode 100644 index 0000000..cf55f7d --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Meeting/MeetingEndedEvent.cs @@ -0,0 +1,19 @@ +using Impostor.Api.Events.Meeting; +using Impostor.Api.Games; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Meeting +{ + public class MeetingEndedEvent : IMeetingEndedEvent + { + public MeetingEndedEvent(IGame game, IInnerMeetingHud meetingHud) + { + Game = game; + MeetingHud = meetingHud; + } + + public IGame Game { get; } + + public IInnerMeetingHud MeetingHud { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Meeting/MeetingStartedEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Meeting/MeetingStartedEvent.cs new file mode 100644 index 0000000..aa689a3 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Meeting/MeetingStartedEvent.cs @@ -0,0 +1,19 @@ +using Impostor.Api.Events.Meeting; +using Impostor.Api.Games; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Meeting +{ + public class MeetingStartedEvent : IMeetingStartedEvent + { + public MeetingStartedEvent(IGame game, IInnerMeetingHud meetingHud) + { + Game = game; + MeetingHud = meetingHud; + } + + public IGame Game { get; } + + public IInnerMeetingHud MeetingHud { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerChatEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerChatEvent.cs new file mode 100644 index 0000000..7b7eb22 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerChatEvent.cs @@ -0,0 +1,26 @@ +using Impostor.Api.Events.Player; +using Impostor.Api.Games; +using Impostor.Api.Net; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Player +{ + public class PlayerChatEvent : IPlayerChatEvent + { + public PlayerChatEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl, string message) + { + Game = game; + ClientPlayer = clientPlayer; + PlayerControl = playerControl; + Message = message; + } + + public IGame Game { get; } + + public IClientPlayer ClientPlayer { get; } + + public IInnerPlayerControl PlayerControl { get; } + + public string Message { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerCompletedTaskEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerCompletedTaskEvent.cs new file mode 100644 index 0000000..330135d --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerCompletedTaskEvent.cs @@ -0,0 +1,27 @@ +using Impostor.Api.Events.Player; +using Impostor.Api.Games; +using Impostor.Api.Innersloth; +using Impostor.Api.Net; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Player +{ + public class PlayerCompletedTaskEvent : IPlayerCompletedTaskEvent + { + public PlayerCompletedTaskEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl, ITaskInfo task) + { + Game = game; + ClientPlayer = clientPlayer; + PlayerControl = playerControl; + Task = task; + } + + public IGame Game { get; } + + public IClientPlayer ClientPlayer { get; } + + public IInnerPlayerControl PlayerControl { get; } + + public ITaskInfo Task { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerDestroyedEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerDestroyedEvent.cs new file mode 100644 index 0000000..69a20c9 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerDestroyedEvent.cs @@ -0,0 +1,23 @@ +using Impostor.Api.Events.Player; +using Impostor.Api.Games; +using Impostor.Api.Net; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Player +{ + public class PlayerDestroyedEvent : IPlayerDestroyedEvent + { + public PlayerDestroyedEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl) + { + Game = game; + ClientPlayer = clientPlayer; + PlayerControl = playerControl; + } + + public IGame Game { get; } + + public IClientPlayer ClientPlayer { get; } + + public IInnerPlayerControl PlayerControl { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerExileEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerExileEvent.cs new file mode 100644 index 0000000..a8660da --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerExileEvent.cs @@ -0,0 +1,23 @@ +using Impostor.Api.Events.Player; +using Impostor.Api.Games; +using Impostor.Api.Net; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Player +{ + public class PlayerExileEvent : IPlayerExileEvent + { + public PlayerExileEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl) + { + Game = game; + ClientPlayer = clientPlayer; + PlayerControl = playerControl; + } + + public IGame Game { get; } + + public IClientPlayer ClientPlayer { get; } + + public IInnerPlayerControl PlayerControl { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerMovementEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerMovementEvent.cs new file mode 100644 index 0000000..31ac388 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerMovementEvent.cs @@ -0,0 +1,24 @@ +using Impostor.Api.Events.Player; +using Impostor.Api.Games; +using Impostor.Api.Net; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Player +{ + // TODO: Finish and use event, needs to be pooled + public class PlayerMovementEvent : IPlayerEvent + { + public PlayerMovementEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl) + { + Game = game; + ClientPlayer = clientPlayer; + PlayerControl = playerControl; + } + + public IGame Game { get; } + + public IClientPlayer ClientPlayer { get; } + + public IInnerPlayerControl PlayerControl { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerMurderEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerMurderEvent.cs new file mode 100644 index 0000000..ca64c35 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerMurderEvent.cs @@ -0,0 +1,26 @@ +using Impostor.Api.Events.Player; +using Impostor.Api.Games; +using Impostor.Api.Net; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Player +{ + public class PlayerMurderEvent : IPlayerMurderEvent + { + public PlayerMurderEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl, IInnerPlayerControl victim) + { + Game = game; + ClientPlayer = clientPlayer; + PlayerControl = playerControl; + Victim = victim; + } + + public IGame Game { get; } + + public IClientPlayer ClientPlayer { get; } + + public IInnerPlayerControl PlayerControl { get; } + + public IInnerPlayerControl Victim { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerSetStartCounterEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerSetStartCounterEvent.cs new file mode 100644 index 0000000..71c25d7 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerSetStartCounterEvent.cs @@ -0,0 +1,26 @@ +using Impostor.Api.Events.Player; +using Impostor.Api.Games; +using Impostor.Api.Net; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Player +{ + public class PlayerSetStartCounterEvent : IPlayerSetStartCounterEvent + { + public PlayerSetStartCounterEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl, byte secondsLeft) + { + Game = game; + ClientPlayer = clientPlayer; + PlayerControl = playerControl; + SecondsLeft = secondsLeft; + } + + public byte SecondsLeft { get; } + + public IClientPlayer ClientPlayer { get; } + + public IInnerPlayerControl PlayerControl { get; } + + public IGame Game { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerSpawnedEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerSpawnedEvent.cs new file mode 100644 index 0000000..4b6fda1 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerSpawnedEvent.cs @@ -0,0 +1,23 @@ +using Impostor.Api.Events.Player; +using Impostor.Api.Games; +using Impostor.Api.Net; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Player +{ + public class PlayerSpawnedEvent : IPlayerSpawnedEvent + { + public PlayerSpawnedEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl) + { + Game = game; + ClientPlayer = clientPlayer; + PlayerControl = playerControl; + } + + public IGame Game { get; } + + public IClientPlayer ClientPlayer { get; } + + public IInnerPlayerControl PlayerControl { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerStartMeetingEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerStartMeetingEvent.cs new file mode 100644 index 0000000..70cb0d8 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerStartMeetingEvent.cs @@ -0,0 +1,26 @@ +using Impostor.Api.Events.Player; +using Impostor.Api.Games; +using Impostor.Api.Net; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Player +{ + public class PlayerStartMeetingEvent : IPlayerStartMeetingEvent + { + public PlayerStartMeetingEvent(IGame game, IClientPlayer clientPlayer, IInnerPlayerControl playerControl, IInnerPlayerControl? body) + { + Game = game; + ClientPlayer = clientPlayer; + PlayerControl = playerControl; + Body = body; + } + + public IGame Game { get; } + + public IClientPlayer ClientPlayer { get; } + + public IInnerPlayerControl PlayerControl { get; } + + public IInnerPlayerControl? Body { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerVentEvent.cs b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerVentEvent.cs new file mode 100644 index 0000000..0798d40 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerVentEvent.cs @@ -0,0 +1,30 @@ +using Impostor.Api.Events.Player; +using Impostor.Api.Games; +using Impostor.Api.Innersloth; +using Impostor.Api.Net; +using Impostor.Api.Net.Inner.Objects; + +namespace Impostor.Server.Events.Player +{ + public class PlayerVentEvent : IPlayerVentEvent + { + public PlayerVentEvent(IGame game, IClientPlayer sender, IInnerPlayerControl innerPlayerPhysics, VentLocation ventId, bool ventEnter) + { + Game = game; + ClientPlayer = sender; + PlayerControl = innerPlayerPhysics; + VentId = ventId; + VentEnter = ventEnter; + } + + public IGame Game { get; } + + public IClientPlayer ClientPlayer { get; } + + public IInnerPlayerControl PlayerControl { get; } + + public VentLocation VentId { get; } + + public bool VentEnter { get; } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/MultiDisposable.cs b/Impostor-dev/src/Impostor.Server/Events/MultiDisposable.cs new file mode 100644 index 0000000..b68f064 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/MultiDisposable.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Impostor.Server.Events +{ + /// <summary> + /// Disposes multiple <see cref="IDisposable"/>. + /// </summary> + internal class MultiDisposable : IDisposable + { + private readonly IEnumerable<IDisposable> _disposables; + + public MultiDisposable(IEnumerable<IDisposable> disposables) + { + _disposables = disposables; + } + + public void Dispose() + { + foreach (var disposable in _disposables) + { + disposable?.Dispose(); + } + } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Register/IRegisteredEventListener.cs b/Impostor-dev/src/Impostor.Server/Events/Register/IRegisteredEventListener.cs new file mode 100644 index 0000000..479a3f6 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Register/IRegisteredEventListener.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading.Tasks; +using Impostor.Api.Events; + +namespace Impostor.Server.Events.Register +{ + internal interface IRegisteredEventListener + { + Type EventType { get; } + + EventPriority Priority { get; } + + ValueTask InvokeAsync(object eventHandler, object @event, IServiceProvider provider); + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Server/Events/Register/InvokedRegisteredEventListener.cs b/Impostor-dev/src/Impostor.Server/Events/Register/InvokedRegisteredEventListener.cs new file mode 100644 index 0000000..a21c3b1 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Register/InvokedRegisteredEventListener.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using Impostor.Api.Events; + +namespace Impostor.Server.Events.Register +{ + internal class InvokedRegisteredEventListener : IRegisteredEventListener + { + private readonly IRegisteredEventListener _innerObject; + private readonly Func<Func<Task>, Task> _invoker; + + public InvokedRegisteredEventListener(IRegisteredEventListener innerObject, Func<Func<Task>, Task> invoker) + { + _innerObject = innerObject; + _invoker = invoker; + } + + public Type EventType => _innerObject.EventType; + + public EventPriority Priority => _innerObject.Priority; + + public ValueTask InvokeAsync(object eventHandler, object @event, IServiceProvider provider) + { + return new ValueTask(_invoker(() => _innerObject.InvokeAsync(eventHandler, @event, provider).AsTask())); + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Server/Events/Register/ManualRegisteredEventListener.cs b/Impostor-dev/src/Impostor.Server/Events/Register/ManualRegisteredEventListener.cs new file mode 100644 index 0000000..e81e8f8 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Register/ManualRegisteredEventListener.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using Impostor.Api.Events; + +namespace Impostor.Server.Events.Register +{ + internal class ManualRegisteredEventListener : IRegisteredEventListener + { + public Type EventType { get; } = typeof(object); + + private readonly IManualEventListener _manualEventListener; + + public ManualRegisteredEventListener(IManualEventListener manualEventListener) + { + _manualEventListener = manualEventListener; + } + + public EventPriority Priority => _manualEventListener.Priority; + + public ValueTask InvokeAsync(object eventHandler, object @event, IServiceProvider provider) + { + if (@event is IEvent typedEvent) + { + return _manualEventListener.Execute(typedEvent); + } + + return ValueTask.CompletedTask; + } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Register/RegisteredEventListener.cs b/Impostor-dev/src/Impostor.Server/Events/Register/RegisteredEventListener.cs new file mode 100644 index 0000000..120a45e --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Register/RegisteredEventListener.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; +using Impostor.Api.Events; +using Microsoft.Extensions.DependencyInjection; + +namespace Impostor.Server.Events.Register +{ + internal class RegisteredEventListener : IRegisteredEventListener + { + private static readonly PropertyInfo IsCancelledProperty = typeof(IEventCancelable).GetProperty(nameof(IEventCancelable.IsCancelled))!; + + private static readonly ConcurrentDictionary<Type, RegisteredEventListener[]> Instances = new ConcurrentDictionary<Type, RegisteredEventListener[]>(); + private readonly Func<object, object, IServiceProvider, ValueTask> _invoker; + private readonly Type _eventListenerType; + + public RegisteredEventListener(Type eventType, MethodInfo method, EventListenerAttribute attribute, Type eventListenerType) + { + EventType = eventType; + _eventListenerType = eventListenerType; + Priority = attribute.Priority; + IgnoreCancelled = attribute.IgnoreCancelled; + Method = method.GetFriendlyName(showParameters: false); + _invoker = CreateInvoker(method, attribute.IgnoreCancelled); + } + + public Type EventType { get; } + + public EventPriority Priority { get; } + + public int PriorityOrder { get; set; } + + public bool IgnoreCancelled { get; } + + public string Method { get; } + + public ValueTask InvokeAsync(object eventHandler, object @event, IServiceProvider provider) + { + return _invoker(eventHandler, @event, provider); + } + + private Func<object, object, IServiceProvider, ValueTask> CreateInvoker(MethodInfo method, bool ignoreCancelled) + { + var instance = Expression.Parameter(typeof(object), "instance"); + var eventParameter = Expression.Parameter(typeof(object), "event"); + var provider = Expression.Parameter(typeof(IServiceProvider), "provider"); + var @event = Expression.Convert(eventParameter, EventType); + + var getRequiredService = typeof(ServiceProviderServiceExtensions) + .GetMethod("GetRequiredService", new[] { typeof(IServiceProvider) }); + + if (getRequiredService == null) + { + throw new InvalidOperationException("The method GetRequiredService could not be found."); + } + + var methodArguments = method.GetParameters(); + var arguments = new Expression[methodArguments.Length]; + + for (var i = 0; i < methodArguments.Length; i++) + { + var methodArgument = methodArguments[i]; + + if (typeof(IEvent).IsAssignableFrom(methodArgument.ParameterType) + && methodArgument.ParameterType.IsAssignableFrom(EventType)) + { + arguments[i] = @event; + } + else + { + arguments[i] = Expression.Call( + getRequiredService.MakeGenericMethod(methodArgument.ParameterType), + provider); + } + } + + var returnTarget = Expression.Label(typeof(ValueTask)); + + Expression invoke = Expression.Call(Expression.Convert(instance, _eventListenerType), method, arguments); + + if (method.ReturnType == typeof(void)) + { + if (!ignoreCancelled && typeof(IEventCancelable).IsAssignableFrom(EventType)) + { + invoke = Expression.Block( + Expression.IfThenElse( + Expression.Property(@event, IsCancelledProperty), + Expression.Return(returnTarget, Expression.Default(typeof(ValueTask))), + Expression.Block( + invoke, + Expression.Return(returnTarget, Expression.Default(typeof(ValueTask))))), + Expression.Label(returnTarget, Expression.Default(typeof(ValueTask)))); + } + else + { + invoke = Expression.Block( + invoke, + Expression.Label(returnTarget, Expression.Default(typeof(ValueTask)))); + } + } + else if (method.ReturnType == typeof(ValueTask)) + { + if (!ignoreCancelled && typeof(IEventCancelable).IsAssignableFrom(EventType)) + { + invoke = Expression.Block( + Expression.IfThenElse( + Expression.Property(@event, IsCancelledProperty), + Expression.Return(returnTarget, Expression.Default(typeof(ValueTask))), + Expression.Return(returnTarget, invoke)), + Expression.Label(returnTarget, Expression.Default(typeof(ValueTask)))); + } + } + else + { + throw new InvalidOperationException($"The method {method.GetFriendlyName()} must return void or ValueTask."); + } + + return Expression.Lambda<Func<object, object, IServiceProvider, ValueTask>>(invoke, instance, eventParameter, provider) + .Compile(); + } + + public static IReadOnlyList<RegisteredEventListener> FromType(Type type) + { + return Instances.GetOrAdd(type, t => + { + return t.GetMethods() + .Where(m => !m.IsStatic && m.GetCustomAttributes(typeof(EventListenerAttribute), false).Any()) + .SelectMany(m => FromMethod(t, m)) + .ToArray(); + }); + } + + public static IEnumerable<RegisteredEventListener> FromMethod(Type listenerType, MethodInfo methodType) + { + // Get the return type. + var returnType = methodType.ReturnType; + + if (returnType != typeof(void) && returnType != typeof(ValueTask)) + { + throw new InvalidOperationException($"The method {methodType.GetFriendlyName()} does not return void or ValueTask."); + } + + // Register the event. + foreach (var attribute in methodType.GetCustomAttributes<EventListenerAttribute>(false)) + { + var eventType = attribute.Event; + + if (eventType == null) + { + if (methodType.GetParameters().Length == 0 || !typeof(IEvent).IsAssignableFrom(methodType.GetParameters()[0].ParameterType)) + { + throw new InvalidOperationException($"The first parameter of the method {methodType.GetFriendlyName()} should be the type {nameof(IEvent)}."); + } + + eventType = methodType.GetParameters()[0].ParameterType; + } + + yield return new RegisteredEventListener(eventType, methodType, attribute, listenerType); + } + } + } +} diff --git a/Impostor-dev/src/Impostor.Server/Events/Register/TemporaryEventRegister.cs b/Impostor-dev/src/Impostor.Server/Events/Register/TemporaryEventRegister.cs new file mode 100644 index 0000000..1446ad1 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Register/TemporaryEventRegister.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +namespace Impostor.Server.Events.Register +{ + internal class TemporaryEventRegister + { + private readonly ConcurrentDictionary<int, IRegisteredEventListener> _callbacks; + private int _idLast; + + public TemporaryEventRegister() + { + _callbacks = new ConcurrentDictionary<int, IRegisteredEventListener>(); + } + + public IEnumerable<IRegisteredEventListener> GetEventListeners() + { + return _callbacks.Select(i => i.Value); + } + + public IDisposable Add(IRegisteredEventListener callback) + { + var id = Interlocked.Increment(ref _idLast); + + if (!_callbacks.TryAdd(id, callback)) + { + Debug.Fail("Failed to register the event listener"); + } + + return new UnregisterEvent(this, id); + } + + private void Remove(int id) + { + _callbacks.TryRemove(id, out _); + } + + private class UnregisterEvent : IDisposable + { + private readonly TemporaryEventRegister _register; + private readonly int _id; + + public UnregisterEvent(TemporaryEventRegister register, int id) + { + _register = register; + _id = id; + } + + public void Dispose() + { + _register.Remove(_id); + } + } + } +}
\ No newline at end of file diff --git a/Impostor-dev/src/Impostor.Server/Events/Register/WrappedRegisteredEventListener.cs b/Impostor-dev/src/Impostor.Server/Events/Register/WrappedRegisteredEventListener.cs new file mode 100644 index 0000000..dd668c5 --- /dev/null +++ b/Impostor-dev/src/Impostor.Server/Events/Register/WrappedRegisteredEventListener.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using Impostor.Api.Events; + +namespace Impostor.Server.Events.Register +{ + internal class WrappedRegisteredEventListener : IRegisteredEventListener + { + private readonly IRegisteredEventListener _innerObject; + private readonly object _object; + + public WrappedRegisteredEventListener(IRegisteredEventListener innerObject, object o) + { + _innerObject = innerObject; + _object = o; + } + + public Type EventType => _innerObject.EventType; + + public EventPriority Priority => _innerObject.Priority; + + public ValueTask InvokeAsync(object eventHandler, object @event, IServiceProvider provider) + { + return _innerObject.InvokeAsync(_object, @event, provider); + } + } +}
\ No newline at end of file |