From 665dd192d6100f1eb1ea219358c0696f04758886 Mon Sep 17 00:00:00 2001 From: AeonLucid Date: Sun, 20 Sep 2020 23:06:20 +0200 Subject: [PATCH] Improve game code, now possible to start / end games --- src/AmongUs.Server/Net/Client.cs | 81 +++++-- src/AmongUs.Server/Net/Game.cs | 219 ++++++++++++++---- .../Innersloth/Data/AlterGameTags.cs | 7 + .../Innersloth/Data}/RequestFlag.cs | 2 +- .../Innersloth/GameOptionsData.cs | 4 +- 5 files changed, 255 insertions(+), 58 deletions(-) create mode 100644 src/AmongUs.Shared/Innersloth/Data/AlterGameTags.cs rename src/{AmongUs.Server/Net/Request => AmongUs.Shared/Innersloth/Data}/RequestFlag.cs (90%) diff --git a/src/AmongUs.Server/Net/Client.cs b/src/AmongUs.Server/Net/Client.cs index 0ce5551..6f9db3d 100644 --- a/src/AmongUs.Server/Net/Client.cs +++ b/src/AmongUs.Server/Net/Client.cs @@ -1,8 +1,6 @@ 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; @@ -36,6 +34,40 @@ namespace AmongUs.Server.Net 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 @@ -105,31 +137,41 @@ namespace AmongUs.Server.Net 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; } @@ -140,12 +182,12 @@ namespace AmongUs.Server.Net { 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; @@ -155,9 +197,16 @@ namespace AmongUs.Server.Net 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); diff --git a/src/AmongUs.Server/Net/Game.cs b/src/AmongUs.Server/Net/Game.cs index 23318e7..b6d348c 100644 --- a/src/AmongUs.Server/Net/Game.cs +++ b/src/AmongUs.Server/Net/Game.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Linq; using AmongUs.Server.Exceptions; using AmongUs.Server.Extensions; @@ -18,29 +17,36 @@ namespace AmongUs.Server.Net private static readonly ILogger Logger = Log.ForContext(); private readonly ConcurrentDictionary _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(); } 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); } } @@ -48,7 +54,13 @@ namespace AmongUs.Server.Net { 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 { @@ -56,6 +68,17 @@ namespace AmongUs.Server.Net } } + 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) @@ -63,10 +86,10 @@ namespace AmongUs.Server.Net 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; @@ -75,6 +98,43 @@ namespace AmongUs.Server.Net } } + 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); @@ -89,55 +149,136 @@ namespace AmongUs.Server.Net 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 diff --git a/src/AmongUs.Shared/Innersloth/Data/AlterGameTags.cs b/src/AmongUs.Shared/Innersloth/Data/AlterGameTags.cs new file mode 100644 index 0000000..a89c628 --- /dev/null +++ b/src/AmongUs.Shared/Innersloth/Data/AlterGameTags.cs @@ -0,0 +1,7 @@ +namespace AmongUs.Shared.Innersloth.Data +{ + public enum AlterGameTags : byte + { + ChangePrivacy = 1 + } +} \ No newline at end of file diff --git a/src/AmongUs.Server/Net/Request/RequestFlag.cs b/src/AmongUs.Shared/Innersloth/Data/RequestFlag.cs similarity index 90% rename from src/AmongUs.Server/Net/Request/RequestFlag.cs rename to src/AmongUs.Shared/Innersloth/Data/RequestFlag.cs index 4f527fb..8d82426 100644 --- a/src/AmongUs.Server/Net/Request/RequestFlag.cs +++ b/src/AmongUs.Shared/Innersloth/Data/RequestFlag.cs @@ -1,4 +1,4 @@ -namespace AmongUs.Server.Net.Request +namespace AmongUs.Shared.Innersloth.Data { public enum RequestFlag : byte { diff --git a/src/AmongUs.Shared/Innersloth/GameOptionsData.cs b/src/AmongUs.Shared/Innersloth/GameOptionsData.cs index 689b8aa..4fd19f6 100644 --- a/src/AmongUs.Shared/Innersloth/GameOptionsData.cs +++ b/src/AmongUs.Shared/Innersloth/GameOptionsData.cs @@ -26,7 +26,7 @@ namespace AmongUs.Shared.Innersloth public int VotingTime { get; set; } public bool ConfirmImpostor { get; set; } public bool VisualTasks { get; set; } - public bool isDefaults { get; set; } + public bool IsDefaults { get; set; } public void Serialize(BinaryWriter writer, byte version) { @@ -56,7 +56,7 @@ namespace AmongUs.Shared.Innersloth result.KillDistance = reader.ReadByte(); result.DiscussionTime = reader.ReadInt32(); result.VotingTime = reader.ReadInt32(); - result.isDefaults = reader.ReadBoolean(); + result.IsDefaults = reader.ReadBoolean(); if (result.Version > 1) { -- 2.39.5