// This will Handle the initial connection setup and Heartbeats of a client private void _handleConnectionSetup(PlayerInfo player, NetworkMessage message) { // Make sure the message is from the correct client provided bool sentByPlayer = message.Sender.Equals(player.Endpoint); if (sentByPlayer) { // Record the last time we've heard from them player.LastPacketReceivedTime = message.ReceiveTime; // Do they need their Side? or a heartbeat ACK switch (message.Packet.Type) { case PacketType.RequestJoin: Console.WriteLine("[{0:000}] Join Request from {1}", Id, player.Endpoint); _sendAcceptJoin(player); break; case PacketType.AcceptJoinAck: // They acknowledged (they will send heartbeats until game start) player.HavePaddle = true; break; case PacketType.Heartbeat: // They are waiting for the game start, Respond with an ACK HeartbeatAckPacket hap = new HeartbeatAckPacket(); _sendTo(player, hap); // Incase their ACK didn't reach us if (!player.HavePaddle) { _sendAcceptJoin(player); } break; } } }
static void EncodeHeartbeatAck(IByteBufferAllocator bufferAllocator, HeartbeatAckPacket packet, List <object> output) { byte[] SubjectNameBytes = EncodeStringInUtf8(packet.Subject); int variablePartSize = SubjectNameBytes.Length + SPACES_BYTES.Length; byte[] PayloadSize = EncodeStringInUtf8("0"); variablePartSize += PayloadSize.Length + CRLF_BYTES.Length; variablePartSize += CRLF_BYTES.Length; int fixedHeaderBufferSize = PUB_BYTES.Length + SPACES_BYTES.Length; IByteBuffer buf = null; try { buf = bufferAllocator.Buffer(fixedHeaderBufferSize + variablePartSize); buf.WriteBytes(PUB_BYTES); buf.WriteBytes(SPACES_BYTES); buf.WriteBytes(SubjectNameBytes); buf.WriteBytes(SPACES_BYTES); buf.WriteBytes(PayloadSize); buf.WriteBytes(CRLF_BYTES); buf.WriteBytes(CRLF_BYTES); output.Add(buf); buf = null; } finally { buf?.SafeRelease(); } }
// This runs in its own Thread // It is the actual game private void _arenaRun() { Console.WriteLine("[{0:000}] Waiting for players", Id); GameTime gameTime = new GameTime(); // Varibables used in the switch TimeSpan notifyGameStartTimeout = TimeSpan.FromSeconds(2.5); TimeSpan sendGameStateTimeout = TimeSpan.FromMilliseconds(1000f / 30f); // How often to update the players // The loop bool running = true; bool playerDropped = false; while (running) { // Pop off a message (if there is one) NetworkMessage message; bool haveMsg = _messages.TryDequeue(out message); switch (State.Value) { case ArenaState.WaitingForPlayers: if (haveMsg) { // Wait until we have two players _handleConnectionSetup(LeftPlayer, message); _handleConnectionSetup(RightPlayer, message); // Check if we are ready or not if (LeftPlayer.HavePaddle && RightPlayer.HavePaddle) { // Try sending the GameStart packet immediately _notifyGameStart(LeftPlayer, new TimeSpan()); _notifyGameStart(RightPlayer, new TimeSpan()); // Shift the state State.Value = ArenaState.NotifyingGameStart; } } break; case ArenaState.NotifyingGameStart: // Try sending the GameStart packet _notifyGameStart(LeftPlayer, notifyGameStartTimeout); _notifyGameStart(RightPlayer, notifyGameStartTimeout); // Check for ACK if (haveMsg && (message.Packet.Type == PacketType.GameStartAck)) { // Mark true for those who have sent something if (message.Sender.Equals(LeftPlayer.Endpoint)) { LeftPlayer.Ready = true; } else if (message.Sender.Equals(RightPlayer.Endpoint)) { RightPlayer.Ready = true; } } // Are we ready to send/received game data? if (LeftPlayer.Ready && RightPlayer.Ready) { // Initlize some game object positions _ball.Initialize(); LeftPlayer.Paddle.Initialize(); RightPlayer.Paddle.Initialize(); // Send a basic game state _sendGameState(LeftPlayer, new TimeSpan()); _sendGameState(RightPlayer, new TimeSpan()); // Start the game timer State.Value = ArenaState.InGame; Console.WriteLine("[{0:000}] Starting Game", Id); _gameTimer.Start(); } break; case ArenaState.InGame: // Update the game timer TimeSpan now = _gameTimer.Elapsed; gameTime = new GameTime(now, now - gameTime.TotalGameTime); // Get paddle postions from clients if (haveMsg) { switch (message.Packet.Type) { case PacketType.PaddlePosition: _handlePaddleUpdate(message); break; case PacketType.Heartbeat: // Respond with an ACK HeartbeatAckPacket hap = new HeartbeatAckPacket(); PlayerInfo player = message.Sender.Equals(LeftPlayer.Endpoint) ? LeftPlayer : RightPlayer; _sendTo(player, hap); // Record time player.LastPacketReceivedTime = message.ReceiveTime; break; } } //Update the game components _ball.ServerSideUpdate(gameTime); _checkForBallCollisions(); // Send the data _sendGameState(LeftPlayer, sendGameStateTimeout); _sendGameState(RightPlayer, sendGameStateTimeout); break; } // Check for a quit from one of the clients if (haveMsg && (message.Packet.Type == PacketType.Bye)) { // Well, someone dropped PlayerInfo player = message.Sender.Equals(LeftPlayer.Endpoint) ? LeftPlayer : RightPlayer; running = false; Console.WriteLine("[{0:000}] Quit detected from {1} at {2}", Id, player.Paddle.Side, _gameTimer.Elapsed); // Tell the other one if (player.Paddle.Side == PaddleSide.Left) { // Left Quit, tell Right if (RightPlayer.IsSet) { _server.SendBye(RightPlayer.Endpoint); } } else { // Right Quit, tell Left if (LeftPlayer.IsSet) { _server.SendBye(LeftPlayer.Endpoint); } } } // Check for timeouts playerDropped |= _timedOut(LeftPlayer); playerDropped |= _timedOut(RightPlayer); // Small nap Thread.Sleep(1); // Check quit values running &= !_stopRequested.Value; running &= !playerDropped; } // End the game _gameTimer.Stop(); State.Value = ArenaState.GameOver; Console.WriteLine("[{0:000}] Game Over, total game time was {1}", Id, _gameTimer.Elapsed); // If the stop was requested, gracefully tell the players to quit if (_stopRequested.Value) { Console.WriteLine("[{0:000}] Notifying Players of server shutdown", Id); if (LeftPlayer.IsSet) { _server.SendBye(LeftPlayer.Endpoint); } if (RightPlayer.IsSet) { _server.SendBye(RightPlayer.Endpoint); } } // Tell the server that we're finished _server.NotifyDone(this); }