From efe25fe61007b2c3dc021c149cd6db6aaa661441 Mon Sep 17 00:00:00 2001 From: xethm55 Date: Mon, 1 Oct 2012 21:17:49 -0400 Subject: [PATCH] Connection disconnects now working --- src/client/Client.cc | 55 +++++++++++++-- src/common/Network.cc | 143 ++++++++++++++++++++++++++++++++------ src/common/Network.h | 28 +++++--- src/common/Player.cc | 1 + src/common/Player.h | 2 + src/common/Timer.cc | 9 ++- src/common/Timer.h | 3 - src/server/Server.cc | 155 +++++++++++++++++++++++++----------------- 8 files changed, 291 insertions(+), 105 deletions(-) diff --git a/src/client/Client.cc b/src/client/Client.cc index be301b1..b66a391 100644 --- a/src/client/Client.cc +++ b/src/client/Client.cc @@ -15,8 +15,12 @@ Client::Client() Client::~Client() { // Send disconnect message + bool connection_closed = false; + double close_timer; sf::Packet client_packet; sf::Uint8 packet_type = PLAYER_DISCONNECT; + Timer client_timer; + client_timer.Init(); client_packet.clear(); client_packet << packet_type; client_packet << current_player; @@ -25,16 +29,52 @@ Client::~Client() // No time out needed here, since the // message will timeout after a couple of attempts // then exit anyway. - while(m_net_client->pendingMessages()) + close_timer = Timer::GetTimeDouble(); + while(!connection_closed) { + // Time must be updated before any messages are sent + // Especially guaranteed messages, since the time needs to be + // non zero. + client_timer.Update(); + m_net_client->Receive(); + + while(m_net_client->getData(client_packet)) + { + sf::Uint8 packet_type; + client_packet >> packet_type; + switch(packet_type) + { + case PLAYER_DISCONNECT: + { + sf::Uint8 player_index; + // This completely removes the player from the game + // Deletes member from the player list + client_packet >> player_index; + if(player_index == current_player) + { + connection_closed = true; + } + break; + } + } + } + m_net_client->Transmit(); // temporary for now. otherwise this thread consumed way too processing sf::sleep(sf::seconds(0.005)); // 5 milli-seconds + + // If the server does not respond within one second just close + // and the server can deal with the problems. + if((Timer::GetTimeDouble() - close_timer) > 1.0) + { + connection_closed = true; + } } m_net_client->Destroy(); + m_players.clear(); } @@ -120,15 +160,16 @@ void Client::update(double elapsed_time) { case PLAYER_CONNECT: { - sf::Uint32 players_address = 0u; + sf::Uint16 players_port = sf::Socket::AnyPort; sf::Uint8 pindex; std::string name = ""; client_packet >> pindex; client_packet >> name; - client_packet >> players_address; + client_packet >> players_port; // Should be a much better way of doing this. // Perhaps generate a random number - if((sf::Uint32)((sf::Uint64)(&m_players)) == players_address) + if((name == current_player_name) && + (m_net_client->getLocalPort() == players_port)) { current_player = pindex; } @@ -245,16 +286,16 @@ void Client::update(double elapsed_time) else if(!registered_player) { // Needs to be 32 bit so that the packet << overload will work. - sf::Uint32 players_address = (sf::Uint32)((sf::Uint64)(&m_players)); + sf::Uint16 players_port = m_net_client->getLocalPort(); sf::Uint8 packet_type = PLAYER_CONNECT; client_packet.clear(); client_packet << packet_type; client_packet << current_player; client_packet << current_player_name; - // Send the address of the players map. This will server as a unique + // Send the players port. This will server as a unique // identifier and prevent users with the same name from controlling // each other. - client_packet << players_address; + client_packet << players_port; m_net_client->sendData(client_packet, true); registered_player = true; } diff --git a/src/common/Network.cc b/src/common/Network.cc index 8cb7de5..da8da37 100644 --- a/src/common/Network.cc +++ b/src/common/Network.cc @@ -33,6 +33,7 @@ void Network::Create(sf::Uint16 port, sf::IpAddress address ) { tmpclient.addr = address; tmpclient.port = port; + tmpclient.disconnect = DISCONNECTED; numclients = addClients(&tmpclient, ¤t_client); net_socket.bind( sf::Socket::AnyPort ); @@ -48,10 +49,11 @@ void Network::Create(sf::Uint16 port, sf::IpAddress address ) void Network::Destroy( void ) { /* Clean and exit */ + Reset(); net_socket.unbind(); } -bool Network::getData(sf::Packet& p) +bool Network::getData(sf::Packet& p, sf::Uint8* sending_client) { bool rtn = false; sf::Uint16 curcl; @@ -64,6 +66,10 @@ bool Network::getData(sf::Packet& p) { p = clients[curcl].receive.front(); clients[curcl].receive.pop(); + if(sending_client != NULL) + { + *sending_client = curcl; + } rtn = true; break; } @@ -75,26 +81,33 @@ bool Network::getData(sf::Packet& p) bool Network::queueTransmitMessage(Network_Messages_T msg_type , sf::Packet p, Client_t * dest) { - sf::Packet packet; - sf::Uint8 type = (sf::Uint8)msg_type; - sf::Uint32 uid = UNIQUE_ID; - sf::Uint32 msg_id = getUniqueMessageId(); - Transmit_Message_t* message = new Transmit_Message_t(); + bool added_message_to_queue = false; + // Only queue a message if there are clients to receive it + if(numclients > 0) + { + sf::Packet packet; + sf::Uint8 type = (sf::Uint8)msg_type; + sf::Uint32 uid = UNIQUE_ID; + sf::Uint32 msg_id = getUniqueMessageId(); + Transmit_Message_t* message = new Transmit_Message_t(); - // Add this to the list of packets expecting a response - packet << uid; - packet << type; - packet << msg_id; - packet.append(p.getData(), p.getDataSize()); + // Add this to the list of packets expecting a response + packet << uid; + packet << type; + packet << msg_id; + packet.append(p.getData(), p.getDataSize()); - message->msg_type = msg_type; - message->Data = packet; - message->TimeStarted = 0.0; - message->dest = dest; + message->msg_type = msg_type; + message->Data = packet; + message->TimeStarted = 0.0; + message->dest = dest; - transmit_queue[msg_id] = message; + transmit_queue[msg_id] = message; - return true; + added_message_to_queue = true; + } + + return added_message_to_queue; } bool Network::sendData(sf::Packet& p, bool guaranteed) @@ -131,6 +144,8 @@ int Network::addClients(Client_t *client, sf::Uint16 *curcl) { clients[i].addr = client->addr; clients[i].port = client->port; + // Set that a client is now connected + clients[i].disconnect = CONNECTED; *curcl = i; i++; } @@ -202,6 +217,11 @@ void Network::Receive() if(MAX_NUM_CLIENTS > client_id) { clients[client_id].ping = Timer::GetTimeDouble() - transmit_queue[msg_id]->TimeStarted; + + // Need to also register that a ping message was received. + transmit_queue[msg_id]->Responses[&clients[client_id]] = Timer::GetTimeDouble(); + // Received a response, so reset send attempts. + clients[client_id].num_send_attempts = 0u; } break; } @@ -212,6 +232,9 @@ void Network::Receive() if(MAX_NUM_CLIENTS > client_id) { transmit_queue[msg_id]->Responses[&clients[client_id]] = Timer::GetTimeDouble(); + + // Received a response, so reset send attempts. + clients[client_id].num_send_attempts = 0u; } break; } @@ -262,16 +285,31 @@ void Network::Transmit() // Broadcast the mesages to all clients sf::Uint32 msg_id = 0; static double ping_timer = Timer::GetTimeDouble(); + double current_time = Timer::GetTimeDouble(); - // Once per second, send ping messages ( just for fun ) - if((Timer::GetTimeDouble() - ping_timer) > 1000.0) + // Every five seconds, send ping messages + // Note this time must be longer than the combined + // timeout and retry count, otherwise a client will never + // be removed. + if((current_time - ping_timer) > 1.0) { - ping_timer = Timer::GetTimeDouble(); + ping_timer = current_time; sf::Packet response; response.clear(); queueTransmitMessage(NETWORK_PING, response); } + // Set any clients waiting to be removed to the + // do removal state. This will get changed in the + // Transmit loop below if there are any pending messages + for(int client_ndx = 0; client_ndx < MAX_NUM_CLIENTS; client_ndx++) + { + if(clients[client_ndx].disconnect == WAIT_DISCONNECT) + { + clients[client_ndx].disconnect = DO_DISCONNECT; + } + } + // Send any pending messages while(transmit_queue.find(msg_id) != transmit_queue.end()) { @@ -306,7 +344,7 @@ void Network::Transmit() for ( std::map::iterator iter = message->ClientTimeSent.begin(); iter != message->ClientTimeSent.end(); ++iter ) { // Determine if enough time has elapsed to try and re-send - if((curTime - iter->second) >= 0.5) + if((curTime - iter->second) >= NETWORK_TIMEOUT) { // Determine if a response was already received from this client if(message->Responses.find(iter->first) == message->Responses.end()) @@ -314,8 +352,26 @@ void Network::Transmit() // Resend the message to the client net_socket.send(message->Data, iter->first->addr, iter->first->port); message->ClientTimeSent[iter->first] = curTime; + + // Keep track of the number of attempts + // if the number of attempts is exceeded, fake a receive message + // and set a timeout disconnect state + iter->first->num_send_attempts++; + if(MAX_NUM_SEND_ATTEMPTS < iter->first->num_send_attempts) + { + // Fake a receive message so that it will complete and be removed from the queue + message->Responses[iter->first] = Timer::GetTimeDouble(); + iter->first->disconnect = TIMEOUT_DISCONNECT; + } } } + + // There are still pending messages for this client + // Move back to wait state. + if(iter->first->disconnect == DO_DISCONNECT) + { + iter->first->disconnect = WAIT_DISCONNECT; + } } } else @@ -356,6 +412,28 @@ void Network::Transmit() } msg_id++; } + + // Any clients that still have the disconnect action + // are now safe to remove (i.e. no longer have pending messages) + for(int client_ndx = 0; client_ndx < MAX_NUM_CLIENTS; client_ndx++) + { + if(clients[client_ndx].disconnect == DO_DISCONNECT) + { + // Reset all client information. + clients[client_ndx].addr = sf::IpAddress(); + clients[client_ndx].port = 0; + clients[client_ndx].disconnect = DISCONNECTED; + while(!clients[client_ndx].receive.empty()) + { + clients[client_ndx].receive.pop(); + } + // Decrement the number of connected clients. + if(numclients > 0) + { + numclients--; + } + } + } } int Network::getNumConnected( void ) @@ -370,6 +448,7 @@ void Network::Reset() { clients[i].addr = sf::IpAddress(); clients[i].port = 0; + clients[i].disconnect = DISCONNECTED; while(!clients[i].receive.empty()) { clients[i].receive.pop(); @@ -393,6 +472,28 @@ bool Network::pendingMessages() return (transmit_queue.size() > 0); } +sf::Uint16 Network::getLocalPort() +{ + return net_socket.getLocalPort(); +} + +void Network::disconnectClient(Client_t* player_client) +{ + clients[findClient(player_client)].disconnect = WAIT_DISCONNECT; +} + +Client_t* Network::getClient( sf::Uint8 client_ndx ) +{ + Client_t* tmp_client = NULL; + if(client_ndx < MAX_NUM_CLIENTS) + { + tmp_client = &clients[client_ndx]; + } + return tmp_client; +} + + + sf::Packet& operator <<(sf::Packet& Packet, const Network_Messages_T& NMT) { sf::Uint8 net_msg_t = (sf::Uint8)NMT; diff --git a/src/common/Network.h b/src/common/Network.h index 8200fd0..b4ba324 100644 --- a/src/common/Network.h +++ b/src/common/Network.h @@ -14,10 +14,10 @@ #define UNIQUE_ID 0xDEADBEEF #define RECEIVE_BUFFER_SIZE 1024 -// In milliseconds -#define NETWORK_TIMEOUT 5000 - +// In seconds +#define NETWORK_TIMEOUT 1 +#define MAX_NUM_SEND_ATTEMPTS 3 // The bit indicating if the message requires a response #define MSG_REQUIRES_RESPONSE_BIT ((sf::Uint16)1 << 15) @@ -35,11 +35,20 @@ typedef enum{ NETWORK_GUARANTEED }Network_Messages_T; +typedef enum{ + DISCONNECTED, + CONNECTED, + TIMEOUT_DISCONNECT, + WAIT_DISCONNECT, + DO_DISCONNECT +}Disconnect_States_t; typedef struct{ sf::IpAddress addr; unsigned short port; double ping; + Disconnect_States_t disconnect; + sf::Uint8 num_send_attempts; std::queue receive; }Client_t; @@ -72,22 +81,25 @@ class Network{ static std::map transmit_queue; static char rxbuff[RECEIVE_BUFFER_SIZE]; static sf::Clock message_timer; - static sf::Uint32 getUniqueMessageId( void ); + static sf::Uint32 getUniqueMessageId(); static int addClients(Client_t *client, sf::Uint16 *curcl); static int findClient(Client_t *client); static bool queueTransmitMessage(Network_Messages_T msg_type , sf::Packet p, Client_t * dest = NULL); + static Client_t clients[MAX_NUM_CLIENTS]; public: - static Client_t clients[MAX_NUM_CLIENTS]; static void Create( sf::Uint16 port, sf::IpAddress address ); - static void Destroy( void ); - static bool getData(sf::Packet& p); + static void Destroy(); + static bool getData(sf::Packet& p, sf::Uint8* sending_client = NULL); static bool sendData(sf::Packet& p, bool guaranteed = false); - static int getNumConnected( void ); + static int getNumConnected(); static void Transmit(); static void Receive(); static void Reset(); static bool pendingMessages(); + static sf::Uint16 getLocalPort(); + static void disconnectClient(Client_t* player_client); + static Client_t* getClient( sf::Uint8 client_ndx ); }; sf::Packet& operator <<(sf::Packet& Packet, const Network_Messages_T& NMT); diff --git a/src/common/Player.cc b/src/common/Player.cc index 2b76977..dc7ff82 100644 --- a/src/common/Player.cc +++ b/src/common/Player.cc @@ -14,4 +14,5 @@ Player::Player() d_pressed = KEY_NOT_PRESSED; rel_mouse_movement = 0; updated = false; + m_client = NULL; } diff --git a/src/common/Player.h b/src/common/Player.h index 015e0c5..6877225 100644 --- a/src/common/Player.h +++ b/src/common/Player.h @@ -4,6 +4,7 @@ #include #include "Types.h" #include "SFML/Config.hpp" +#include "Network.h" class Player { @@ -19,6 +20,7 @@ class Player sf::Uint8 d_pressed; sf::Int32 rel_mouse_movement; bool updated; + Client_t* m_client; Player(); }; diff --git a/src/common/Timer.cc b/src/common/Timer.cc index 8c854e9..727a6a0 100644 --- a/src/common/Timer.cc +++ b/src/common/Timer.cc @@ -13,7 +13,7 @@ void Timer::Init(void) myClock.restart(); // Set the time keepers to zero - myTotalElapsedTime = 0; + totalElapsedTime = 0; // Reset the game speed gameSpeed = 1.0f; @@ -22,12 +22,11 @@ void Timer::Init(void) void Timer::Update(void) { // Record the time step - stepTime = ((myClock.getElapsedTime().asSeconds() / 1000.0f) * gameSpeed); + stepTime = (myClock.getElapsedTime().asSeconds() * gameSpeed); myClock.restart(); // Add the time to the total time - myTotalElapsedTime += stepTime; - totalElapsedTime = myTotalElapsedTime; + totalElapsedTime += stepTime; // Calculate the game step curTimeStep = (sf::Uint32)(totalElapsedTime * STEPS_PER_SECOND); @@ -45,7 +44,7 @@ float Timer::GetStepTime(void) sf::Uint32 Timer::GetTotalTime(void) { - return (sf::Uint32)(totalElapsedTime * 1000.0f); + return (sf::Uint32)(totalElapsedTime); } double Timer::GetTimeDouble(void) diff --git a/src/common/Timer.h b/src/common/Timer.h index 7a0810b..8c5b6e8 100644 --- a/src/common/Timer.h +++ b/src/common/Timer.h @@ -35,9 +35,6 @@ private: // The game speed float gameSpeed; - // The total elapsed time since the start of the game - double myTotalElapsedTime; - // The total elapsed time since the start of the game static double totalElapsedTime; diff --git a/src/server/Server.cc b/src/server/Server.cc index 39d799b..780082e 100644 --- a/src/server/Server.cc +++ b/src/server/Server.cc @@ -46,10 +46,11 @@ void Server::update( double elapsed_time ) static Player player_prev; const double move_speed = 75.0; sf::Packet server_packet; + sf::Uint8 tmp_player_client; m_net_server->Receive(); // Handle all received data (only really want the latest) - while(m_net_server->getData(server_packet)) + while(m_net_server->getData(server_packet, &tmp_player_client)) { sf::Uint8 ptype; // Get packet type @@ -58,14 +59,14 @@ void Server::update( double elapsed_time ) { case PLAYER_CONNECT: { - sf::Uint32 players_address = 0u; + sf::Uint16 players_port = sf::Socket::AnyPort; refptr p = new Player(); std::string pname; sf::Uint8 pindex; server_packet >> pindex; server_packet >> pname; - server_packet >> players_address; + server_packet >> players_port; // When a player connects, we need to associate // that player with a new ID. find first unused id // player zero means a player does not exist. @@ -79,17 +80,18 @@ void Server::update( double elapsed_time ) } } p->name = pname; + p->m_client = m_net_server->getClient(tmp_player_client); m_players[pindex] = p; // Alert all connected clients of all the connected players. for(std::map >::iterator piter = m_players.begin(); piter != m_players.end(); piter++) { - sf::Uint32 paddress = ((piter->first == pindex) ? players_address : 0u); + sf::Uint16 port = ((piter->first == pindex) ? players_port : 0u); server_packet.clear(); server_packet << ptype; server_packet << piter->first; server_packet << piter->second->name; - server_packet << paddress; + server_packet << port; // Send correct starting locations so that they match // the other players screens. server_packet << piter->second->direction; @@ -106,7 +108,8 @@ void Server::update( double elapsed_time ) // then update the stored contents. sf::Uint8 pindex; server_packet >> pindex; - if(m_players.end() != m_players.find(pindex)) + if((m_players.end() != m_players.find(pindex)) && + (m_net_server->getClient(tmp_player_client) == m_players[pindex]->m_client)) { server_packet >> m_players[pindex]->w_pressed; server_packet >> m_players[pindex]->a_pressed; @@ -123,16 +126,21 @@ void Server::update( double elapsed_time ) // This completely removes the player from the game // Deletes member from the player list server_packet >> pindex; - num_erased = m_players.erase(pindex); - if(1 == num_erased) + if((m_players.end() != m_players.find(pindex)) && + (m_net_server->getClient(tmp_player_client) == m_players[pindex]->m_client)) { - // Player existed, alert all connected clients. - server_packet.clear(); - server_packet << ptype; - server_packet << pindex; - m_net_server->sendData(server_packet, true); + // Tell networking code to remove the client. + m_net_server->disconnectClient(m_players[pindex]->m_client); + num_erased = m_players.erase(pindex); + if(1 == num_erased) + { + // Player exited, alert all connected clients. + server_packet.clear(); + server_packet << ptype; + server_packet << pindex; + m_net_server->sendData(server_packet, true); + } } - break; } case PLAYER_DEATH: @@ -149,57 +157,82 @@ void Server::update( double elapsed_time ) } } - for(std::map >::iterator piter = m_players.begin(); piter != m_players.end(); piter++) + for(std::map >::iterator piter = m_players.begin(); piter != m_players.end();) { sf::Uint8 pindex = piter->first; - if (KEY_PRESSED == m_players[pindex]->a_pressed) - { - double direction = m_players[pindex]->direction + M_PI_2; - m_players[pindex]->x += cos(direction) * move_speed * elapsed_time; - m_players[pindex]->y += sin(direction) * move_speed * elapsed_time; - m_players[pindex]->updated = true; - } - if (KEY_PRESSED == m_players[pindex]->d_pressed) - { - double direction = m_players[pindex]->direction - M_PI_2; - m_players[pindex]->x += cos(direction) * move_speed * elapsed_time; - m_players[pindex]->y += sin(direction) * move_speed * elapsed_time; - m_players[pindex]->updated = true; - } - if (KEY_PRESSED == m_players[pindex]->w_pressed) - { - double direction = m_players[pindex]->direction; - m_players[pindex]->x += cos(direction) * move_speed * elapsed_time; - m_players[pindex]->y += sin(direction) * move_speed * elapsed_time; - m_players[pindex]->updated = true; - } - if (KEY_PRESSED == m_players[pindex]->s_pressed) - { - double direction = m_players[pindex]->direction + M_PI; - m_players[pindex]->x += cos(direction) * move_speed * elapsed_time; - m_players[pindex]->y += sin(direction) * move_speed * elapsed_time; - m_players[pindex]->updated = true; - } - if(0 != m_players[pindex]->rel_mouse_movement) - { - m_players[pindex]->direction -= M_PI * 0.5 * m_players[pindex]->rel_mouse_movement / 1000; - m_players[pindex]->updated = true; - } + // Increment to the next element. This is done in loop so that the + // erasing of players will not cause issues + piter++; - server_packet.clear(); - - // Send the player update if there were changes - if(m_players[pindex]->updated) + if(m_players[pindex]->m_client->disconnect == CONNECTED) { - sf::Uint8 ptype = PLAYER_UPDATE; - server_packet << ptype; - server_packet << pindex; - server_packet << m_players[pindex]->direction; - server_packet << m_players[pindex]->x; - server_packet << m_players[pindex]->y; - m_net_server->sendData(server_packet); - m_players[pindex]->updated = false; - } + if (KEY_PRESSED == m_players[pindex]->a_pressed) + { + double direction = m_players[pindex]->direction + M_PI_2; + m_players[pindex]->x += cos(direction) * move_speed * elapsed_time; + m_players[pindex]->y += sin(direction) * move_speed * elapsed_time; + m_players[pindex]->updated = true; + } + if (KEY_PRESSED == m_players[pindex]->d_pressed) + { + double direction = m_players[pindex]->direction - M_PI_2; + m_players[pindex]->x += cos(direction) * move_speed * elapsed_time; + m_players[pindex]->y += sin(direction) * move_speed * elapsed_time; + m_players[pindex]->updated = true; + } + if (KEY_PRESSED == m_players[pindex]->w_pressed) + { + double direction = m_players[pindex]->direction; + m_players[pindex]->x += cos(direction) * move_speed * elapsed_time; + m_players[pindex]->y += sin(direction) * move_speed * elapsed_time; + m_players[pindex]->updated = true; + } + if (KEY_PRESSED == m_players[pindex]->s_pressed) + { + double direction = m_players[pindex]->direction + M_PI; + m_players[pindex]->x += cos(direction) * move_speed * elapsed_time; + m_players[pindex]->y += sin(direction) * move_speed * elapsed_time; + m_players[pindex]->updated = true; + } + if(0 != m_players[pindex]->rel_mouse_movement) + { + m_players[pindex]->direction -= M_PI * 0.5 * m_players[pindex]->rel_mouse_movement / 1000; + m_players[pindex]->updated = true; + } + + server_packet.clear(); + + // Send the player update if there were changes + if(m_players[pindex]->updated) + { + sf::Uint8 ptype = PLAYER_UPDATE; + server_packet << ptype; + server_packet << pindex; + server_packet << m_players[pindex]->direction; + server_packet << m_players[pindex]->x; + server_packet << m_players[pindex]->y; + m_net_server->sendData(server_packet); + m_players[pindex]->updated = false; + } + } + else + { + if(m_players[pindex]->m_client->disconnect == TIMEOUT_DISCONNECT) + { + // Tell networking code to remove the client. + m_net_server->disconnectClient(m_players[pindex]->m_client); + + if(m_players.erase(pindex)) + { + sf::Uint8 ptype = PLAYER_DISCONNECT; + // Player exited, alert all connected clients. + server_packet.clear(); + server_packet << ptype; + server_packet << pindex; + m_net_server->sendData(server_packet, true); + } + } + } } m_net_server->Transmit(); }