/* net/netserver.h This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include #include #include #include #include #include #include #include #include #include "sys/sys.h" #include "core/gameserver.h" #include "core/netclient.h" #include "core/netserver.h" #include "core/cvar.h" #include "core/func.h" #include "core/core.h" namespace core { NetServer::NetServer(std::string const host, unsigned int const port) { con_print << "Initializing network server..." << std::endl; // initialize variables netserver_fd = -1; netserver_error = true; // initialize socket netserver_fd = ::socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); 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_ZERO(&serverset); FD_SET(netserver_fd, &serverset); netserver_error = false; } NetServer::~NetServer() { con_print << "Shutting down network server..." << std::endl; // delete all clients std::list:: iterator it; for (it = clients.begin(); it != clients.end(); it++) { // notify the game server if ((*it)->state() == NetClient::Connected) server()->player_disconnect((*it)->player()); delete (*it); } clients.clear(); if (valid()) ::close(fd()); } // remove disconnected clients void NetServer::reap() { for (std::list:: iterator it = clients.begin(); it != clients.end(); it++) { NetClient *client = *it; if (client->error()) { // notify the game server if (client->state() == NetClient::Connected) server()->player_disconnect((*it)->player()); // remove the client clients.erase(it); delete client; it=clients.begin(); } } } void NetServer::transmit() { for (std::list::iterator it = clients.begin(); it != clients.end(); it++) { (*it)->transmit(); } } void NetServer::receive() { if (error()) return; timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; fd_set readset = serverset; 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; } // 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; 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()); transmit(); // 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, 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->state() == NetClient::Connected) && client->player() != ignore_player) client->send(message); } } // find the client corresponding to a player id NetClient *NetServer::find_client(Player const *player) { for (std::list::iterator it = clients.begin(); it != clients.end(); it++) { if ((*it)->player() == player) { return (*it); } } return 0; } // parse incoming client messages /** * The following incoming protocol messages are parsed; * * disconnect * cmd * cup * pif * say * */ void NetServer::parse_incoming_message(NetClient *client, const std::string & message) { if (!message.size()) return; std::stringstream msgstream(message); std::string command; msgstream >> command; // disconnect if (command == "disconnect") { client->abort(); 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) { std::string cmdline(message.substr(command.size()+1)); server()->exec(client->player(), cmdline); } return; } // cup - client update entity if (command == "cup") { //con_debug << message << "\n"; unsigned int id; if (msgstream >> id) { Entity *entity = Entity::find(id); if (!entity) { con_warn << client->host() << ":" << client->port() << " update for unknown entity " << id << "\n"; } else { entity->entity_dirty = true; entity->recieve_client_update(msgstream); } } return; } // say if (command == "say") { if (message.size() > command.size()+1) { std::ostringstream osstream; osstream << "msg public " << client->player()->name() << " " << message.substr(command.size()+1) << "\n"; broadcast(osstream.str()); con_print << client->player()->name() << " " << message.substr(command.size()+1) << std::endl; } return; } } }