Only keep players loaded while they're connected
authorShadowNinja <shadowninja@minetest.net>
Fri, 30 May 2014 20:04:07 +0000 (16:04 -0400)
committerShadowNinja <shadowninja@minetest.net>
Mon, 23 Jun 2014 19:45:59 +0000 (15:45 -0400)
src/content_sao.cpp
src/environment.cpp
src/environment.h
src/player.cpp
src/player.h
src/server.cpp

index 095c6b5bf26c969f064e752fe815aa3d272df2aa..4ee92f4d34255408e83f1159cfadb73f359fb26b 100644 (file)
@@ -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());
        }
 }
 
index 6bbc715d078372fb3fef5f73ba3c7a1f2bfb3643..91f5ea2b6fe045c1a06c438f491cc3ebeb725389 100644 (file)
@@ -100,6 +100,18 @@ void Environment::removePlayer(u16 peer_id)
        }
 }
 
+void Environment::removePlayer(const char *name)
+{
+       for (std::list<Player*>::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<Player*>::iterator i = m_players.begin();
@@ -332,10 +344,12 @@ void ActiveBlockList::update(std::list<v3s16> &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<Player*> saved_players;
-
-       std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
-       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 = players_path + "/" + player_files[i].name;
-
-               //infostream<<"Checking player file "<<path<<std::endl;
-
-               // Load player to see what is its name
-               RemotePlayer testplayer(m_gamedef);
-               {
-                       // Open file and deserialize
-                       std::ifstream is(path.c_str(), std::ios_base::binary);
-                       if(is.good() == false)
-                       {
-                               infostream<<"Failed to read "<<path<<std::endl;
-                               continue;
-                       }
-                       testplayer.deSerialize(is, player_files[i].name);
-               }
-
-               //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
-               
-               // Search for the player
-               std::string playername = testplayer.getName();
-               Player *player = getPlayer(playername.c_str());
-               if(player == NULL)
-               {
-                       infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
-                       continue;
-               }
-
-               //infostream<<"Found matching player, overwriting."<<std::endl;
-
-               // OK, found. Save player there.
-               if(player->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 "<<path<<std::endl;
-                               continue;
-                       }
-                       saved_players.insert(player);
-               } else {
-                       saved_players.insert(player);
+       for (std::list<Player*>::iterator it = m_players.begin();
+                       it != m_players.end();
+                       ++it) {
+               RemotePlayer *player = static_cast<RemotePlayer*>(*it);
+               if (player->checkModified()) {
+                       player->save(players_path);
                }
        }
+}
 
-       for(std::list<Player*>::iterator i = m_players.begin();
-                       i != m_players.end(); ++i)
-       {
-               Player *player = *i;
-               if(saved_players.find(player) != saved_players.end())
-               {
-                       /*infostream<<"Player "<<player->getName()
-                                       <<" was already saved."<<std::endl;*/
-                       continue;
-               }
-               std::string playername = player->getName();
-               // Don't save unnamed player
-               if(playername == "")
-               {
-                       //infostream<<"Not saving unnamed player."<<std::endl;
-                       continue;
-               }
-               /*
-                       Find a sane filename
-               */
-               if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
-                       playername = "player";
-               std::string path = players_path + "/" + playername;
-               bool found = false;
-               for(u32 i=0; i<1000; i++)
-               {
-                       if(fs::PathExists(path) == false)
-                       {
-                               found = true;
-                               break;
-                       }
-                       path = players_path + "/" + playername + itos(i);
-               }
-               if(found == false)
-               {
-                       infostream<<"Didn't find free file for player"<<std::endl;
-                       continue;
-               }
+void ServerEnvironment::savePlayer(const std::string &playername)
+{
+       std::string players_path = m_path_world + DIR_DELIM "players";
+       fs::CreateDir(players_path);
 
-               {
-                       /*infostream<<"Saving player "<<player->getName()<<" to "
-                                       <<path<<std::endl;*/
-                       // Open file and serialize
-                       std::ostringstream ss(std::ios_base::binary);
-                       player->serialize(ss);
-                       if(!fs::safeWriteToFile(path, ss.str()))
-                       {
-                               infostream<<"Failed to write "<<path<<std::endl;
-                               continue;
-                       }
-                       saved_players.insert(player);
-               }
+       RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
+       if (player) {
+               player->save(players_path);
        }
-
-       //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
 }
 
-void ServerEnvironment::deSerializePlayers(const std::string &savedir)
+Player *ServerEnvironment::loadPlayer(const std::string &playername)
 {
-       std::string players_path = savedir + "/players";
+       std::string players_path = m_path_world + DIR_DELIM "players";
+
+       RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
+       bool newplayer = false;
+       bool foundplayer = false;
+       if (!player) {
+               player = new RemotePlayer(m_gamedef);
+               newplayer = true;
+       }
 
        std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
-       for(u32 i=0; i<player_files.size(); i++)
-       {
-               if(player_files[i].dir)
+       for (u32 i = 0; i < player_files.size(); i++) {
+               if (player_files[i].dir)
                        continue;
                
                // Full path to this file
                std::string path = players_path + "/" + player_files[i].name;
 
-               //infostream<<"Checking player file "<<path<<std::endl;
-
                // Load player to see what is its name
-               RemotePlayer testplayer(m_gamedef);
-               {
-                       // Open file and deserialize
-                       std::ifstream is(path.c_str(), std::ios_base::binary);
-                       if(is.good() == false)
-                       {
-                               infostream<<"Failed to read "<<path<<std::endl;
-                               continue;
-                       }
-                       testplayer.deSerialize(is, player_files[i].name);
-               }
-
-               if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
-               {
-                       infostream<<"Not loading player with invalid name: "
-                                       <<testplayer.getName()<<std::endl;
+               std::ifstream is(path.c_str(), std::ios_base::binary);
+               if (!is.good()) {
+                       infostream << "Failed to read " << path << std::endl;
+                       continue;
                }
+               player->deSerialize(is, player_files[i].name);
 
-               /*infostream<<"Loaded test player with name "<<testplayer.getName()
-                               <<std::endl;*/
-               
-               // Search for the player
-               std::string playername = testplayer.getName();
-               Player *player = getPlayer(playername.c_str());
-               bool newplayer = false;
-               if(player == NULL)
-               {
-                       //infostream<<"Is a new player"<<std::endl;
-                       player = new RemotePlayer(m_gamedef);
-                       newplayer = true;
+               if (!string_allowed(player->getName(), PLAYERNAME_ALLOWED_CHARS)) {
+                       infostream << "Not loading player with invalid name: "
+                                       << player->getName() << std::endl;
+                       continue;
                }
 
-               // Load player
-               {
-                       verbosestream<<"Reading player "<<testplayer.getName()<<" from "
-                                       <<path<<std::endl;
-                       // Open file and deserialize
-                       std::ifstream is(path.c_str(), std::ios_base::binary);
-                       if(is.good() == false)
-                       {
-                               infostream<<"Failed to read "<<path<<std::endl;
-                               continue;
-                       }
-                       player->deSerialize(is, player_files[i].name);
+               if (player->getName() == playername) {
+                       // We found our player
+                       foundplayer = true;
+                       break;
                }
 
-               if(newplayer)
-               {
-                       addPlayer(player);
-               }
        }
+       if (!foundplayer) {
+               return NULL;
+       }
+       if (newplayer) {
+               addPlayer(player);
+       }
+       return player;
 }
 
-void ServerEnvironment::saveMeta(const std::string &savedir)
+void ServerEnvironment::saveMeta()
 {
-       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);
index a59bbd9a1d51fe996e24dc9f904717afcef343c4..e8ae044e30ce0d32bab7ffbeebcb03acddf2cded 100644 (file)
@@ -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<u16, ServerActiveObject*> m_active_objects;
        // Outgoing network message buffer for active objects
index 4dadf26d0b0e2eb2fc986f7830449d339c3a9d0a..78ba17e8992ef9e1986d6b7613c40d2c02340d63 100644 (file)
@@ -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<fs::DirListNode> 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);
 }
+
index 2b04a310cfc835564a15a6a0834a9bb2546970b4..098a4d1537d017fb951e1dc0b6f3311aab41f9ce 100644 (file)
@@ -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)
index 54a139849e554bb0edb45ceef6a965fe10b03054..516a9d2f9c32b3a4cd51d04d5bad7b1dad7802ef 100644 (file)
@@ -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"<<std::endl;
-               m_env->loadMeta(m_path_world);
+               m_env->loadMeta();
        }
 
-       // Load players
-       infostream<<"Server: Loading players"<<std::endl;
-       m_env->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"<<std::endl;
 
-       /*
-               Send shutdown message
-       */
-       {
-               std::wstring line = L"*** Server shutting down";
-               SendChatMessage(PEER_ID_INEXISTENT, line);
-       }
+       // Send shutdown message
+       SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
 
        {
                JMutexAutoLock envlock(m_env_mutex);
 
-               /*
-                       Execute script shutdown hooks
-               */
+               // Execute script shutdown hooks
                m_script->on_shutdown();
-       }
-
-       {
-               JMutexAutoLock envlock(m_env_mutex);
 
-               /*
-                       Save players
-               */
                infostream<<"Server: Saving players"<<std::endl;
-               m_env->serializePlayers(m_path_world);
+               m_env->saveLoadedPlayers();
 
-               /*
-                       Save environment metadata
-               */
                infostream<<"Server: Saving environment metadata"<<std::endl;
-               m_env->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<std::string, Inventory*>::iterator
-                               i = m_detached_inventories.begin();
-                               i != m_detached_inventories.end(); i++){
-                       delete i->second;
-               }
+       for (std::map<std::string, Inventory*>::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()="
                                <<e.what()<<std::endl;
        }
-       catch(con::PeerNotFoundException &e)
-       {
-               //NOTE: This is not needed anymore
-
-               // The peer has been disconnected.
-               // Find the associated player and remove it.
-
-               /*JMutexAutoLock envlock(m_env_mutex);
-
-               infostream<<"ServerThread: peer_id="<<peer_id
-                               <<" has apparently closed connection. "
-                               <<"Removing player."<<std::endl;
-
-               m_env->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<RemotePlayer*>(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 \""
                                <<name<<"\""<<std::endl;
@@ -5051,9 +5015,7 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
                m_env->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;
 }