private static async Task Main(string[] args) { Log.Logger = new LoggerConfiguration() .WriteTo.Console() .CreateLogger(); var writeHandshake = MessageWriter.Get(MessageType.Reliable); writeHandshake.Write(50516550); writeHandshake.Write("AeonLucid"); var writeGameCreate = MessageWriter.Get(MessageType.Reliable); Message00HostGameC2S.Serialize(writeGameCreate, new GameOptionsData { MaxPlayers = 4, NumImpostors = 2 }); // TODO: ObjectPool for MessageReaders using (var connection = new UdpClientConnection(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 22023), null)) { var e = new ManualResetEvent(false); // Register events. connection.DataReceived = DataReceived; connection.Disconnected = Disconnected; // Connect and send handshake. await connection.ConnectAsync(writeHandshake.ToByteArray(false)); Log.Information("Connected."); // Create a game. await connection.SendAsync(writeGameCreate); Log.Information("Requested game creation."); // Recycle. writeHandshake.Recycle(); writeGameCreate.Recycle(); e.WaitOne(); } }
public override async ValueTask HandleMessageAsync(IMessageReader reader, MessageType messageType) { byte flag = reader.Tag; _logger.LogTrace("[{0}] Server got {1}.", Id, flag); switch (flag) { case MessageFlags.HostGame: { // Read game settings. GameOptionsData gameInfo = Message00HostGameC2S.Deserialize(reader, out _); // Create game. IGame?game = await _gameManager.CreateAsync(this, gameInfo); if (game == null) { await DisconnectAsync(DisconnectReason.GameMissing); return; } // Code in the packet below will be used in JoinGame. using (var writer = MessageWriter.Get(MessageType.Reliable)) { Message00HostGameS2C.Serialize(writer, game.Code); await Connection.SendAsync(writer); } break; } case MessageFlags.JoinGame: { Message01JoinGameC2S.Deserialize(reader, out GameCode gameCode); Game?game = _gameManager.Find(gameCode); if (game == null) { await DisconnectAsync(DisconnectReason.GameMissing); return; } GameJoinResult result = await game.AddClientAsync(this); switch (result.Error) { case GameJoinError.None: break; case GameJoinError.InvalidClient: await DisconnectAsync(DisconnectReason.Custom, "Client is in an invalid state."); break; case GameJoinError.Banned: await DisconnectAsync(DisconnectReason.Banned); break; case GameJoinError.GameFull: await DisconnectAsync(DisconnectReason.GameFull); break; case GameJoinError.InvalidLimbo: await DisconnectAsync(DisconnectReason.Custom, "Invalid limbo state while joining."); break; case GameJoinError.GameStarted: await DisconnectAsync(DisconnectReason.GameStarted); break; case GameJoinError.GameDestroyed: await DisconnectAsync(DisconnectReason.Custom, DisconnectMessages.Destroyed); break; case GameJoinError.Custom: await DisconnectAsync(DisconnectReason.Custom, result.Message); break; default: await DisconnectAsync(DisconnectReason.Custom, "Unknown error."); break; } break; } case MessageFlags.StartGame: { if (!IsPacketAllowed(reader, true)) { return; } await Player !.Game.HandleStartGame(reader); break; } // No idea how this flag is triggered. case MessageFlags.RemoveGame: break; case MessageFlags.RemovePlayer: { if (!IsPacketAllowed(reader, true)) { return; } Message04RemovePlayerC2S.Deserialize( reader, out int playerId, out byte reason); await Player !.Game.HandleRemovePlayer(playerId, (DisconnectReason)reason); break; } case MessageFlags.GameData: case MessageFlags.GameDataTo: { if (!IsPacketAllowed(reader, false)) { return; } bool toPlayer = flag == MessageFlags.GameDataTo; int position = reader.Position; bool verified = await Player !.Game.HandleGameDataAsync(reader, Player, toPlayer); reader.Seek(position); if (verified && Player != null) { // Broadcast packet to all other players. using (var writer = MessageWriter.Get(messageType)) { if (toPlayer) { int target = reader.ReadPackedInt32(); reader.CopyTo(writer); await Player.Game.SendToAsync(writer, target); } else { reader.CopyTo(writer); await Player.Game.SendToAllExceptAsync(writer, Id); } } } break; } case MessageFlags.EndGame: { if (!IsPacketAllowed(reader, true)) { return; } Message08EndGameC2S.Deserialize( reader, out GameOverReason gameOverReason); await Player !.Game.HandleEndGame(reader, gameOverReason); break; } case MessageFlags.AlterGame: { if (!IsPacketAllowed(reader, true)) { return; } Message10AlterGameC2S.Deserialize( reader, out AlterGameTags gameTag, out bool value); if (gameTag != AlterGameTags.ChangePrivacy) { return; } await Player !.Game.HandleAlterGame(reader, Player, value); break; } case MessageFlags.KickPlayer: { if (!IsPacketAllowed(reader, true)) { return; } Message11KickPlayerC2S.Deserialize( reader, out int playerId, out bool isBan); await Player !.Game.HandleKickPlayer(playerId, isBan); break; } case MessageFlags.GetGameListV2: { Message16GetGameListC2S.Deserialize(reader, out var options, out _); await OnRequestGameListAsync(options); break; } default: if (_customMessageManager.TryGet(flag, out var customRootMessage)) { await customRootMessage.HandleMessageAsync(this, reader, messageType); break; } _logger.LogWarning("Server received unknown flag {0}.", flag); break; } #if DEBUG if (flag != MessageFlags.GameData && flag != MessageFlags.GameDataTo && flag != MessageFlags.EndGame && reader.Position < reader.Length) { _logger.LogWarning( "Server did not consume all bytes from {0} ({1} < {2}).", flag, reader.Position, reader.Length); } #endif }
private static async Task ParsePacket(BinaryReader reader) { var dataType = (RecordedPacketType)reader.ReadByte(); // Read client id. var clientId = reader.ReadInt32(); switch (dataType) { case RecordedPacketType.Connect: // Read data. var addressLength = reader.ReadByte(); var addressBytes = reader.ReadBytes(addressLength); var addressPort = reader.ReadUInt16(); var address = new IPEndPoint(new IPAddress(addressBytes), addressPort); var name = reader.ReadString(); // Create and register connection. var connection = new MockHazelConnection(address); await _clientManager.RegisterConnectionAsync(connection, name, 50516550, null); // Store reference for ourselfs. Connections.Add(clientId, connection); break; case RecordedPacketType.Disconnect: string reason = null; if (reader.BaseStream.Position < reader.BaseStream.Length) { reason = reader.ReadString(); } await Connections[clientId].Client !.HandleDisconnectAsync(reason); Connections.Remove(clientId); break; case RecordedPacketType.Message: { var messageType = (MessageType)reader.ReadByte(); var tag = reader.ReadByte(); var length = reader.ReadInt32(); var buffer = reader.ReadBytes(length); using var message = _readerPool.Get(); message.Update(buffer, tag: tag); if (tag == MessageFlags.HostGame) { GameOptions.Add(clientId, Message00HostGameC2S.Deserialize(message)); } else if (Connections.TryGetValue(clientId, out var client)) { await client.Client !.HandleMessageAsync(message, messageType); } break; } case RecordedPacketType.GameCreated: _gameCodeFactory.Result = GameCode.From(reader.ReadString()); await _gameManager.CreateAsync(GameOptions[clientId]); GameOptions.Remove(clientId); break; default: throw new ArgumentOutOfRangeException(); } }
public override async ValueTask HandleMessageAsync(IMessageReader reader, MessageType messageType) { var flag = reader.Tag; _logger.LogTrace("[{0}] Server got {1}.", Id, flag); switch (flag) { case MessageFlags.HostGame: { // Read game settings. var gameInfo = Message00HostGameC2S.Deserialize(reader); // Create game. var game = await _gameManager.CreateAsync(gameInfo); // Code in the packet below will be used in JoinGame. using (var writer = MessageWriter.Get(MessageType.Reliable)) { Message00HostGameS2C.Serialize(writer, game.Code); await Connection.SendAsync(writer); } break; } case MessageFlags.JoinGame: { Message01JoinGameC2S.Deserialize( reader, out var gameCode, out _); var game = _gameManager.Find(gameCode); if (game == null) { await DisconnectAsync(DisconnectReason.GameMissing); return; } var result = await game.AddClientAsync(this); switch (result.Error) { case GameJoinError.None: break; case GameJoinError.InvalidClient: await DisconnectAsync(DisconnectReason.Custom, "Client is in an invalid state."); break; case GameJoinError.Banned: await DisconnectAsync(DisconnectReason.Banned); break; case GameJoinError.GameFull: await DisconnectAsync(DisconnectReason.GameFull); break; case GameJoinError.InvalidLimbo: await DisconnectAsync(DisconnectReason.Custom, "Invalid limbo state while joining."); break; case GameJoinError.GameStarted: await DisconnectAsync(DisconnectReason.GameStarted); break; case GameJoinError.GameDestroyed: await DisconnectAsync(DisconnectReason.Custom, DisconnectMessages.Destroyed); break; case GameJoinError.Custom: await DisconnectAsync(DisconnectReason.Custom, result.Message); break; default: await DisconnectAsync(DisconnectReason.Custom, "Unknown error."); break; } break; } case MessageFlags.StartGame: { if (!IsPacketAllowed(reader, true)) { return; } await Player.Game.HandleStartGame(reader); break; } // No idea how this flag is triggered. case MessageFlags.RemoveGame: break; case MessageFlags.RemovePlayer: { if (!IsPacketAllowed(reader, true)) { return; } Message04RemovePlayerC2S.Deserialize( reader, out var playerId, out var reason); await Player.Game.HandleRemovePlayer(playerId, (DisconnectReason)reason); break; } case MessageFlags.GameData: case MessageFlags.GameDataTo: { if (!IsPacketAllowed(reader, false)) { return; } var toPlayer = flag == MessageFlags.GameDataTo; // Handle packet. using var readerCopy = reader.Copy(); // TODO: Return value, either a bool (to cancel) or a writer (to cancel (null) or modify/overwrite). try { var verified = await Player.Game.HandleGameDataAsync(readerCopy, Player, toPlayer); if (verified) { // Broadcast packet to all other players. using (var writer = MessageWriter.Get(messageType)) { if (toPlayer) { var target = reader.ReadPackedInt32(); reader.CopyTo(writer); await Player.Game.SendToAsync(writer, target); } else { reader.CopyTo(writer); await Player.Game.SendToAllExceptAsync(writer, Id); } } } } catch (ImpostorCheatException e) { if (_antiCheatConfig.BanIpFromGame) { Player.Game.BanIp(Connection.EndPoint.Address); } await DisconnectAsync(DisconnectReason.Hacking, e.Message); } break; } case MessageFlags.EndGame: { if (!IsPacketAllowed(reader, true)) { return; } Message08EndGameC2S.Deserialize( reader, out var gameOverReason); await Player.Game.HandleEndGame(reader, gameOverReason); break; } case MessageFlags.AlterGame: { if (!IsPacketAllowed(reader, true)) { return; } Message10AlterGameC2S.Deserialize( reader, out var gameTag, out var value); if (gameTag != AlterGameTags.ChangePrivacy) { return; } await Player.Game.HandleAlterGame(reader, Player, value); break; } case MessageFlags.KickPlayer: { if (!IsPacketAllowed(reader, true)) { return; } Message11KickPlayerC2S.Deserialize( reader, out var playerId, out var isBan); await Player.Game.HandleKickPlayer(playerId, isBan); break; } case MessageFlags.GetGameListV2: { Message16GetGameListC2S.Deserialize(reader, out var options); await OnRequestGameListAsync(options); break; } default: _logger.LogWarning("Server received unknown flag {0}.", flag); break; } #if DEBUG if (flag != MessageFlags.GameData && flag != MessageFlags.GameDataTo && flag != MessageFlags.EndGame && reader.Position < reader.Length) { _logger.LogWarning( "Server did not consume all bytes from {0} ({1} < {2}).", flag, reader.Position, reader.Length); } #endif }