From 7e6db1b80344a519e53a9967a159c8d3585a9b9d Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Fri, 30 May 2014 16:04:07 -0400 Subject: [PATCH] Only keep players loaded while they're connected --- src/content_sao.cpp | 2 + src/environment.cpp | 245 +++++++++++++------------------------------- src/environment.h | 18 ++-- src/player.cpp | 67 ++++++++++++ src/player.h | 2 + src/server.cpp | 103 ++++++------------- 6 files changed, 188 insertions(+), 249 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 095c6b5bf..4ee92f4d3 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -1027,6 +1027,8 @@ void PlayerSAO::removingFromEnvironment() { m_player->setPlayerSAO(NULL); m_player->peer_id = 0; + m_env->savePlayer(m_player->getName()); + m_env->removePlayer(m_player->getName()); } } diff --git a/src/environment.cpp b/src/environment.cpp index 6bbc715d0..91f5ea2b6 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -100,6 +100,18 @@ void Environment::removePlayer(u16 peer_id) } } +void Environment::removePlayer(const char *name) +{ + for (std::list::iterator it = m_players.begin(); + it != m_players.end(); ++it) { + if (strcmp((*it)->getName(), name) == 0) { + delete *it; + m_players.erase(it); + return; + } + } +} + Player * Environment::getPlayer(u16 peer_id) { for(std::list::iterator i = m_players.begin(); @@ -332,10 +344,12 @@ void ActiveBlockList::update(std::list &active_positions, */ ServerEnvironment::ServerEnvironment(ServerMap *map, - GameScripting *scriptIface, IGameDef *gamedef): + GameScripting *scriptIface, IGameDef *gamedef, + const std::string &path_world) : m_map(map), m_script(scriptIface), m_gamedef(gamedef), + m_path_world(path_world), m_send_recommended_timer(0), m_active_block_interval_overload_skip(0), m_game_time(0), @@ -401,196 +415,85 @@ bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 return true; } -void ServerEnvironment::serializePlayers(const std::string &savedir) +void ServerEnvironment::saveLoadedPlayers() { - std::string players_path = savedir + "/players"; + std::string players_path = m_path_world + DIR_DELIM "players"; fs::CreateDir(players_path); - std::set saved_players; + for (std::list::iterator it = m_players.begin(); + it != m_players.end(); + ++it) { + RemotePlayer *player = static_cast(*it); + if (player->checkModified()) { + player->save(players_path); + } + } +} + +void ServerEnvironment::savePlayer(const std::string &playername) +{ + std::string players_path = m_path_world + DIR_DELIM "players"; + fs::CreateDir(players_path); + + RemotePlayer *player = static_cast(getPlayer(playername.c_str())); + if (player) { + player->save(players_path); + } +} + +Player *ServerEnvironment::loadPlayer(const std::string &playername) +{ + std::string players_path = m_path_world + DIR_DELIM "players"; + + RemotePlayer *player = static_cast(getPlayer(playername.c_str())); + bool newplayer = false; + bool foundplayer = false; + if (!player) { + player = new RemotePlayer(m_gamedef); + newplayer = true; + } std::vector player_files = fs::GetDirListing(players_path); - for(u32 i=0; ideSerialize(is, player_files[i].name); - //infostream<<"Loaded test player with name "<getName(), PLAYERNAME_ALLOWED_CHARS)) { + infostream << "Not loading player with invalid name: " + << player->getName() << std::endl; continue; } - //infostream<<"Found matching player, overwriting."<checkModified()) - { - // Open file and serialize - std::ostringstream ss(std::ios_base::binary); - player->serialize(ss); - if(!fs::safeWriteToFile(path, ss.str())) - { - infostream<<"Failed to write "<getName() == playername) { + // We found our player + foundplayer = true; + break; } + } - - for(std::list::iterator i = m_players.begin(); - i != m_players.end(); ++i) - { - Player *player = *i; - if(saved_players.find(player) != saved_players.end()) - { - /*infostream<<"Player "<getName() - <<" was already saved."<getName(); - // Don't save unnamed player - if(playername == "") - { - //infostream<<"Not saving unnamed player."<getName()<<" to " - <serialize(ss); - if(!fs::safeWriteToFile(path, ss.str())) - { - infostream<<"Failed to write "< player_files = fs::GetDirListing(players_path); - for(u32 i=0; ideSerialize(is, player_files[i].name); - } - - if(newplayer) - { - addPlayer(player); - } - } -} - -void ServerEnvironment::saveMeta(const std::string &savedir) -{ - std::string path = savedir + "/env_meta.txt"; + std::string path = m_path_world + DIR_DELIM "env_meta.txt"; // Open file and serialize std::ostringstream ss(std::ios_base::binary); @@ -609,9 +512,9 @@ void ServerEnvironment::saveMeta(const std::string &savedir) } } -void ServerEnvironment::loadMeta(const std::string &savedir) +void ServerEnvironment::loadMeta() { - std::string path = savedir + "/env_meta.txt"; + std::string path = m_path_world + DIR_DELIM "env_meta.txt"; // Open file and deserialize std::ifstream is(path.c_str(), std::ios_base::binary); diff --git a/src/environment.h b/src/environment.h index a59bbd9a1..e8ae044e3 100644 --- a/src/environment.h +++ b/src/environment.h @@ -70,6 +70,7 @@ public: virtual void addPlayer(Player *player); void removePlayer(u16 peer_id); + void removePlayer(const char *name); Player * getPlayer(u16 peer_id); Player * getPlayer(const char *name); Player * getRandomConnectedPlayer(); @@ -199,7 +200,7 @@ class ServerEnvironment : public Environment { public: ServerEnvironment(ServerMap *map, GameScripting *scriptIface, - IGameDef *gamedef); + IGameDef *gamedef, const std::string &path_world); ~ServerEnvironment(); Map & getMap(); @@ -216,17 +217,16 @@ public: float getSendRecommendedInterval() { return m_recommended_send_interval; } - /* - Save players - */ - void serializePlayers(const std::string &savedir); - void deSerializePlayers(const std::string &savedir); + // Save players + void saveLoadedPlayers(); + void savePlayer(const std::string &playername); + Player *loadPlayer(const std::string &playername); /* Save and load time of day and game timer */ - void saveMeta(const std::string &savedir); - void loadMeta(const std::string &savedir); + void saveMeta(); + void loadMeta(); /* External ActiveObject interface @@ -368,6 +368,8 @@ private: GameScripting* m_script; // Game definition IGameDef *m_gamedef; + // World path + const std::string m_path_world; // Active object list std::map m_active_objects; // Outgoing network message buffer for active objects diff --git a/src/player.cpp b/src/player.cpp index 4dadf26d0..78ba17e89 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -283,6 +283,72 @@ void Player::clearHud() } } + +void RemotePlayer::save(const std::string &savedir) +{ + bool newplayer = true; + + /* We have to iterate through all files in the players directory + * and check their player names because some file systems are not + * case-sensitive and player names are case-sensitive. + */ + + // A player to deserialize files into to check their names + RemotePlayer testplayer(m_gamedef); + + std::vector player_files = fs::GetDirListing(savedir); + for(u32 i = 0; i < player_files.size(); i++) { + if (player_files[i].dir || player_files[i].name[0] == '.') { + continue; + } + + // Full path to this file + std::string path = savedir + "/" + player_files[i].name; + + // Open file and deserialize + std::ifstream is(path.c_str(), std::ios_base::binary); + if (!is.good()) { + infostream << "Failed to read " << path << std::endl; + continue; + } + testplayer.deSerialize(is, player_files[i].name); + + if (strcmp(testplayer.getName(), m_name) == 0) { + // Open file and serialize + std::ostringstream ss(std::ios_base::binary); + serialize(ss); + if (!fs::safeWriteToFile(path, ss.str())) { + infostream << "Failed to write " << path << std::endl; + } + newplayer = false; + break; + } + } + + if (newplayer) { + bool found = false; + std::string path = savedir + "/" + m_name; + for (u32 i = 0; i < 1000; i++) { + if (!fs::PathExists(path)) { + found = true; + break; + } + path = savedir + "/" + m_name + itos(i); + } + if (!found) { + infostream << "Didn't find free file for player " << m_name << std::endl; + return; + } + + // Open file and serialize + std::ostringstream ss(std::ios_base::binary); + serialize(ss); + if (!fs::safeWriteToFile(path, ss.str())) { + infostream << "Failed to write " << path << std::endl; + } + } +} + /* RemotePlayer */ @@ -292,3 +358,4 @@ void RemotePlayer::setPosition(const v3f &position) if(m_sao) m_sao->setBasePosition(position); } + diff --git a/src/player.h b/src/player.h index 2b04a310c..098a4d153 100644 --- a/src/player.h +++ b/src/player.h @@ -335,6 +335,8 @@ public: RemotePlayer(IGameDef *gamedef): Player(gamedef), m_sao(0) {} virtual ~RemotePlayer() {} + void save(const std::string &savedir); + PlayerSAO *getPlayerSAO() { return m_sao; } void setPlayerSAO(PlayerSAO *sao) diff --git a/src/server.cpp b/src/server.cpp index 54a139849..516a9d2f9 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -344,7 +344,7 @@ Server::Server( // Initialize Environment ServerMap *servermap = new ServerMap(path_world, this, m_emerge); - m_env = new ServerEnvironment(servermap, m_script, this); + m_env = new ServerEnvironment(servermap, m_script, this, m_path_world); m_clients.setEnv(m_env); @@ -361,19 +361,13 @@ Server::Server( servermap->addEventReceiver(this); // If file exists, load environment metadata - if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt")) + if(fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) { infostream<<"Server: Loading environment metadata"<loadMeta(m_path_world); + m_env->loadMeta(); } - // Load players - infostream<<"Server: Loading players"<deSerializePlayers(m_path_world); - - /* - Add some test ActiveBlockModifiers to environment - */ + // Add some test ActiveBlockModifiers to environment add_legacy_abms(m_env, m_nodedef); m_liquid_transform_every = g_settings->getFloat("liquid_update"); @@ -383,42 +377,23 @@ Server::~Server() { infostream<<"Server destructing"<on_shutdown(); - } - { - JMutexAutoLock envlock(m_env_mutex); - - /* - Save players - */ infostream<<"Server: Saving players"<serializePlayers(m_path_world); + m_env->saveLoadedPlayers(); - /* - Save environment metadata - */ infostream<<"Server: Saving environment metadata"<saveMeta(m_path_world); + m_env->saveMeta(); } - /* - Stop threads - */ + // Stop threads stop(); delete m_thread; @@ -444,12 +419,10 @@ Server::~Server() delete m_script; // Delete detached inventories - { - for(std::map::iterator - i = m_detached_inventories.begin(); - i != m_detached_inventories.end(); i++){ - delete i->second; - } + for (std::map::iterator + i = m_detached_inventories.begin(); + i != m_detached_inventories.end(); i++) { + delete i->second; } } @@ -1141,18 +1114,19 @@ void Server::AsyncRunStep(bool initial_step) ScopeProfiler sp(g_profiler, "Server: saving stuff"); - //Ban stuff - if(m_banmanager->isModified()) + // Save ban file + if (m_banmanager->isModified()) { m_banmanager->save(); + } // Save changed parts of map m_env->getMap().save(MOD_STATE_WRITE_NEEDED); // Save players - m_env->serializePlayers(m_path_world); + m_env->saveLoadedPlayers(); // Save environment metadata - m_env->saveMeta(m_path_world); + m_env->saveMeta(); } } } @@ -1178,27 +1152,16 @@ void Server::Receive() "SerializationError: what()=" <removePlayer(peer_id);*/ - } catch(ClientStateError &e) { errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl; DenyAccess(peer_id, L"Your client sent something server didn't expect." L"Try reconnecting or updating your client"); } + catch(con::PeerNotFoundException &e) + { + // Do nothing + } } PlayerSAO* Server::StageTwoClientInit(u16 peer_id) @@ -5032,15 +4995,16 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id) return NULL; } - /* - Create a new player if it doesn't exist yet - */ - if(player == NULL) - { + // Load player if it isn't already loaded + if (!player) { + player = static_cast(m_env->loadPlayer(name)); + } + + // Create player if it doesn't exist + if (!player) { newplayer = true; player = new RemotePlayer(this); player->updateName(name); - /* Set player position */ infostream<<"Server: Finding spawn place for player \"" <addPlayer(player); } - /* - Create a new player active object - */ + // Create a new player active object PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id, getPlayerEffectivePrivs(player->getName()), isSingleplayer()); @@ -5065,8 +5027,9 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id) m_env->addActiveObject(playersao); /* Run scripts */ - if(newplayer) + if (newplayer) { m_script->on_newplayer(playersao); + } return playersao; }