/* 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 "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) : net::TCPServer(host, port) { //con_print << "Initializing network server..." << std::endl; FD_ZERO(&serverset); // add the listening socket to the file descriptor set FD_SET(fd(), &serverset); fdmax=fd(); } 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 server()->player_disconnect((*it)->player()); con_print << (*it)->host() << ":" << (*it)->port() << " disconnected.\n"; 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"; // notify the game server server()->player_connect(client->player()); } // remove disconnected clients void NetServer::reap() { std::list:: iterator it; for (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"; // remove the client clients.erase(it); delete client; it=clients.begin(); } } } void NetServer::frame(float seconds) { if (error()) return; timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; fd_set readset = serverset; int nb = select(fdmax+1, &readset, NULL, NULL, &timeout); if (nb == 0) return; if (nb == -1) { con_error << "Network error on select()" << std::endl; //perror("select"); abort(); } // accept incoming connections if(FD_ISSET(this->fd(), &readset)) { accept(); } // 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); } } } // remove dead connections reap(); } 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) { //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) 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)->fd() == (int) player->id()) { return (*it); } } return 0; } // parse server messages /** * The following incoming protocol messages are parsed; * * disconnect * help * list_players * name * say * */ void NetServer::parse_incoming_message(NetClient *client, const std::string & message) { std::stringstream msgstream(message); std::string command; msgstream >> command; // disconnect if (command == "disconnect") { client->abort(); 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; } // 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; } // name is an alias for set 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"); } } }