From 852badb48e71d28fe252191332223380d71530db Mon Sep 17 00:00:00 2001 From: Gerard Smit Date: Mon, 19 Oct 2020 11:31:25 +0200 Subject: [PATCH] Plugin loader (#59) * Parse GameData headers * Added GameData spawn * GameData playerinfo parsing * Added BufferMessageReader tests * Fix InnerGameData * Added CustomNetworkTransform and VoteBanSystem * Revert changes * Finish CustomNetworkTransform * Implemented a simple plugin loader * Replaced Reference with PackageReference Note to myself: don't trust Rider auto-add reference * Use classes and moved all API-related stuff to one folder * Added PlayerJoinedGameEvent and PlayerLeftGameEvent * Removed Impostor.Server.Hazel * Moved more API-specific stuff Co-authored-by: AeonLucid --- src/Impostor.Plugins.Debugger/App.razor | 10 ++ src/Impostor.Plugins.Debugger/DebugPlugin.cs | 36 +++++++ .../Impostor.Plugins.Debugger.csproj | 12 +++ .../Pages/Index.razor | 68 +++++++++++++ .../Pages/_Host.cshtml | 19 ++++ .../Shared/MainLayout.razor | 5 + src/Impostor.Plugins.Debugger/_Imports.razor | 8 ++ .../Attributes/EventListenerAttribute.cs | 7 +- .../Events/Game/GameDestroyedEvent.cs | 22 +++++ .../Events/Game/PlayerJoinedGameEvent.cs | 18 ++++ .../Events/Game/PlayerLeftGameEvent.cs | 21 ++++ .../Events/Managers/IEventManager.cs | 22 ++++- .../Extensions/GameManagerExtensions.cs | 2 +- src/Impostor.Server.Api/Games/IGame.cs | 27 ----- .../Games/Managers/IGameManager.cs | 9 -- .../Impostor.Server.Api.csproj | 2 +- .../Impostor.Server.Api.csproj.DotSettings | 1 + .../Net/Factories/IClientFactory.cs | 15 --- src/Impostor.Server.Api/Net/IClient.cs | 6 +- src/Impostor.Server.Api/Net/IConnection.cs | 4 +- .../Net/Manager/IClientManager.cs | 10 +- .../Net/Manager/IMatchmaker.cs | 34 ------- src/Impostor.Server.Api/Plugins/IPlugin.cs | 6 ++ src/Impostor.Server.Api/Plugins/PluginBase.cs | 10 ++ .../Extensions/ServiceExtensions.cs | 14 --- .../Impostor.Server.Hazel.csproj | 24 ----- .../Impostor.Server.Hazel.csproj.DotSettings | 2 - src/Impostor.Server/Api/ClientBase.Api.cs | 10 ++ src/Impostor.Server/Api/ClientManager.Api.cs | 10 ++ src/Impostor.Server/Api/ClientPlayer.Api.cs | 14 +++ src/Impostor.Server/Api/Game.Api.cs | 10 ++ src/Impostor.Server/Api/GameManager.Api.cs | 15 +++ .../Api/HazelConnection.Api.cs | 10 ++ src/Impostor.Server/Events/EventManager.cs | 53 ++++++++++ src/Impostor.Server/Events/MultiDisposable.cs | 26 +++++ .../Events/RegisteredEventListener.cs | 52 ++++------ .../Events/TemporaryEventRegister.cs | 78 +++++++++++++++ src/Impostor.Server/Impostor.Server.csproj | 3 +- src/Impostor.Server/Net/Client.cs | 7 +- src/Impostor.Server/Net/ClientBase.cs | 10 +- .../Net/Factories/ClientFactory.cs | 3 +- .../Net/Factories/IClientFactory.cs | 9 ++ .../Net/Hazel}/HazelConnection.cs | 4 +- .../Hazel}/Messages/BufferMessageReader.cs | 0 .../Messages/HazelConnectionMessageWriter.cs | 0 .../Hazel}/Messages/HazelGameMessageWriter.cs | 5 +- .../Net/Hazel}/Messages/HazelMessage.cs | 0 .../Net/Hazel}/Messages/HazelMessageWriter.cs | 0 .../Net/Manager/ClientManager.cs | 17 ++-- .../Net/Manager/GameManager.cs | 14 +-- .../Net/Matchmaker.cs} | 18 ++-- src/Impostor.Server/Net/MatchmakerService.cs | 7 +- .../Net/Redirector/ClientRedirector.cs | 7 +- src/Impostor.Server/Net/State/ClientPlayer.cs | 12 +-- src/Impostor.Server/Net/State/Game.Data.cs | 4 +- .../Net/State/Game.Incoming.cs | 10 +- src/Impostor.Server/Net/State/Game.State.cs | 7 +- src/Impostor.Server/Net/State/Game.cs | 32 +++--- .../Plugins/AssemblyInformation.cs | 38 +++++++ .../Plugins/IAssemblyInformation.cs | 14 +++ .../Plugins/LoadedAssemblyInformation.cs | 25 +++++ src/Impostor.Server/Plugins/PluginConfig.cs | 11 +++ src/Impostor.Server/Plugins/PluginLoader.cs | 99 +++++++++++++++++++ src/Impostor.Server/Program.cs | 40 ++++++-- .../Properties/AssemblyInfo.cs | 3 + .../Hazel/BufferMessageReaderTests.cs | 2 +- src/Impostor.Tests/Impostor.Tests.csproj | 4 +- src/Impostor.sln | 24 ++--- 68 files changed, 840 insertions(+), 281 deletions(-) create mode 100644 src/Impostor.Plugins.Debugger/App.razor create mode 100644 src/Impostor.Plugins.Debugger/DebugPlugin.cs create mode 100644 src/Impostor.Plugins.Debugger/Impostor.Plugins.Debugger.csproj create mode 100644 src/Impostor.Plugins.Debugger/Pages/Index.razor create mode 100644 src/Impostor.Plugins.Debugger/Pages/_Host.cshtml create mode 100644 src/Impostor.Plugins.Debugger/Shared/MainLayout.razor create mode 100644 src/Impostor.Plugins.Debugger/_Imports.razor create mode 100644 src/Impostor.Server.Api/Events/Game/GameDestroyedEvent.cs create mode 100644 src/Impostor.Server.Api/Events/Game/PlayerJoinedGameEvent.cs create mode 100644 src/Impostor.Server.Api/Events/Game/PlayerLeftGameEvent.cs rename src/Impostor.Server.Api/{ => Games}/Extensions/GameManagerExtensions.cs (91%) delete mode 100644 src/Impostor.Server.Api/Net/Factories/IClientFactory.cs delete mode 100644 src/Impostor.Server.Api/Net/Manager/IMatchmaker.cs delete mode 100644 src/Impostor.Server.Hazel/Extensions/ServiceExtensions.cs delete mode 100644 src/Impostor.Server.Hazel/Impostor.Server.Hazel.csproj delete mode 100644 src/Impostor.Server.Hazel/Impostor.Server.Hazel.csproj.DotSettings create mode 100644 src/Impostor.Server/Api/ClientBase.Api.cs create mode 100644 src/Impostor.Server/Api/ClientManager.Api.cs create mode 100644 src/Impostor.Server/Api/ClientPlayer.Api.cs create mode 100644 src/Impostor.Server/Api/Game.Api.cs create mode 100644 src/Impostor.Server/Api/GameManager.Api.cs create mode 100644 src/Impostor.Server/Api/HazelConnection.Api.cs create mode 100644 src/Impostor.Server/Events/MultiDisposable.cs create mode 100644 src/Impostor.Server/Events/TemporaryEventRegister.cs create mode 100644 src/Impostor.Server/Net/Factories/IClientFactory.cs rename src/{Impostor.Server.Hazel => Impostor.Server/Net/Hazel}/HazelConnection.cs (95%) rename src/{Impostor.Server.Hazel => Impostor.Server/Net/Hazel}/Messages/BufferMessageReader.cs (100%) rename src/{Impostor.Server.Hazel => Impostor.Server/Net/Hazel}/Messages/HazelConnectionMessageWriter.cs (100%) rename src/{Impostor.Server.Hazel => Impostor.Server/Net/Hazel}/Messages/HazelGameMessageWriter.cs (92%) rename src/{Impostor.Server.Hazel => Impostor.Server/Net/Hazel}/Messages/HazelMessage.cs (100%) rename src/{Impostor.Server.Hazel => Impostor.Server/Net/Hazel}/Messages/HazelMessageWriter.cs (100%) rename src/{Impostor.Server.Hazel/HazelMatchmaker.cs => Impostor.Server/Net/Matchmaker.cs} (78%) create mode 100644 src/Impostor.Server/Plugins/AssemblyInformation.cs create mode 100644 src/Impostor.Server/Plugins/IAssemblyInformation.cs create mode 100644 src/Impostor.Server/Plugins/LoadedAssemblyInformation.cs create mode 100644 src/Impostor.Server/Plugins/PluginConfig.cs create mode 100644 src/Impostor.Server/Plugins/PluginLoader.cs create mode 100644 src/Impostor.Server/Properties/AssemblyInfo.cs diff --git a/src/Impostor.Plugins.Debugger/App.razor b/src/Impostor.Plugins.Debugger/App.razor new file mode 100644 index 0000000..38e8633 --- /dev/null +++ b/src/Impostor.Plugins.Debugger/App.razor @@ -0,0 +1,10 @@ + + + + + + +

Sorry, there's nothing at this address.

+
+
+
\ No newline at end of file diff --git a/src/Impostor.Plugins.Debugger/DebugPlugin.cs b/src/Impostor.Plugins.Debugger/DebugPlugin.cs new file mode 100644 index 0000000..de18d73 --- /dev/null +++ b/src/Impostor.Plugins.Debugger/DebugPlugin.cs @@ -0,0 +1,36 @@ +using System; +using Impostor.Server.Plugins; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Impostor.Plugins.Debugger +{ + public class DebugPlugin : PluginBase + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddRazorPages(); + services.AddServerSideBlazor(); + } + + public override void ConfigureHost(IHostBuilder host) + { + host.ConfigureWebHostDefaults(webBuilder => + { + webBuilder.Configure(app => + { + app.UseStaticFiles(); + app.UseRouting(); + + app.UseEndpoints(endpoints => + { + endpoints.MapBlazorHub(); + endpoints.MapFallbackToPage("/_Host"); + }); + }); + }); + } + } +} \ No newline at end of file diff --git a/src/Impostor.Plugins.Debugger/Impostor.Plugins.Debugger.csproj b/src/Impostor.Plugins.Debugger/Impostor.Plugins.Debugger.csproj new file mode 100644 index 0000000..1852d26 --- /dev/null +++ b/src/Impostor.Plugins.Debugger/Impostor.Plugins.Debugger.csproj @@ -0,0 +1,12 @@ + + + + net5.0 + Library + + + + + + + \ No newline at end of file diff --git a/src/Impostor.Plugins.Debugger/Pages/Index.razor b/src/Impostor.Plugins.Debugger/Pages/Index.razor new file mode 100644 index 0000000..7ca76ed --- /dev/null +++ b/src/Impostor.Plugins.Debugger/Pages/Index.razor @@ -0,0 +1,68 @@ +@page "/" +@using Impostor.Server.Events +@using Impostor.Server.Events.Managers +@using Impostor.Server.Games.Managers +@implements IDisposable +@implements IEventListener +@inject IEventManager EventManager +@inject IGameManager GameManager + +
+

