diff options
-rw-r--r-- | src/core/Makefile.am | 6 | ||||
-rw-r--r-- | src/core/application.cc | 17 | ||||
-rw-r--r-- | src/core/core.h | 7 | ||||
-rw-r--r-- | src/core/cvar.cc | 2 | ||||
-rw-r--r-- | src/core/cvar.h | 1 | ||||
-rw-r--r-- | src/core/entity.cc | 4 | ||||
-rw-r--r-- | src/core/gameconnection.cc | 6 | ||||
-rw-r--r-- | src/core/gameinterface.cc | 5 | ||||
-rw-r--r-- | src/core/gameserver.cc | 29 | ||||
-rw-r--r-- | src/core/gameserver.h | 3 | ||||
-rw-r--r-- | src/core/net.h | 30 | ||||
-rw-r--r-- | src/core/netclient.cc | 87 | ||||
-rw-r--r-- | src/core/netclient.h | 48 | ||||
-rw-r--r-- | src/core/netconnection.cc | 167 | ||||
-rw-r--r-- | src/core/netconnection.h | 38 | ||||
-rw-r--r-- | src/core/netserver.cc | 331 | ||||
-rw-r--r-- | src/core/netserver.h | 48 | ||||
-rw-r--r-- | src/core/player.cc | 3 | ||||
-rw-r--r-- | src/server/Makefile.am | 5 |
19 files changed, 598 insertions, 239 deletions
diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 6f78e6a..a73a8a7 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -5,10 +5,10 @@ libcore_la_SOURCES = application.cc commandbuffer.cc core.cc cvar.cc entity.cc \ func.cc gameconnection.cc gameinterface.cc gameserver.cc model.cc module.cc \ netclient.cc netconnection.cc netserver.cc player.cc libcore_la_LDFLAGS = -avoid-version -no-undefined -libcore_la_LIBADD = $(top_builddir)/src/math/libmath.la \ - $(top_builddir)/src/filesystem/libfilesystem.la $(top_builddir)/src/sys/libsys.la $(top_builddir)/src/net/libnet.la +libcore_la_LIBADD = $(top_builddir)/src/filesystem/libfilesystem.la \ + $(top_builddir)/src/math/libmath.la $(top_builddir)/src/sys/libsys.la noinst_LTLIBRARIES = libcore.la noinst_HEADERS = application.h commandbuffer.h core.h cvar.h entity.h func.h \ - gameconnection.h gameinterface.h gameserver.h module.h netconnection.h player.h + gameconnection.h gameinterface.h gameserver.h module.h net.h netconnection.h player.h diff --git a/src/core/application.cc b/src/core/application.cc index 358c655..5394b92 100644 --- a/src/core/application.cc +++ b/src/core/application.cc @@ -165,8 +165,9 @@ void Application::init() Cvar::sv_framerate = Cvar::get("sv_framerate", "25"); // network settings - Cvar::net_host = Cvar::get("net_host", "0.0.0.0"); - Cvar::net_port = Cvar::get("net_port", "8042"); + Cvar::net_host = Cvar::get("net_host", "0.0.0.0", Cvar::Archive); + Cvar::net_port = Cvar::get("net_port", "8042", Cvar::Archive); + Cvar::net_maxclients = Cvar::get("net_maxclients", "16", Cvar::Archive); // register our engine functions Func::add("print", func_print); @@ -282,7 +283,10 @@ void Application::frame(float seconds) void Application::save_config() { std::string filename(filesystem::writedir); - filename.append("config.txt"); + if (!Cvar::sv_dedicated->value()) + filename.append("client.cfg"); + else + filename.append("server.cfg"); std::ofstream ofs(filename.c_str()); if (!ofs.is_open()) { @@ -294,7 +298,7 @@ void Application::save_config() for (it = Cvar::registry.begin(); it != Cvar::registry.end(); it++) { if (((*it).second->flags() & Cvar::Archive) == Cvar::Archive) - ofs << "set " << (*it).first << " " << (*it).second->str() << std::endl; + ofs << "set " << (*it).first << " " << (*it).second->str() << std::endl; } ofs.close(); } @@ -302,7 +306,10 @@ void Application::save_config() void Application::load_config() { std::string filename(filesystem::writedir); - filename.append("config.txt"); + if (!Cvar::sv_dedicated->value()) + filename.append("client.cfg"); + else + filename.append("server.cfg"); std::ifstream ifs(filename.c_str(), std::ifstream::in); if (!ifs.is_open()) { diff --git a/src/core/core.h b/src/core/core.h index ddf884f..a3e13e4 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -16,19 +16,16 @@ #include "core/func.h" #include "core/gameinterface.h" #include "core/module.h" -#include "core/netserver.h" -#include "core/netclient.h" -#include "core/netconnection.h" #include "core/player.h" /// core contains the basic functionality of the engine namespace core { -// name of the engine core +/// name of the engine core const std::string & name(); -// version of the engine core +/// version of the engine core const std::string & version(); } diff --git a/src/core/cvar.cc b/src/core/cvar.cc index 363ebb4..e95f61c 100644 --- a/src/core/cvar.cc +++ b/src/core/cvar.cc @@ -20,8 +20,10 @@ namespace core Cvar *Cvar::sv_dedicated = 0; Cvar *Cvar::sv_private = 0; Cvar *Cvar::sv_framerate = 0; + Cvar *Cvar::net_host = 0; Cvar *Cvar::net_port = 0; +Cvar *Cvar::net_maxclients = 0; std::map<std::string, Cvar*> Cvar::registry; diff --git a/src/core/cvar.h b/src/core/cvar.h index f43e532..21f2601 100644 --- a/src/core/cvar.h +++ b/src/core/cvar.h @@ -105,6 +105,7 @@ public: static Cvar *sv_framerate; // server framerate static Cvar *net_host; // network server ip (default binds to all interfaces) static Cvar *net_port; // network port + static Cvar *net_maxclients; // maximum number of connected clients private: std::string cvar_name; diff --git a/src/core/entity.cc b/src/core/entity.cc index d64ae61..fd28a1c 100644 --- a/src/core/entity.cc +++ b/src/core/entity.cc @@ -316,7 +316,7 @@ void EntityControlable::set_thrust(float thrust) if ((flags() & Static) == Static) return; - if (!(thrust == target_thrust)) { + if (thrust != target_thrust) { target_thrust = thrust; entity_dirty = true; } @@ -327,7 +327,7 @@ void EntityControlable::set_direction(float direction) if ((flags() & Static) == Static) return; - if (!(target_direction == direction)) { + if (target_direction != direction) { target_direction = direction; entity_dirty = true; } diff --git a/src/core/gameconnection.cc b/src/core/gameconnection.cc index b307fee..634a6ec 100644 --- a/src/core/gameconnection.cc +++ b/src/core/gameconnection.cc @@ -8,9 +8,9 @@ #include <sstream> #include "sys/sys.h" -#include "net/net.h" #include "core/cvar.h" #include "core/gameconnection.h" +#include "core/net.h" namespace core { @@ -23,7 +23,7 @@ GameConnection::GameConnection(std::string const &connectionstr) connection_network = 0; connection_running = false; - unsigned int port = net::DEFAULTPORT; + unsigned int port = DEFAULTPORT; std::string host(connectionstr); size_t found = host.find(':'); @@ -129,11 +129,11 @@ void GameConnection::frame(float seconds) localplayer()->serialize_client_update(osstream); osstream << '\n'; connection_network->send(osstream.str()); + localplayer()->player_dirty = false; } connection_network->transmit(); connection_frametime += f; - } } diff --git a/src/core/gameinterface.cc b/src/core/gameinterface.cc index 69126e7..9f4dd29 100644 --- a/src/core/gameinterface.cc +++ b/src/core/gameinterface.cc @@ -45,6 +45,7 @@ GameInterface::GameInterface() if (Cvar::sv_dedicated->value()) game_localplayer.player_name.assign("Console"); else { + game_localplayer.clear(); Cvar *cl_name = Cvar::find("cl_name"); if (cl_name) { game_localplayer.player_name = cl_name->str(); @@ -52,7 +53,7 @@ GameInterface::GameInterface() Cvar *cl_color = Cvar::find("cl_color"); math::Color color(1.0, 1.0, 1.0, 1.0); - if (cl_color) { + if (cl_color && cl_color->value()) { std::istringstream is(cl_color->str()); is >> color; } @@ -65,6 +66,8 @@ GameInterface::~GameInterface() { core::Func::remove("list_model"); + game_localplayer.clear(); + clear(); } diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc index 985fac4..add0ca9 100644 --- a/src/core/gameserver.cc +++ b/src/core/gameserver.cc @@ -58,8 +58,9 @@ GameServer::GameServer() : GameInterface() Func::add("say", (GameFuncPtr) func_say); - if (!Cvar::sv_dedicated->value()) + if (!Cvar::sv_dedicated->value()) { player_connect(localplayer()); + } server_running = true; } @@ -113,7 +114,7 @@ void GameServer::say(Player *player, std::string const &message) } } -void GameServer::broadcast(std::string const & message, int ignoreplayer) +void GameServer::broadcast(std::string const & message, Player *ignore_player) { // send to console con_print << message << "\n"; @@ -123,7 +124,7 @@ void GameServer::broadcast(std::string const & message, int ignoreplayer) std::string netmessage("msg info "); netmessage.append(message); netmessage += '\n'; - server_network->broadcast(netmessage, ignoreplayer); + server_network->broadcast(netmessage, ignore_player); } } @@ -179,7 +180,7 @@ void GameServer::player_connect(Player *player) { std::string message(player->name()); message.append(" connects."); - broadcast(message, player->id()); + broadcast(message, player); // notify the game module server_module->player_connect(player); @@ -189,7 +190,7 @@ void GameServer::player_disconnect(Player *player) { std::string message(player->name()); message.append(" disconnects."); - broadcast(message, player->id()); + broadcast(message, player); // notify the game module server_module->player_disconnect(player); @@ -201,8 +202,9 @@ void GameServer::frame(float seconds) return; // process incoming network messages - if (server_network != 0 ) { - server_network->frame(seconds); + if (server_network) { + server_network->receive(); + if (server_network->error()) { abort(); return; @@ -230,6 +232,10 @@ void GameServer::frame(float seconds) // send updates if (server_network) { + // transmit buffered sends + server_network->transmit(); + + // TODO - start server frame std::map<unsigned int, Entity *>::iterator it; for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { Entity *entity = (*it).second; @@ -261,7 +267,7 @@ void GameServer::frame(float seconds) // update player info for (std::list<NetClient *>::iterator it = server_network->clients.begin(); it != server_network->clients.end(); it++) { NetClient *client = *it; - if (client->player()->dirty()) { + if (client->player()->dirty() && (client->state() == NetClient::Connected)) { // send player data std::ostringstream netmsg; netmsg.str(""); @@ -272,12 +278,11 @@ void GameServer::frame(float seconds) client->player()->player_dirty = false; } } + // TODO - end server frame // transmit buffered sends - for (std::list<NetClient *>::iterator it = server_network->clients.begin(); it != server_network->clients.end(); it++) { - (*it)->transmit(); - } - + server_network->transmit(); + } else { // local update stub diff --git a/src/core/gameserver.h b/src/core/gameserver.h index 17d581c..fd2021e 100644 --- a/src/core/gameserver.h +++ b/src/core/gameserver.h @@ -47,7 +47,7 @@ public: void say(Player *player, std::string const &args); /// broadcast a message to all players - void broadcast(std::string const & message, int ignoreplayer = -1); + void broadcast(std::string const & message, Player *ignore_player = 0); /// send a message to a single player void send(Player *player, std::string message); @@ -69,7 +69,6 @@ private: Module *server_module; static GameServer *server_instance; NetServer *server_network; - }; inline GameServer *server() { return GameServer::instance(); } diff --git a/src/core/net.h b/src/core/net.h new file mode 100644 index 0000000..dd81580 --- /dev/null +++ b/src/core/net.h @@ -0,0 +1,30 @@ +/* + core/net.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#ifndef __INCLUDED_CORE_NET_H__ +#define __INCLUDED_CORE_NET_H__ + +/// this namespace contains the network subsystem +namespace core +{ + +/// maximum lenght of a (compressed) network message block +const unsigned int FRAMESIZE = 1152; +const unsigned int BLOCKSIZE = 8192; + +/// maximum number of pending client connections, hard limit +const unsigned int MAXPENDINGCONNECTIONS = 32; + +/// default network port +const unsigned int DEFAULTPORT = 8042; +} + +#include "core/netserver.h" +#include "core/netclient.h" +#include "core/netconnection.h" + +#endif // __INCLUDED_CORE_NET_H__ + diff --git a/src/core/netclient.cc b/src/core/netclient.cc index 0f56bb9..32e0f4b 100644 --- a/src/core/netclient.cc +++ b/src/core/netclient.cc @@ -8,31 +8,61 @@ #include <sstream> #include "sys/sys.h" -#include "net/net.h" -#include "core/netclient.h" +#include "core/net.h" namespace core { -NetClient::NetClient(int clientfd, std::string host, int port) : - net::TCPClient(clientfd), +NetClient::NetClient(std::string host, int port) : client_host(host) { - std::ostringstream osstream; - //osstream << "host << ":" << port; - osstream << "Player" << clientfd; - client_player.player_name = osstream.str(); - client_player.player_id = (unsigned int) clientfd; + client_error = true; + client_state = Connecting; + + con_print << host << ":" << port << " connects." << std::endl; + // Get a socket file descriptor + client_fd = socket(PF_INET, SOCK_DGRAM, 0); + if (client_fd == -1) { + con_warn << "Network client socket() failed!" << std::endl; + abort(); + return; + } + client_host = host; client_port = port; - sendq.clear(); + client_addr.sin_family = AF_INET; + client_addr.sin_port = htons(port); + client_addr.sin_addr.s_addr = inet_addr(host.c_str()); + if (client_addr.sin_addr.s_addr == INADDR_NONE) { + con_warn << "Network invalid client address " << host << "!" << std::endl; + abort(); + return; + } + memset(client_addr.sin_zero, '\0', sizeof(client_addr.sin_zero)); + + std::ostringstream osstream; + client_player.player_id = client_fd; + + sendq.erase(); messageblock.clear(); + + client_error = false; } NetClient::~NetClient() { + con_print << host() << ":" << port() << " disconnects." << std::endl; + + if (client_fd) { + ::close(client_fd); + } +} + +void NetClient::abort() +{ + client_error = true; } std::string NetClient::host() const @@ -64,10 +94,10 @@ void NetClient::retreive(std::string & message) { } // receive data and decode it into lines -void NetClient::receive() +void NetClient::receive(char *data) { std::string datablock; - net::TCPClient::receive(datablock); + datablock.assign(data); while(datablock.size() > 0 ) { // scan the datablock for enters @@ -75,19 +105,18 @@ void NetClient::receive() // TODO detect "begin binary block" message for zlib compression if (messageblock.size() > 0 ) { recvq.push_back(messageblock); - messageblock.clear(); + messageblock.erase(); } } else { - if (messageblock.size() < net::FRAMESIZE) { + if (messageblock.size() < FRAMESIZE) { messageblock.append(datablock.substr(0,1)); } else { - con_warn << "NetClient " << fd() << ": message block overflow" << std::endl; - messageblock.clear(); + con_warn << "Incoming message exceeds " << FRAMESIZE << " bytes!\n"; + messageblock.erase(); } } datablock.erase(0,1); } - datablock.clear(); } void NetClient::send(std::string const &msg) @@ -97,14 +126,26 @@ void NetClient::send(std::string const &msg) void NetClient::transmit() { - while (sendq.size() && valid() && !error()) { - TCPClient::send(sendq.substr(0, net::FRAMESIZE-1)); - if (sendq.size() < net::FRAMESIZE) { - sendq.clear(); - } else { - sendq.erase(0, net::FRAMESIZE-1); + if (sendq.size() >= BLOCKSIZE) { + con_warn << "Outgoing message exceeds " << BLOCKSIZE << " bytes!\n"; + sendq.clear(); + return; + } + + ssize_t bytes_sent = 0; + + while (sendq.size() && !error()) { + bytes_sent = sendto(client_fd, sendq.c_str(), sendq.size(), 0, + (struct sockaddr *)&client_addr, sizeof(client_addr)); + + if (bytes_sent < 0) { + abort(); + return; } + + sendq.erase(0, bytes_sent); } + sendq.clear(); } } diff --git a/src/core/netclient.h b/src/core/netclient.h index 23a2787..2fde530 100644 --- a/src/core/netclient.h +++ b/src/core/netclient.h @@ -7,21 +7,30 @@ #ifndef __INCLUDED_CORE_NETCLIENT_H__ #define __INCLUDED_CORE_NETCLIENT_H__ +#include <unistd.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + #include <string> #include <deque> #include <map> -#include "net/tcpclient.h" #include "core/player.h" namespace core { /// queues incoming client messages -class NetClient : public net::TCPClient +class NetClient { public: - NetClient(int clientfd, std::string host, int port); + NetClient(std::string host, int port); ~NetClient(); /// the remote hostname the client is connected to @@ -33,26 +42,41 @@ public: /// the player info associated with this client Player *player(); - /// return true if there are incoming messages + /// bufer incoming data + void receive(char *data); + + /// buffer outgoing data + void send(std::string const &msg); + + /// return true if there is buffered incoming data bool has_messages() const; - /// receive an incoming message + /// retreive buffered incoming data void retreive(std::string & message); - /// receive incoming data and store messages - void receive(); + /// transmit buffered outgoing data + void transmit(); + + inline bool error() const { return client_error; } - /// buffer outgoing data - void send(std::string const &msg); + inline int fd() const { return client_fd; } - /// send bufered outgoing data - void transmit(); + void abort(); + + enum State {Connecting=0, Connected=1}; + + inline State state() const { return client_state; } + State client_state; + private: + struct sockaddr_in client_addr; std::string client_host; int client_port; Player client_player; - + bool client_error; + int client_fd; + std::string messageblock; std::deque<std::string> recvq; std::string sendq; diff --git a/src/core/netconnection.cc b/src/core/netconnection.cc index 9790655..379df14 100644 --- a/src/core/netconnection.cc +++ b/src/core/netconnection.cc @@ -7,7 +7,6 @@ #include <sstream> #include "sys/sys.h" -#include "net/net.h" #include "core/application.h" #include "core/gameconnection.h" #include "core/netconnection.h" @@ -22,27 +21,86 @@ NetConnection::NetConnection() NetConnection::~NetConnection() { + disconnect(); +} + +void NetConnection::abort() +{ + connection_error= true; } void NetConnection::connect(std::string const &to_host, int to_port) { - TCPConnection::connect(to_host, to_port); - if(connected()) { - FD_ZERO(&clientset); - FD_SET(fd(), &clientset); + connection_error = false; + connection_fd = -1; + + if (valid()) + return; + + // resolve serverhostname + struct hostent *serverhostent; + serverhostent = gethostbyname(to_host.c_str()); + if (!serverhostent) { + con_warn << "Could not resolve '" << to_host.c_str() << "'" << std::endl; + abort(); + return; } + + con_print << "Connecting to " << inet_ntoa(*((struct in_addr *)serverhostent->h_addr)) << ":" << to_port << "..." << std::endl; + + // Get a socket file descriptor + connection_fd = socket(PF_INET, SOCK_DGRAM, 0); + if (connection_fd == -1) { + //con_error << "Network socket() failed!" << std::endl; + abort(); + return; + } + + // make the connection + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(to_port); + // FIXME inet_addr can still fail + server_addr.sin_addr.s_addr = inet_addr(inet_ntoa(*((struct in_addr *)serverhostent->h_addr))); + if (server_addr.sin_addr.s_addr == INADDR_NONE) { + con_error << "Network invalid address " << to_host << "!" << std::endl; + abort(); + return; + } + memset(server_addr.sin_zero, '\0', sizeof server_addr.sin_zero); + + /* + if (::connect(connection_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) != 0) { + //con_error << "Network connect() failed!" << std::endl; + abort(); + return; + } + */ + + connection_host = to_host; + connection_port = to_port; + connection_error = false; - std::ostringstream osstream; - osstream << "pif "; - game()->localplayer()->serialize_client_update(osstream); - osstream << '\n'; - send(osstream.str()); + FD_ZERO(&clientset); + FD_SET(fd(), &clientset); + + game()->localplayer()->player_dirty = true; } void NetConnection::disconnect() { - TCPConnection::disconnect(); - FD_ZERO(&clientset); + if (connection_fd != -1) { + sendq.clear(); + sendq.assign("disconnect\n"); + transmit(); + + FD_ZERO(&clientset); + close(connection_fd); + } + + connection_fd = -1; + connection_error = false; + connection_host.clear(); + connection_port = 0; } bool NetConnection::has_messages() const @@ -64,25 +122,41 @@ void NetConnection::retreive(std::string & message) void NetConnection::receive() { // TODO: binary mode data transfer - std::string datablock; - TCPConnection::receive(datablock); + if (error() || invalid()) + return; + + ssize_t bytes_received; + + memset(recvbuf, '\0', BLOCKSIZE); + bytes_received = ::recv(connection_fd, recvbuf, BLOCKSIZE-1, 0); - if (error()) + if (bytes_received == 0) { + con_print << "Disconnected."; + abort(); + return; + } else if (bytes_received < 0) { + con_error << "Network receive() error!" << std::endl; + //perror("recv"); + abort(); return; - while (datablock.size() > 0) { + } + + std::string datablock; + datablock.assign(recvbuf); + + while (datablock.size()) { // scan the datablock for enters if (datablock[0] == '\n' || datablock[0] == '\r') { - if (messageblock.size() > 0) { + if (messageblock.size() >= FRAMESIZE) { + con_warn << "Incoming message exceeds " << FRAMESIZE << " bytes!\n"; + messageblock.clear(); + } else if (messageblock.size()) { recvq.push_back(messageblock); + //con_debug << "Incoming message '" << messageblock << "'" << std::endl; messageblock.clear(); } } else { - if (messageblock.size() < net::FRAMESIZE) { - messageblock.append(datablock.substr(0,1)); - } else { - con_warn << "Network message exceeds " << net::FRAMESIZE << " bytes!" << std::endl; - messageblock.clear(); - } + messageblock.append(datablock.substr(0,1)); } datablock.erase(0,1); } @@ -97,22 +171,33 @@ void NetConnection::frame(float seconds) fd_set readset = clientset; int nb = select(fd()+1, &readset, NULL, NULL, &timeout); - if (nb == 0) + if (nb == 0) { return; - + } if (nb == -1) { con_error << "Network error on select()" << std::endl; //perror("select"); abort(); } - if (FD_ISSET(this->fd(), &readset) && !error()) { + while (FD_ISSET(this->fd(), &readset) && !error()) { receive(); + while (has_messages()) { std::string message; retreive(message); parse_incoming_message(message); } + + nb = select(fd()+1, &readset, NULL, NULL, &timeout); + if (nb == 0) { + return; + } + if (nb == -1) { + con_error << "Network error on select()" << std::endl; + //perror("select"); + abort(); + } } } @@ -123,14 +208,32 @@ void NetConnection::send(std::string const &msg) void NetConnection::transmit() { - while (sendq.size() && valid() && !error()) { - TCPConnection::send(sendq.substr(0, net::FRAMESIZE-1)); - if (sendq.size() < net::FRAMESIZE) { - sendq.clear(); - } else { - sendq.erase(0, net::FRAMESIZE-1); + + if (error() || invalid()) + return; + + if (sendq.size() > FRAMESIZE) { + con_warn << "Outgoing message exceeds " << FRAMESIZE << " bytes!\n"; + //sendq.clear(); + //return; + } + + ssize_t bytes_sent = 0; + + while (sendq.size()) { + bytes_sent = ::sendto(connection_fd, sendq.c_str(), sendq.size(), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)); + if (bytes_sent <= 0) { + con_error << "Network send() error!" << std::endl; + //perror("send"); + abort(); + return; } + + // assert (bytes_sent <= sendbuf.size()); + sendq.erase(0, bytes_sent); } + + sendq.clear(); } // parse incoming client messages diff --git a/src/core/netconnection.h b/src/core/netconnection.h index 7623e64..f378afb 100644 --- a/src/core/netconnection.h +++ b/src/core/netconnection.h @@ -7,19 +7,30 @@ #ifndef __INCLUDED_CORE_NETCONNECTION_H__ #define __INCLUDED_CORE_NETCONNECTION_H__ +#include <unistd.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + #include <sys/select.h> +#include <netdb.h> #include <string> #include <deque> #include <map> -#include "net/tcpconnection.h" +#include "core/net.h" namespace core { /// a client to server connection -class NetConnection : public net::TCPConnection +class NetConnection { public: NetConnection(); @@ -40,6 +51,22 @@ public: /// send bufered outgoing data void transmit(); + void abort(); + + inline int fd() const { return connection_fd; } + + inline std::string host() const { return connection_host; } + + inline int port() const { return connection_port; } + + inline bool valid() const { return (connection_fd != -1); } + + inline bool invalid() const { return (connection_fd == -1); } + + inline bool error() const { return connection_error; } + + inline bool connected() const { return ((connection_fd != -1) && !connection_error); } + protected: /// receive incoming data and store messages void receive(); @@ -58,6 +85,13 @@ private: std::deque<std::string> recvq; std::string sendq; fd_set clientset; + + int connection_fd; + bool connection_error; + std::string connection_host; + int connection_port; + struct sockaddr_in server_addr; + char recvbuf[BLOCKSIZE]; }; } 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 <unistd.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + #include <iostream> #include <sstream> @@ -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<NetClient *>:: 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<unsigned int, Entity *>::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<NetClient *>:: iterator it; - for (it = clients.begin(); it != clients.end(); it++) { + for (std::list<NetClient *>:: 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<NetClient *>::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<NetClient *>::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<NetClient *>::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<unsigned int, Entity *>::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<NetClient *>::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<NetClient *>::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<NetClient *>::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"); - } - */ } } diff --git a/src/core/netserver.h b/src/core/netserver.h index 4de87be..df098df 100644 --- a/src/core/netserver.h +++ b/src/core/netserver.h @@ -12,35 +12,56 @@ #include <list> #include <string> -#include "net/tcpserver.h" +#include "core/net.h" #include "core/netclient.h" +#include "core/player.h" namespace core { /// Network server -class NetServer : public net::TCPServer +class NetServer { public: NetServer(std::string const host, unsigned int const port); - virtual ~NetServer(); + ~NetServer(); - /// run one server frame - void frame(float seconds); +/*----- inspectors ------------------------------------------------ */ - /// broadcast a message to all clients - void broadcast(std::string const & message, int ignorefd=-1); + /// Returns true if the TCP server has a valid file descriptor + inline bool valid() const { return netserver_fd != -1; } + + /// Returns true if the TCP server has an invalid file descriptor + inline bool invalid() const { return netserver_fd == -1; } + + /// Returns the error state of the TCP server + inline bool error() const { return netserver_error; } + + /// Return the socket file descriptor + inline int fd() const { return netserver_fd; } + +/*----- mutators -------------------------------------------------- */ + + /// receive data from clients + void receive(); - /// send a message to a client + /// transmit data to clients + void transmit(); + + /// broadcast network data to all clients + void broadcast(std::string const & message, Player *ignore_player=0); + + /// send network data to one client void send(NetClient * client, std::string const & message); /// find the client corresponding to a player NetClient *find_client(Player const *player); std::list<NetClient *> clients; + protected: - /// called by accept() when a new client connects - virtual void client_connect(int const clientfd, std::string const host, int const port); + /// called when a new client connects + NetClient *client_connect(std::string const host, int const port); /// remove terminated clients void reap(); @@ -48,8 +69,15 @@ protected: /// parse incoming client messages void parse_incoming_message(NetClient *client, const std::string & message); +private: + bool netserver_error; + int netserver_fd; + struct sockaddr_in netserver_addr; + fd_set serverset; int fdmax; + + char recbuf[FRAMESIZE]; }; } diff --git a/src/core/player.cc b/src/core/player.cc index 4c4616a..b89da99 100644 --- a/src/core/player.cc +++ b/src/core/player.cc @@ -73,8 +73,7 @@ void Player::recieve_client_update(std::istream &is) n += c; if (n.size()) - player_name = n; - + player_name = n; } void Player::serialize_server_update(std::ostream & os) const diff --git a/src/server/Makefile.am b/src/server/Makefile.am index f9b2ada..2c46543 100644 --- a/src/server/Makefile.am +++ b/src/server/Makefile.am @@ -4,6 +4,5 @@ noinst_HEADERS = console.h server.h timer.h noinst_LTLIBRARIES = libserver.la INCLUDES = -I$(top_srcdir)/src libserver_la_LDFLAGS = -avoid-version -no-undefined -libserver_la_LIBADD = $(top_builddir)/src/sys/libsys.la \ - $(top_builddir)/src/filesystem/libfilesystem.la $(top_builddir)/src/math/libmath.la \ - $(top_builddir)/src/net/libnet.la $(top_builddir)/src/core/libcore.la +libserver_la_LIBADD = $(top_builddir)/src/core/libcore.la \ + $(top_builddir)/src/filesystem/libfilesystem.la $(top_builddir)/src/math/libmath.la $(top_builddir)/src/sys/libsys.la |