From 8aa04fc836116a58f8ffd1e0c3539b9ea8a94ddf Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Thu, 21 Feb 2008 19:06:15 +0000 Subject: dedicated server, entity transfer --- src/core/application.cc | 1 + src/core/cvar.cc | 1 + src/core/cvar.h | 1 + src/core/entity.cc | 166 ++++++++++++++++++++++++++++++++++++++++++++- src/core/entity.h | 88 ++++++++++++++++++++++-- src/core/gameconnection.cc | 29 +++++++- src/core/gameconnection.h | 1 + src/core/gameserver.cc | 53 +++++++++++++-- src/core/gameserver.h | 2 +- src/core/netconnection.cc | 50 ++++++++++++++ src/core/netserver.cc | 46 ++++++++++++- src/core/netserver.h | 2 +- src/core/player.cc | 40 ++++++++++- src/core/player.h | 15 +++- 14 files changed, 476 insertions(+), 19 deletions(-) (limited to 'src/core') diff --git a/src/core/application.cc b/src/core/application.cc index a95bd22..b55df34 100644 --- a/src/core/application.cc +++ b/src/core/application.cc @@ -157,6 +157,7 @@ void Application::init() Cvar::sv_dedicated = Cvar::get("sv_dedicated", "0", Cvar::ReadOnly); // client can set this to 1 Cvar::sv_private = Cvar::get("sv_private", "0"); + Cvar::sv_framerate = Cvar::get("sv_framerate", "25"); // network settings Cvar::net_host = Cvar::get("net_host", "0.0.0.0"); diff --git a/src/core/cvar.cc b/src/core/cvar.cc index 9419db3..9ce1fe6 100644 --- a/src/core/cvar.cc +++ b/src/core/cvar.cc @@ -19,6 +19,7 @@ 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; diff --git a/src/core/cvar.h b/src/core/cvar.h index 7350eee..3847a78 100644 --- a/src/core/cvar.h +++ b/src/core/cvar.h @@ -101,6 +101,7 @@ public: static Cvar *sv_dedicated; // dedicated server static Cvar *sv_private; // client with private server + static Cvar *sv_framerate; // server framerate static Cvar *net_host; // network server ip (default binds to all interfaces) static Cvar *net_port; // network port diff --git a/src/core/entity.cc b/src/core/entity.cc index 229fb52..7e9c788 100644 --- a/src/core/entity.cc +++ b/src/core/entity.cc @@ -7,6 +7,11 @@ #include #include +namespace core +{ + class Entity; +} + #include "sys/sys.h" #include "core/entity.h" @@ -31,6 +36,16 @@ void Entity::add(Entity *ent) registry[id] = ent; } +void Entity::add(Entity *ent, unsigned int id) +{ + if (find(id)) { + con_warn << "Duplicate entity " << id << "!\n"; + return; + } + ent->entity_id = id; + registry[id] = ent; +} + Entity *Entity::find(unsigned int id) { std::map::iterator it = registry.find(id); @@ -77,13 +92,78 @@ Entity::Entity(unsigned int flags) : entity_direction = 0; entity_shape = Diamond; + entity_created = true; + entity_destroyed = false; + entity_dirty = false; + add(this); } +Entity::Entity(std::istream & is) +{ + // type is already determined + unsigned int s; + std::string n; + + is >> entity_id; + is >> entity_moduletypeid; + is >> entity_flags; + is >> entity_location; + is >> entity_color; + is >> s; // shape + is >> entity_radius; + is >> entity_direction; + + char c; + while ( (is.get(c)) && (c != '"')); + while ( (is.get(c)) && (c != '"')) + n += c; + + entity_shape = (Shape) s ; + entity_name = n; + + entity_created = true; + entity_destroyed = false; + entity_dirty = false; + + add(this, entity_id); +} + Entity::~Entity() { } +void Entity::serialize(std::ostream & os) const +{ + os << type() << " " + << entity_id << " " + << entity_moduletypeid << " " + << entity_flags << " " + << entity_location << " " + << entity_color << " " + << entity_shape << " " + << entity_radius << " " + << entity_direction << " " + << "\"" << entity_name << "\""; +} + +void Entity::serialize_client_update(std::ostream & os) const +{ +} + +void Entity::recieve_client_update(std::istream &is) +{ +} + +void Entity::serialize_server_update(std::ostream & os) const +{ +} + +void Entity::recieve_server_update(std::istream &is) +{ +} + + void Entity::frame(float seconds) { } @@ -96,15 +176,51 @@ EntityDynamic::EntityDynamic(unsigned int flags) : entity_speed = 0.0f; } +EntityDynamic::EntityDynamic(std::istream & is) : + Entity(is) +{ + is >> entity_speed; +} + EntityDynamic::~EntityDynamic() { } void EntityDynamic::frame(float seconds) { + if (entity_speed == 0) + return; + // location avoid sin/cos calculations entity_location.x += cosf(entity_direction * M_PI / 180) * entity_speed * seconds; entity_location.z -= sinf(entity_direction * M_PI / 180) * entity_speed * seconds; + entity_dirty = true; +} + +void EntityDynamic::serialize(std::ostream & os) const +{ + Entity::serialize(os); + os << " " << entity_speed; +} + +void EntityDynamic::serialize_client_update(std::ostream & os) const +{ +} + +void EntityDynamic::recieve_client_update(std::istream &is) +{ +} + +void EntityDynamic::serialize_server_update(std::ostream & os) const +{ + os << entity_location << " " << entity_direction << " " << entity_speed; +} + +void EntityDynamic::recieve_server_update(std::istream &is) +{ + is >> entity_location; + is >> entity_direction; + is >> entity_speed; } /*----- EntityControlable ------------------------------------------ */ @@ -119,26 +235,74 @@ EntityControlable::EntityControlable(Player *player, unsigned int flags) : target_thrust = 0.0f; } +EntityControlable::EntityControlable(std::istream & is) : + EntityDynamic(is) +{ + unsigned int o; + + is >> entity_thrust; + is >> o; + + // FIXME resolve owner + entity_owner = 0; +} + + EntityControlable::~EntityControlable() { } +void EntityControlable::serialize(std::ostream & os) const +{ + EntityDynamic::serialize(os); + os << " " << entity_thrust; + os << " " << entity_owner->id(); +} + +void EntityControlable::serialize_client_update(std::ostream & os) const +{ + EntityDynamic::serialize_client_update(os); + os << " " << target_direction; + os << " " << target_thrust; +} + +void EntityControlable::recieve_client_update(std::istream &is) +{ + EntityDynamic::recieve_client_update(is); + is >> target_direction; + is >> target_thrust; +} + +void EntityControlable::serialize_server_update(std::ostream & os) const +{ + EntityDynamic::serialize_server_update(os); + os << " " << entity_thrust; +} + +void EntityControlable::recieve_server_update(std::istream &is) +{ + EntityDynamic::recieve_server_update(is); + is >> entity_thrust; +} + void EntityControlable::frame(float seconds) { entity_direction = target_direction; entity_thrust = target_thrust; - + entity_dirty = true; EntityDynamic::frame(seconds); } void EntityControlable::set_thrust(float thrust) { target_thrust = thrust; + entity_dirty = true; } void EntityControlable::set_direction(float direction) { target_direction = direction; + entity_dirty = true; } } diff --git a/src/core/entity.h b/src/core/entity.h index df16075..9c90ef9 100644 --- a/src/core/entity.h +++ b/src/core/entity.h @@ -15,6 +15,7 @@ class EntityControlable; #include "core/player.h" #include "math/mathlib.h" +#include #include #include @@ -29,7 +30,7 @@ public: enum Flags {Static=1, Solid=2}; /// Entity type constants - enum Type {Default = 0, Dynamic = 1, Controlable = 2}; + enum Type {Default=0, Dynamic=1, Controlable=2}; /// Entity shape constants enum Shape {Diamond=0, Sphere=1, Cube=2}; @@ -37,6 +38,9 @@ public: /// create a new entity and add it to the registry Entity(unsigned int flags = 0); + /// create an entity froms stream data + Entity(std::istream & is); + /// destroy an entity virtual ~Entity(); @@ -49,14 +53,17 @@ public: inline unsigned int moduletype() const { return entity_moduletypeid; } /// core type id - virtual inline unsigned int type() { return Default; } + virtual inline unsigned int type() const { return Default; } /// entity flags inline unsigned int flags() const { return entity_flags; } - /// entity name + /// entity name (can not contain double qoutes ") inline std::string const & name() { return entity_name; } + /// dirty flag + inline bool dirty() const { return entity_dirty; } + /// entity location inline math::Vector3f const & location() const { return entity_location; } @@ -72,8 +79,27 @@ public: /// base radius of the entity inline float radius() const { return entity_radius; } + /// serialize the entity to a stream + virtual void serialize(std::ostream & os) const; + + /// serialize a client-to-server update on a stream + virtual void serialize_client_update(std::ostream & os) const; + + /// serialize a server-to-client update on a stream + virtual void serialize_server_update(std::ostream & os) const; + + /*----- mutators -------------------------------------------------- */ + /// receive a client-to-server update from a stream + virtual void recieve_client_update(std::istream &is); + + /// receive a server-to-client update from a stream + virtual void recieve_server_update(std::istream &is); + + /// mark the entity as destroyed + inline void die() { entity_destroyed = true; } + /// runs one game frame for the entity /** * The default implementation does nothing @@ -108,30 +134,59 @@ public: unsigned int entity_moduletypeid; unsigned int entity_flags; + bool entity_dirty; + bool entity_created; + bool entity_destroyed; + private: /// add an entity to the registry static void add(Entity *ent); + /// add an entity with id to the registry + void add(Entity *ent, unsigned int id); + /// the id is set by add() unsigned int entity_id; }; + /// an entity that can move around in the game world class EntityDynamic : public Entity { public: + /// create a dynamic entity EntityDynamic(unsigned int flags = 0); + + /// create a dynamic entity from stream data + EntityDynamic(std::istream & is); + virtual ~EntityDynamic(); + /*----- inspectors ------------------------------------------------ */ /// core type id - virtual inline unsigned int type() { return Entity::Dynamic; } + virtual inline unsigned int type() const { return Entity::Dynamic; } /// current speed of the entity in game units per second inline float speed() const { return entity_speed; } + /// serialize the entity to a stream + virtual void serialize(std::ostream & os) const; + + /// serialize a client-to-server update on a stream + virtual void serialize_client_update(std::ostream & os) const ; + + /// serialize a server-to-client update on a stream + virtual void serialize_server_update(std::ostream & os) const; + /*----- mutators -------------------------------------------------- */ + /// receive a client-to-server update from a stream + virtual void recieve_client_update(std::istream &is); + + /// receive a server-to-client update from a stream + virtual void recieve_server_update(std::istream &is); + /// runs one game frame for the entity /** * The default implementation will update the position() of the entity, @@ -147,14 +202,19 @@ public: class EntityControlable : public EntityDynamic { public: - /// create + /// create a controlable entity EntityControlable(Player *player, unsigned int flags = 0); + + /// create a controlable entity from stream data + EntityControlable(std::istream & is); + virtual ~EntityControlable(); + /*----- inspectors ------------------------------------------------ */ /// core type id - virtual inline unsigned int type() { return Entity::Controlable; } + virtual inline unsigned int type() const { return Entity::Controlable; } /// owner of this entity inline Player *owner() const { return entity_owner; } @@ -162,8 +222,24 @@ public: /// thrust inline float thrust() const { return entity_thrust; } + /// serialize the entity to a stream + virtual void serialize(std::ostream & os) const; + + /// serialize a client-to-server update on a stream + virtual void serialize_client_update(std::ostream & os) const; + + /// serialize a server-to-client update on a stream + virtual void serialize_server_update(std::ostream & os) const; + + /*----- mutators -------------------------------------------------- */ + /// receive a client-to-server update from a stream + virtual void recieve_client_update(std::istream &is); + + /// receive a server-to-client update from a stream + virtual void recieve_server_update(std::istream &is); + /// set the target thrust void set_thrust(float t); diff --git a/src/core/gameconnection.cc b/src/core/gameconnection.cc index 43df1be..eeb4692 100644 --- a/src/core/gameconnection.cc +++ b/src/core/gameconnection.cc @@ -9,6 +9,7 @@ #include "sys/sys.h" #include "net/net.h" +#include "core/cvar.h" #include "core/gameconnection.h" namespace core @@ -46,6 +47,7 @@ GameConnection::GameConnection(std::string const &connectionstr) return; } + connection_frametime = 0; connection_running = true; } @@ -86,7 +88,32 @@ void GameConnection::frame(float seconds) return; } - connection_network->frame(seconds); + + float f = 0; + if (core::Cvar::sv_framerate->value()) { + connection_frametime += seconds; + f = 1.0f / core::Cvar::sv_framerate->value(); + if (connection_frametime < f) + return; + } else { + connection_frametime = seconds; + } + + connection_network->frame(connection_frametime); + + if (localplayer()->control && localplayer()->control->dirty()) { + std::ostringstream netmsg; + netmsg << "cup " << localplayer()->control->id() << " "; + localplayer()->control->serialize_client_update(netmsg); + netmsg << "\n"; + + connection_network->send(netmsg.str()); + localplayer()->control->entity_dirty = false; + //con_debug << netmsg.str(); + } + + connection_frametime += f; + } } diff --git a/src/core/gameconnection.h b/src/core/gameconnection.h index c91ef22..65fd495 100644 --- a/src/core/gameconnection.h +++ b/src/core/gameconnection.h @@ -49,6 +49,7 @@ private: bool connection_running; static GameConnection *connection_instance; NetConnection *connection_network; + float connection_frametime; }; diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc index e5795ad..8ddd824 100644 --- a/src/core/gameserver.cc +++ b/src/core/gameserver.cc @@ -168,7 +168,7 @@ void GameServer::exec(Player *player, std::string const & cmdline) std::string message("Unknown command '"); message.append(command); - message.append("'\n"); + message.append("'"); send(player, message); } @@ -178,8 +178,6 @@ void GameServer::player_connect(Player *player) message.append(" connects."); broadcast(message, player->id()); - // TODO transferplayer info, transfer entities - // notify the game module server_module->player_connect(player); } @@ -208,15 +206,17 @@ void GameServer::frame(float seconds) } } - // update entities + // run a time frame on each entity std::map::iterator it; for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { Entity *entity = (*it).second; + if ((entity->type() == Entity::Controlable) || (entity->type() == Entity::Dynamic)) { entity->frame(seconds); } } + // run a frame on the module if (server_module) { server_module->frame(seconds); if (server_module->error()) { @@ -224,6 +224,51 @@ void GameServer::frame(float seconds) return; } } + + // send updates + if (server_network) { + std::map::iterator it; + for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { + Entity *entity = (*it).second; + if (entity->entity_destroyed) { + if (!entity->entity_created) { + std::ostringstream netmsg; + netmsg << "die " << entity->id() << "\n"; + server_network->broadcast(netmsg.str()); + } + core::Entity::remove(entity->id()); + } else if (entity->entity_created) { + std::ostringstream netmsg; + netmsg << "ent "; + entity->serialize(netmsg); + netmsg << "\n"; + server_network->broadcast(netmsg.str()); + entity->entity_created = false; + + } else if (entity->dirty()) { + std::ostringstream netmsg; + netmsg << "sup " << entity->id() << " "; + entity->serialize_server_update(netmsg); + netmsg << "\n"; + server_network->broadcast(netmsg.str()); + } + entity->entity_dirty = false; + } + + for (std::list::iterator it = server_network->clients.begin(); it != server_network->clients.end(); it++) { + NetClient *client = *it; + if (client->player()->dirty()) { + // send player data + std::ostringstream netmsg; + netmsg.str(""); + netmsg << "pif "; + client->player()->serialize_server_update(netmsg); + netmsg << "\n"; + client->send(netmsg.str()); + client->player()->player_dirty = false; + } + } + } } diff --git a/src/core/gameserver.h b/src/core/gameserver.h index d4bd0eb..17d581c 100644 --- a/src/core/gameserver.h +++ b/src/core/gameserver.h @@ -37,7 +37,7 @@ public: /// is called when a player connects to the game server void player_connect(Player *player); - /// a caleld when a player disconnects from the game server + /// is called when a player disconnects from the game server void player_disconnect(Player *player); /// run a game server time frame diff --git a/src/core/netconnection.cc b/src/core/netconnection.cc index 63c906a..4770528 100644 --- a/src/core/netconnection.cc +++ b/src/core/netconnection.cc @@ -9,6 +9,7 @@ #include "sys/sys.h" #include "net/net.h" #include "core/application.h" +#include "core/gameconnection.h" #include "core/netconnection.h" #include "core/player.h" @@ -119,6 +120,10 @@ void NetConnection::frame(float seconds) * * msg info * msg public + * die + * ent + * sup + * pif */ void NetConnection::parse_incoming_message(const std::string & message) { @@ -142,6 +147,51 @@ void NetConnection::parse_incoming_message(const std::string & message) } } + } else if (command == "die") { + unsigned int id; + msgstream >> id; + + Entity *e = Entity::find(id); + con_debug << "Received die entity id " << id << "\n"; + + if (game()->localplayer()->control == e) + game()->localplayer()-> control = 0; + if (e) + Entity::remove(id); + + } else if (command == "ent") { + unsigned int type; + msgstream >> type; + + con_debug << "Received create entity type " << type << "\n"; + switch (type) + { + case Entity::Default: + new Entity(msgstream); + break; + case Entity::Dynamic: + new EntityDynamic(msgstream); + break; + case Entity::Controlable: + new EntityControlable(msgstream); + break; + default: + break; + } + + } else if (command == "sup") { + unsigned int id; + if (msgstream >> id) { + Entity *entity = Entity::find(id); + if (!entity) { + con_warn << "Update for unknown entity " << id << "\n"; + } else + entity->recieve_server_update(msgstream); + } + return; + + } else if (command == "pif") { + connection()->localplayer()->recieve_server_update(msgstream); } } diff --git a/src/core/netserver.cc b/src/core/netserver.cc index 3473e6a..43067d7 100644 --- a/src/core/netserver.cc +++ b/src/core/netserver.cc @@ -58,6 +58,29 @@ void NetServer::client_connect(int const clientfd, std::string const host, int c con_print << client->host() << ":" << client->port() << " connected.\n"; + std::ostringstream netmsg; + + // send entities + std::map::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()); @@ -169,6 +192,9 @@ NetClient *NetServer::find_client(Player const *player) */ void NetServer::parse_incoming_message(NetClient *client, const std::string & message) { + if (!message.size()) + return; + std::stringstream msgstream(message); std::string command; @@ -189,6 +215,24 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me return; } + // cup - client update entity + if (command == "cup") { + //con_debug << message << "\n"; + unsigned int id; + if (msgstream >> id) { + Entity *entity = Entity::find(id); + if (!entity) { + con_warn << client->host() << ":" << client->port() << " update for unknown entity " << id << "\n"; + + } else { + entity->entity_dirty = true; + entity->recieve_client_update(msgstream); + } + + } + return; + } + // say if (command == "say") { if (message.size() > command.size()+1) { @@ -200,7 +244,7 @@ void NetServer::parse_incoming_message(NetClient *client, const std::string & me return; } - // name is an alias for set name + // name if (command == "name") { std::string name; if (msgstream >> name) { diff --git a/src/core/netserver.h b/src/core/netserver.h index dd44285..4de87be 100644 --- a/src/core/netserver.h +++ b/src/core/netserver.h @@ -37,6 +37,7 @@ public: /// find the client corresponding to a player NetClient *find_client(Player const *player); + std::list clients; protected: /// called by accept() when a new client connects virtual void client_connect(int const clientfd, std::string const host, int const port); @@ -47,7 +48,6 @@ protected: /// parse incoming client messages void parse_incoming_message(NetClient *client, const std::string & message); - std::list clients; fd_set serverset; int fdmax; }; diff --git a/src/core/player.cc b/src/core/player.cc index 8fc4ebf..f4ee6ea 100644 --- a/src/core/player.cc +++ b/src/core/player.cc @@ -4,6 +4,7 @@ the terms of the GNU General Public License version 2. */ +#include "sys/sys.h" #include "core/player.h" namespace core @@ -23,9 +24,46 @@ void Player::clear() { player_id = 0; player_name.clear(); - dirty = false; + player_dirty = false; control = 0; } +void Player::serialize_server_update(std::ostream & os) const +{ + unsigned int co; + if (control) + co = control->id(); + else + co = 0; + + os << player_id << " " << co << " \"" << player_name << "\""; +} + +void Player::recieve_server_update(std::istream &is) +{ + is >> player_id; + unsigned int co = 0; + is >> co; + if (co) { + Entity *e = Entity::find(co); + if (e && e->type() == Entity::Controlable) { + control = (EntityControlable *) e; + } else { + control = 0; + con_warn << "control set to unknown entity " << co << "\n"; + } + } else { + control = 0; + } + + std::string n; + char c; + while ( (is.get(c)) && (c != '"')); + while ( (is.get(c)) && (c != '"')) + n += c; + + player_name = n; +} + } diff --git a/src/core/player.h b/src/core/player.h index 11711b5..fd4053c 100644 --- a/src/core/player.h +++ b/src/core/player.h @@ -35,17 +35,26 @@ public: /// id of the player inline int id() const { return player_id; } + /// dirty flag + inline bool dirty() const { return player_dirty; } + /// id of the player int player_id; /// name of the player std::string player_name; - /// dirty state - bool dirty; - /// the entity the Player is currently controling EntityControlable *control; + + /// serialize player info to a stream + void serialize_server_update(std::ostream & os) const; + + /// receive player info from a stream + void recieve_server_update(std::istream &is); + + /// dirty state + bool player_dirty; }; } -- cgit v1.2.3