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:
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)
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);
}
}
{
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))
WriteJoinedGameMessage(message, false, sender);
WriteAlterGameMessage(message, false);
+ sender.Limbo = LimboStates.NotLimbo;
sender.Client.Send(message);
BroadcastJoinMessage(message, true, sender);
{
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))
{
--- /dev/null
+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
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;
}
{
Message01JoinGame.SerializeJoin(message, clear, Code, player.Client.Id, HostId);
- SendToAllExcept(message, player);
+ SendToAllExcept(message, player.Client.Id);
}
}
}
\ No newline at end of file