From 22e136d88817b64ec904a7e7232a2cf9e80e74bd Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Sun, 23 Mar 2008 18:12:56 +0000 Subject: improved network connection handling, keep alive and time out --- README | 25 +++++++++++++-------- osirion.kdevelop.pcs | Bin 518254 -> 514385 bytes osirion.kdevses | 8 ++++++- src/client/console.cc | 2 +- src/client/view.cc | 13 ++++++----- src/core/application.cc | 5 +---- src/core/gameconnection.cc | 2 ++ src/core/gameinterface.cc | 21 +++++------------- src/core/gameserver.cc | 3 +++ src/core/net.h | 4 ++++ src/core/netclient.cc | 30 ++++++++++++++++++++----- src/core/netclient.h | 3 +++ src/core/netconnection.cc | 53 ++++++++++++++++++++++++++++++++------------- src/core/netconnection.h | 10 +++++++-- src/core/netserver.cc | 47 ++++++++++++++++++++++++++++++++++------ src/core/player.cc | 16 +++++++++----- src/core/player.h | 3 +++ 17 files changed, 173 insertions(+), 72 deletions(-) diff --git a/README b/README index 6610a90..6a88cca 100644 --- a/README +++ b/README @@ -7,11 +7,6 @@ The Osirion Project - README Read INSTALL for instructions on building, installing and udpating. -Dedicated server - - By default, the dedicated server will accept incoming connections - on UDP port 8042. There is no server console. - Client Starting the client will show the loader screen. @@ -25,7 +20,7 @@ Client This wil allow remote players to connect to your client. To connect to a dedicated server, type 'connect address', where - adress is the hostname of IP address of the remote server. + adress is the hostname or IP address of the remote server. In-game, you can purchase a new ship with the 'buy' command. @@ -56,6 +51,7 @@ Console functions join join the game spectate spectate buy purchase a new ship + who list connected players To change the video resolution, set the r_width and r_height variables and execute r_restart. @@ -72,8 +68,8 @@ Console functions r_restart You can set your name and player color through the cl_color - and cl_name variables. You will have to disconnect and reconnect - for the changes to take effect: + and cl_name variables. Note that the color if your ship + won't change until you buy a new one. cl_name SpaceCowboy cl_color 1.0 1.0 0.0 @@ -86,6 +82,16 @@ Configuration server reads its configuration from server.ini, the client will use client.ini. +Dedicated server + + By default, the dedicated server will accept incoming connections + on UDP port 8042. There is no server console. + + If you have trouble connecting, try editing the server configuration + file ~/.osirion/base/server.ini and change the net_server setting to + the server's actual IP address. The default listening port can + be altered by changing the net_port setting. + Organization of the distribution C++ source code @@ -161,6 +167,7 @@ Project contributors Thorn[mDc] - Canasta and Sharkan ship models Alpha testing + Josky=KCT= - Shuttle ship model IRC @@ -185,7 +192,7 @@ Acknowledgements The Satgnu crew - Thanks to bobke, blaze-x and Thorn[mDc] + Thanks to bobke and blaze-x DVSoftware for bandwidth, diskpace and CPU power diff --git a/osirion.kdevelop.pcs b/osirion.kdevelop.pcs index b0fd24b..02ebf51 100644 Binary files a/osirion.kdevelop.pcs and b/osirion.kdevelop.pcs differ diff --git a/osirion.kdevses b/osirion.kdevses index 5dd7e63..7c59f5b 100644 --- a/osirion.kdevses +++ b/osirion.kdevses @@ -1,13 +1,19 @@ - + + + + + + + diff --git a/src/client/console.cc b/src/client/console.cc index fc53b09..67a6ac9 100644 --- a/src/client/console.cc +++ b/src/client/console.cc @@ -194,7 +194,7 @@ void flush() // save notification notify_text[notify_pos] = line; - notify_time[notify_pos] = sys::time(); + notify_time[notify_pos] = core::application()->time(); notify_pos = (notify_pos+1) % MAXNOTIFYLINES; // print to stdout diff --git a/src/client/view.cc b/src/client/view.cc index 5d14992..15b8606 100644 --- a/src/client/view.cc +++ b/src/client/view.cc @@ -146,10 +146,12 @@ void draw_status() // print stats if desired if (render::r_drawstats && render::r_drawstats->value()) { std::stringstream stats; - stats << "fps " << std::setw(6) << fps << "\n"; - stats << "tris " << std::setw(6) << render::Stats::tris << "\n"; - stats << "spheres " << std::setw(4) << render::Stats::spheres << "\n"; - draw_text(video::width-CHARWIDTH*13, CHARHEIGHT*3, stats); + stats << "fps " << std::setw(6) << fps << "\n"; + if (core::application()->connected()) { + stats << "tris " << std::setw(6) << render::Stats::tris << "\n"; + stats << "spheres " << std::setw(3) << render::Stats::spheres << "\n"; + } + draw_text(video::width-CHARWIDTH*12, CHARHEIGHT*2, stats); } // print the version number in the upper right corner @@ -161,10 +163,9 @@ void draw_status() // draw notifications gl::color(1.0f, 1.0f, 1.0f, 1.0f); size_t n = console::notify_pos % MAXNOTIFYLINES; - float now = sys::time(); int h = 4 + CHARHEIGHT; for (size_t l = 0; l < MAXNOTIFYLINES; l++) { - if (console::notify_text[n].size() > 2 && console::notify_time[n] + 4 > now) { + if (console::notify_text[n].size() > 2 && console::notify_time[n] + 4 > core::application()->time()) { if (console::notify_text[n][0] == '?') gl::color(0.7f,0.7f,0.7f, 1.0f); else if (console::notify_text[n][0] == '*') diff --git a/src/core/application.cc b/src/core/application.cc index 5394b92..efa7bca 100644 --- a/src/core/application.cc +++ b/src/core/application.cc @@ -229,12 +229,9 @@ void Application::connect(std::string const &host) if (host.size()) { application_game = new GameConnection(host); - if (application_game->running()) { - con_print << "Connected to '" << host << "'\n"; - } else { + if (!application_game->running()) { delete application_game; application_game = 0; - con_warn << "Could not connect to '" << host << "'!\n"; } } else { application_game = new GameServer(); diff --git a/src/core/gameconnection.cc b/src/core/gameconnection.cc index 634a6ec..923d9a3 100644 --- a/src/core/gameconnection.cc +++ b/src/core/gameconnection.cc @@ -124,6 +124,8 @@ void GameConnection::frame(float seconds) } if (localplayer()->dirty()) { + localplayer()->update_info(); + std::ostringstream osstream; osstream << "pif "; localplayer()->serialize_client_update(osstream); diff --git a/src/core/gameinterface.cc b/src/core/gameinterface.cc index 9f4dd29..a739eb2 100644 --- a/src/core/gameinterface.cc +++ b/src/core/gameinterface.cc @@ -42,23 +42,14 @@ GameInterface::GameInterface() { clear(); - if (Cvar::sv_dedicated->value()) + game_localplayer.clear(); + 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(); - } - - Cvar *cl_color = Cvar::find("cl_color"); - math::Color color(1.0, 1.0, 1.0, 1.0); - if (cl_color && cl_color->value()) { - std::istringstream is(cl_color->str()); - is >> color; - } - game_localplayer.player_color = color; + } else { + game_localplayer.player_name.assign("Player"); + game_localplayer.update_info(); } + core::Func::add("list_model", (core::FuncPtr) func_list_model); } diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc index 211bb96..1aa57f0 100644 --- a/src/core/gameserver.cc +++ b/src/core/gameserver.cc @@ -252,6 +252,9 @@ void GameServer::frame(float seconds) } } + if (localplayer()->dirty()) + localplayer()->update_info(); + // run a time frame on each entity std::map::iterator it; for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { diff --git a/src/core/net.h b/src/core/net.h index dd81580..150bbc1 100644 --- a/src/core/net.h +++ b/src/core/net.h @@ -20,6 +20,10 @@ const unsigned int MAXPENDINGCONNECTIONS = 32; /// default network port const unsigned int DEFAULTPORT = 8042; + +/// network timeout in seconds +const float NETTIMEOUT = 15; + } #include "core/netserver.h" diff --git a/src/core/netclient.cc b/src/core/netclient.cc index c7186f5..c910e26 100644 --- a/src/core/netclient.cc +++ b/src/core/netclient.cc @@ -9,6 +9,7 @@ #include "sys/sys.h" #include "core/net.h" +#include "core/application.h" namespace core { @@ -38,6 +39,9 @@ NetClient::NetClient(std::string host, int port) : sendq.clear(); messageblock.clear(); + client_keepalive = application()->time(); + client_timeout = application()->time(); + client_error = false; } @@ -85,12 +89,15 @@ void NetClient::receive(char *data) std::string datablock; datablock.assign(data); + if (!datablock.size()) + return; + while(datablock.size() > 0 ) { // scan the datablock for enters if (datablock[0] == '\n' || datablock[0] == '\r') { // TODO detect "begin binary block" message for zlib compression if (messageblock.size() > 0 ) { - recvq.push_back(messageblock); + recvq.push_back(messageblock); messageblock.clear(); } } else { @@ -103,6 +110,8 @@ void NetClient::receive(char *data) } datablock.erase(0,1); } + + client_timeout = application()->time(); } void NetClient::send(std::string const &msg) @@ -112,16 +121,23 @@ void NetClient::send(std::string const &msg) void NetClient::transmit(int serverfd) { - if (sendq.size() >= BLOCKSIZE) { - con_warn << "Outgoing message exceeds " << BLOCKSIZE << " bytes!\n"; - sendq.clear(); - return; + + if (!sendq.size()) { + if (client_keepalive + NETTIMEOUT/2 < application()->time()) { + sendq.assign("ping\n"); + } else { + return; + } + } else if (sendq.size() >= FRAMESIZE) { + con_warn << "Outgoing message exceeds " << FRAMESIZE << " bytes!\n"; + //sendq.clear(); + //return; } ssize_t bytes_sent = 0; while (sendq.size() && !error()) { - bytes_sent = sendto(serverfd, sendq.c_str(), sendq.size(), 0, + bytes_sent = ::sendto(serverfd, sendq.c_str(), sendq.size(), 0, (struct sockaddr *)&client_addr, sizeof(client_addr)); if (bytes_sent < 0) { @@ -132,6 +148,8 @@ void NetClient::transmit(int serverfd) sendq.erase(0, bytes_sent); } sendq.clear(); + + client_keepalive = application()->time(); } } diff --git a/src/core/netclient.h b/src/core/netclient.h index 1a7c716..49f509c 100644 --- a/src/core/netclient.h +++ b/src/core/netclient.h @@ -66,6 +66,9 @@ public: inline State state() const { return client_state; } State client_state; + + float client_timeout; + float client_keepalive; private: struct sockaddr_in client_addr; diff --git a/src/core/netconnection.cc b/src/core/netconnection.cc index e3b4c69..b640595 100644 --- a/src/core/netconnection.cc +++ b/src/core/netconnection.cc @@ -17,7 +17,8 @@ namespace core NetConnection::NetConnection() { - timeout = core::application()->time(); + connection_timeout = core::application()->time(); + connection_state = Connecting; } NetConnection::~NetConnection() @@ -34,6 +35,7 @@ void NetConnection::connect(std::string const &to_host, int to_port) { connection_error = false; connection_fd = -1; + connection_state = Connecting; if (valid()) return; @@ -47,8 +49,6 @@ void NetConnection::connect(std::string const &to_host, int to_port) 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) { @@ -69,7 +69,6 @@ void NetConnection::connect(std::string const &to_host, int to_port) return; } - connection_host = to_host; connection_port = to_port; connection_error = false; @@ -77,8 +76,12 @@ void NetConnection::connect(std::string const &to_host, int to_port) FD_ZERO(&clientset); FD_SET(fd(), &clientset); - timeout = core::application()->time(); + connection_timeout = application()->time(); + connection_keepalive = application()->time(); + game()->localplayer()->player_dirty = true; + + con_print << "Connecting to " << inet_ntoa(*((struct in_addr *)serverhostent->h_addr)) << ":" << to_port << "..." << std::endl; } void NetConnection::disconnect() @@ -96,6 +99,7 @@ void NetConnection::disconnect() connection_error = false; connection_host.clear(); connection_port = 0; + connection_state = Connecting; } bool NetConnection::has_messages() const @@ -124,7 +128,7 @@ void NetConnection::receive() memset(recvbuf, '\0', BLOCKSIZE); bytes_received = ::recv(connection_fd, recvbuf, BLOCKSIZE-1, 0); - timeout = core::application()->time(); + connection_timeout = core::application()->time(); if (bytes_received == 0) { con_print << "Disconnected."; @@ -168,12 +172,10 @@ void NetConnection::frame(float seconds) int nb = select(fd()+1, &readset, NULL, NULL, &timeout); if (nb == 0) { - /* - if (timout + TIMEOUT < core::application()->time()) { - con_error << "Connection time out\n"; + if (connection_timeout + NETTIMEOUT < core::application()->time()) { + con_error << "Connection timeout!\n"; abort(); } - */ return; } if (nb == -1) { @@ -213,8 +215,14 @@ void NetConnection::transmit() if (error() || invalid()) return; - - if (sendq.size() > FRAMESIZE) { + + if (!sendq.size()) { + if (connection_keepalive + NETTIMEOUT /2 < application()->time()) { + sendq.assign("ping\n"); + } else { + return; + } + } else if (sendq.size() > FRAMESIZE) { con_warn << "Outgoing message exceeds " << FRAMESIZE << " bytes!\n"; //sendq.clear(); //return; @@ -234,14 +242,16 @@ void NetConnection::transmit() // assert (bytes_sent <= sendbuf.size()); sendq.erase(0, bytes_sent); } - - sendq.clear(); + + connection_keepalive = application()->time(); } // parse incoming client messages /** * The following incoming messages are parsed; * + * connect + * disconnect * msg info * msg public * die @@ -271,6 +281,19 @@ void NetConnection::parse_incoming_message(const std::string & message) } } + } else if (command == "connect") { + + connection_state = Connected; + con_print << "Connected." << std::endl; + return; + + } else if (command == "disconnect") { + + con_error << "Server disconnected!" << std::endl; + abort(); + + } else if (command == "ping") { + } else if (command == "die") { unsigned int id; msgstream >> id; @@ -312,9 +335,9 @@ void NetConnection::parse_incoming_message(const std::string & message) } else entity->recieve_server_update(msgstream); } - return; } else if (command == "pif") { + connection()->localplayer()->recieve_server_update(msgstream); } diff --git a/src/core/netconnection.h b/src/core/netconnection.h index dca85c7..08536de 100644 --- a/src/core/netconnection.h +++ b/src/core/netconnection.h @@ -67,6 +67,12 @@ public: inline bool connected() const { return ((connection_fd != -1) && !connection_error); } + enum State {Connecting=0, Connected=1}; + + inline State state() const { return connection_state; } + + State connection_state; + protected: /// receive incoming data and store messages void receive(); @@ -86,14 +92,14 @@ private: std::string sendq; fd_set clientset; + float connection_timeout; + float connection_keepalive; int connection_fd; bool connection_error; std::string connection_host; int connection_port; struct sockaddr_in server_addr; char recvbuf[BLOCKSIZE]; - - float timeout; }; } diff --git a/src/core/netserver.cc b/src/core/netserver.cc index 16beee0..c9870c5 100644 --- a/src/core/netserver.cc +++ b/src/core/netserver.cc @@ -86,6 +86,8 @@ NetServer::~NetServer() { con_print << "Shutting down network server..." << std::endl; + std::string netmsg("disconnect\n"); + // delete all clients std::list:: iterator it; for (it = clients.begin(); it != clients.end(); it++) { @@ -94,6 +96,9 @@ NetServer::~NetServer() if ((*it)->state() == NetClient::Connected) server()->player_disconnect((*it)->player()); + (*it)->send(netmsg); + (*it)->transmit(fd()); + delete (*it); } clients.clear(); @@ -108,6 +113,24 @@ void NetServer::reap() for (std::list:: iterator it = clients.begin(); it != clients.end(); it++) { NetClient *client = *it; + if (client->client_timeout + NETTIMEOUT < application()->time()) { + // client timed out, send a disconnect + std::string netmsg("disconnect\n"); + (*it)->send(netmsg); + (*it)->transmit(fd()); + (*it)->abort(); + + // print a message + std::string message(client->player()->name()); + message.append(" timed out."); + + if (client->state() == NetClient::Connected) { + server()->broadcast(message, client->player()); + } else { + con_print << message << std::endl; + } + } + if (client->error()) { // notify the game server @@ -234,15 +257,15 @@ NetClient * NetServer::client_connect(std::string const host, int const port) clients.push_back(client); + // send welcome message std::ostringstream netmsg; netmsg.str(""); netmsg << "msg info Receiving data from remote server...\n"; client->send(netmsg.str()); - transmit(); + client->transmit(fd()); // send entities std::map::iterator it; - for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { netmsg.str(""); switch ((*it).second->type()) { @@ -260,8 +283,7 @@ NetClient * NetServer::client_connect(std::string const host, int const port) break; } } - - transmit(); + client->transmit(fd()); client->player()->player_dirty = false; @@ -303,6 +325,7 @@ NetClient *NetServer::find_client(Player const *player) * cmd * cup * pif + * ping * say * */ @@ -331,13 +354,23 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me if (client->state() == NetClient::Connecting) { client->client_state = NetClient::Connected; server()->player_connect(client->player()); + + std::string netmsg("connect\n"); + client->send(netmsg); + } else if ((client->state() == NetClient::Connected) && (client->player()->name() != oldname)) { - oldname.append(" renamed to "); - oldname.append(client->player()->name()); - server()->broadcast(oldname); + + std::string netmsg(oldname); + netmsg.append(" renamed to "); + netmsg.append(client->player()->name()); + server()->broadcast(netmsg); } } + if (command == "ping") { + return; + } + if (client->state() != NetClient::Connected) return; diff --git a/src/core/player.cc b/src/core/player.cc index b89da99..71ac8de 100644 --- a/src/core/player.cc +++ b/src/core/player.cc @@ -44,21 +44,25 @@ void Player::clear_assets() player_control = 0; } -void Player::serialize_client_update(std::ostream & os) +void Player::update_info() { Cvar *cl_name = Cvar::find("cl_name"); if (cl_name) { - player_name = cl_name->str(); + if (cl_name->str().size()) + player_name = cl_name->str(); } - + Cvar *cl_color = Cvar::find("cl_color"); math::Color color(1.0, 1.0, 1.0, 1.0); if (cl_color) { std::istringstream is(cl_color->str()); - is >> color; - player_color = color; + if (is >> color) + player_color = color; } - +} + +void Player::serialize_client_update(std::ostream & os) +{ os << " " << player_color << " \"" << player_name << "\""; } diff --git a/src/core/player.h b/src/core/player.h index 59b18c1..08abac2 100644 --- a/src/core/player.h +++ b/src/core/player.h @@ -74,6 +74,9 @@ public: /// remove an asset void remove_asset(unsigned int id); + /// update player info from client variables + void update_info(); + /* -- should actually not be public --*/ // dirty state -- cgit v1.2.3