From 83c9d657773fa4f829b533791697ed07e0d9d962 Mon Sep 17 00:00:00 2001 From: Stijn Buys Date: Mon, 11 Jul 2011 19:33:27 +0000 Subject: Initial support for saving player data in multiplayer games, have ships remember their docks and spawns. --- src/core/gameconnection.cc | 3 +- src/core/gameserver.cc | 23 ++++-- src/core/gameserver.h | 10 ++- src/core/module.cc | 8 ++ src/core/module.h | 6 ++ src/game/base/game.cc | 195 ++++++++++++++++++++++++++++++++++++--------- src/game/base/game.h | 11 ++- src/game/base/ship.cc | 51 ++++++++++++ src/game/base/ship.h | 29 +++++++ 9 files changed, 288 insertions(+), 48 deletions(-) (limited to 'src') diff --git a/src/core/gameconnection.cc b/src/core/gameconnection.cc index ddf4610..70d19ef 100644 --- a/src/core/gameconnection.cc +++ b/src/core/gameconnection.cc @@ -64,7 +64,8 @@ GameConnection::GameConnection(std::string const &connectionstr) if (!ofs.is_open()) { con_warn << "Could not write " << filename << std::endl; } else { - ofs << "; keys.ini - osirion client identification" << std::endl; + ofs << "; keys.ini" << std::endl; + ofs << "; Project::OSiRiON client identification" << std::endl; ofs << "; DO NOT EDIT OR DELETE THIS FILE" << std::endl; ofs << "; If you do, you will not be able to use existing characters in multiplayer games" << std::endl; ofs << std::endl; diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc index a1102bd..96dd51e 100644 --- a/src/core/gameserver.cc +++ b/src/core/gameserver.cc @@ -152,11 +152,17 @@ GameServer::GameServer() : GameInterface() server_previoustime = 0; server_maxplayerid = 1; + if (Cvar::sv_dedicated->value() || Cvar::sv_private->value()) { + server_mode = MultiPlayer; + } else { + server_mode = SinglePlayer; + } + // create the default infotype for entities Entity::set_infotype(new InfoType("entity")); Physics::init(); - + server_module = Loader::init(); if (!server_module) { @@ -173,9 +179,12 @@ GameServer::GameServer() : GameInterface() set_interactive(server_module->interactive()); + if (interactive()) { //FIXME interferes with command line because of cmd.exec load_config(); + } else { + server_mode = SinglePlayer; } // set the name of the game @@ -184,7 +193,7 @@ GameServer::GameServer() : GameInterface() con_print << " module '^B" << server_module->name() << "^N'\n"; - if (interactive() && (Cvar::sv_dedicated->value() || Cvar::sv_private->value())) { + if (mode() == MultiPlayer) { server_network = new NetServer(Cvar::net_host->str(), (unsigned int) Cvar::net_port->value()); if (!server_network->valid()) { delete server_network; @@ -194,13 +203,14 @@ GameServer::GameServer() : GameInterface() return; } } else { - con_print << " network server disabled.\n"; server_network = 0; } Func *func = 0; /* -- admin functions -- */ + // FIXME these have to become shared functions and check the player's admin level + func = Func::add("mute", func_mute); func->set_info("[player] mute a player"); @@ -527,6 +537,10 @@ void GameServer::player_connect(Player *player) void GameServer::player_disconnect(Player *player) { + // notify the game module + server_module->player_disconnect(player); + + // print a message std::string message("^B"); message.append(player->name()); message.append("^B disconnects."); @@ -535,9 +549,6 @@ void GameServer::player_disconnect(Player *player) // clear all player assets player->clear_assets(); - // notify the game module - server_module->player_disconnect(player); - // manage player list std::list:: iterator it = game_players.begin(); while ((it != game_players.end()) && ((*it)->id() != player->id())) { diff --git a/src/core/gameserver.h b/src/core/gameserver.h index 56d0361..4f25a9b 100644 --- a/src/core/gameserver.h +++ b/src/core/gameserver.h @@ -23,13 +23,15 @@ namespace core class GameServer : public GameInterface { public: + enum Mode {SinglePlayer = 1, MultiPlayer = 2}; + GameServer(); virtual ~GameServer(); /*----- inspectors ------------------------------------------------ */ /// current module - inline const Module *module() const { + inline Module *module() { return server_module; } @@ -88,6 +90,10 @@ public: /// request inventory for entity with id virtual Inventory *request_inventory(Entity *entity); + + inline Mode mode() const { + return server_mode; + } /*----- static ---------------------------------------------------- */ @@ -108,6 +114,8 @@ private: unsigned long server_previoustime; sys::Timer server_timer; + + Mode server_mode; }; inline GameServer *server() diff --git a/src/core/module.cc b/src/core/module.cc index 6271331..18c318e 100644 --- a/src/core/module.cc +++ b/src/core/module.cc @@ -44,4 +44,12 @@ void Module::abort() module_running = false; } +void Module::player_load(Player *player) +{ +} + +void Module::player_save(Player *player) +{ +} + } diff --git a/src/core/module.h b/src/core/module.h index ac6b380..51b1824 100644 --- a/src/core/module.h +++ b/src/core/module.h @@ -56,6 +56,12 @@ public: /// is called when a player disconnects virtual void player_disconnect(Player *player) = 0; + + /// is called when player data needs to be saved + virtual void player_save(Player *player); + + /// is called when player data needs to be loaded + virtual void player_load(Player *player); /// set the module label void set_label(const std::string &label); diff --git a/src/game/base/game.cc b/src/game/base/game.cc index 4bc94f0..f954c53 100644 --- a/src/game/base/game.cc +++ b/src/game/base/game.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include "auxiliary/functions.h" @@ -68,28 +69,30 @@ void Game::func_join(core::Player *player, std::string const &args) if (player->control()) return; - player->set_credits(Default::credits); - Ship *ship = new Ship(player, Default::shipmodel); - ship->set_zone(player->zone()); - player->set_control(ship); - - core::Entity *dock = ship->zone()->default_view(); - if (dock) { - ship->get_location().assign(dock->location() + (dock->axis().forward() *((ship->radius() + dock->radius())*2.0f))); - ship->get_axis().assign(dock->axis()); - ship->set_state(core::Entity::Docked); - ship->reset(); - player->set_view(dock); - } - std::string message("^B"); message.append(player->name()); message.append("^B joins the game."); core::server()->broadcast(message); - player->send("^BYou received " + aux::article(Default::shipmodel->name())); - player->sound("game/buy-ship"); + // load existing player data + core::server()->module()->player_load(player); + + if (!player->control()) { + player->set_credits(Default::credits); + Ship *ship = new Ship(player, Default::shipmodel); + ship->set_zone(player->zone()); + player->set_control(ship); + core::Entity *dock = ship->zone()->default_view(); + if (dock) { + ship->set_dock(dock); + player->set_view(dock); + } + + player->send("^BYou received " + aux::article(Default::shipmodel->name())); + player->sound("game/buy-ship"); + } + player->set_dirty(); } @@ -98,6 +101,8 @@ void Game::func_spectate(core::Player *player, std::string const &args) { if (!player->control()) return; + + core::server()->module()->player_save(player); std::string message("^B"); message.append(player->name()); @@ -194,10 +199,8 @@ void Game::func_target_dock(core::Player *player, core::Entity *entity) RaceTrack *race = static_cast(entity); race->func_dock(ship); } else { - ship->get_location().assign(entity->location()); - ship->set_state(core::Entity::Docked); - ship->reset(); - + ship->set_dock(entity); + if (player->control() == ship) { player->set_view(entity); if (owner) { @@ -205,7 +208,7 @@ void Game::func_target_dock(core::Player *player, core::Entity *entity) } else { player->send("^BDocking at " + entity->name()); } - } + } } } @@ -760,12 +763,13 @@ void Game::func_launch(core::Player *player, std::string const &args) return; assert(player->view()->zone() == player->control()->zone()); + assert(player->control()->moduletype() == ship_enttype); + + Ship *ship = static_cast(player->control()); - core::Entity *dock = player->view(); - - if (dock->moduletype() == ship_enttype) { + if (ship->dock()->moduletype() == ship_enttype) { - switch(static_cast(dock)->state()) { + switch(static_cast(ship->dock())->state()) { case core::Entity::Normal: case core::Entity::Docked: @@ -795,21 +799,13 @@ void Game::func_launch(core::Player *player, std::string const &args) } } - assert(player->control()->moduletype() == ship_enttype); - - Ship *ship = static_cast(player->control()); + player->send("^BLaunching from " + ship->dock()->name()); + + ship->launch(); - if (dock->type() == core::Entity::Globe) - ship->get_location().assign(dock->location() + (dock->axis().forward() * (planet_safe_distance + ship->radius() + dock->radius()))); - else - ship->get_location().assign(dock->location() + (dock->axis().forward() * (ship->radius() + dock->radius()))); - - ship->get_axis().assign(dock->axis()); - ship->set_state(core::Entity::Normal); - ship->reset(); player->set_view(0); - player->send("^BLaunching from " + dock->name()); + } // respawn @@ -1695,12 +1691,135 @@ void Game::player_connect(core::Player *player) player->set_zone(Default::zone); player->set_view(0); // not docked + // FIXME load player func_spectate(player, args); } void Game::player_disconnect(core::Player *player) { - player->remove_asset(player->control()); + if (player->control()) { + core::server()->module()->player_save(player); + } } +void Game::player_load(core::Player *player) +{ + if (!player->guid().is_valid()) { + return; + } + + if (player->control()) + return; + + if (core::server()->mode() == core::GameServer::MultiPlayer) { + std::string guid(player->guid().str()); + std::string directory(guid.substr(0,4)); + + std::string filename(filesystem::writedir()); + // create players/ directory + filename.append("players"); + filename += '/'; + // second level directory + filename.append(directory); + filename += '/'; + // guid.ini + filename.append(guid); + filename.append(".ini"); + } + +} + +void Game::player_save(core::Player *player) +{ + if (!player->guid().is_valid()) { + return;} + + if (core::server()->mode() == core::GameServer::MultiPlayer) { + std::string guid(player->guid().str()); + std::string directory(guid.substr(0,4)); + + std::string filename(filesystem::writedir()); + // create players/ directory + filename.append("players"); + if (!sys::directory_exists(filename)) { + sys::mkdir(filename); + } + filename += '/'; + // second level directory + filename.append(directory); + if (!sys::directory_exists(filename)) { + sys::mkdir(filename); + } + filename += '/'; + // guid.ini + filename.append(guid); + filename.append(".ini"); + + std::ofstream ofs(filename.c_str()); + if (ofs.is_open()) { + // *--- HEADER + ofs << "; " << guid << ".ini" << std::endl; + ofs << "; Project::OSiRiON player data" << std::endl; + ofs << std::endl; + + // *--- PLAYER + ofs << "[player]" << std::endl; + + // player name + ofs << "name=" << player->name() << std::endl; + + // credit + ofs << "credits=" << player->credits() << std::endl; + + // current zone + ofs << "zone="; + if (player->zone()) { + ofs << player->zone()->label(); + } + ofs << std::endl; + + // *--- SHIP + // FIXME special case: docked at another player's vessel + ofs << std::endl; + if ((player->control()) && (player->control()->moduletype() == ship_enttype)) { + Ship * ship = static_cast(player->control()); + ofs << "[ship]" << std::endl; + ofs << "model=" << ship->shipmodel()->label() << std::endl; + // ship zone + ofs << "zone=" << ship->zone()->label()<< std::endl; + // ship location + ofs << "location=" << ship->location() << std::endl; + // ship orientation + ofs << "foward=" << std::setprecision(8) << ship->axis().forward() << std::endl; + ofs << "left=" << std::setprecision(8) << ship->axis().left() << std::endl; + // ship dock + ofs << "dock="; + if (ship->dock() && (ship->dock() == ship->spawn())) { + ofs << ship->dock()->zone()->label() << ":" << ship->dock()->label(); + + } else if (ship->state() == core::Entity::Destroyed) { + if (ship->spawn()) { + ofs << ship->spawn()->zone()->label() << ":" << ship->spawn()->label(); + } + } + ofs << std::endl; + // ship spawn + ofs << "spawn="; + if (ship->spawn()) { + ofs << ship->spawn()->zone()->label() << ":" << ship->spawn()->label(); + } + ofs << std::endl; + + // TODO inventory + } + + ofs.close(); + + con_debug << "Saving data for player id " << player->id() << std::endl; + } else { + con_warn << "Could not write " << filename << std::endl; + } + } +} + } // namespace game diff --git a/src/game/base/game.h b/src/game/base/game.h index 27ba31b..945671c 100644 --- a/src/game/base/game.h +++ b/src/game/base/game.h @@ -68,10 +68,17 @@ public: void frame(float seconds); /// is called when a player connects - void player_connect(core::Player *player); + virtual void player_connect(core::Player *player); /// is called when a player disconnects - void player_disconnect(core::Player *player); + virtual void player_disconnect(core::Player *player); + + /// load a player data + virtual void player_load(core::Player *player); + + /// save player data + virtual void player_save(core::Player *player); + /* --- game variables -------------------------------------- */ diff --git a/src/game/base/ship.cc b/src/game/base/ship.cc index a18616e..0bc1b7c 100644 --- a/src/game/base/ship.cc +++ b/src/game/base/ship.cc @@ -38,6 +38,9 @@ Ship::Ship(core::Player *owner, const ShipModel *shipmodel) : core::EntityContro ship_jumpdrive_timer = 0; ship_jumpdepart = 0; + ship_dock = 0; + ship_spawn = 0; + current_impulse = false; // apply ship type settings @@ -289,6 +292,54 @@ void Ship::set_zone(core::Zone *zone) owner()->set_zone(zone); } +void Ship::set_state(int state) +{ + EntityControlable::set_state(state); + + if (state != core::Entity::Docked) + ship_dock = 0; +} +void Ship::set_dock(core::Entity *dock) +{ + if (!dock) + return; + + ship_dock = dock; + get_location().assign(dock->location()); + get_axis().assign(dock->axis()); + set_state(core::Entity::Docked); + + // if the dock is not owned by a player. set it as spawn + const core::Player *owner = (dock->type() == core::Entity::Controlable ? static_cast(dock)->owner() : 0 ); + if (!owner) + set_spawn(dock); + reset(); +} + +void Ship::launch() +{ + if (!ship_dock) + return; + + get_axis().assign(ship_dock->axis()); + + if (ship_dock->type() == core::Entity::Globe) + get_location().assign(ship_dock->location() + (ship_dock->axis().forward() * (planet_safe_distance + this->radius() + ship_dock->radius()))); + else + get_location().assign(ship_dock->location() + (ship_dock->axis().forward() * (this->radius() + ship_dock->radius()))); + + set_state(core::Entity::Normal); + + ship_dock = 0; + + reset(); +} + +void Ship::set_spawn(core::Entity *spawn) +{ + ship_spawn = spawn; +} + void Ship::action (btScalar seconds) { //float maxspeed = 0; diff --git a/src/game/base/ship.h b/src/game/base/ship.h index 37dae0d..f705231 100644 --- a/src/game/base/ship.h +++ b/src/game/base/ship.h @@ -23,6 +23,8 @@ public: Ship(core::Player *owner, const ShipModel *shipmodel); ~Ship(); + /* -- inspectors ------------------------------------------- */ + /// shipmodel inline const ShipModel *shipmodel() const { return ship_shipmodel; @@ -52,7 +54,19 @@ public: inline const float roll_force() const { return ship_roll_force; } + + /// entity the ship is currently docked at + inline core::Entity *dock() { + return ship_dock; + } + + /// (dockable) entity where the ship will respawn if destroyed + core::Entity *spawn() { + return ship_spawn; + } + /* -- mutators --------------------------------------------- */ + /// physics frame virtual void action (btScalar seconds); @@ -112,6 +126,17 @@ public: /// toggle jump drive activation void func_jump(std::string const & args); + + virtual void set_state(int state); + /** + * @brief dock the ship at a station, planet or another player's ship_dock + * This will set the ship's state to Entity::Docked and reset spawn if required + */ + void set_dock(core::Entity *dock); + + void launch(); + + void set_spawn(core::Entity *spawn); private: JumpPoint *find_closest_jumppoint(); @@ -138,6 +163,10 @@ private: float ship_strafe_force; float ship_turn_force; float ship_roll_force; + + core::Entity *ship_dock; + + core::Entity *ship_spawn; }; } -- cgit v1.2.3