]> git.deb.at Git - rhonda/impostor.git/commitdiff
Fixes host migration (#5) and proper player count checks
authorAeonLucid <aeonlucid@gmail.com>
Tue, 29 Sep 2020 03:31:17 +0000 (05:31 +0200)
committerAeonLucid <aeonlucid@gmail.com>
Tue, 29 Sep 2020 03:31:17 +0000 (05:31 +0200)
src/Impostor.Server/Net/Client.cs
src/Impostor.Server/Net/State/ClientPlayer.cs
src/Impostor.Server/Net/State/Game.Incoming.cs
src/Impostor.Server/Net/State/Game.State.cs [new file with mode: 0644]
src/Impostor.Server/Net/State/Game.cs

index 56305b4fe4387f27145ad23203c0eaac2a58427e..2dbf25768237c965bed7b981a6c1e3f5faa4517c 100644 (file)
@@ -191,7 +191,7 @@ namespace Impostor.Server.Net
                         else
                         {
                             writer.CopyFrom(message);
-                            Player.Game.SendToAllExcept(writer, Player);
+                            Player.Game.SendToAllExcept(writer, Player.Client.Id);
                         }
                     }
                     break;
index 5242cab929f87b04497a827cd5e3b4dc296fcb01..cce8a1bc12311361e7b8ed76ddaeddce61c71c8b 100644 (file)
@@ -14,10 +14,12 @@ namespace Impostor.Server.Net.State
             _gameManager = gameManager;
             
             Client = client;
+            Limbo = LimboStates.PreSpawn;
         }
         
         public Client Client { get; }
         public Game Game { get; set; }
+        public LimboStates Limbo { get; set; }
 
         public void SendDisconnectReason(DisconnectReason reason, string message = null)
         {
index bf91cc98b426cea81eda95fd50d21347bf42de98..168429c1906028eb69fcc0e3a89e40b4dc086465 100644 (file)
@@ -22,12 +22,29 @@ namespace Impostor.Server.Net.State
 
         public void HandleJoinGame(ClientPlayer sender)
         {
+            // Check if the IP of the player is banned.
             if (_bannedIps.Contains(sender.Client.Connection.EndPoint.Address))
             {
                 sender.SendDisconnectReason(DisconnectReason.Banned);
                 return;
             }
             
+            // Check if;
+            // - The player is already in this game.
+            // - The game is full.
+            if (sender.Game != this && _players.Count >= Options.MaxPlayers)
+            {
+                sender.SendDisconnectReason(DisconnectReason.GameFull);
+                return;
+            }
+            
+            // Check current player state.
+            if (sender.Limbo == LimboStates.NotLimbo)
+            {
+                sender.SendDisconnectReason(DisconnectReason.Custom, "Invalid limbo state while joining.");
+                return;
+            }
+            
             switch (GameState)
             {
                 case GameStates.NotStarted:
@@ -58,13 +75,11 @@ namespace Impostor.Server.Net.State
                 SendToAllExcept(packet, null);
             }
             
-            // Remove all players from this game.
+            // Put all players in the correct limbo state.
             foreach (var player in _players)
             {
-                player.Value.Game = null;
+                player.Value.Limbo = LimboStates.PreSpawn;
             }
-            
-            _players.Clear();
         }
 
         public void HandleAlterGame(MessageReader message, ClientPlayer sender, bool isPublic)
@@ -74,68 +89,47 @@ namespace Impostor.Server.Net.State
             using (var packet = MessageWriter.Get(SendOption.Reliable))
             {
                 packet.CopyFrom(message);
-                SendToAllExcept(packet, sender);
+                SendToAllExcept(packet, sender.Client.Id);
             }
         }
         
         public void HandleRemovePlayer(int playerId, DisconnectReason reason)
         {
-            if (_players.TryRemove(playerId, out var player))
-            {
-                player.Game = null;
-            }
-            
-            Logger.Information("{0} - Player {1} ({2}) has left.", CodeStr, player?.Client.Name, playerId);
+            PlayerRemove(playerId, out _);
 
-            // Game is empty, remove it.
-            if (_players.Count == 0)
+            // It's possible that the last player was removed, so check if the game is still around.
+            if (GameState == GameStates.Destroyed)
             {
-                GameState = GameStates.Destroyed;
-
-                // Remove instance reference.
-                _gameManager.Remove(Code);
                 return;
             }
-
-            // Host migration.
-            if (HostId == playerId)
-            {
-                var newHost = _players.First().Value;
-                HostId = newHost.Client.Id;
-                Logger.Information("{0} - Assigned {1} ({2}) as new host.", CodeStr, newHost.Client.Name, newHost.Client.Id);
-            }
-
+            
             using (var packet = MessageWriter.Get(SendOption.Reliable))
             {
                 WriteRemovePlayerMessage(packet, false, playerId, reason);
-                SendToAllExcept(packet, player);
+                SendToAllExcept(packet, playerId);
             }
         }
 
         public void HandleKickPlayer(int playerId, bool isBan)
         {
-            _players.TryGetValue(playerId, out var p);
-            Logger.Information("{0} - Player {1} ({2}) has left.", CodeStr, p?.Client.Name, playerId);
+            Logger.Information("{0} - Player {1} has left.", CodeStr, playerId);
             
             using (var message = MessageWriter.Get(SendOption.Reliable))
             {
+                // Send message to everyone that this player was kicked.
                 WriteKickPlayerMessage(message, false, playerId, isBan);
                 SendToAllExcept(message, null);
                 
-                if (_players.TryRemove(playerId, out var player))
+                if (PlayerRemove(playerId, out var player) && isBan)
                 {
-                    player.Game = null;
-
-                    if (isBan)
-                    {
-                        _bannedIps.Add(player.Client.Connection.EndPoint.Address);
-                    }
+                    _bannedIps.Add(player.Client.Connection.EndPoint.Address);
                 }
                 
+                // Rmeove the player from everyone's game.
                 WriteRemovePlayerMessage(message, true, playerId, isBan 
                     ? DisconnectReason.Banned 
                     : DisconnectReason.Kicked);
-                SendToAllExcept(message, player);
+                SendToAllExcept(message, player?.Client.Id);
             }
         }
         
