summaryrefslogtreecommitdiff
path: root/Impostor-dev/src/Impostor.Server/Events
diff options
context:
space:
mode:
Diffstat (limited to 'Impostor-dev/src/Impostor.Server/Events')
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/EventHandler.cs24
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/EventManager.cs167
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/GameAlterEvent.cs18
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/GameCreatedEvent.cs15
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/GameDestroyedEvent.cs15
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/GameEndedEvent.cs19
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/GamePlayerJoinedEvent.cs19
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/GamePlayerLeftEvent.cs22
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/GameStartedEvent.cs15
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/GameStartingEvent.cs15
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Meeting/MeetingEndedEvent.cs19
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Meeting/MeetingStartedEvent.cs19
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerChatEvent.cs26
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerCompletedTaskEvent.cs27
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerDestroyedEvent.cs23
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerExileEvent.cs23
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerMovementEvent.cs24
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerMurderEvent.cs26
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerSetStartCounterEvent.cs26
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerSpawnedEvent.cs23
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerStartMeetingEvent.cs26
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Game/Player/PlayerVentEvent.cs30
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/MultiDisposable.cs26
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Register/IRegisteredEventListener.cs15
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Register/InvokedRegisteredEventListener.cs27
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Register/ManualRegisteredEventListener.cs30
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Register/RegisteredEventListener.cs166
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Register/TemporaryEventRegister.cs59
-rw-r--r--Impostor-dev/src/Impostor.Server/Events/Register/WrappedRegisteredEventListener.cs27
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