Only keep players loaded while they're connected

This commit is contained in:
ShadowNinja 2014-05-30 16:04:07 -04:00
parent 50127510e7
commit 7e6db1b803
6 changed files with 188 additions and 249 deletions

@ -1027,6 +1027,8 @@ void PlayerSAO::removingFromEnvironment()
{ {
m_player->setPlayerSAO(NULL); m_player->setPlayerSAO(NULL);
m_player->peer_id = 0; m_player->peer_id = 0;
m_env->savePlayer(m_player->getName());
m_env->removePlayer(m_player->getName());
} }
} }

@ -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) Player * Environment::getPlayer(u16 peer_id)
{ {
for(std::list<Player*>::iterator i = m_players.begin(); 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, ServerEnvironment::ServerEnvironment(ServerMap *map,
GameScripting *scriptIface, IGameDef *gamedef): GameScripting *scriptIface, IGameDef *gamedef,
const std::string &path_world) :
m_map(map), m_map(map),
m_script(scriptIface), m_script(scriptIface),
m_gamedef(gamedef), m_gamedef(gamedef),
m_path_world(path_world),
m_send_recommended_timer(0), m_send_recommended_timer(0),
m_active_block_interval_overload_skip(0), m_active_block_interval_overload_skip(0),
m_game_time(0), m_game_time(0),
@ -401,196 +415,85 @@ bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16
return true; 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); fs::CreateDir(players_path);
std::set<Player*> saved_players; 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);
}
}
}
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<RemotePlayer*>(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<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); std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
for(u32 i=0; i<player_files.size(); i++) for (u32 i = 0; i < player_files.size(); i++) {
{ if (player_files[i].dir)
if(player_files[i].dir || player_files[i].name[0] == '.')
continue; continue;
// Full path to this file // Full path to this file
std::string path = players_path + "/" + player_files[i].name; std::string path = players_path + "/" + player_files[i].name;
//infostream<<"Checking player file "<<path<<std::endl;
// Load player to see what is its name // Load player to see what is its name
RemotePlayer testplayer(m_gamedef); std::ifstream is(path.c_str(), std::ios_base::binary);
{ if (!is.good()) {
// Open file and deserialize infostream << "Failed to read " << path << std::endl;
std::ifstream is(path.c_str(), std::ios_base::binary); continue;
if(is.good() == false)
{
infostream<<"Failed to read "<<path<<std::endl;
continue;
}
testplayer.deSerialize(is, player_files[i].name);
} }
player->deSerialize(is, player_files[i].name);
//infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl; if (!string_allowed(player->getName(), PLAYERNAME_ALLOWED_CHARS)) {
infostream << "Not loading player with invalid name: "
// Search for the player << player->getName() << std::endl;
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; continue;
} }
//infostream<<"Found matching player, overwriting."<<std::endl; if (player->getName() == playername) {
// We found our player
// OK, found. Save player there. foundplayer = true;
if(player->checkModified()) break;
{
// 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);
} }
} }
if (!foundplayer) {
for(std::list<Player*>::iterator i = m_players.begin(); return NULL;
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;
}
{
/*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);
}
} }
if (newplayer) {
//infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl; addPlayer(player);
}
return player;
} }
void ServerEnvironment::deSerializePlayers(const std::string &savedir) void ServerEnvironment::saveMeta()
{ {
std::string players_path = savedir + "/players"; std::string path = m_path_world + DIR_DELIM "env_meta.txt";
std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
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;
}
/*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;
}
// 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(newplayer)
{
addPlayer(player);
}
}
}
void ServerEnvironment::saveMeta(const std::string &savedir)
{
std::string path = savedir + "/env_meta.txt";
// Open file and serialize // Open file and serialize
std::ostringstream ss(std::ios_base::binary); 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 // Open file and deserialize
std::ifstream is(path.c_str(), std::ios_base::binary); std::ifstream is(path.c_str(), std::ios_base::binary);

@ -70,6 +70,7 @@ public:
virtual void addPlayer(Player *player); virtual void addPlayer(Player *player);
void removePlayer(u16 peer_id); void removePlayer(u16 peer_id);
void removePlayer(const char *name);
Player * getPlayer(u16 peer_id); Player * getPlayer(u16 peer_id);
Player * getPlayer(const char *name); Player * getPlayer(const char *name);
Player * getRandomConnectedPlayer(); Player * getRandomConnectedPlayer();
@ -199,7 +200,7 @@ class ServerEnvironment : public Environment
{ {
public: public:
ServerEnvironment(ServerMap *map, GameScripting *scriptIface, ServerEnvironment(ServerMap *map, GameScripting *scriptIface,
IGameDef *gamedef); IGameDef *gamedef, const std::string &path_world);
~ServerEnvironment(); ~ServerEnvironment();
Map & getMap(); Map & getMap();
@ -216,17 +217,16 @@ public:
float getSendRecommendedInterval() float getSendRecommendedInterval()
{ return m_recommended_send_interval; } { return m_recommended_send_interval; }
/* // Save players
Save players void saveLoadedPlayers();
*/ void savePlayer(const std::string &playername);
void serializePlayers(const std::string &savedir); Player *loadPlayer(const std::string &playername);
void deSerializePlayers(const std::string &savedir);
/* /*
Save and load time of day and game timer Save and load time of day and game timer
*/ */
void saveMeta(const std::string &savedir); void saveMeta();
void loadMeta(const std::string &savedir); void loadMeta();
/* /*
External ActiveObject interface External ActiveObject interface
@ -368,6 +368,8 @@ private:
GameScripting* m_script; GameScripting* m_script;
// Game definition // Game definition
IGameDef *m_gamedef; IGameDef *m_gamedef;
// World path
const std::string m_path_world;
// Active object list // Active object list
std::map<u16, ServerActiveObject*> m_active_objects; std::map<u16, ServerActiveObject*> m_active_objects;
// Outgoing network message buffer for active objects // Outgoing network message buffer for active objects

@ -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 RemotePlayer
*/ */
@ -292,3 +358,4 @@ void RemotePlayer::setPosition(const v3f &position)
if(m_sao) if(m_sao)
m_sao->setBasePosition(position); m_sao->setBasePosition(position);
} }

