using System;
using AmongUs.Server.Data;
-using AmongUs.Server.Exceptions;
using AmongUs.Server.Extensions;
-using AmongUs.Server.Net.Request;
using AmongUs.Server.Net.Response;
using AmongUs.Shared.Innersloth;
using AmongUs.Shared.Innersloth.Data;
public Connection Connection { get; }
public ClientPlayer Player { get; }
+ public void Send(MessageWriter writer)
+ {
+ Connection.Send(writer);
+ }
+
+ private bool IsPacketAllowed(MessageReader message, bool hostOnly)
+ {
+ var game = Player.Game;
+ if (game == null)
+ {
+ return false;
+ }
+
+ // GameCode must match code of the current game assigned to the player.
+ if (message.ReadInt32() != game.Code)
+ {
+ return false;
+ }
+
+ // Some packets should only be sent by the host of the game.
+ if (hostOnly)
+ {
+ if (game.HostId == Id)
+ {
+ return true;
+ }
+
+ Logger.Warning("[{0}] Client sent packet only allowed by the host ({1}).", Id, game.HostId);
+ return false;
+ }
+
+ return true;
+ }
+
private void OnDataReceived(DataReceivedEventArgs e)
{
try
game.HandleJoinGame(Player);
break;
}
-
- // 101A3960
+
case RequestFlag.StartGame:
+ {
+ if (!IsPacketAllowed(message, true))
+ {
+ return;
+ }
+
+ Player.Game.HandleStartGame(message);
break;
+ }
// 101A39EC
case RequestFlag.RemoveGame:
break;
case RequestFlag.RemovePlayer:
+ {
+ if (!IsPacketAllowed(message, true))
+ {
+ return;
+ }
+
+ var playerId = message.ReadPackedInt32();
+ var reason = message.ReadByte();
+
+ Player.Game.HandleRemovePlayer(playerId, reason);
break;
+ }
case RequestFlag.GameData:
case RequestFlag.GameDataTo:
{
- var game = Player.Game;
- if (game == null)
+ if (!IsPacketAllowed(message, false))
{
- throw new NullReferenceException("Game was not set for the client.");
- }
-
- var code = message.ReadInt32();
- if (code != game.Code)
- {
- // Packet was meant for another game.
return;
}
{
var target = message.ReadPackedInt32();
writer.CopyFrom(message);
- game.SendTo(writer, target);
+ Player.Game.SendTo(writer, target);
}
else
{
writer.CopyFrom(message);
- game.SendToAllExcept(writer, Player);
+ Player.Game.SendToAllExcept(writer, Player);
}
}
break;
case RequestFlag.JoinedGame:
break;
- // 101A3BD0
case RequestFlag.EndGame:
+ {
+ if (!IsPacketAllowed(message, true))
+ {
+ return;
+ }
+
+ Player.Game.HandleEndGame(message);
break;
+ }
default:
Logger.Warning("Server received unknown flag {0}.", flag);
using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
using System.Linq;
using AmongUs.Server.Exceptions;
using AmongUs.Server.Extensions;
private static readonly ILogger Logger = Log.ForContext<Game>();
private readonly ConcurrentDictionary<int, ClientPlayer> _players;
- private int _hostId;
-
+
public Game(int code, GameOptionsData options)
{
Code = code;
CodeStr = GameCode.IntToGameName(code);
+ HostId = -1;
GameState = GameStates.NotStarted;
Options = options;
- _hostId = -1;
_players = new ConcurrentDictionary<int, ClientPlayer>();
}
public int Code { get; }
public string CodeStr { get; }
- public GameStates GameState { get; }
+ public bool IsPublic { get; private set; }
+ public int HostId { get; private set; }
+ public GameStates GameState { get; private set; }
public GameOptionsData Options { get; }
public void SendToAllExcept(MessageWriter message, ClientPlayer sender)
{
foreach (var (_, player) in _players.Where(x => x.Value != sender))
{
- player.Client.Connection.Send(message);
+ if (player.Client.Connection.State != ConnectionState.Connected)
+ {
+ Logger.Warning("[{0}] Tried to sent data to a disconnected player ({1}).", sender?.Client.Id, player.Client.Id);
+ continue;
+ }
+
+ player.Client.Send(message);
}
}
{
if (_players.TryGetValue(playerId, out var player))
{
- player.Client.Connection.Send(message);
+ if (player.Client.Connection.State != ConnectionState.Connected)
+ {
+ Logger.Warning("[{0}] Sending data to {1} failed, player is not connected.", CodeStr, player.Client.Id);
+ return;
+ }
+
+ player.Client.Send(message);
}
else
{
}
}
+ public void HandleStartGame(MessageReader message)
+ {
+ GameState = GameStates.Started;
+
+ using (var packet = MessageWriter.Get(SendOption.Reliable))
+ {
+ packet.CopyFrom(message);
+ SendToAllExcept(packet, null);
+ }
+ }
+
public void HandleJoinGame(ClientPlayer player)
{
switch (GameState)
case GameStates.NotStarted:
HandleJoinGameNew(player);
break;
- case GameStates.Started:
+ case GameStates.Ended:
HandleJoinGameNext(player);
break;
- case GameStates.Ended:
+ case GameStates.Started:
case GameStates.Destroyed:
player.Client.Connection.Send(new Message1DisconnectReason(DisconnectReason.GameStarted));
return;
}
}
+ public void HandleEndGame(MessageReader message)
+ {
+ GameState = GameStates.Ended;
+
+ // Broadcast end of the game.
+ using (var packet = MessageWriter.Get(SendOption.Reliable))
+ {
+ packet.CopyFrom(message);
+ SendToAllExcept(packet, null);
+ }
+
+ // Remove all players from this game.
+ foreach (var player in _players)
+ {
+ player.Value.Game = null;
+ }
+
+ _players.Clear();
+ }
+
+ public void HandleRemovePlayer(int playerId, byte reason)
+ {
+ _players.TryRemove(playerId, out var player);
+
+ // TODO: Host migration
+
+ using (var packet = MessageWriter.Get(SendOption.Reliable))
+ {
+ packet.Write(Code);
+ packet.Write(playerId);
+ packet.Write(HostId);
+ packet.Write(reason);
+
+ SendToAllExcept(packet, player);
+ }
+ }
+
private void HandleJoinGameNew(ClientPlayer player)
{
Logger.Verbose("[{0}] Player joined.", CodeStr);
player.Game = this;
// Assign hostId if none is set.
- if (_hostId == -1)
+ if (HostId == -1)
{
- _hostId = player.Client.Id;
+ HostId = player.Client.Id;
}
- if (_hostId == player.Client.Id)
+ if (HostId == player.Client.Id)
{
player.LimboState = LimboStates.NotLimbo;
}
using (var message = MessageWriter.Get(SendOption.Reliable))
{
- // TODO: WriteJoinedMessage - Move to own method / class
- message.StartMessage(7);
- message.Write(Code);
- message.Write(player.Client.Id);
- message.Write(_hostId);
- message.WritePacked(_players.Count - 1);
-
- foreach (var (_, p) in _players.Where(x => x.Value != player))
+ WriteJoinedGameMessage(message, player, false);
+ WriteAlterGameMessage(message, false);
+
+ player.Client.Send(message);
+
+ BroadcastJoinMessage(message, player, true);
+ }
+ }
+
+ private void HandleJoinGameNext(ClientPlayer sender)
+ {
+ if (sender.Client.Id == HostId)
+ {
+ GameState = GameStates.NotStarted;
+ HandleJoinGameNew(sender);
+
+ using (var message = MessageWriter.Get(SendOption.Reliable))
{
- message.WritePacked(p.Client.Id);
+ foreach (var (_, player) in _players.Where(x => x.Value != sender))
+ {
+ WriteJoinedGameMessage(message, player, true);
+ WriteAlterGameMessage(message, false);
+ player.Client.Send(message);
+ }
}
+
+ return;
+ }
+
+ if (_players.Count >= 9)
+ {
+ sender.Client.Connection.Send(new Message1DisconnectReason(DisconnectReason.GameFull));
+ return;
+ }
+
+ // Store player.
+ if (!_players.TryAdd(sender.Client.Id, sender))
+ {
+ throw new AmongUsException("Failed to add player to game.");
+ }
- message.EndMessage();
+ // Assign player to this game for future packets.
+ sender.Game = this;
+
+ // Limbo, yes.
+ sender.LimboState = LimboStates.WaitingForHost;
- message.StartMessage(10);
- message.Write(Code);
- message.Write((sbyte)1);
- message.Write(false); // Private / Public
- message.EndMessage();
-
- player.Client.Connection.Send(message);
+ using (var packet = MessageWriter.Get(SendOption.Reliable))
+ {
+ WriteWaitForHostMessage(packet, sender, false);
+ sender.Client.Send(packet);
+
+ BroadcastJoinMessage(packet, sender, true);
+ }
+ }
+
+ private void WriteJoinedGameMessage(MessageWriter message, ClientPlayer player, bool clear)
+ {
+ if (clear)
+ {
+ message.Clear(SendOption.Reliable);
+ }
- // TODO: BroadcastJoinMessage - Move to own method / class
+ message.StartMessage((byte) RequestFlag.JoinedGame);
+ message.Write(Code);
+ message.Write(player.Client.Id);
+ message.Write(HostId);
+ message.WritePacked(_players.Count - 1);
+
+ foreach (var (_, p) in _players.Where(x => x.Value != player))
+ {
+ message.WritePacked(p.Client.Id);
+ }
+
+ message.EndMessage();
+ }
+
+ private void WriteAlterGameMessage(MessageWriter message, bool clear)
+ {
+ if (clear)
+ {
message.Clear(SendOption.Reliable);
- message.StartMessage(1);
- message.Write(Code);
- message.Write(player.Client.Id);
- message.Write(_hostId);
- message.EndMessage();
-
- SendToAllExcept(message, player);
}
+
+ message.StartMessage((byte) RequestFlag.AlterGame);
+ message.Write(Code);
+ message.Write((byte) AlterGameTags.ChangePrivacy);
+ message.Write(IsPublic);
+ message.EndMessage();
}
- private void HandleJoinGameNext(ClientPlayer player)
+ private void WriteWaitForHostMessage(MessageWriter message, ClientPlayer player, bool clear)
+ {
+ if (clear)
+ {
+ message.Clear(SendOption.Reliable);
+ }
+
+ message.StartMessage((byte) RequestFlag.WaitForHost);
+ message.Write(Code);
+ message.Write(player.Client.Id);
+ message.EndMessage();
+ }
+
+ private void BroadcastJoinMessage(MessageWriter message, ClientPlayer player, bool clear)
{
- throw new NotImplementedException();
+ if (clear)
+ {
+ message.Clear(SendOption.Reliable);
+ }
+
+ message.StartMessage((byte) RequestFlag.JoinGame);
+ message.Write(Code);
+ message.Write(player.Client.Id);
+ message.Write(HostId);
+ message.EndMessage();
+
+ SendToAllExcept(message, player);
}
}
}
\ No newline at end of file