From 715d0c3952a3a1d59b64074e472d0a9a3b414351 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Thu, 14 Feb 2008 18:04:25 +0000 Subject: dedicated server accepts incoming connections --- src/core/Makefile.am | 4 +- src/core/application.cc | 37 +++++-- src/core/application.h | 4 + src/core/commandbuffer.cc | 12 +-- src/core/entity.h | 62 ++++------- src/core/gameinterface.h | 27 +++-- src/core/netclient.cc | 90 ++++++++++++++++ src/core/netclient.h | 57 ++++++++++ src/core/netserver.cc | 268 ++++++++++++++++++++++++++++++++++++++++++++++ src/core/netserver.h | 60 +++++++++++ src/core/player.h | 2 + 11 files changed, 556 insertions(+), 67 deletions(-) create mode 100644 src/core/netclient.cc create mode 100644 src/core/netclient.h create mode 100644 src/core/netserver.cc create mode 100644 src/core/netserver.h (limited to 'src/core') diff --git a/src/core/Makefile.am b/src/core/Makefile.am index bf98501..c5fb791 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -2,10 +2,10 @@ METASOURCES = AUTO INCLUDES = -I$(top_srcdir)/src libcore_la_SOURCES = application.cc commandbuffer.cc core.cc cvar.cc entity.cc \ - func.cc gameinterface.cc player.cc + func.cc gameinterface.cc netclient.cc netserver.cc player.cc libcore_la_LDFLAGS = -avoid-version -no-undefined libcore_la_LIBADD = $(top_builddir)/src/math/libmath.la \ - $(top_builddir)/src/sys/libsys.la $(top_builddir)/src/filesystem/libfilesystem.la + $(top_builddir)/src/filesystem/libfilesystem.la $(top_builddir)/src/sys/libsys.la $(top_builddir)/src/net/libnet.la noinst_LTLIBRARIES = libcore.la noinst_HEADERS = application.h commandbuffer.h core.h cvar.h entity.h func.h \ diff --git a/src/core/application.cc b/src/core/application.cc index 9fea8ee..2d29fad 100644 --- a/src/core/application.cc +++ b/src/core/application.cc @@ -19,12 +19,16 @@ #include "core/entity.h" #include "core/func.h" #include "core/cvar.h" +#include "core/netserver.h" namespace core { Cvar sv_dedicated; +Cvar net_host; +Cvar net_port; + // --------------- engine functions ------------------------------ void func_print(std::stringstream &args) { @@ -107,6 +111,8 @@ Application *Application::application_instance = 0; Application::Application() { + netserver = 0; + if (application_instance) { std::cerr << "multiple core::Application instances!" << std::endl; sys::quit(2); @@ -137,13 +143,16 @@ void Application::init() // initialize core subsystems filesystem::init(); - // dedicated or not + // dedicated or not, client should have set this to 0 core::sv_dedicated = core::cvar::get("sv_dedicated", "1", core::cvar::ReadOnly); if (sv_dedicated->value()) localplayer.name = "Console"; else - localplayer.name = "Client"; + localplayer.name = "Player"; + // network settings + core::net_host = core::cvar::get("net_host", "0.0.0.0"); + core::net_port = core::cvar::get("net_port", "8042"); // register our functions func::add("print", func_print); @@ -168,9 +177,12 @@ void Application::shutdown() if (game() && game()->connected) disconnect(); - - //if (game()) unload(); - + + if (netserver) { + delete netserver; + netserver = 0; + } + filesystem::shutdown(); } @@ -197,6 +209,10 @@ void Application::connect() con_print << "Connected." << std::endl; } else { con_warn << "Connect failed." << std::endl; + return; + } + if (core::sv_dedicated->value() && !netserver) { + netserver = new NetServer(net_host->text(), (unsigned int)net_port->value()); } } @@ -211,12 +227,13 @@ void Application::disconnect() con_warn << "Not connected." << std::endl; return; } - + game()->shutdown(); game()->connected = false; game()->current_time = 0; + // remove all entities entity::clear(); // TODO remove all game functions @@ -230,12 +247,18 @@ void Application::frame(float seconds) { current_time += seconds; + if (netserver) { + // TODO limit netserver frames in local mode + netserver->frame(seconds); + } + if (game() && game()->connected) { entity::frame(seconds); - + game()->current_time += seconds; game()->frame(seconds); } + // execute commands in the buffer commandbuffer::execute(); diff --git a/src/core/application.h b/src/core/application.h index d5c83d6..9a531de 100644 --- a/src/core/application.h +++ b/src/core/application.h @@ -8,6 +8,7 @@ #define __INCLUDED_CORE_APPLICATION_H__ #include "core/cvar.h" +#include "core/netserver.h" namespace core { @@ -51,6 +52,9 @@ public: /// global application object static Application *application_instance; + + /// network server instance + NetServer *netserver; }; diff --git a/src/core/commandbuffer.cc b/src/core/commandbuffer.cc index 7f1cda0..04fc406 100644 --- a/src/core/commandbuffer.cc +++ b/src/core/commandbuffer.cc @@ -23,13 +23,13 @@ namespace commandbuffer void exec(const char *text) { std::stringstream cmdstream(text); - std::string cmdname; + std::string command; - if (!(cmdstream >> cmdname)) + if (!(cmdstream >> command)) return; // is it a function - Func f = func::find(cmdname); + Func f = func::find(command); if (f) { // function exists, execute it if (f->flags && func::Game) { @@ -47,7 +47,7 @@ void exec(const char *text) } // is it a cvar - Cvar cv = cvar::find(cmdname); + Cvar cv = cvar::find(command); if (cv) { // cvar exists std::string args; @@ -59,11 +59,11 @@ void exec(const char *text) (*cv) = args; } - con_print << cmdname << " " << cv->text() << std::endl; + con_print << command << " " << cv->text() << std::endl; return; } - con_print << "Unknown command '" << cmdname << "'" << std::endl; + con_print << "Unknown command '" << command << "'" << std::endl; } void execute() diff --git a/src/core/entity.h b/src/core/entity.h index 6ff3be1..b3dd7ef 100644 --- a/src/core/entity.h +++ b/src/core/entity.h @@ -12,7 +12,6 @@ namespace core class EntityControlable; } -#include "core/core.h" #include "core/player.h" #include "math/mathlib.h" @@ -43,9 +42,7 @@ public: Entity(unsigned int entity_flags = 0); virtual ~Entity(); - /** - * @brief core type id - */ + /// core type id virtual inline unsigned int core_type() { return entity::Default; } /// unique instance identifier, automaticly set @@ -54,19 +51,20 @@ public: /// core shape id entity::Shape core_shape; - /// core color id + /// core color math::Color core_color; /// core radius, in game units float core_radius; - /// label + /// the entities label std::string label; /// custom game type id of this entity unsigned int type; /// flags + /// @see core::entity::Flags unsigned int flags; /* updateable by game */ @@ -74,72 +72,54 @@ public: /// location of the entity math::Vector3f location; - /** - * @brief direction the entity is facing, in degrees - * A direction of 0 degrees means the entity is 'looking' - * along the positive X-axis. - */ + /// direction the entity is facing, in degrees + /// A direction of 0 degrees means the entity is looking + /// along the positive X-axis. float direction; }; -/** - * @brief an entity that can move around in the game world - */ +/// an entity that can move around in the game world class EntityDynamic : public Entity { public: EntityDynamic(unsigned int entity_flags = 0); virtual ~EntityDynamic(); - /** - * @brief core type id - */ + /// core type id virtual inline unsigned int core_type() { return entity::Dynamic; } /* updateable by game */ - /** - * @brief current speed of the entity in game units per second - */ + /// current speed of the entity in game units per second float speed; }; -/** - * @brief an entity that can be controlled by a player - */ +/// an entity that can be controlled by a player class EntityControlable : public EntityDynamic { public: EntityControlable(unsigned int entity_flags = 0); virtual ~EntityControlable(); - /** - * @brief core type id - */ + /// core type id virtual inline unsigned int core_type() { return entity::Controlable; } - /** - * @brief owner of this controllable entity - */ + /// runs one game frame for the entity, to be implemented by game + virtual void frame(float seconds) = 0; + + /* updateable by game */ + + /// owner of this controllable entity Player *owner; /* updatable by client */ - /** - * @brief the direction the client wants to travel the entity to - * @see direction - */ + /// the direction the client wants to travel the entity to + /// @see direction float target_direction; - /** - * @brief engine thrust as set by the client, 0.0f - 1.0f - */ + /// engine thrust as set by the client, 0.0f - 1.0f float target_thrust; - - /** - * @brief runs one game frame for the entity, to be implemented by game - */ - virtual void frame(float seconds) = 0; }; diff --git a/src/core/gameinterface.h b/src/core/gameinterface.h index 6e53575..1fbae03 100644 --- a/src/core/gameinterface.h +++ b/src/core/gameinterface.h @@ -23,17 +23,6 @@ public: /// destroy the game singleton virtual ~GameInterface(); - /// initialize the game - virtual bool init() = 0; - - /// shutdown the game - virtual void shutdown() = 0; - - /// run one frame of the game - /** @param sec time since the previous frame, in seconds - */ - virtual void frame(float seconds) = 0; - /// a pointer to the current game instance static GameInterface * instance(); @@ -42,6 +31,22 @@ public: /// time the game has been running, in seconds float current_time; + + /// run one frame of the game + /// @param sec time since the previous frame, in seconds + virtual void frame(float seconds) = 0; + + /// initialize the game + virtual bool init() = 0; + + /// shutdown the game + virtual void shutdown() = 0; + + /// is called when a player connects + virtual void player_connect(Player &player) = 0; + + /// is called when a player disconnects + virtual void player_disconnect(Player &player) = 0; private: static GameInterface *gameinterface_instance; diff --git a/src/core/netclient.cc b/src/core/netclient.cc new file mode 100644 index 0000000..2df024b --- /dev/null +++ b/src/core/netclient.cc @@ -0,0 +1,90 @@ +/* + net/netclient.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include +#include + +#include "sys/sys.h" +#include "net/net.h" +#include "core/netclient.h" + +namespace core +{ + +NetClient::NetClient(int clientfd, std::string host, int port) : + net::TCPClient(clientfd), + client_host(host) +{ + std::ostringstream osstream; + osstream << host << ":" << port; + client_player.name = osstream.str(); + client_player.id = (unsigned int) clientfd; + + client_host = host; + client_port = port; + //con_debug << "NetClient " << fd() << ": starting." << std::endl; +} + +NetClient::~NetClient() +{ +} + +std::string NetClient::host() const +{ + return client_host; +} + +int NetClient::port() const +{ + return client_port; +} + +Player &NetClient::player() +{ + return client_player; +} + +bool NetClient::has_messages() const { + return (recvq.size() > 0 ); +} + +void NetClient::retreive(std::string & message) { + if (recvq.size() > 0 ) { + message.assign(recvq.front()); + recvq.pop_front(); + } else { + message.clear(); + } +} + +// receive data and decode it into lines +void NetClient::receive() +{ + std::string datablock; + net::TCPClient::receive(datablock); + + 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); + messageblock.clear(); + } + } else { + if (messageblock.size() < net::FRAMESIZE) { + messageblock.append(datablock.substr(0,1)); + } else { + con_warn << "NetClient " << fd() << ": message block overflow" << std::endl; + messageblock.clear(); + } + } + datablock.erase(0,1); + } + datablock.clear(); +} + +} diff --git a/src/core/netclient.h b/src/core/netclient.h new file mode 100644 index 0000000..dcffb3c --- /dev/null +++ b/src/core/netclient.h @@ -0,0 +1,57 @@ +/* + core/netclient.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_NETCLIENT_H__ +#define __INCLUDED_CORE_NETCLIENT_H__ + +#include +#include +#include + +#include "net/tcpclient.h" +#include "core/player.h" + +namespace core +{ + +/// queues incoming client messages +class NetClient : public net::TCPClient +{ +public: + NetClient(int clientfd, std::string host, int port); + ~NetClient(); + + /// the remote hostname the client is connected to + std::string host() const; + + /// the remote port the client is connected to + int port() const; + + /// the player info associated with this client + Player & player(); + + /// return true if there are incoming messages + bool has_messages() const; + + /// receive an incoming message + void retreive(std::string & message); + + /// receive incoming data and store messages + void receive(); + +private: + std::string client_host; + int client_port; + Player client_player; + + std::string messageblock; + std::deque recvq; +}; + +} + +#endif // __INCLUDED_CORE_NETCLIENT_H__ + diff --git a/src/core/netserver.cc b/src/core/netserver.cc new file mode 100644 index 0000000..c37b71d --- /dev/null +++ b/src/core/netserver.cc @@ -0,0 +1,268 @@ +/* + net/netserver.h + This file is part of the Osirion project and is distributed under + the terms of the GNU General Public License version 2 +*/ + +#include +#include + +#include "sys/sys.h" +#include "core/netclient.h" +#include "core/netserver.h" +#include "core/cvar.h" +#include "core/func.h" +#include "core/core.h" + +namespace core +{ + +NetServer::NetServer(std::string const host, unsigned int const port) : + net::TCPServer(host, port) +{ + FD_ZERO(&serverset); + // add the listening socket to the file descriptor set + FD_SET(fd(), &serverset); + + fdmax=fd(); +} + +NetServer::~NetServer() +{ + con_print << "Shutting down network..." << std::endl; + + // delete all clients + std::list:: iterator it; + for (it = clients.begin(); it != clients.end(); it++) { + // notify the game + if (core::connected()) { + core::game()->player_disconnect((*it)->player()); + con_print << (*it)->player().name << " disconnected."<< std::endl; + } + delete (*it); + } + clients.clear(); +} + +void NetServer::client_connect(int const clientfd, std::string const host, int const port) +{ + NetClient *client = new NetClient(clientfd, host, port); + if (client->error()) { + con_warn << "Client " << client->fd() << " " << + client->host() << ":" << client->port() << " connection failed!" << std::endl; + delete(client); + return; + } + + clients.push_back(client); + FD_SET(client->fd(), &serverset); + + // TODO send infos + + con_print << client->player().name << " connected."<< std::endl; + + // notify the game + if (core::game()) + core::game()->player_connect(client->player()); + + // BROADCAST connect message + std::ostringstream osstream; + osstream << "msg info " << client->player().name << " connected."<< std::endl; + broadcast(osstream); +} + +// remove disconnected clients +void NetServer::reap() +{ + std::list:: iterator it; + for (it = clients.begin(); it != clients.end(); it++) { + NetClient *client = *it; + if (client->error()) { + FD_CLR(client->fd(), &serverset); + + // BROADCAST disconnect message + std::ostringstream osstream; + osstream << "msg info " << client->player().name << " disconnected."<< std::endl; + broadcast(osstream); + + // notify the game + if (core::game()) + core::game()->player_disconnect(client->player()); + + con_print << client->player().name << " disconnected."<< std::endl; + + // remove the client + clients.erase(it); + delete client; + it=clients.begin(); + + } + } +} + + +void NetServer::frame(float seconds) { + if (error()) + return; + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + fd_set readset = serverset; + + int nb = select(fdmax+1, &readset, NULL, NULL, &timeout); + if (nb == 0) + return; + + if (nb == -1) { + con_error << "Network error on select()" << std::endl; + perror("select"); + abort(); + } + + // accept incoming connections + if(FD_ISSET(this->fd(), &readset)) { + accept(); + } + + // get messages from clients + for (std::list::iterator it = clients.begin(); it != clients.end(); it++) { + NetClient *client = *it; + if (client->fd() > fdmax) + fdmax = client->fd(); + if (FD_ISSET(client->fd(), &readset) && !client->error()) { + client->receive(); + while (client->has_messages()) { + std::string message; + client->retreive(message); + parse_incoming_message(client, message); + } + } + } + + // remove dead connections + reap(); +} + +// send a message to one client +void NetServer::send(NetClient * client, std::ostringstream &osstream) { + // FIXME large messages + //std::cout << "NetServer: " << osstream.str(); + client->send(osstream.str()); + osstream.str(""); +} + +void NetServer::send(NetClient * client, std::string message) { + //std::cout << "NetServer: " << message; + client->send(message); +} + +// send a message to all clients +void NetServer::broadcast(std::ostringstream &osstream, int ignorefd) +{ + //std::cout << "NetServer: " << osstream.str(); + for (std::list::iterator it = clients.begin(); it != clients.end(); it++) { + NetClient *client = *it; + if (!client->error() && client->fd() != ignorefd) + client->send(osstream.str()); + } + osstream.str(""); +} + +void NetServer::parse_incoming_message(NetClient *client, const std::string & message) +{ + std::stringstream msgstream(message); + + std::string command; + msgstream >> command; + + // disconnect + if (command == "disconnect") { + client->abort(); + return; + } + + // say + if (command == "say") { + if (message.size() > command.size()+1) { + std::ostringstream osstream; + osstream << "msg public " << client->player().name << " " << message.substr(command.size()+1) << std::endl; + broadcast(osstream); + con_print << client->player().name << " " << message.substr(command.size()+1) << std::endl; + } + return; + } + + // name is an alias for set name + if (command == "name") { + std::string name; + if (msgstream >> name) { + if (name.size() > 16) + name = name.substr(0,16); + if (name != client->player().name) { + std::ostringstream osstream; + osstream << "msg info " << client->player().name << " renamed to " << name << std::endl; + broadcast(osstream); + client->player().name = name; + con_print << client->player().name << " renamed to " << name << std::endl; + } + } + return; + } + + if (command == "list_players") { + std::ostringstream osstream; + for (std::list::iterator it = clients.begin(); it != clients.end(); it++) { + osstream << "msg info client " << (*it)->player().name << " " << (*it)->host() << ":" << (*it)->port() << std::endl; + } + osstream << "msg info client " << clients.size() << " players" << std::endl; + send(client, osstream); + } + + if (command == "help") { + send(client, "msg info Available commands:\n"); + send(client, "msg info help - shows this help message\n"); + send(client, "msg info name nickname - changes your nickname\n"); + send(client, "msg info say text - say something on the public channel\n"); + send(client, "msg info list_players - shows a list of connected players\n"); + send(client, "msg info disconnect - disconnect\n"); + } + + // execute game functions + if (game() && game()->connected) { + Func f = func::find(command); + if (f) { + if (f->flags && func::Game) { + GameFuncPtr function = (GameFuncPtr) f->ptr; + function(client->player(), msgstream); + } else { + // FIXME rcon + // FuncPtr function = (FuncPtr) f->ptr; + // function(msgstream); + } + return; + } + } + + // TODO rcon +} + +void NetServer::parse_client_variable(NetClient * client, const std::string varname, std::istringstream &istringstream) +{ + if (varname=="name") { + std::string name; + if (istringstream >> name) { + std::ostringstream osstream; + if (name.size() > 16) + name = name.substr(0,16); + if (name != client->player().name) { + osstream << "msg info " << client->player().name << " renamed to " << name << std::endl; + broadcast(osstream); + client->player().name = name; + } + } + return; + } +} + +} diff --git a/src/core/netserver.h b/src/core/netserver.h new file mode 100644 index 0000000..4d1eb73 --- /dev/null +++ b/src/core/netserver.h @@ -0,0 +1,60 @@ +/* + core/netserver.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_NETSERVER_H__ +#define __INCLUDED_CORE_NETSERVER_H__ + +#include + +#include +#include + +#include "net/tcpserver.h" +#include "core/netclient.h" + +namespace core +{ + +/// Network server +class NetServer : public net::TCPServer +{ +public: + NetServer(std::string const host, unsigned int const port); + virtual ~NetServer(); + + /// run one server frame + void frame(float seconds); + + /// broadcast a message to all clients + void broadcast(std::ostringstream &osstream, int ignorefd=-1); + + /// send a message to a client + void send(NetClient * client, std::ostringstream &osstream); + + /// send a message to a client + void send(NetClient * client, std::string message); + +protected: + /// called by accept() when a new client connects + virtual void client_connect(int const clientfd, std::string const host, int const port); + + /// remove terminated clients + void reap(); + + /// parse incoming client messages + void parse_incoming_message(NetClient *client, const std::string & message); + + /// parse client variable + void parse_client_variable(NetClient * client, const std::string varname, std::istringstream &istringstream); + + std::list clients; + fd_set serverset; + int fdmax; +}; + +} + +#endif // __INCLUDED_CORE_NETSERVER_H__ diff --git a/src/core/player.h b/src/core/player.h index 1f63d63..859eddc 100644 --- a/src/core/player.h +++ b/src/core/player.h @@ -19,6 +19,7 @@ class Player; namespace core { +/// a player in the game class Player { public: @@ -38,6 +39,7 @@ public: EntityControlable *controled; }; +/// the local player, always has id 0 extern Player localplayer; } -- cgit v1.2.3