@ -335,6 +335,8 @@ public:
RemotePlayer(IGameDef *gamedef): Player(gamedef), m_sao(0) {} RemotePlayer(IGameDef *gamedef): Player(gamedef), m_sao(0) {}
virtual ~RemotePlayer() {} virtual ~RemotePlayer() {}
void save(const std::string &savedir);
PlayerSAO *getPlayerSAO() PlayerSAO *getPlayerSAO()
{ return m_sao; } { return m_sao; }
void setPlayerSAO(PlayerSAO *sao) void setPlayerSAO(PlayerSAO *sao)

@ -344,7 +344,7 @@ Server::Server(
// Initialize Environment // Initialize Environment
ServerMap *servermap = new ServerMap(path_world, this, m_emerge); 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); m_clients.setEnv(m_env);
@ -361,19 +361,13 @@ Server::Server(
servermap->addEventReceiver(this); servermap->addEventReceiver(this);
// If file exists, load environment metadata // 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; infostream<<"Server: Loading environment metadata"<<std::endl;
m_env->loadMeta(m_path_world); m_env->loadMeta();
} }
// Load players // Add some test ActiveBlockModifiers to environment
infostream<<"Server: Loading players"<<std::endl;
m_env->deSerializePlayers(m_path_world);
/*
Add some test ActiveBlockModifiers to environment
*/
add_legacy_abms(m_env, m_nodedef); add_legacy_abms(m_env, m_nodedef);
m_liquid_transform_every = g_settings->getFloat("liquid_update"); m_liquid_transform_every = g_settings->getFloat("liquid_update");
@ -383,42 +377,23 @@ Server::~Server()
{ {
infostream<<"Server destructing"<<std::endl; infostream<<"Server destructing"<<std::endl;
/* // Send shutdown message
Send shutdown message SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
*/
{
std::wstring line = L"*** Server shutting down";
SendChatMessage(PEER_ID_INEXISTENT, line);
}
{ {
JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock envlock(m_env_mutex);
/* // Execute script shutdown hooks
Execute script shutdown hooks
*/
m_script->on_shutdown(); m_script->on_shutdown();
}
{
JMutexAutoLock envlock(m_env_mutex);
/*
Save players
*/
infostream<<"Server: Saving players"<<std::endl; 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; infostream<<"Server: Saving environment metadata"<<std::endl;
m_env->saveMeta(m_path_world); m_env->saveMeta();
} }
/* // Stop threads
Stop threads
*/
stop(); stop();
delete m_thread; delete m_thread;
@ -444,12 +419,10 @@ Server::~Server()
delete m_script; delete m_script;
// Delete detached inventories // Delete detached inventories
{ for (std::map<std::string, Inventory*>::iterator
for(std::map<std::string, Inventory*>::iterator i = m_detached_inventories.begin();
i = m_detached_inventories.begin(); i != m_detached_inventories.end(); i++) {
i != m_detached_inventories.end(); i++){ delete i->second;
delete i->second;
}
} }
} }
@ -1141,18 +1114,19 @@ void Server::AsyncRunStep(bool initial_step)
ScopeProfiler sp(g_profiler, "Server: saving stuff"); ScopeProfiler sp(g_profiler, "Server: saving stuff");
//Ban stuff // Save ban file
if(m_banmanager->isModified()) if (m_banmanager->isModified()) {
m_banmanager->save(); m_banmanager->save();
}
// Save changed parts of map // Save changed parts of map
m_env->getMap().save(MOD_STATE_WRITE_NEEDED); m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
// Save players // Save players
m_env->serializePlayers(m_path_world); m_env->saveLoadedPlayers();
// Save environment metadata // Save environment metadata
m_env->saveMeta(m_path_world); m_env->saveMeta();
} }
} }
} }
@ -1178,27 +1152,16 @@ void Server::Receive()
"SerializationError: what()=" "SerializationError: what()="
<<e.what()<<std::endl; <<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) catch(ClientStateError &e)
{ {
errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl; errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
DenyAccess(peer_id, L"Your client sent something server didn't expect." DenyAccess(peer_id, L"Your client sent something server didn't expect."
L"Try reconnecting or updating your client"); L"Try reconnecting or updating your client");
} }
catch(con::PeerNotFoundException &e)
{
// Do nothing
}
} }
PlayerSAO* Server::StageTwoClientInit(u16 peer_id) PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
@ -5032,15 +4995,16 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
return NULL; return NULL;
} }
/* // Load player if it isn't already loaded
Create a new player if it doesn't exist yet if (!player) {
*/ player = static_cast<RemotePlayer*>(m_env->loadPlayer(name));
if(player == NULL) }
{
// Create player if it doesn't exist
if (!player) {
newplayer = true; newplayer = true;
player = new RemotePlayer(this); player = new RemotePlayer(this);
player->updateName(name); player->updateName(name);
/* Set player position */ /* Set player position */
infostream<<"Server: Finding spawn place for player \"" infostream<<"Server: Finding spawn place for player \""
<<name<<"\""<<std::endl; <<name<<"\""<<std::endl;
@ -5051,9 +5015,7 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
m_env->addPlayer(player); 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, PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
getPlayerEffectivePrivs(player->getName()), getPlayerEffectivePrivs(player->getName()),
isSingleplayer()); isSingleplayer());
@ -5065,8 +5027,9 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
m_env->addActiveObject(playersao); m_env->addActiveObject(playersao);
/* Run scripts */ /* Run scripts */
if(newplayer) if (newplayer) {
m_script->on_newplayer(playersao); m_script->on_newplayer(playersao);
}
return playersao; return playersao;
} }