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  | 
