/// <summary> /// Accept the client and add to our list of connected clients /// </summary> /// <param name="packet">The BMSByte packet received from connecting client</param> /// <param name="steamId">The SteamId of the connecting client</param> private void SetupClient(BMSByte packet, SteamId steamId) { if (Players.Count == MaxConnections) { // Tell the client why they are being disconnected Send(Error.CreateErrorMessage(Time.Timestep, "Max Players Reached On Server", false, MessageGroupIds.MAX_CONNECTIONS, true)); // Send the close connection frame to the client Send(new ConnectionClose(Time.Timestep, false, Receivers.Target, MessageGroupIds.DISCONNECT, false)); return; } else if (!AcceptingConnections) { // Tell the client why they are being disconnected Send(Error.CreateErrorMessage(Time.Timestep, "The server is busy and not accepting connections", false, MessageGroupIds.MAX_CONNECTIONS, true)); // Send the close connection frame to the client Send(new ConnectionClose(Time.Timestep, false, Receivers.Target, MessageGroupIds.DISCONNECT, false)); return; } // Validate that the connection headers are properly formatted byte[] response = Websockets.ValidateConnectionHeader(packet.CompressBytes()); // The response will be null if the header sent is invalid, if so then disconnect client as they are sending invalid headers if (response == null) { return; } var player = new FacepunchNetworkingPlayer(ServerPlayerCounter++, steamId, false, this); // If all is in order then send the validated response to the client Client.Send(response, response.Length, steamId); OnPlayerConnected(player); steamPlayers.Add(steamId, player); // The player has successfully connected player.Connected = true; }
/// <summary> /// Infinite loop listening for new data from all connected clients on a separate thread. /// This loop breaks when readThreadCancel is set to true /// </summary> private void ReadClients() { SteamId messageFrom = default; BMSByte packet = null; // Intentional infinite loop while (IsBound) { // If the read has been flagged to be canceled then break from this loop if (readThreadCancel) { return; } try { packet = Client.Receive(out messageFrom); if (messageFrom == default) { Thread.Sleep(1); continue; } if (packet == null) { Logging.BMSLog.Log("null packet received from non-null player - should not see me!"); Thread.Sleep(1); continue; } if (PacketLossSimulation > 0.0f && new Random().NextDouble() <= PacketLossSimulation) { // Skip this message continue; } BandwidthIn += (ulong)packet.Size; } catch (Exception e) { Logging.BMSLog.LogException(e); FacepunchNetworkingPlayer player; if (steamPlayers.TryGetValue(messageFrom, out player)) { FinalizeRemovePlayer(player, true); } continue; } // Check to make sure a message was received if (packet == null || packet.Size <= 0) { continue; } if (!steamPlayers.ContainsKey(messageFrom)) { SetupClient(packet, messageFrom); continue; } else { currentReadingPlayer = steamPlayers[messageFrom]; if (!currentReadingPlayer.Accepted && !currentReadingPlayer.PendingAccepted) { // It is possible that the response validation was dropped so // check if the client is resending for a response byte[] response = Websockets.ValidateConnectionHeader(packet.CompressBytes()); // The client has sent the connection request again if (response != null) { Client.Send(response, response.Length, messageFrom, P2PSend.Reliable); continue; } else { currentReadingPlayer.PendingAccepted = true; ReadPacket(packet); } } else { // Due to the Forge Networking protocol, the only time that packet 1 // will be 71 and the second packet be 69 is a forced disconnect reconnect if (packet[0] == 71 && packet[1] == 69) { Logging.BMSLog.LogFormat("Received packet[0]=71 & packet[1]=69"); steamPlayers.Remove(messageFrom); FinalizeRemovePlayer(currentReadingPlayer, true); continue; } currentReadingPlayer.Ping(); ReadPacket(packet); } } } }