From b884d5a669f207d23527c1095dad6d7f172b487b Mon Sep 17 00:00:00 2001 From: AeonLucid Date: Tue, 29 Sep 2020 05:31:17 +0200 Subject: [PATCH] Fixes host migration (#5) and proper player count checks --- src/Impostor.Server/Net/Client.cs | 2 +- src/Impostor.Server/Net/State/ClientPlayer.cs | 2 + .../Net/State/Game.Incoming.cs | 120 +++++++----------- src/Impostor.Server/Net/State/Game.State.cs | 95 ++++++++++++++ src/Impostor.Server/Net/State/Game.cs | 14 +- 5 files changed, 153 insertions(+), 80 deletions(-) create mode 100644 src/Impostor.Server/Net/State/Game.State.cs diff --git a/src/Impostor.Server/Net/Client.cs b/src/Impostor.Server/Net/Client.cs index 56305b4..2dbf257 100644 --- a/src/Impostor.Server/Net/Client.cs +++ b/src/Impostor.Server/Net/Client.cs @@ -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; diff --git a/src/Impostor.Server/Net/State/ClientPlayer.cs b/src/Impostor.Server/Net/State/ClientPlayer.cs index 5242cab..cce8a1b 100644 --- a/src/Impostor.Server/Net/State/ClientPlayer.cs +++ b/src/Impostor.Server/Net/State/ClientPlayer.cs @@ -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) { diff --git a/src/Impostor.Server/Net/State/Game.Incoming.cs b/src/Impostor.Server/Net/State/Game.Incoming.cs index bf91cc9..168429c 100644 --- a/src/Impostor.Server/Net/State/Game.Incoming.cs +++ b/src/Impostor.Server/Net/State/Game.Incoming.cs @@ -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 index 0000000..057526e --- /dev/null +++ b/src/Impostor.Server/Net/State/Game.State.cs @@ -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 diff --git a/src/Impostor.Server/Net/State/Game.cs b/src/Impostor.Server/Net/State/Game.cs index 57a94a6..4d0031f 100644 --- a/src/Impostor.Server/Net/State/Game.cs +++ b/src/Impostor.Server/Net/State/Game.cs @@ -47,22 +47,24 @@ namespace Impostor.Server.Net.State public int PlayerCount => _players.Count; public ClientPlayer Host => _players[HostId]; - + /// /// Send a message to all players except one. /// /// The message to send. - /// + /// /// The player to exclude from sending the message. /// Set to null to send a message to everyone. /// - 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 -- 2.39.5