/* 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/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) { 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..." << std::endl; // delete all clients std::list:: iterator it; for (it = clients.begin(); it != clients.end(); it++) { // notify the game if (game() && game()->connected) { core::game()->player_disconnect((*it)->player()); con_print << (*it)->player()->name() << " disconnected."<< std::endl; } 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 " << client->fd() << " " << client->host() << ":" << client->port() << " connection failed!" << std::endl; delete(client); return; } clients.push_back(client); FD_SET(client->fd(), &serverset); // TODO send infos con_print << client->host() << ":" << client->port() << " " << client->player()->name() << " connected."<< std::endl; // BROADCAST connect message std::ostringstream osstream; osstream << "msg info " << client->player()->name() << " connected."<< std::endl; broadcast(osstream.str(), clientfd); // notify the game if (game() && game()->connected) { core::game()->player_connect(client->player()); } else { // TODO send disconnect message and remove client } } // 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); // BROADCAST disconnect message std::ostringstream osstream; osstream << "msg info " << client->player()->name() << " disconnected."<< std::endl; broadcast(osstream.str()); // notify the game if (core::game()) core::game()->player_disconnect(client->player()); con_print << client->player()->name() << " disconnected."<< std::endl; // 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 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; } // 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 commands:\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 list_players - shows a list of connected players\n"); send(client, "msg info disconnect - disconnect\n"); } // execute game functions if (game() && game()->connected) { Func *function = Func::find(command); if (function ) { std::string args; char c; if (msgstream >> args) while (msgstream >> c) args += c; if (function ->flags() && Func::Game) { function->exec(client->player(), args); } else { // instant rcon //function->exec(args); } } } } void NetServer::parse_client_variable(NetClient * client, const std::string varname, std::istringstream &istringstream) { if (varname=="name") { std::string name; if (istringstream >> name) { std::ostringstream osstream; if (name.size() > 16) name = name.substr(0,16); if (name != client->player()->name()) { osstream << "msg info " << client->player()->name() << " renamed to " << name << "\n"; broadcast(osstream.str()); client->player()->player_name = name; } } return; } } }