@@ -143,19 +137,10 @@ namespace Impostor.Server.Net.State
         {
             Logger.Information("{0} - Player {1} ({2}) is joining.", CodeStr, sender.Client.Name, sender.Client.Id);
             
-            // Store player.
-            if (!_players.TryAdd(sender.Client.Id, sender))
+            // Add player to the game.
+            if (sender.Game == null)
             {
-                throw new AmongUsException("Failed to add player to game.");
-            }
-            
-            // Assign player to this game for future packets.
-            sender.Game = this;
-
-            // Assign hostId if none is set.
-            if (HostId == -1)
-            {
-                HostId = sender.Client.Id;
+                PlayerAdd(sender);
             }
 
             using (var message = MessageWriter.Get(SendOption.Reliable))
@@ -163,6 +148,7 @@ namespace Impostor.Server.Net.State
                 WriteJoinedGameMessage(message, false, sender);
                 WriteAlterGameMessage(message, false);
                 
+                sender.Limbo = LimboStates.NotLimbo;
                 sender.Client.Send(message);
 
                 BroadcastJoinMessage(message, true, sender);
@@ -173,38 +159,26 @@ namespace Impostor.Server.Net.State
         {
             Logger.Information("{0} - Player {1} ({2}) is rejoining.", CodeStr, sender.Client.Name, sender.Client.Id);
             
+            // Add player to the game.
+            if (sender.Game == null)
+            {
+                PlayerAdd(sender);
+            }
+            
+            // Check if the host joined and let everyone join.
             if (sender.Client.Id == HostId)
             {
                 GameState = GameStates.NotStarted;
+                
+                // Spawn the host.
                 HandleJoinGameNew(sender);
-
-                using (var message = MessageWriter.Get(SendOption.Reliable))
-                {
-                    foreach (var (_, player) in _players.Where(x => x.Value != sender))
-                    {
-                        WriteJoinedGameMessage(message, true, player);
-                        WriteAlterGameMessage(message, false);
-                        player.Client.Send(message);
-                    }
-                }
                 
+                // Pull players out of limbo.
+                CheckLimboPlayers();
                 return;
             }
 
-            if (_players.Count >= 9)
-            {
-                sender.SendDisconnectReason(DisconnectReason.GameFull);
-                return;
-            }
-
-            // Store player.
-            if (!_players.TryAdd(sender.Client.Id, sender))
-            {
-                throw new AmongUsException("Failed to add player to game.");
-            }
-            
-            // Assign player to this game for future packets.
-            sender.Game = this;
+            sender.Limbo = LimboStates.WaitingForHost;
 
             using (var packet = MessageWriter.Get(SendOption.Reliable))
             {
diff --git a/src/Impostor.Server/Net/State/Game.State.cs b/src/Impostor.Server/Net/State/Game.State.cs
new file mode 100644 (file)
index 0000000..057526e
--- /dev/null
@@ -0,0 +1,95 @@
+using System.Linq;
+using Hazel;
+using Impostor.Server.Exceptions;
+using Impostor.Shared.Innersloth.Data;
+
+namespace Impostor.Server.Net.State
+{
+    internal partial class Game
+    {
+        private void PlayerAdd(ClientPlayer player)
+        {
+            // Store player.
+            if (!_players.TryAdd(player.Client.Id, player))
+            {
+                throw new AmongUsException("Failed to add player to game.");
+            }
+            
+            // Assign player to this game for future packets.
+            player.Game = this;
+
+            // Assign hostId if none is set.
+            if (HostId == -1)
+            {
+                HostId = player.Client.Id;
+            }
+        }
+
+        private bool PlayerRemove(int playerId, out ClientPlayer player)
+        {
+            if (!_players.TryRemove(playerId, out player))
+            {
+                return false;
+            }
+
+            player.Limbo = LimboStates.PreSpawn;
+            player.Game = null;
+            
+            Logger.Information("{0} - Player {1} ({2}) has left.", CodeStr, player.Client.Name, playerId);
+            
+            // Game is empty, remove it.
+            if (_players.Count == 0)
+            {
+                GameState = GameStates.Destroyed;
+
+                // Remove instance reference.
+                _gameManager.Remove(Code);
+                return true;
+            }
+
+            // Host migration.
+            if (HostId == playerId)
+            {
+                MigrateHost();
+            }
+
+            return true;
+        }
+
+        private void MigrateHost()
+        {
+            // Pick the first player as new host.
+            var host = _players.First().Value;
+            
+            HostId = host.Client.Id;
+            Logger.Information("{0} - Assigned {1} ({2}) as new host.", CodeStr, host.Client.Name, host.Client.Id);
+            
+            // Check our current game state.
+            if (GameState == GameStates.Ended && host.Limbo == LimboStates.WaitingForHost)
+            {
+                GameState = GameStates.NotStarted;
+                
+                // Spawn the host.
+                HandleJoinGameNew(host);
+                
+                // Pull players out of limbo.
+                CheckLimboPlayers();
+            }
+        }
+
+        private void CheckLimboPlayers()
+        {
+            using (var message = MessageWriter.Get(SendOption.Reliable))
+            {
+                foreach (var (_, player) in _players.Where(x => x.Value.Limbo == LimboStates.WaitingForHost))
+                {
+                    WriteJoinedGameMessage(message, true, player);
+                    WriteAlterGameMessage(message, false);
+                        
+                    player.Limbo = LimboStates.NotLimbo;
+                    player.Client.Send(message);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
index 57a94a6150bcf327d83278b01571ee0473498215..4d0031ff64bcf783f160cfc6dd6a8b051967e61b 100644 (file)
@@ -47,22 +47,24 @@ namespace Impostor.Server.Net.State
         
         public int PlayerCount => _players.Count;
         public ClientPlayer Host => _players[HostId];
-
+        
         /// <summary>
         ///     Send a message to all players except one.
         /// </summary>
         /// <param name="message">The message to send.</param>
-        /// <param name="sender">
+        /// <param name="senderId">
         ///     The player to exclude from sending the message.
         ///     Set to null to send a message to everyone.
         /// </param>
-        public void SendToAllExcept(MessageWriter message, ClientPlayer sender)
+        public void SendToAllExcept(MessageWriter message, int? senderId)
         {
-            foreach (var (_, player) in _players.Where(x => x.Value != sender))
+            foreach (var (_, player) in _players.Where(x => 
+                x.Value.Limbo == LimboStates.NotLimbo && 
+                x.Value.Client.Id != senderId))
             {
                 if (player.Client.Connection.State != ConnectionState.Connected)
                 {
-                    Logger.Warning("[{0}] Tried to send data to a disconnected player ({1}).", sender?.Client.Id, player.Client.Id);
+                    Logger.Warning("[{0}] Tried to send data to a disconnected player ({1}).", senderId, player.Client.Id);
                     continue;
                 }
                 
@@ -97,7 +99,7 @@ namespace Impostor.Server.Net.State
         {
             Message01JoinGame.SerializeJoin(message, clear, Code, player.Client.Id, HostId);
             
-            SendToAllExcept(message, player);
+            SendToAllExcept(message, player.Client.Id);
         }
     }
 }
\ No newline at end of file