From 2314c27dd650dc02c0b5bdd3bef75818393a9ab5 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Fri, 21 Mar 2008 19:25:11 +0000 Subject: switched to UDP networking --- src/core/netserver.cc | 331 +++++++++++++++++++++++++++++++------------------- 1 file changed, 209 insertions(+), 122 deletions(-) (limited to 'src/core/netserver.cc') diff --git a/src/core/netserver.cc b/src/core/netserver.cc index 87f5320..16f042e 100644 --- a/src/core/netserver.cc +++ b/src/core/netserver.cc @@ -4,6 +4,16 @@ the terms of the GNU General Public License version 2 */ +#include +#include + +#include +#include +#include + +#include +#include + #include #include @@ -18,15 +28,58 @@ namespace core { -NetServer::NetServer(std::string const host, unsigned int const port) : - net::TCPServer(host, port) +NetServer::NetServer(std::string const host, unsigned int const port) { - //con_print << "Initializing network server..." << std::endl; - FD_ZERO(&serverset); + con_print << "Initializing network server..." << std::endl; + + // initialize variables + netserver_fd = -1; + netserver_error = true; + + // initialize socket + netserver_fd = ::socket(PF_INET, SOCK_DGRAM, 0); + if (netserver_fd == -1) { + con_error << "Network can't create socket!" << std::endl; + //perror("socket"); + return; + } + + // set socket options + socklen_t yes = 1; + if (::setsockopt(netserver_fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(socklen_t)) == -1) { + con_error << "Network can't set socket options!" << std::endl; + //perror("setsockopt"); + return; + } + + // Get the local adress to bind to + netserver_addr.sin_family = AF_INET; + netserver_addr.sin_port = htons(port); + if (host.size()) { + netserver_addr.sin_addr.s_addr = inet_addr(host.c_str()); + if ( netserver_addr.sin_addr.s_addr == INADDR_NONE) { + con_error << "Network invalid address " << host << "!" << std::endl; + return; + } + } else { + netserver_addr.sin_addr.s_addr = htonl(INADDR_ANY); + } + memset(netserver_addr.sin_zero, '\0', sizeof(netserver_addr.sin_zero)); + + // bind the local address to the socket ( note the typecast) + if (::bind(netserver_fd, (struct sockaddr *) &netserver_addr, sizeof(struct sockaddr)) == -1) { + con_error << "Network can't bind to local address!" << std::endl; + //perror("bind"); + return; + } + + con_print << " Listening on " << inet_ntoa(netserver_addr.sin_addr) << ":" << ntohs(netserver_addr.sin_port) << std::endl; + // add the listening socket to the file descriptor set - FD_SET(fd(), &serverset); + FD_ZERO(&serverset); + FD_SET(netserver_fd, &serverset); - fdmax=fd(); + netserver_error = false; } NetServer::~NetServer() @@ -36,80 +89,49 @@ NetServer::~NetServer() // delete all clients std::list:: iterator it; for (it = clients.begin(); it != clients.end(); it++) { + // notify the game server - server()->player_disconnect((*it)->player()); - con_print << (*it)->host() << ":" << (*it)->port() << " disconnected.\n"; + if ((*it)->state() == NetClient::Connected) + server()->player_disconnect((*it)->player()); + delete (*it); } clients.clear(); -} - -void NetServer::client_connect(int const clientfd, std::string const host, int const port) -{ - NetClient *client = new NetClient(clientfd, host, port); - if (client->error()) { - con_warn << client->host() << ":" << client->port() << " connection failed!\n"; - delete(client); - return; - } - - clients.push_back(client); - FD_SET(client->fd(), &serverset); - - con_print << client->host() << ":" << client->port() << " connected.\n"; - - std::ostringstream netmsg; - - // send entities - std::map::iterator it; - for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { - netmsg.str(""); - switch ((*it).second->type()) { - case Entity::Default: - case Entity::Dynamic: - case Entity::Controlable: - netmsg << "ent "; - (*it).second->serialize(netmsg); - netmsg << "\n"; - client->send(netmsg.str()); - break; - default: - break; - } - } - - // mark player as dirty - client->player()->player_dirty = true; - - // notify the game server - server()->player_connect(client->player()); + if (valid()) + ::close(fd()); } // remove disconnected clients void NetServer::reap() { - std::list:: iterator it; - for (it = clients.begin(); it != clients.end(); it++) { + for (std::list:: iterator it = clients.begin(); it != clients.end(); it++) { NetClient *client = *it; + if (client->error()) { - FD_CLR(client->fd(), &serverset); // notify the game server - server()->player_disconnect((*it)->player()); - con_print << client->host() << ":" << client->port() << " disconnected.\n"; + if (client->state() == NetClient::Connected) + server()->player_disconnect((*it)->player()); // remove the client clients.erase(it); delete client; - it=clients.begin(); - + it=clients.begin(); } } } -void NetServer::frame(float seconds) { +void NetServer::transmit() +{ + for (std::list::iterator it = clients.begin(); it != clients.end(); it++) { + (*it)->transmit(); + } +} + +void NetServer::receive() +{ if (error()) return; @@ -118,52 +140,145 @@ void NetServer::frame(float seconds) { timeout.tv_usec = 0; fd_set readset = serverset; - int nb = select(fdmax+1, &readset, NULL, NULL, &timeout); - if (nb == 0) - return; - + int nb = select(fd()+1, &readset, NULL, NULL, &timeout); if (nb == -1) { con_error << "Network error on select()" << std::endl; //perror("select"); abort(); + return; } + + while (nb && FD_ISSET(fd(), &readset)) { + // receive incoming data + struct sockaddr_in client_addr; + socklen_t client_addr_len = sizeof(client_addr); + memset(recbuf, '\0', sizeof(recbuf)); + ssize_t bytes_received = ::recvfrom(fd(), recbuf, FRAMESIZE-1, 0, (struct sockaddr *)&client_addr, &client_addr_len); + if (bytes_received == -1) { + con_error << "Network error on recvfrom()!" << std::endl; + abort(); + return; + } else { + //con_debug << "Incoming data '" << recbuf << "'"<< bytes_received << " bytes" << std::endl; + } - // accept incoming connections - if(FD_ISSET(this->fd(), &readset)) { - accept(); - } + // originator + std::string client_host (inet_ntoa(client_addr.sin_addr)); + unsigned int client_port = ntohs(client_addr.sin_port); + + // get messages from clients + bool msg_received = false; - // get messages from clients - for (std::list::iterator it = clients.begin(); it != clients.end(); it++) { - NetClient *client = *it; - if (client->fd() > fdmax) - fdmax = client->fd(); - if (FD_ISSET(client->fd(), &readset) && !client->error()) { - client->receive(); - while (client->has_messages()) { - std::string message; - client->retreive(message); - parse_incoming_message(client, message); + for (std::list::iterator it = clients.begin(); it != clients.end() && !msg_received; it++) { + NetClient *client = *it; + + if ((client->host() == client_host) && (client->port() == (int) client_port)) { + // receive data + client->receive(recbuf); + + // process parsed messages + while (client->has_messages()) { + std::string message; + client->retreive(message); + parse_incoming_message(client, message); + } + + msg_received = true; + } + } + + if (!msg_received) { + // new connection + // FIXME maxclients + NetClient *client = client_connect(client_host , client_port); + + if (client) { + // receive data + client->receive(recbuf); + + // process parsed messages + while (client->has_messages()) { + std::string message; + client->retreive(message); + parse_incoming_message(client, message); + } } } + + readset = serverset; + nb = select(fd(), &readset, NULL, NULL, &timeout); + if (nb == -1) { + con_error << "Network error on select()" << std::endl; + //perror("select"); + abort(); + return; + } } // remove dead connections + // FIXME timeout reap(); } + +NetClient * NetServer::client_connect(std::string const host, int const port) +{ + con_debug << "client_connect " << host << ":" << port << "\n"; + + NetClient *client = new NetClient(host, port); + if (client->error()) { + con_warn << client->host() << ":" << client->port() << " connection failed!\n"; + delete(client); + return 0; + } + + clients.push_back(client); + + std::ostringstream netmsg; + netmsg.str(""); + netmsg << "msg info Receiving data from remote server...\n"; + client->send(netmsg.str()); + + // send entities + std::map::iterator it; + + for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { + netmsg.str(""); + switch ((*it).second->type()) { + case Entity::Default: + case Entity::Dynamic: + case Entity::Controlable: + netmsg << "ent "; + (*it).second->serialize(netmsg); + netmsg << "\n"; + + client->send(netmsg.str()); + + break; + default: + break; + } + } + + transmit(); + + client->player()->player_dirty = false; + + return client; +} + void NetServer::send(NetClient * client, std::string const & message) { client->send(message); } // send a message to all clients -void NetServer::broadcast(std::string const & message, int ignorefd) +void NetServer::broadcast(std::string const & message, Player *ignore_player) { //std::cout << "NetServer: " << osstream.str(); for (std::list::iterator it = clients.begin(); it != clients.end(); it++) { NetClient *client = *it; - if (!client->error() && client->fd() != ignorefd) + if (!client->error() && (client->state() == NetClient::Connected) && client->player() != ignore_player) client->send(message); } } @@ -172,14 +287,14 @@ void NetServer::broadcast(std::string const & message, int ignorefd) NetClient *NetServer::find_client(Player const *player) { for (std::list::iterator it = clients.begin(); it != clients.end(); it++) { - if ((*it)->fd() == (int) player->id()) { + if ((*it)->player() == player) { return (*it); } } return 0; } -// parse server messages +// parse incoming client messages /** * The following incoming protocol messages are parsed; * @@ -206,6 +321,20 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me return; } + // pif - update player information + // pif is the first command expected from the client + if (command == "pif") { + client->player()->recieve_client_update(msgstream); + + if (client->state() == NetClient::Connecting) { + client->client_state = NetClient::Connected; + server()->player_connect(client->player()); + } + } + + if (client->state() != NetClient::Connected) + return; + // cmd if (command == "cmd") { if (message.size() > command.size()+1) { @@ -233,11 +362,6 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me return; } - // pif - update player information - if (command == "pif") { - client->player()->recieve_client_update(msgstream); - } - // say if (command == "say") { if (message.size() > command.size()+1) { @@ -249,43 +373,6 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me return; } - /* - // name - if (command == "name") { - std::string name; - if (msgstream >> name) { - if (name.size() > 16) - name = name.substr(0,16); - if (name != client->player()->name()) { - std::ostringstream osstream; - osstream << "msg info " << client->player()->name() << " renamed to " << name << "\n"; - broadcast(osstream.str()); - con_print << client->player()->name() << " renamed to " << name << std::endl; - client->player()->player_name = name; - } - } - return; - } - - if (command == "list_players") { - std::ostringstream osstream; - for (std::list::iterator it = clients.begin(); it != clients.end(); it++) { - osstream << "msg info " << (*it)->player()->name() << " " << (*it)->host() << ":" << (*it)->port() << "\n"; - } - osstream << "msg info " << clients.size() << " connected players\n" << std::endl; - send(client, osstream.str()); - } - - if (command == "help") { - send(client, "msg info Available protocol messages:\n"); - send(client, "msg info help - shows this help message\n"); - send(client, "msg info name [nickname] - changes your nickname\n"); - send(client, "msg info say [text] - say something on the public channel\n"); - send(client, "msg info cmd [text] - execute a game command\n"); - send(client, "msg info list_players - shows a list of connected players\n"); - send(client, "msg info disconnect - disconnect\n"); - } - */ } } -- cgit v1.2.3