]> git.deb.at Git - rhonda/impostor.git/commitdiff
BeforeGameCreatedEvent (#266)
authorMinorusama <pitilulu@gmail.com>
Tue, 13 Apr 2021 16:29:44 +0000 (18:29 +0200)
committerGitHub <noreply@github.com>
Tue, 13 Apr 2021 16:29:44 +0000 (16:29 +0000)
Co-authored-by: js6pak <kubastaron@hotmail.com>
src/Impostor.Api/Events/Game/IGameCreationEvent.cs [new file with mode: 0644]
src/Impostor.Api/Games/GameCode.cs
src/Impostor.Api/Games/Managers/IGameManager.cs
src/Impostor.Plugins.Example/ExamplePlugin.cs
src/Impostor.Plugins.Example/Handlers/GameEventListener.cs
src/Impostor.Server/Events/Game/GameCreationEvent.cs [new file with mode: 0644]
src/Impostor.Server/Net/Client.cs
src/Impostor.Server/Net/Manager/GameManager.cs
src/Impostor.Tools.ServerReplay/Program.cs

diff --git a/src/Impostor.Api/Events/Game/IGameCreationEvent.cs b/src/Impostor.Api/Events/Game/IGameCreationEvent.cs
new file mode 100644 (file)
index 0000000..b51b9f4
--- /dev/null
@@ -0,0 +1,25 @@
+using Impostor.Api.Games;
+using Impostor.Api.Net;
+
+namespace Impostor.Api.Events
+{
+    /// <summary>
+    ///     Called just before a new <see cref="IGame"/> is created.
+    /// </summary>
+    public interface IGameCreationEvent : IEventCancelable
+    {
+        /// <summary>
+        ///     Gets the client that requested creation of the game.
+        /// </summary>
+        /// <remarks>
+        ///     Will be null if game creation was requested by a plugin.
+        /// </remarks>
+        IClient? Client { get; }
+
+        /// <summary>
+        ///     Gets or sets the desired <see cref="Games.GameCode"/>.
+        /// </summary>
+        /// <exception cref="ImpostorException">If the GameCode is invalid or already used in another game.</exception>
+        GameCode? GameCode { get; set; }
+    }
+}
index e24c28a48ec7831c98331d92260c107952b0ca4c..915ffb7e434f0418da03fbd8407e03a17897126c 100644 (file)
@@ -1,4 +1,4 @@
-using System;
+using System;
 using Impostor.Api.Innersloth;
 
 namespace Impostor.Api.Games
@@ -14,13 +14,15 @@ namespace Impostor.Api.Games
         public GameCode(string code)
         {
             Value = GameCodeParser.GameNameToInt(code);
-            Code = code;
+            Code = code.ToUpperInvariant();
         }
 
         public string Code { get; }
 
         public int Value { get; }
 
+        public bool IsInvalid => Value == -1;
+
         public static implicit operator string(GameCode code) => code.Code;
 
         public static implicit operator int(GameCode code) => code.Value;
index cdf8558cf7332559cfc7f36ea0510cfab75e4847..f417d310892feefd86810cbd2d848fb16fef6db0 100644 (file)
@@ -10,6 +10,12 @@ namespace Impostor.Api.Games.Managers
 
         IGame? Find(GameCode code);
 
-        ValueTask<IGame> CreateAsync(GameOptionsData options);
+        /// <summary>
+        /// Creates a new game.
+        /// </summary>
+        /// <param name="options">Game options.</param>
+        /// <returns>Created game or null if creation was cancelled by a plugin.</returns>
+        /// <exception cref="ImpostorException">Thrown when game creation failed.</exception>
+        ValueTask<IGame?> CreateAsync(GameOptionsData options);
     }
 }
index 73fea6ad66eced0da6c0b53be9ae2578fef7abc7..a3c50e47cd5738589709f69a4775a2751aad97b4 100644 (file)
@@ -27,10 +27,17 @@ namespace Impostor.Plugins.Example
             _logger.LogInformation("Example is being enabled.");
 
             var game = await _gameManager.CreateAsync(new GameOptionsData());
-            game.DisplayName = "Example game";
-            await game.SetPrivacyAsync(true);
+            if (game == null)
+            {
+                _logger.LogWarning("Example game creation was cancelled");
+            }
+            else
+            {
+                game.DisplayName = "Example game";
+                await game.SetPrivacyAsync(true);
 
-            _logger.LogInformation("Created game {0}.", game.Code.Code);
+                _logger.LogInformation("Created game {0}.", game.Code.Code);
+            }
         }
 
         public override ValueTask DisableAsync()
index 0125a19a1121d556e032485f6ff4f0485d635928..1384e8b528e75b6474573a823929d252c52e1e09 100644 (file)
@@ -1,4 +1,6 @@
 using Impostor.Api.Events;
+using Impostor.Api.Games;
+using Impostor.Api.Innersloth;
 using Microsoft.Extensions.Logging;
 
 namespace Impostor.Plugins.Example.Handlers
@@ -12,6 +14,28 @@ namespace Impostor.Plugins.Example.Handlers
             _logger = logger;
         }
 