Games

+ @if (GameManager.Games.Any()) + { + + + + + + + + + @foreach (var game in GameManager.Games) + { + + + + + } + +
CodePlayers
@game.Code +
    + @foreach (var player in game.Players) + { +
  • @player.Client.Name
  • + } +
+
+ } + else + { +
+ There are no active games. +
+ } +
+ +@code { + private IDisposable _disposable; + + [EventListener(typeof(GameCreatedEvent))] + [EventListener(typeof(GameDestroyedEvent))] + [EventListener(typeof(PlayerJoinedGameEvent))] + [EventListener(typeof(PlayerLeftGameEvent))] + public void OnGameCreated(IGameEvent e) + { + StateHasChanged(); + } + + protected override void OnInitialized() + { + _disposable = EventManager.RegisterListener(this, InvokeAsync); + } + + public void Dispose() + { + _disposable?.Dispose(); + } +} \ No newline at end of file diff --git a/src/Impostor.Plugins.Debugger/Pages/_Host.cshtml b/src/Impostor.Plugins.Debugger/Pages/_Host.cshtml new file mode 100644 index 0000000..eed3aaf --- /dev/null +++ b/src/Impostor.Plugins.Debugger/Pages/_Host.cshtml @@ -0,0 +1,19 @@ +@page "/" +@namespace Impostor.Plugins.Debugger.Pages +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers + + + + + + + Impostor Debugger + + + + + + + + + \ No newline at end of file diff --git a/src/Impostor.Plugins.Debugger/Shared/MainLayout.razor b/src/Impostor.Plugins.Debugger/Shared/MainLayout.razor new file mode 100644 index 0000000..07da9d6 --- /dev/null +++ b/src/Impostor.Plugins.Debugger/Shared/MainLayout.razor @@ -0,0 +1,5 @@ +@inherits LayoutComponentBase + +
+ @Body +
\ No newline at end of file diff --git a/src/Impostor.Plugins.Debugger/_Imports.razor b/src/Impostor.Plugins.Debugger/_Imports.razor new file mode 100644 index 0000000..109ec4e --- /dev/null +++ b/src/Impostor.Plugins.Debugger/_Imports.razor @@ -0,0 +1,8 @@ +@using System.Net.Http +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Components.Authorization +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.JSInterop +@using Impostor.Plugins.Debugger.Shared \ No newline at end of file diff --git a/src/Impostor.Server.Api/Events/Attributes/EventListenerAttribute.cs b/src/Impostor.Server.Api/Events/Attributes/EventListenerAttribute.cs index bea45c1..c8b9360 100644 --- a/src/Impostor.Server.Api/Events/Attributes/EventListenerAttribute.cs +++ b/src/Impostor.Server.Api/Events/Attributes/EventListenerAttribute.cs @@ -2,19 +2,18 @@ namespace Impostor.Server.Events { - [AttributeUsage(AttributeTargets.Method)] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class EventListenerAttribute : Attribute { public EventListenerAttribute(EventPriority priority = EventPriority.Normal) { Priority = priority; - Events = new Type[0]; } public EventListenerAttribute(Type @event, EventPriority priority = EventPriority.Normal) { Priority = priority; - Events = new[] { @event }; + Event = @event; } /// @@ -25,7 +24,7 @@ namespace Impostor.Server.Events /// /// The events that the listener is listening to. /// - public Type[] Events { get; set; } + public Type? Event { get; set; } /// /// If set to true, the listener will be called regardless of the . diff --git a/src/Impostor.Server.Api/Events/Game/GameDestroyedEvent.cs b/src/Impostor.Server.Api/Events/Game/GameDestroyedEvent.cs new file mode 100644 index 0000000..5d32143 --- /dev/null +++ b/src/Impostor.Server.Api/Events/Game/GameDestroyedEvent.cs @@ -0,0 +1,22 @@ +using Impostor.Server.Games; + +namespace Impostor.Server.Events +{ + /// + /// Called whenever a new is destroyed. + /// + public sealed class GameDestroyedEvent : IGameEvent + { + /// + /// Initializes a new instance of the class. + /// + /// Instance of the game. + public GameDestroyedEvent(IGame game) + { + Game = game; + } + + /// + public IGame Game { get; } + } +} \ No newline at end of file diff --git a/src/Impostor.Server.Api/Events/Game/PlayerJoinedGameEvent.cs b/src/Impostor.Server.Api/Events/Game/PlayerJoinedGameEvent.cs new file mode 100644 index 0000000..0a47f64 --- /dev/null +++ b/src/Impostor.Server.Api/Events/Game/PlayerJoinedGameEvent.cs @@ -0,0 +1,18 @@ +using Impostor.Server.Games; +using Impostor.Server.Net; + +namespace Impostor.Server.Events +{ + public class PlayerJoinedGameEvent : IGameEvent + { + public PlayerJoinedGameEvent(IGame game, IClientPlayer player) + { + Game = game; + Player = player; + } + + public IGame Game { get; } + + public IClientPlayer Player { get; } + } +} \ No newline at end of file diff --git a/src/Impostor.Server.Api/Events/Game/PlayerLeftGameEvent.cs b/src/Impostor.Server.Api/Events/Game/PlayerLeftGameEvent.cs new file mode 100644 index 0000000..e17c333 --- /dev/null +++ b/src/Impostor.Server.Api/Events/Game/PlayerLeftGameEvent.cs @@ -0,0 +1,21 @@ +using Impostor.Server.Games; +using Impostor.Server.Net; + +namespace Impostor.Server.Events +{ + public class PlayerLeftGameEvent : IGameEvent + { + public PlayerLeftGameEvent(IGame game, IClientPlayer player, bool isBan) + { + Game = game; + Player = player; + IsBan = isBan; + } + + public IGame Game { get; } + + public IClientPlayer Player { get; } + + public bool IsBan { get; } + } +} \ No newline at end of file diff --git a/src/Impostor.Server.Api/Events/Managers/IEventManager.cs b/src/Impostor.Server.Api/Events/Managers/IEventManager.cs index 488e38c..11ae01d 100644 --- a/src/Impostor.Server.Api/Events/Managers/IEventManager.cs +++ b/src/Impostor.Server.Api/Events/Managers/IEventManager.cs @@ -1,9 +1,29 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace Impostor.Server.Events.Managers { public interface IEventManager { + /// + /// Register a temporary event listener. + /// + /// Event callback. + /// Disposable that unregisters the callback from the event manager. + /// Type of the event. + IDisposable Register(Func callback) + where TEvent : IEvent; + + /// + /// Register a temporary event listener. + /// + /// Event listener. + /// Middleware between the events, which can be used to swap to the correct thread dispatcher. + /// Disposable that unregisters the callback from the event manager. + /// Type of the event listener. + IDisposable RegisterListener(TListener listener, Func, Task>? invoker = null) + where TListener : IEventListener; + /// /// Returns true if an event with the type is registered. /// diff --git a/src/Impostor.Server.Api/Extensions/GameManagerExtensions.cs b/src/Impostor.Server.Api/Games/Extensions/GameManagerExtensions.cs similarity index 91% rename from src/Impostor.Server.Api/Extensions/GameManagerExtensions.cs rename to src/Impostor.Server.Api/Games/Extensions/GameManagerExtensions.cs index df89168..db173c2 100644 --- a/src/Impostor.Server.Api/Extensions/GameManagerExtensions.cs +++ b/src/Impostor.Server.Api/Games/Extensions/GameManagerExtensions.cs @@ -2,7 +2,7 @@ using Impostor.Server.Games.Managers; using Impostor.Shared.Innersloth.Data; -namespace Impostor.Server +namespace Impostor.Server.Games { public static class GameManagerExtensions { diff --git a/src/Impostor.Server.Api/Games/IGame.cs b/src/Impostor.Server.Api/Games/IGame.cs index 84a301d..c4931b7 100644 --- a/src/Impostor.Server.Api/Games/IGame.cs +++ b/src/Impostor.Server.Api/Games/IGame.cs @@ -32,32 +32,5 @@ namespace Impostor.Server.Games int HostId { get; } IGameMessageWriter CreateMessage(MessageType type); - - bool TryGetPlayer(int id, [NotNullWhen(true)] out IClientPlayer player); - - /// - /// Register a new client to the game. - /// - /// Client to register. - /// Join result. - ValueTask AddClientAsync(IClient client); - - /// - /// Kicks all the players from the game to end the game. - /// - /// A representing the asynchronous operation. - ValueTask EndAsync(); - - ValueTask HandleStartGame(IMessageReader reader); - - ValueTask HandleEndGame(IMessageReader reader); - - ValueTask HandleKickPlayer(int playerId, bool isBan); - - ValueTask HandleRemovePlayer(int playerId, DisconnectReason reason); - - ValueTask HandleAlterGame(IMessageReader message, IClientPlayer sender, bool isPublic); - - ValueTask HandleGameData(IMessageReader parent, IClientPlayer sender, bool toPlayer); } } \ No newline at end of file diff --git a/src/Impostor.Server.Api/Games/Managers/IGameManager.cs b/src/Impostor.Server.Api/Games/Managers/IGameManager.cs index 9cb9646..d089772 100644 --- a/src/Impostor.Server.Api/Games/Managers/IGameManager.cs +++ b/src/Impostor.Server.Api/Games/Managers/IGameManager.cs @@ -1,7 +1,4 @@ using System.Collections.Generic; -using System.Threading.Tasks; -using Impostor.Shared.Innersloth; -using Impostor.Shared.Innersloth.Data; namespace Impostor.Server.Games.Managers { @@ -9,12 +6,6 @@ namespace Impostor.Server.Games.Managers { IEnumerable Games { get; } - ValueTask CreateAsync(GameOptionsData options); - IGame? Find(GameCode code); - - IEnumerable FindListings(MapFlags map, int impostorCount, GameKeywords language, int count = 10); - - ValueTask RemoveAsync(GameCode code); } } \ No newline at end of file diff --git a/src/Impostor.Server.Api/Impostor.Server.Api.csproj b/src/Impostor.Server.Api/Impostor.Server.Api.csproj index 1518fb6..0e72d47 100644 --- a/src/Impostor.Server.Api/Impostor.Server.Api.csproj +++ b/src/Impostor.Server.Api/Impostor.Server.Api.csproj @@ -14,7 +14,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Impostor.Server.Api/Impostor.Server.Api.csproj.DotSettings b/src/Impostor.Server.Api/Impostor.Server.Api.csproj.DotSettings index c8f8b80..2df7445 100644 --- a/src/Impostor.Server.Api/Impostor.Server.Api.csproj.DotSettings +++ b/src/Impostor.Server.Api/Impostor.Server.Api.csproj.DotSettings @@ -4,4 +4,5 @@ True True True + True True \ No newline at end of file diff --git a/src/Impostor.Server.Api/Net/Factories/IClientFactory.cs b/src/Impostor.Server.Api/Net/Factories/IClientFactory.cs deleted file mode 100644 index ac0faec..0000000 --- a/src/Impostor.Server.Api/Net/Factories/IClientFactory.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Threading.Tasks; - -namespace Impostor.Server.Net.Factories -{ - public interface IClientFactory - { - /// - /// Creates a client for the Hazel . - /// - /// Hazel connection. - /// - /// - IClient Create(IConnection connection, string name, int clientVersion); - } -} \ No newline at end of file diff --git a/src/Impostor.Server.Api/Net/IClient.cs b/src/Impostor.Server.Api/Net/IClient.cs index 0283a75..4d6d22d 100644 --- a/src/Impostor.Server.Api/Net/IClient.cs +++ b/src/Impostor.Server.Api/Net/IClient.cs @@ -57,10 +57,6 @@ namespace Impostor.Server.Net /// /// Gets or sets the current game data of the . /// - IClientPlayer? Player { get; set; } - - ValueTask HandleMessageAsync(IMessage message); - - ValueTask HandleDisconnectAsync(string reason); + IClientPlayer? Player { get; } } } \ No newline at end of file diff --git a/src/Impostor.Server.Api/Net/IConnection.cs b/src/Impostor.Server.Api/Net/IConnection.cs index 371c5ac..69d9119 100644 --- a/src/Impostor.Server.Api/Net/IConnection.cs +++ b/src/Impostor.Server.Api/Net/IConnection.cs @@ -22,9 +22,9 @@ namespace Impostor.Server.Net bool IsConnected { get; } /// - /// Gets or sets the client of the connection. + /// Gets the client of the connection. /// - IClient? Client { get; set; } + IClient? Client { get; } /// /// Create a message writer that can be send to the connection. diff --git a/src/Impostor.Server.Api/Net/Manager/IClientManager.cs b/src/Impostor.Server.Api/Net/Manager/IClientManager.cs index 46da848..6405a65 100644 --- a/src/Impostor.Server.Api/Net/Manager/IClientManager.cs +++ b/src/Impostor.Server.Api/Net/Manager/IClientManager.cs @@ -1,15 +1,9 @@ -using System.Threading.Tasks; +using System.Collections.Generic; namespace Impostor.Server.Net.Manager { public interface IClientManager { - ValueTask RegisterConnectionAsync(IConnection connection, string name, int clientVersion); - - void Register(IClient client); - - void Remove(IClient client); - - bool Validate(IClient client); + IEnumerable Clients { get; } } } \ No newline at end of file diff --git a/src/Impostor.Server.Api/Net/Manager/IMatchmaker.cs b/src/Impostor.Server.Api/Net/Manager/IMatchmaker.cs deleted file mode 100644 index 19ef796..0000000 --- a/src/Impostor.Server.Api/Net/Manager/IMatchmaker.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Net; -using System.Threading.Tasks; -using Impostor.Server.Games; -using Impostor.Server.Net.Messages; - -namespace Impostor.Server.Net.Manager -{ - /// - /// Represents the matchmaker which will listen for incoming connections. - /// - public interface IMatchmaker - { - /// - /// Starts the matchmaker on the given endpoint. - /// - /// Endpoint where the matchmaker should listen to. - /// A representing the asynchronous operation. - ValueTask StartAsync(IPEndPoint ipEndPoint); - - /// - /// Stop the matchmaker. - /// - /// A representing the asynchronous operation. - ValueTask StopAsync(); - - /// - /// Create a message writer that can be send to players in the game. - /// - /// The game. - /// Type of the message. - /// Message writer for the given game. - IGameMessageWriter CreateGameMessageWriter(IGame game, MessageType messageType); - } -} \ No newline at end of file diff --git a/src/Impostor.Server.Api/Plugins/IPlugin.cs b/src/Impostor.Server.Api/Plugins/IPlugin.cs index d31520b..e5f8645 100644 --- a/src/Impostor.Server.Api/Plugins/IPlugin.cs +++ b/src/Impostor.Server.Api/Plugins/IPlugin.cs @@ -1,5 +1,7 @@ using System.Threading.Tasks; using Impostor.Server.Events; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace Impostor.Server.Plugins { @@ -10,5 +12,9 @@ namespace Impostor.Server.Plugins ValueTask DisableAsync(); ValueTask ReloadAsync(); + + void ConfigureHost(IHostBuilder host); + + void ConfigureServices(IServiceCollection services); } } \ No newline at end of file diff --git a/src/Impostor.Server.Api/Plugins/PluginBase.cs b/src/Impostor.Server.Api/Plugins/PluginBase.cs index c392bba..5ef6cf3 100644 --- a/src/Impostor.Server.Api/Plugins/PluginBase.cs +++ b/src/Impostor.Server.Api/Plugins/PluginBase.cs @@ -1,4 +1,6 @@ using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace Impostor.Server.Plugins { @@ -18,5 +20,13 @@ namespace Impostor.Server.Plugins { return default; } + + public virtual void ConfigureHost(IHostBuilder host) + { + } + + public virtual void ConfigureServices(IServiceCollection services) + { + } } } \ No newline at end of file diff --git a/src/Impostor.Server.Hazel/Extensions/ServiceExtensions.cs b/src/Impostor.Server.Hazel/Extensions/ServiceExtensions.cs deleted file mode 100644 index b97a65f..0000000 --- a/src/Impostor.Server.Hazel/Extensions/ServiceExtensions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Impostor.Server.Net.Manager; -using Microsoft.Extensions.DependencyInjection; - -namespace Impostor.Server.Hazel -{ - public static class ServiceExtensions - { - public static IServiceCollection UseHazelMatchmaking(this IServiceCollection services) - { - services.AddSingleton(); - return services; - } - } -} \ No newline at end of file diff --git a/src/Impostor.Server.Hazel/Impostor.Server.Hazel.csproj b/src/Impostor.Server.Hazel/Impostor.Server.Hazel.csproj deleted file mode 100644 index 7d699cf..0000000 --- a/src/Impostor.Server.Hazel/Impostor.Server.Hazel.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net5.0 - true - None - - - - - - - - - - - - - - <_Parameter1>Impostor.Tests - - - - diff --git a/src/Impostor.Server.Hazel/Impostor.Server.Hazel.csproj.DotSettings b/src/Impostor.Server.Hazel/Impostor.Server.Hazel.csproj.DotSettings deleted file mode 100644 index 17962b1..0000000 --- a/src/Impostor.Server.Hazel/Impostor.Server.Hazel.csproj.DotSettings +++ /dev/null @@ -1,2 +0,0 @@ - - True \ No newline at end of file diff --git a/src/Impostor.Server/Api/ClientBase.Api.cs b/src/Impostor.Server/Api/ClientBase.Api.cs new file mode 100644 index 0000000..1a375d2 --- /dev/null +++ b/src/Impostor.Server/Api/ClientBase.Api.cs @@ -0,0 +1,10 @@ +// ReSharper disable once CheckNamespace +namespace Impostor.Server.Net +{ + internal abstract partial class ClientBase : IClient + { + IConnection IClient.Connection => Connection; + + IClientPlayer IClient.Player => Player; + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Api/ClientManager.Api.cs b/src/Impostor.Server/Api/ClientManager.Api.cs new file mode 100644 index 0000000..597dd5a --- /dev/null +++ b/src/Impostor.Server/Api/ClientManager.Api.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +// ReSharper disable once CheckNamespace +namespace Impostor.Server.Net.Manager +{ + internal partial class ClientManager : IClientManager + { + IEnumerable IClientManager.Clients => _clients.Values; + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Api/ClientPlayer.Api.cs b/src/Impostor.Server/Api/ClientPlayer.Api.cs new file mode 100644 index 0000000..b945f08 --- /dev/null +++ b/src/Impostor.Server/Api/ClientPlayer.Api.cs @@ -0,0 +1,14 @@ +using Impostor.Server.Games; + +// ReSharper disable once CheckNamespace +namespace Impostor.Server.Net.State +{ + internal partial class ClientPlayer + { + /// + IClient IClientPlayer.Client => Client; + + /// + IGame IClientPlayer.Game => Game; + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Api/Game.Api.cs b/src/Impostor.Server/Api/Game.Api.cs new file mode 100644 index 0000000..74743a9 --- /dev/null +++ b/src/Impostor.Server/Api/Game.Api.cs @@ -0,0 +1,10 @@ +using Impostor.Server.Games; + +// ReSharper disable once CheckNamespace +namespace Impostor.Server.Net.State +{ + internal partial class Game : IGame + { + IClientPlayer IGame.Host => Host; + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Api/GameManager.Api.cs b/src/Impostor.Server/Api/GameManager.Api.cs new file mode 100644 index 0000000..05010d3 --- /dev/null +++ b/src/Impostor.Server/Api/GameManager.Api.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.Linq; +using Impostor.Server.Games; +using Impostor.Server.Games.Managers; + +// ReSharper disable once CheckNamespace +namespace Impostor.Server.Net.Manager +{ + internal partial class GameManager : IGameManager + { + IEnumerable IGameManager.Games => _games.Select(kv => kv.Value); + + IGame IGameManager.Find(GameCode code) => Find(code); + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Api/HazelConnection.Api.cs b/src/Impostor.Server/Api/HazelConnection.Api.cs new file mode 100644 index 0000000..d70a8b8 --- /dev/null +++ b/src/Impostor.Server/Api/HazelConnection.Api.cs @@ -0,0 +1,10 @@ +using Impostor.Server.Net; + +// ReSharper disable once CheckNamespace +namespace Impostor.Server.Hazel +{ + internal partial class HazelConnection : IConnection + { + IClient IConnection.Client => Client; + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Events/EventManager.cs b/src/Impostor.Server/Events/EventManager.cs index f3d33ca..69ceef5 100644 --- a/src/Impostor.Server/Events/EventManager.cs +++ b/src/Impostor.Server/Events/EventManager.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Threading.Tasks; using Impostor.Server.Events.Managers; using Microsoft.Extensions.DependencyInjection; @@ -9,11 +11,49 @@ namespace Impostor.Server.Events { internal class EventManager : IEventManager { + private readonly ConcurrentDictionary _temporaryEventListeners; private readonly IServiceProvider _serviceProvider; public EventManager(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; + _temporaryEventListeners = new ConcurrentDictionary(); + } + + /// + public IDisposable Register(Func callback) + where TEvent : IEvent + { + var register = (TemporaryEventRegister) _temporaryEventListeners.GetOrAdd( + typeof(TEvent), + _ => new TemporaryEventRegister()); + + return register.Add(callback); + } + + /// + public IDisposable RegisterListener(TListener listener, Func, Task> invoker = null) + where TListener : IEventListener + { + if (listener == null) + { + throw new ArgumentNullException(nameof(listener)); + } + + var registerMethod = typeof(EventManager).GetMethod(nameof(RegisterListenerImpl), BindingFlags.Instance | BindingFlags.NonPublic); + var methods = RegisteredEventListener.FromType(listener.GetType()); + var disposes = new IDisposable[methods.Count]; + + for (var i = 0; i < methods.Count; i++) + { + var method = methods[i]; + + disposes[i] = (IDisposable) registerMethod! + .MakeGenericMethod(method.EventType) + .Invoke(this, new object[] { listener, method, invoker }); + } + + return new MultiDisposable(disposes); } /// @@ -37,6 +77,11 @@ namespace Impostor.Server.Events { await eventListener.InvokeAsync(handler, @event, scope.ServiceProvider); } + + if (_temporaryEventListeners.TryGetValue(typeof(T), out var cb)) + { + await ((TemporaryEventRegister) cb).CallAsync(scope.ServiceProvider, @event); + } } finally { @@ -67,5 +112,13 @@ namespace Impostor.Server.Events } } } + + private IDisposable RegisterListenerImpl(object obj, RegisteredEventListener listener, Func, Task> invoker = null) + where TEvent : IEvent + { + return invoker == null + ? Register((provider, @event) => listener.InvokeAsync(obj, @event, provider)) + : Register((provider, @event) => new ValueTask(invoker(() => listener.InvokeAsync(obj, @event, provider).AsTask()))); + } } } \ No newline at end of file diff --git a/src/Impostor.Server/Events/MultiDisposable.cs b/src/Impostor.Server/Events/MultiDisposable.cs new file mode 100644 index 0000000..fc6a6f0 --- /dev/null +++ b/src/Impostor.Server/Events/MultiDisposable.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Impostor.Server.Events +{ + /// + /// Disposes multiple . + /// + internal class MultiDisposable : IDisposable + { + private readonly IEnumerable _disposables; + + public MultiDisposable(IEnumerable disposables) + { + _disposables = disposables; + } + + public void Dispose() + { + foreach (var disposable in _disposables) + { + disposable.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Events/RegisteredEventListener.cs b/src/Impostor.Server/Events/RegisteredEventListener.cs index 058f099..d0021b3 100644 --- a/src/Impostor.Server/Events/RegisteredEventListener.cs +++ b/src/Impostor.Server/Events/RegisteredEventListener.cs @@ -63,7 +63,8 @@ namespace Impostor.Server.Events { var methodArgument = methodArguments[i]; - if (methodArgument.ParameterType == EventType) + if (typeof(IEvent).IsAssignableFrom(methodArgument.ParameterType) + && methodArgument.ParameterType.IsAssignableFrom(EventType)) { arguments[i] = @event; } @@ -85,17 +86,17 @@ namespace Impostor.Server.Events invoke = Expression.Block( Expression.IfThenElse( Expression.Property(@event, nameof(IEventCancelable.IsCancelled)), - Expression.Return(returnTarget, Expression.Constant(Task.CompletedTask)), + Expression.Return(returnTarget, Expression.Default(typeof(ValueTask))), Expression.Block( invoke, - Expression.Return(returnTarget, Expression.Constant(Task.CompletedTask)))), - Expression.Label(returnTarget, Expression.Constant(Task.CompletedTask))); + Expression.Return(returnTarget, Expression.Default(typeof(ValueTask))))), + Expression.Label(returnTarget, Expression.Default(typeof(ValueTask)))); } else { invoke = Expression.Block( invoke, - Expression.Label(returnTarget, Expression.Constant(Task.CompletedTask))); + Expression.Label(returnTarget, Expression.Default(typeof(ValueTask)))); } } else if (method.ReturnType == typeof(ValueTask)) @@ -105,9 +106,9 @@ namespace Impostor.Server.Events invoke = Expression.Block( Expression.IfThenElse( Expression.Property(@event, nameof(IEventCancelable.IsCancelled)), - Expression.Return(returnTarget, Expression.Constant(Task.CompletedTask)), + Expression.Return(returnTarget, Expression.Default(typeof(ValueTask))), Expression.Return(returnTarget, invoke)), - Expression.Label(returnTarget, Expression.Constant(Task.CompletedTask))); + Expression.Label(returnTarget, Expression.Default(typeof(ValueTask)))); } } else @@ -119,12 +120,12 @@ namespace Impostor.Server.Events .Compile(); } - public static IEnumerable FromType(Type type) + public static IReadOnlyList FromType(Type type) { return Instances.GetOrAdd(type, t => { return t.GetMethods() - .Where(m => !m.IsStatic && m.GetCustomAttribute(typeof(EventListenerAttribute), false) != null) + .Where(m => !m.IsStatic && m.GetCustomAttributes(typeof(EventListenerAttribute), false).Any()) .SelectMany(m => FromMethod(t, m)) .ToArray(); }); @@ -141,34 +142,21 @@ namespace Impostor.Server.Events } // Register the event. - var attribute = methodType.GetCustomAttribute(false); - - if (attribute == null) + foreach (var attribute in methodType.GetCustomAttributes(false)) { - yield break; - } - - Type[] eventTypes; + var eventType = attribute.Event; - if (attribute.Events.Length == 0) - { - if (methodType.GetParameters().Length == 0 || !typeof(IEvent).IsAssignableFrom(methodType.GetParameters()[0].ParameterType)) + if (eventType == null) { - throw new InvalidOperationException($"The first parameter of the method {methodType.GetFriendlyName()} should be the type {nameof(IEvent)}."); - } - - eventTypes = new[] { methodType.GetParameters()[0].ParameterType }; - } - else - { - eventTypes = attribute.Events; - } + 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)}."); + } - foreach (var eventType in eventTypes) - { - var listener = new RegisteredEventListener(eventType, methodType, attribute, listenerType); + eventType = methodType.GetParameters()[0].ParameterType; + } - yield return listener; + yield return new RegisteredEventListener(eventType, methodType, attribute, listenerType); } } } diff --git a/src/Impostor.Server/Events/TemporaryEventRegister.cs b/src/Impostor.Server/Events/TemporaryEventRegister.cs new file mode 100644 index 0000000..834fe41 --- /dev/null +++ b/src/Impostor.Server/Events/TemporaryEventRegister.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Impostor.Server.Events +{ + internal class TemporaryEventRegister + where T : IEvent + { + private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); + private readonly List> _callbacks = new List>(); + + public async ValueTask CallAsync(IServiceProvider provider, T @event) + { + await semaphoreSlim.WaitAsync(); + + try + { + foreach (var callback in _callbacks) + { + await callback.Invoke(provider, @event); + } + } + finally + { + semaphoreSlim.Release(); + } + } + + public IDisposable Add(Func callback) + { + semaphoreSlim.Wait(); + + try + { + _callbacks.Add(callback); + } + finally + { + semaphoreSlim.Release(); + } + + return new UnregisterEvent(this, callback); + } + + private void Remove(Func callback) + { + semaphoreSlim.Wait(); + + try + { + _callbacks.Remove(callback); + } + finally + { + semaphoreSlim.Release(); + } + } + + private class UnregisterEvent : IDisposable + { + private readonly TemporaryEventRegister _register; + private readonly Func _callback; + + public UnregisterEvent(TemporaryEventRegister register, Func callback) + { + _register = register; + _callback = callback; + } + + public void Dispose() + { + _register.Remove(_callback); + } + } + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Impostor.Server.csproj b/src/Impostor.Server/Impostor.Server.csproj index 2f44e1d..c7472cc 100644 --- a/src/Impostor.Server/Impostor.Server.csproj +++ b/src/Impostor.Server/Impostor.Server.csproj @@ -21,13 +21,14 @@ + - + diff --git a/src/Impostor.Server/Net/Client.cs b/src/Impostor.Server/Net/Client.cs index 8293540..9a7217b 100644 --- a/src/Impostor.Server/Net/Client.cs +++ b/src/Impostor.Server/Net/Client.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Impostor.Server.Data; using Impostor.Server.Games; using Impostor.Server.Games.Managers; +using Impostor.Server.Hazel; using Impostor.Server.Net.Manager; using Impostor.Server.Net.Messages; using Impostor.Shared.Innersloth; @@ -16,10 +17,10 @@ namespace Impostor.Server.Net internal class Client : ClientBase { private readonly ILogger _logger; - private readonly IClientManager _clientManager; - private readonly IGameManager _gameManager; + private readonly ClientManager _clientManager; + private readonly GameManager _gameManager; - public Client(ILogger logger, IClientManager clientManager, IGameManager gameManager, string name, IConnection connection) + public Client(ILogger logger, ClientManager clientManager, GameManager gameManager, string name, HazelConnection connection) : base(name, connection) { _logger = logger; diff --git a/src/Impostor.Server/Net/ClientBase.cs b/src/Impostor.Server/Net/ClientBase.cs index 825acc1..dfeacd3 100644 --- a/src/Impostor.Server/Net/ClientBase.cs +++ b/src/Impostor.Server/Net/ClientBase.cs @@ -2,13 +2,15 @@ using System.Collections.Generic; using System.Threading.Tasks; using Hazel; +using Impostor.Server.Hazel; using Impostor.Server.Net.Messages; +using Impostor.Server.Net.State; namespace Impostor.Server.Net { - public abstract class ClientBase : IClient + internal abstract partial class ClientBase { - protected ClientBase(string name, IConnection connection) + protected ClientBase(string name, HazelConnection connection) { Name = name; Connection = connection; @@ -19,13 +21,13 @@ namespace Impostor.Server.Net public string Name { get; } - public IConnection Connection { get; } + public HazelConnection Connection { get; } public bool IsBot => false; public IDictionary Items { get; } - public IClientPlayer Player { get; set; } + public ClientPlayer Player { get; set; } public abstract ValueTask HandleMessageAsync(IMessage message); diff --git a/src/Impostor.Server/Net/Factories/ClientFactory.cs b/src/Impostor.Server/Net/Factories/ClientFactory.cs index 653eeda..e9ff13e 100644 --- a/src/Impostor.Server/Net/Factories/ClientFactory.cs +++ b/src/Impostor.Server/Net/Factories/ClientFactory.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Impostor.Server.Hazel; using Microsoft.Extensions.DependencyInjection; namespace Impostor.Server.Net.Factories @@ -14,7 +15,7 @@ namespace Impostor.Server.Net.Factories _serviceProvider = serviceProvider; } - public IClient Create(IConnection connection, string name, int clientVersion) + public ClientBase Create(HazelConnection connection, string name, int clientVersion) { var client = ActivatorUtilities.CreateInstance(_serviceProvider, name, connection); connection.Client = client; diff --git a/src/Impostor.Server/Net/Factories/IClientFactory.cs b/src/Impostor.Server/Net/Factories/IClientFactory.cs new file mode 100644 index 0000000..d09def4 --- /dev/null +++ b/src/Impostor.Server/Net/Factories/IClientFactory.cs @@ -0,0 +1,9 @@ +using Impostor.Server.Hazel; + +namespace Impostor.Server.Net.Factories +{ + internal interface IClientFactory + { + ClientBase Create(HazelConnection connection, string name, int clientVersion); + } +} \ No newline at end of file diff --git a/src/Impostor.Server.Hazel/HazelConnection.cs b/src/Impostor.Server/Net/Hazel/HazelConnection.cs similarity index 95% rename from src/Impostor.Server.Hazel/HazelConnection.cs rename to src/Impostor.Server/Net/Hazel/HazelConnection.cs index b0f5d5e..9e15256 100644 --- a/src/Impostor.Server.Hazel/HazelConnection.cs +++ b/src/Impostor.Server/Net/Hazel/HazelConnection.cs @@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging; namespace Impostor.Server.Hazel { - internal class HazelConnection : IConnection + internal partial class HazelConnection { private readonly ILogger _logger; @@ -27,7 +27,7 @@ namespace Impostor.Server.Hazel public bool IsConnected => InnerConnection.State == ConnectionState.Connected; - public IClient Client { get; set; } + public ClientBase Client { get; set; } private async ValueTask ConnectionOnDisconnected(DisconnectedEventArgs e) { diff --git a/src/Impostor.Server.Hazel/Messages/BufferMessageReader.cs b/src/Impostor.Server/Net/Hazel/Messages/BufferMessageReader.cs similarity index 100% rename from src/Impostor.Server.Hazel/Messages/BufferMessageReader.cs rename to src/Impostor.Server/Net/Hazel/Messages/BufferMessageReader.cs diff --git a/src/Impostor.Server.Hazel/Messages/HazelConnectionMessageWriter.cs b/src/Impostor.Server/Net/Hazel/Messages/HazelConnectionMessageWriter.cs similarity index 100% rename from src/Impostor.Server.Hazel/Messages/HazelConnectionMessageWriter.cs rename to src/Impostor.Server/Net/Hazel/Messages/HazelConnectionMessageWriter.cs diff --git a/src/Impostor.Server.Hazel/Messages/HazelGameMessageWriter.cs b/src/Impostor.Server/Net/Hazel/Messages/HazelGameMessageWriter.cs similarity index 92% rename from src/Impostor.Server.Hazel/Messages/HazelGameMessageWriter.cs rename to src/Impostor.Server/Net/Hazel/Messages/HazelGameMessageWriter.cs index e99533b..a87db88 100644 --- a/src/Impostor.Server.Hazel/Messages/HazelGameMessageWriter.cs +++ b/src/Impostor.Server/Net/Hazel/Messages/HazelGameMessageWriter.cs @@ -6,14 +6,15 @@ using Hazel; using Impostor.Server.Games; using Impostor.Server.Net; using Impostor.Server.Net.Messages; +using Impostor.Server.Net.State; namespace Impostor.Server.Hazel.Messages { internal class HazelGameMessageWriter : HazelMessageWriter, IGameMessageWriter { - private readonly IGame _game; + private readonly Game _game; - public HazelGameMessageWriter(MessageType type, IGame game) + public HazelGameMessageWriter(MessageType type, Game game) : base(type) { _game = game; diff --git a/src/Impostor.Server.Hazel/Messages/HazelMessage.cs b/src/Impostor.Server/Net/Hazel/Messages/HazelMessage.cs similarity index 100% rename from src/Impostor.Server.Hazel/Messages/HazelMessage.cs rename to src/Impostor.Server/Net/Hazel/Messages/HazelMessage.cs diff --git a/src/Impostor.Server.Hazel/Messages/HazelMessageWriter.cs b/src/Impostor.Server/Net/Hazel/Messages/HazelMessageWriter.cs similarity index 100% rename from src/Impostor.Server.Hazel/Messages/HazelMessageWriter.cs rename to src/Impostor.Server/Net/Hazel/Messages/HazelMessageWriter.cs diff --git a/src/Impostor.Server/Net/Manager/ClientManager.cs b/src/Impostor.Server/Net/Manager/ClientManager.cs index a178f77..37a1402 100644 --- a/src/Impostor.Server/Net/Manager/ClientManager.cs +++ b/src/Impostor.Server/Net/Manager/ClientManager.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Impostor.Server.Data; +using Impostor.Server.Hazel; using Impostor.Server.Net.Factories; using Impostor.Server.Net.Messages; using Impostor.Shared.Innersloth; @@ -11,7 +12,7 @@ using Microsoft.Extensions.Logging; namespace Impostor.Server.Net.Manager { - internal class ClientManager : IClientManager + internal partial class ClientManager { public static HashSet SupportedVersions { get; } = new HashSet { @@ -20,7 +21,7 @@ namespace Impostor.Server.Net.Manager }; private readonly ILogger _logger; - private readonly ConcurrentDictionary _clients; + private readonly ConcurrentDictionary _clients; private readonly IClientFactory _clientFactory; private int _idLast; @@ -28,9 +29,11 @@ namespace Impostor.Server.Net.Manager { _logger = logger; _clientFactory = clientFactory; - _clients = new ConcurrentDictionary(); + _clients = new ConcurrentDictionary(); } + public IEnumerable Clients => _clients.Values; + public int NextId() { var clientId = Interlocked.Increment(ref _idLast); @@ -47,7 +50,7 @@ namespace Impostor.Server.Net.Manager return clientId; } - public async ValueTask RegisterConnectionAsync(IConnection connection, string name, int clientVersion) + public async ValueTask RegisterConnectionAsync(HazelConnection connection, string name, int clientVersion) { if (name.Length > 10) { @@ -66,12 +69,6 @@ namespace Impostor.Server.Net.Manager } var client = _clientFactory.Create(connection, name, clientVersion); - - Register(client); - } - - public void Register(IClient client) - { var id = NextId(); client.Id = id; diff --git a/src/Impostor.Server/Net/Manager/GameManager.cs b/src/Impostor.Server/Net/Manager/GameManager.cs index a0e8339..e54f866 100644 --- a/src/Impostor.Server/Net/Manager/GameManager.cs +++ b/src/Impostor.Server/Net/Manager/GameManager.cs @@ -19,7 +19,7 @@ using Microsoft.Extensions.Options; namespace Impostor.Server.Net.Manager { - internal class GameManager : IGameManager + internal partial class GameManager { private readonly ILogger _logger; private readonly INodeLocator _nodeLocator; @@ -38,9 +38,7 @@ namespace Impostor.Server.Net.Manager _games = new ConcurrentDictionary(); } - public IEnumerable Games => _games.Select(kv => kv.Value); - - public async ValueTask CreateAsync(GameOptionsData options) + public async ValueTask CreateAsync(GameOptionsData options) { // TODO: Prevent duplicates when using server redirector using INodeProvider. var gameCode = GameCode.Create(); @@ -60,13 +58,13 @@ namespace Impostor.Server.Net.Manager return game; } - public IGame Find(GameCode code) + public Game Find(GameCode code) { _games.TryGetValue(code, out var game); return game; } - public IEnumerable FindListings(MapFlags map, int impostorCount, GameKeywords language, int count = 10) + public IEnumerable FindListings(MapFlags map, int impostorCount, GameKeywords language, int count = 10) { var results = 0; @@ -115,13 +113,15 @@ namespace Impostor.Server.Net.Manager return; } - if (!_games.TryRemove(gameCode, out _)) + if (!_games.TryRemove(gameCode, out game)) { return; } _logger.LogDebug("Remove game with code {0} ({1}).", GameCodeParser.IntToGameName(gameCode), gameCode); _nodeLocator.Remove(GameCodeParser.IntToGameName(gameCode)); + + await _eventManager.CallAsync(new GameDestroyedEvent(game)); } } } \ No newline at end of file diff --git a/src/Impostor.Server.Hazel/HazelMatchmaker.cs b/src/Impostor.Server/Net/Matchmaker.cs similarity index 78% rename from src/Impostor.Server.Hazel/HazelMatchmaker.cs rename to src/Impostor.Server/Net/Matchmaker.cs index cc647a1..462c26c 100644 --- a/src/Impostor.Server.Hazel/HazelMatchmaker.cs +++ b/src/Impostor.Server/Net/Matchmaker.cs @@ -8,20 +8,21 @@ using Impostor.Server.Games; using Impostor.Server.Hazel.Messages; using Impostor.Server.Net.Manager; using Impostor.Server.Net.Messages; +using Impostor.Server.Net.State; using Microsoft.Extensions.Logging; namespace Impostor.Server.Hazel { - internal class HazelMatchmaker : IMatchmaker + internal class Matchmaker { - private readonly IClientManager _clientManager; - private readonly ILogger _logger; + private readonly ClientManager _clientManager; + private readonly ILogger _logger; private readonly ILogger _connectionLogger; private UdpConnectionListener _connection; - public HazelMatchmaker( - ILogger logger, - IClientManager clientManager, + public Matchmaker( + ILogger logger, + ClientManager clientManager, ILogger connectionLogger) { _logger = logger; @@ -60,10 +61,5 @@ namespace Impostor.Server.Hazel // Register client await _clientManager.RegisterConnectionAsync(connection, name, clientVersion); } - - public IGameMessageWriter CreateGameMessageWriter(IGame game, MessageType messageType) - { - return new HazelGameMessageWriter(messageType, game); - } } } \ No newline at end of file diff --git a/src/Impostor.Server/Net/MatchmakerService.cs b/src/Impostor.Server/Net/MatchmakerService.cs index 1405b97..2384716 100644 --- a/src/Impostor.Server/Net/MatchmakerService.cs +++ b/src/Impostor.Server/Net/MatchmakerService.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Impostor.Server.Data; +using Impostor.Server.Hazel; using Impostor.Server.Net.Manager; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -14,13 +15,13 @@ namespace Impostor.Server.Net private readonly ILogger _logger; private readonly ServerConfig _serverConfig; private readonly ServerRedirectorConfig _redirectorConfig; - private readonly IMatchmaker _matchmaker; + private readonly Matchmaker _matchmaker; public MatchmakerService( ILogger logger, IOptions serverConfig, IOptions redirectorConfig, - IMatchmaker matchmaker) + Matchmaker matchmaker) { _logger = logger; _serverConfig = serverConfig.Value; @@ -35,7 +36,7 @@ namespace Impostor.Server.Net await _matchmaker.StartAsync(endpoint); _logger.LogInformation( - "Matchmaker is listening on {0}:{1}, the public server ip is {2}:{3}.", + "Matchmaker is listening on {0}:{1}, the public server ip is {2}:{3}.", endpoint.Address, endpoint.Port, _serverConfig.PublicIp, diff --git a/src/Impostor.Server/Net/Redirector/ClientRedirector.cs b/src/Impostor.Server/Net/Redirector/ClientRedirector.cs index 1302363..8e18e04 100644 --- a/src/Impostor.Server/Net/Redirector/ClientRedirector.cs +++ b/src/Impostor.Server/Net/Redirector/ClientRedirector.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Impostor.Server.Data; +using Impostor.Server.Hazel; using Impostor.Server.Net.Manager; using Impostor.Server.Net.Messages; using Impostor.Shared.Innersloth; @@ -13,14 +14,14 @@ namespace Impostor.Server.Net.Redirector { private static readonly ILogger Logger = Log.ForContext(); - private readonly IClientManager _clientManager; + private readonly ClientManager _clientManager; private readonly INodeProvider _nodeProvider; private readonly INodeLocator _nodeLocator; public ClientRedirector( string name, - IConnection connection, - IClientManager clientManager, + HazelConnection connection, + ClientManager clientManager, INodeProvider nodeProvider, INodeLocator nodeLocator) : base(name, connection) diff --git a/src/Impostor.Server/Net/State/ClientPlayer.cs b/src/Impostor.Server/Net/State/ClientPlayer.cs index b6734c4..a744e88 100644 --- a/src/Impostor.Server/Net/State/ClientPlayer.cs +++ b/src/Impostor.Server/Net/State/ClientPlayer.cs @@ -4,28 +4,22 @@ using Impostor.Shared.Innersloth.Data; namespace Impostor.Server.Net.State { - internal class ClientPlayer : IClientPlayer + internal partial class ClientPlayer : IClientPlayer { - public ClientPlayer(IClient client, Game game) + public ClientPlayer(ClientBase client, Game game) { Game = game; Client = client; Limbo = LimboStates.PreSpawn; } - public IClient Client { get; } + public ClientBase Client { get; } public Game Game { get; } /// public LimboStates Limbo { get; set; } - /// - IClient IClientPlayer.Client => Client; - - /// - IGame IClientPlayer.Game => Game; - /// public ValueTask KickAsync() { diff --git a/src/Impostor.Server/Net/State/Game.Data.cs b/src/Impostor.Server/Net/State/Game.Data.cs index a1ba361..d3577b3 100644 --- a/src/Impostor.Server/Net/State/Game.Data.cs +++ b/src/Impostor.Server/Net/State/Game.Data.cs @@ -26,10 +26,10 @@ namespace Impostor.Server.Net.State private readonly List _allObjects = new List(); private readonly Dictionary _allObjectsFast = new Dictionary(); - public async ValueTask HandleGameData(IMessageReader parent, IClientPlayer sender, bool toPlayer) + public async ValueTask HandleGameData(IMessageReader parent, ClientPlayer sender, bool toPlayer) { // Find target player. - IClientPlayer target = null; + ClientPlayer target = null; if (toPlayer) { diff --git a/src/Impostor.Server/Net/State/Game.Incoming.cs b/src/Impostor.Server/Net/State/Game.Incoming.cs index f439995..888f6d4 100644 --- a/src/Impostor.Server/Net/State/Game.Incoming.cs +++ b/src/Impostor.Server/Net/State/Game.Incoming.cs @@ -17,7 +17,7 @@ namespace Impostor.Server.Net.State await packet.SendToAllAsync(); } - public async ValueTask AddClientAsync(IClient client) + public async ValueTask AddClientAsync(ClientBase client) { // Check if the IP of the player is banned. if (client.Connection != null && _bannedIps.Contains(client.Connection.EndPoint.Address)) @@ -140,14 +140,14 @@ namespace Impostor.Server.Net.State await message.SendToAllExceptAsync(playerId); } - private async ValueTask HandleJoinGameNew(IClientPlayer sender, bool isNew) + private async ValueTask HandleJoinGameNew(ClientPlayer sender, bool isNew) { Logger.Information("{0} - Player {1} ({2}) is joining.", Code, sender.Client.Name, sender.Client.Id); // Add player to the game. if (isNew) { - PlayerAdd(sender); + await PlayerAdd(sender); } using (var message = CreateMessage(MessageType.Reliable)) @@ -162,14 +162,14 @@ namespace Impostor.Server.Net.State } } - private async ValueTask HandleJoinGameNext(IClientPlayer sender, bool isNew) + private async ValueTask HandleJoinGameNext(ClientPlayer sender, bool isNew) { Logger.Information("{0} - Player {1} ({2}) is rejoining.", Code, sender.Client.Name, sender.Client.Id); // Add player to the game. if (isNew) { - PlayerAdd(sender); + await PlayerAdd(sender); } // Check if the host joined and let everyone join. diff --git a/src/Impostor.Server/Net/State/Game.State.cs b/src/Impostor.Server/Net/State/Game.State.cs index 5e9b38b..fdca5d4 100644 --- a/src/Impostor.Server/Net/State/Game.State.cs +++ b/src/Impostor.Server/Net/State/Game.State.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using Impostor.Server.Events; using Impostor.Server.Exceptions; using Impostor.Server.Net.Messages; using Impostor.Shared.Innersloth.Data; @@ -8,7 +9,7 @@ namespace Impostor.Server.Net.State { internal partial class Game { - private void PlayerAdd(IClientPlayer player) + private async ValueTask PlayerAdd(ClientPlayer player) { // Store player. if (!_players.TryAdd(player.Client.Id, player)) @@ -21,6 +22,8 @@ namespace Impostor.Server.Net.State { HostId = player.Client.Id; } + + await _eventManager.CallAsync(new PlayerJoinedGameEvent(this, player)); } private async ValueTask PlayerRemove(int playerId, bool isBan = false) @@ -55,6 +58,8 @@ namespace Impostor.Server.Net.State _bannedIps.Add(player.Client.Connection.EndPoint.Address); } + await _eventManager.CallAsync(new PlayerLeftGameEvent(this, player, isBan)); + return true; } diff --git a/src/Impostor.Server/Net/State/Game.cs b/src/Impostor.Server/Net/State/Game.cs index ca68fa7..c0d06f6 100644 --- a/src/Impostor.Server/Net/State/Game.cs +++ b/src/Impostor.Server/Net/State/Game.cs @@ -4,8 +4,11 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading.Tasks; +using Impostor.Server.Events.Managers; using Impostor.Server.Games; using Impostor.Server.Games.Managers; +using Impostor.Server.Hazel; +using Impostor.Server.Hazel.Messages; using Impostor.Server.Net.Manager; using Impostor.Server.Net.Messages; using Impostor.Server.Net.Redirector; @@ -16,30 +19,32 @@ using ILogger = Serilog.ILogger; namespace Impostor.Server.Net.State { - internal partial class Game : IGame + internal partial class Game { private static readonly ILogger Logger = Log.ForContext(); private readonly IServiceProvider _serviceProvider; - private readonly IGameManager _gameManager; - private readonly IClientManager _clientManager; - private readonly IMatchmaker _matchmaker; - private readonly ConcurrentDictionary _players; + private readonly GameManager _gameManager; + private readonly ClientManager _clientManager; + private readonly Matchmaker _matchmaker; + private readonly ConcurrentDictionary _players; private readonly HashSet _bannedIps; + private readonly IEventManager _eventManager; public Game( IServiceProvider serviceProvider, - IGameManager gameManager, + GameManager gameManager, INodeLocator nodeLocator, IPEndPoint publicIp, GameCode code, GameOptionsData options, - IMatchmaker matchmaker, - IClientManager clientManager) + Matchmaker matchmaker, + ClientManager clientManager, + IEventManager eventManager) { _serviceProvider = serviceProvider; _gameManager = gameManager; - _players = new ConcurrentDictionary(); + _players = new ConcurrentDictionary(); _bannedIps = new HashSet(); PublicIp = publicIp; @@ -49,6 +54,7 @@ namespace Impostor.Server.Net.State Options = options; _matchmaker = matchmaker; _clientManager = clientManager; + _eventManager = eventManager; Items = new ConcurrentDictionary(); } @@ -68,16 +74,16 @@ namespace Impostor.Server.Net.State public int PlayerCount => _players.Count; - public IClientPlayer Host => _players[HostId]; + public ClientPlayer Host => _players[HostId]; public IEnumerable Players => _players.Select(p => p.Value); public IGameMessageWriter CreateMessage(MessageType type) { - return _matchmaker.CreateGameMessageWriter(this, type); + return new HazelGameMessageWriter(type, this); } - public bool TryGetPlayer(int id, out IClientPlayer player) + public bool TryGetPlayer(int id, out ClientPlayer player) { if (_players.TryGetValue(id, out var result)) { @@ -94,7 +100,7 @@ namespace Impostor.Server.Net.State return _gameManager.RemoveAsync(Code); } - private ValueTask BroadcastJoinMessage(IGameMessageWriter message, bool clear, IClientPlayer player) + private ValueTask BroadcastJoinMessage(IGameMessageWriter message, bool clear, ClientPlayer player) { Message01JoinGame.SerializeJoin(message, clear, Code, player.Client.Id, HostId); diff --git a/src/Impostor.Server/Plugins/AssemblyInformation.cs b/src/Impostor.Server/Plugins/AssemblyInformation.cs new file mode 100644 index 0000000..5f6aee1 --- /dev/null +++ b/src/Impostor.Server/Plugins/AssemblyInformation.cs @@ -0,0 +1,38 @@ +using System.IO; +using System.Reflection; +using System.Runtime.Loader; + +namespace Impostor.Server.Plugins +{ + public class AssemblyInformation : IAssemblyInformation + { + private Assembly _assembly; + + public AssemblyInformation(AssemblyName assemblyName, string path, bool isPlugin) + { + AssemblyName = assemblyName; + Path = path; + IsPlugin = isPlugin; + } + + public string Path { get; } + + public bool IsPlugin { get; } + + public AssemblyName AssemblyName { get; } + + public Assembly Load(AssemblyLoadContext context) + { + if (_assembly != null) + { + return _assembly; + } + + using var stream = File.Open(Path, FileMode.Open, FileAccess.Read, FileShare.Read); + + _assembly = context.LoadFromStream(stream); + + return _assembly; + } + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Plugins/IAssemblyInformation.cs b/src/Impostor.Server/Plugins/IAssemblyInformation.cs new file mode 100644 index 0000000..fb36e92 --- /dev/null +++ b/src/Impostor.Server/Plugins/IAssemblyInformation.cs @@ -0,0 +1,14 @@ +using System.Reflection; +using System.Runtime.Loader; + +namespace Impostor.Server.Plugins +{ + public interface IAssemblyInformation + { + AssemblyName AssemblyName { get; } + + bool IsPlugin { get; } + + Assembly Load(AssemblyLoadContext context); + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Plugins/LoadedAssemblyInformation.cs b/src/Impostor.Server/Plugins/LoadedAssemblyInformation.cs new file mode 100644 index 0000000..720367c --- /dev/null +++ b/src/Impostor.Server/Plugins/LoadedAssemblyInformation.cs @@ -0,0 +1,25 @@ +using System.Reflection; +using System.Runtime.Loader; + +namespace Impostor.Server.Plugins +{ + public class LoadedAssemblyInformation : IAssemblyInformation + { + private readonly Assembly _assembly; + + public LoadedAssemblyInformation(Assembly assembly) + { + AssemblyName = assembly.GetName(); + _assembly = assembly; + } + + public AssemblyName AssemblyName { get; } + + public bool IsPlugin => false; + + public Assembly Load(AssemblyLoadContext context) + { + return _assembly; + } + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Plugins/PluginConfig.cs b/src/Impostor.Server/Plugins/PluginConfig.cs new file mode 100644 index 0000000..22dc9e9 --- /dev/null +++ b/src/Impostor.Server/Plugins/PluginConfig.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Impostor.Server.Plugins +{ + public class PluginConfig + { + public List Paths { get; set; } = new List(); + + public List LibraryPaths { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Plugins/PluginLoader.cs b/src/Impostor.Server/Plugins/PluginLoader.cs new file mode 100644 index 0000000..4fc0997 --- /dev/null +++ b/src/Impostor.Server/Plugins/PluginLoader.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Loader; +using Microsoft.Extensions.FileSystemGlobbing; +using Microsoft.Extensions.Hosting; + +namespace Impostor.Server.Plugins +{ + public static class PluginLoader + { + public static IHostBuilder UsePluginLoader(this IHostBuilder builder, PluginConfig config) + { + var assemblyInfos = new List(); + var context = AssemblyLoadContext.Default; + + // Add the plugins and libraries. + var pluginPaths = new List(config.Paths); + var libraryPaths = new List(config.LibraryPaths); + + var rootFolder = Assembly.GetEntryAssembly()?.Location; + if (rootFolder != null) + { + pluginPaths.Add(Path.Combine(rootFolder, "plugins")); + libraryPaths.Add(Path.Combine(rootFolder, "libraries")); + } + + var matcher = new Matcher(StringComparison.OrdinalIgnoreCase); + matcher.AddInclude("*.dll"); + matcher.AddExclude("Impostor.Server.Api.dll"); + matcher.AddExclude("Impostor.Shared.dll"); + + RegisterAssemblies(pluginPaths, matcher, assemblyInfos, true); + RegisterAssemblies(libraryPaths, matcher, assemblyInfos, false); + + // Register the resolver to the current context. + // TODO: Move this to a new context so we can unload/reload plugins. + context.Resolving += (loadContext, name) => + { + var info = assemblyInfos.FirstOrDefault(a => a.AssemblyName.Name == name.Name); + + return info?.Load(loadContext); + }; + + // TODO: Catch uncaught exceptions. + var assemblies = assemblyInfos + .Where(a => a.IsPlugin) + .Select(a => context.LoadFromAssemblyName(a.AssemblyName)) + .ToList(); + + var plugins = assemblies + .SelectMany(a => a.GetTypes()) + .Where(typeof(IPlugin).IsAssignableFrom) + .Select(Activator.CreateInstance) + .Cast() + .ToList(); + + foreach (var plugin in plugins) + { + plugin.ConfigureHost(builder); + } + + builder.ConfigureServices(services => + { + foreach (var plugin in plugins) + { + plugin.ConfigureServices(services); + } + }); + + return builder; + } + + private static void RegisterAssemblies( + IEnumerable paths, + Matcher matcher, + ICollection assemblyInfos, + bool isPlugin) + { + foreach (var path in paths.SelectMany(matcher.GetResultsInFullPath)) + { + AssemblyName assemblyName; + + try + { + assemblyName = AssemblyName.GetAssemblyName(path); + } + catch (BadImageFormatException) + { + continue; + } + + assemblyInfos.Add(new AssemblyInformation(assemblyName, path, isPlugin)); + } + } + } +} \ No newline at end of file diff --git a/src/Impostor.Server/Program.cs b/src/Impostor.Server/Program.cs index 31433b2..43bc6f6 100644 --- a/src/Impostor.Server/Program.cs +++ b/src/Impostor.Server/Program.cs @@ -8,6 +8,7 @@ using Impostor.Server.Net; using Impostor.Server.Net.Factories; using Impostor.Server.Net.Manager; using Impostor.Server.Net.Redirector; +using Impostor.Server.Plugins; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -49,8 +50,25 @@ namespace Impostor.Server } } - private static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) + private static IConfiguration CreateConfiguration(string[] args) + { + var configurationBuilder = new ConfigurationBuilder(); + + configurationBuilder.AddJsonFile("config.json", true); + configurationBuilder.AddJsonFile("config.Development.json", true); + configurationBuilder.AddEnvironmentVariables(prefix: "IMPOSTOR_"); + configurationBuilder.AddCommandLine(args); + + return configurationBuilder.Build(); + } + + private static IHostBuilder CreateHostBuilder(string[] args) + { + var configuration = CreateConfiguration(args); + var pluginConfig = configuration.GetSection("PluginLoader") + .Get(); + + return Host.CreateDefaultBuilder(args) #if DEBUG .UseEnvironment(Environment.GetEnvironmentVariable("IMPOSTOR_ENV") ?? "Development") #else @@ -58,10 +76,7 @@ namespace Impostor.Server #endif .ConfigureAppConfiguration(builder => { - builder.AddJsonFile("config.json", true); - builder.AddJsonFile("config.Development.json", true); - builder.AddEnvironmentVariables(prefix: "IMPOSTOR_"); - builder.AddCommandLine(args); + builder.AddConfiguration(configuration); }) .ConfigureServices((host, services) => { @@ -73,7 +88,8 @@ namespace Impostor.Server services.Configure(host.Configuration.GetSection(DebugConfig.Section)); #endif services.Configure(host.Configuration.GetSection(ServerConfig.Section)); - services.Configure(host.Configuration.GetSection(ServerRedirectorConfig.Section)); + services.Configure( + host.Configuration.GetSection(ServerRedirectorConfig.Section)); if (redirector.Enabled) { @@ -115,7 +131,8 @@ namespace Impostor.Server services.AddSingleton(); } - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(p => p.GetRequiredService()); if (redirector.Enabled && redirector.Master) { @@ -126,14 +143,17 @@ namespace Impostor.Server else { services.AddSingleton>(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(p => p.GetRequiredService()); } services.AddSingleton(); - services.UseHazelMatchmaking(); + services.AddSingleton(); services.AddHostedService(); }) + .UsePluginLoader(pluginConfig) .UseConsoleLifetime() .UseSerilog(); + } } } \ No newline at end of file diff --git a/src/Impostor.Server/Properties/AssemblyInfo.cs b/src/Impostor.Server/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..eb56780 --- /dev/null +++ b/src/Impostor.Server/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly:InternalsVisibleTo("Impostor.Tests")] \ No newline at end of file diff --git a/src/Impostor.Tests/Hazel/BufferMessageReaderTests.cs b/src/Impostor.Tests/Hazel/BufferMessageReaderTests.cs index f896e69..0b71ed7 100644 --- a/src/Impostor.Tests/Hazel/BufferMessageReaderTests.cs +++ b/src/Impostor.Tests/Hazel/BufferMessageReaderTests.cs @@ -28,7 +28,7 @@ namespace Impostor.Tests.Hazel Assert.Equal(Test1, reader.ReadInt32()); Assert.Equal(Test2, reader.ReadInt32()); } - + [Fact] public void ReadProperBool() { diff --git a/src/Impostor.Tests/Impostor.Tests.csproj b/src/Impostor.Tests/Impostor.Tests.csproj index 536b4e5..407e920 100644 --- a/src/Impostor.Tests/Impostor.Tests.csproj +++ b/src/Impostor.Tests/Impostor.Tests.csproj @@ -14,9 +14,7 @@ - - - + diff --git a/src/Impostor.sln b/src/Impostor.sln index 3ebbabc..d35c75f 100644 --- a/src/Impostor.sln +++ b/src/Impostor.sln @@ -21,8 +21,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Server", "Impostor EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Server.Api", "Impostor.Server.Api\Impostor.Server.Api.csproj", "{E096A7D7-D693-4A13-A526-38CC574D84F8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Server.Hazel", "Impostor.Server.Hazel\Impostor.Server.Hazel.csproj", "{C9E8E1E2-BFE3-41AD-8CB9-82F0F52E2306}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "patcher", "patcher", "{94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Patcher.Shared", "Impostor.Patcher\Impostor.Patcher.Shared\Impostor.Patcher.Shared.csproj", "{7C3EB599-2292-4532-B280-D5BED1094DD4}" @@ -31,6 +29,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Patcher.WinForms", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Patcher.Cli", "Impostor.Patcher\Impostor.Patcher.Cli\Impostor.Patcher.Cli.csproj", "{82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{36AA9913-E6EA-4A6C-90E6-2FD3CC2E3124}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Impostor.Plugins.Debugger", "Impostor.Plugins.Debugger\Impostor.Plugins.Debugger.csproj", "{ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -91,14 +93,6 @@ Global {E096A7D7-D693-4A13-A526-38CC574D84F8}.Release|Any CPU.Build.0 = Release|Any CPU {E096A7D7-D693-4A13-A526-38CC574D84F8}.Release|x86.ActiveCfg = Release|Any CPU {E096A7D7-D693-4A13-A526-38CC574D84F8}.Release|x86.Build.0 = Release|Any CPU - {C9E8E1E2-BFE3-41AD-8CB9-82F0F52E2306}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C9E8E1E2-BFE3-41AD-8CB9-82F0F52E2306}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C9E8E1E2-BFE3-41AD-8CB9-82F0F52E2306}.Debug|x86.ActiveCfg = Debug|Any CPU - {C9E8E1E2-BFE3-41AD-8CB9-82F0F52E2306}.Debug|x86.Build.0 = Debug|Any CPU - {C9E8E1E2-BFE3-41AD-8CB9-82F0F52E2306}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C9E8E1E2-BFE3-41AD-8CB9-82F0F52E2306}.Release|Any CPU.Build.0 = Release|Any CPU - {C9E8E1E2-BFE3-41AD-8CB9-82F0F52E2306}.Release|x86.ActiveCfg = Release|Any CPU - {C9E8E1E2-BFE3-41AD-8CB9-82F0F52E2306}.Release|x86.Build.0 = Release|Any CPU {7C3EB599-2292-4532-B280-D5BED1094DD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7C3EB599-2292-4532-B280-D5BED1094DD4}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C3EB599-2292-4532-B280-D5BED1094DD4}.Debug|x86.ActiveCfg = Debug|Any CPU @@ -115,6 +109,14 @@ Global {82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}.Release|Any CPU.Build.0 = Release|Any CPU {82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}.Release|x86.ActiveCfg = Release|Any CPU {82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69}.Release|x86.Build.0 = Release|Any CPU + {ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}.Debug|x86.ActiveCfg = Debug|Any CPU + {ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}.Debug|x86.Build.0 = Debug|Any CPU + {ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}.Release|Any CPU.Build.0 = Release|Any CPU + {ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}.Release|x86.ActiveCfg = Release|Any CPU + {ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -128,8 +130,8 @@ Global {804CF172-0C87-4423-9688-BD97D549891E} = {94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F} {1B0390AF-A4F3-4FE4-B093-708B0135C0B3} = {F2B205ED-4250-412E-9992-B11B7D6CE136} {E096A7D7-D693-4A13-A526-38CC574D84F8} = {F2B205ED-4250-412E-9992-B11B7D6CE136} - {C9E8E1E2-BFE3-41AD-8CB9-82F0F52E2306} = {F2B205ED-4250-412E-9992-B11B7D6CE136} {7C3EB599-2292-4532-B280-D5BED1094DD4} = {94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F} {82B36C4C-4EBD-49B7-A2DB-0B3308FBEF69} = {94FAED42-15BB-40CF-BE64-5D5C3B4ABC8F} + {ECBCAA3B-B974-41CF-AFFC-6F5AA4C42FA7} = {36AA9913-E6EA-4A6C-90E6-2FD3CC2E3124} EndGlobalSection EndGlobal -- 2.39.5