+        [EventListener]
+        public void OnGameCreated(IGameCreationEvent e)
+        {
+            _logger.LogInformation("Game creation requested by {client}", e.Client == null ? "a plugin" : e.Client.Name);
+
+            if (e.Client != null)
+            {
+                var gameCode = GameCode.From(e.Client.Name);
+
+                if (!gameCode.IsInvalid)
+                {
+                    e.GameCode = gameCode;
+                }
+
+                if (e.Client.Name == "dima")
+                {
+                    e.IsCancelled = true;
+                    e.Client.DisconnectAsync(DisconnectReason.Custom, "No you dont >:(");
+                }
+            }
+        }
+
         [EventListener]
         public void OnGameCreated(IGameCreatedEvent e)
         {
diff --git a/src/Impostor.Server/Events/Game/GameCreationEvent.cs b/src/Impostor.Server/Events/Game/GameCreationEvent.cs
new file mode 100644 (file)
index 0000000..29e95d9
--- /dev/null
@@ -0,0 +1,46 @@
+using Impostor.Api;
+using Impostor.Api.Events;
+using Impostor.Api.Games;
+using Impostor.Api.Games.Managers;
+using Impostor.Api.Net;
+
+namespace Impostor.Server.Events
+{
+    public class GameCreationEvent : IGameCreationEvent
+    {
+        private readonly IGameManager _gameManager;
+        private GameCode? _gameCode;
+
+        public GameCreationEvent(IGameManager gameManager, IClient? client)
+        {
+            _gameManager = gameManager;
+            Client = client;
+        }
+
+        public IClient? Client { get; }
+
+        public GameCode? GameCode
+        {
+            get => _gameCode;
+            set
+            {
+                if (value.HasValue)
+                {
+                    if (value.Value.IsInvalid)
+                    {
+                        throw new ImpostorException("GameCode is invalid.");
+                    }
+
+                    if (_gameManager.Find(value.Value) != null)
+                    {
+                        throw new ImpostorException($"GameCode [{value.Value.Code}] is already used.");
+                    }
+                }
+
+                _gameCode = value;
+            }
+        }
+
+        public bool IsCancelled { get; set; }
+    }
+}
index 6c5547ec00cbbf18adbadfdcd2239fb55e030baa..dcc501c237f1e5fe4b088e6c5a6a0e95e63ec73d 100644 (file)
@@ -64,7 +64,13 @@ namespace Impostor.Server.Net
                     var gameInfo = Message00HostGameC2S.Deserialize(reader, out _);
 
                     // Create game.
-                    var game = await _gameManager.CreateAsync(gameInfo);
+                    var game = await _gameManager.CreateAsync(this, gameInfo);
+
+                    if (game == null)
+                    {
+                        await DisconnectAsync(DisconnectReason.GameMissing);
+                        return;
+                    }
 
                     // Code in the packet below will be used in JoinGame.
                     using (var writer = MessageWriter.Get(MessageType.Reliable))
index b10e791649fc0cbe863eb933edd00a79d046f685..528581b1a937ddfbfe6d20afc735c53ef6fafd8d 100644 (file)
@@ -9,6 +9,7 @@ using Impostor.Api.Events.Managers;
 using Impostor.Api.Games;
 using Impostor.Api.Games.Managers;
 using Impostor.Api.Innersloth;
+using Impostor.Api.Net;
 using Impostor.Server.Config;
 using Impostor.Server.Events;
 using Impostor.Server.Net.Redirector;
@@ -110,10 +111,18 @@ namespace Impostor.Server.Net.Manager
             await _eventManager.CallAsync(new GameDestroyedEvent(game));
         }
 
-        public async ValueTask<IGame> CreateAsync(GameOptionsData options)
+        public async ValueTask<IGame?> CreateAsync(IClient? owner, GameOptionsData options)
         {
+            var @event = new GameCreationEvent(this, owner);
+            await _eventManager.CallAsync(@event);
+
+            if (@event.IsCancelled)
+            {
+                return null;
+            }
+
             // TODO: Prevent duplicates when using server redirector using INodeProvider.
-            var (success, game) = await TryCreateAsync(options);
+            var (success, game) = await TryCreateAsync(options, @event.GameCode);
 
             for (var i = 0; i < 10 && !success; i++)
             {
@@ -128,9 +137,14 @@ namespace Impostor.Server.Net.Manager
             return game;
         }
 
-        private async ValueTask<(bool Success, Game? Game)> TryCreateAsync(GameOptionsData options)
+        public ValueTask<IGame?> CreateAsync(GameOptionsData options)
+        {
+            return CreateAsync(null, options);
+        }
+
+        private async ValueTask<(bool Success, Game? Game)> TryCreateAsync(GameOptionsData options, GameCode? desiredGameCode = null)
         {
-            var gameCode = _gameCodeFactory.Create();
+            var gameCode = desiredGameCode ?? _gameCodeFactory.Create();
             var gameCodeStr = gameCode.Code;
             var game = ActivatorUtilities.CreateInstance<Game>(_serviceProvider, _publicIp, gameCode, options);
 
index 16b756eafb5520d71a88c8c19fde84ca47c1447b..8bfb8e016b2fee5545cc84a2521e79903038d9bb 100644 (file)
@@ -209,7 +209,7 @@ namespace Impostor.Tools.ServerReplay
                 case RecordedPacketType.GameCreated:
                     _gameCodeFactory.Result = GameCode.From(reader.ReadString());
 
-                    await _gameManager.CreateAsync(GameOptions[clientId]);
+                    await _gameManager.CreateAsync(Connections[clientId].Client, GameOptions[clientId]);
 
                     GameOptions.Remove(clientId);
                     break;