diff options
Diffstat (limited to 'src/server.cpp')
-rw-r--r-- | src/server.cpp | 349 |
1 files changed, 222 insertions, 127 deletions
diff --git a/src/server.cpp b/src/server.cpp index a9e5c3d08..4b54c2398 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "profiler.h" #include "log.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "nodedef.h" #include "itemdef.h" #include "craftdef.h" @@ -60,6 +60,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/base64.h" #include "util/sha1.h" #include "util/hex.h" +#include "database.h" class ClientNotFoundException : public BaseException { @@ -89,6 +90,15 @@ void *ServerThread::run() DSTACK(FUNCTION_NAME); BEGIN_DEBUG_EXCEPTION_HANDLER + /* + * The real business of the server happens on the ServerThread. + * How this works: + * AsyncRunStep() runs an actual server step as soon as enough time has + * passed (dedicated_server_loop keeps track of that). + * Receive() blocks at least(!) 30ms waiting for a packet (so this loop + * doesn't busy wait) and will process any remaining packets. + */ + m_server->AsyncRunStep(true); while (!stopRequested()) { @@ -99,7 +109,6 @@ void *ServerThread::run() m_server->Receive(); - } catch (con::NoIncomingDataException &e) { } catch (con::PeerNotFoundException &e) { infostream<<"Server: PeerNotFoundException"<<std::endl; } catch (ClientNotFoundException &e) { @@ -229,32 +238,6 @@ Server::Server( modconf.printUnsatisfiedModsError(); } - Settings worldmt_settings; - std::string worldmt = m_path_world + DIR_DELIM + "world.mt"; - worldmt_settings.readConfigFile(worldmt.c_str()); - std::vector<std::string> names = worldmt_settings.getNames(); - std::set<std::string> load_mod_names; - for(std::vector<std::string>::iterator it = names.begin(); - it != names.end(); ++it) { - std::string name = *it; - if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name)) - load_mod_names.insert(name.substr(9)); - } - // complain about mods declared to be loaded, but not found - for(std::vector<ModSpec>::iterator it = m_mods.begin(); - it != m_mods.end(); ++it) - load_mod_names.erase((*it).name); - for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin(); - it != unsatisfied_mods.end(); ++it) - load_mod_names.erase((*it).name); - if(!load_mod_names.empty()) { - errorstream << "The following mods could not be found:"; - for(std::set<std::string>::iterator it = load_mod_names.begin(); - it != load_mod_names.end(); ++it) - errorstream << " \"" << (*it) << "\""; - errorstream << std::endl; - } - //lock environment MutexAutoLock envlock(m_env_mutex); @@ -282,7 +265,7 @@ Server::Server( if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) { throw ModError("Error loading mod \"" + mod.name + "\": Mod name does not follow naming conventions: " - "Only chararacters [a-z0-9_] are allowed."); + "Only characters [a-z0-9_] are allowed."); } std::string script_path = mod.path + DIR_DELIM + "init.lua"; infostream << " [" << padStringRight(mod.name, 12) << "] [\"" @@ -352,17 +335,14 @@ Server::Server( Server::~Server() { - infostream<<"Server destructing"<<std::endl; + infostream << "Server destructing" << std::endl; // Send shutdown message SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down"); { MutexAutoLock envlock(m_env_mutex); - - // Execute script shutdown hooks - m_script->on_shutdown(); - + infostream << "Server: Saving players" << std::endl; m_env->saveLoadedPlayers(); @@ -378,6 +358,20 @@ Server::~Server() } m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN, kick_msg, reconnect); + } + + // Do this before stopping the server in case mapgen callbacks need to access + // server-controlled resources (like ModStorages). Also do them before + // shutdown callbacks since they may modify state that is finalized in a + // callback. + m_emerge->stopThreads(); + + { + MutexAutoLock envlock(m_env_mutex); + + // Execute script shutdown hooks + infostream << "Executing shutdown hooks" << std::endl; + m_script->on_shutdown(); infostream << "Server: Saving environment metadata" << std::endl; m_env->saveMeta(); @@ -387,10 +381,6 @@ Server::~Server() stop(); delete m_thread; - // stop all emerge threads before deleting players that may have - // requested blocks to be emerged - m_emerge->stopThreads(); - // Delete things in the reverse order of creation delete m_emerge; delete m_env; @@ -402,7 +392,7 @@ Server::~Server() delete m_craftdef; // Deinitialize scripting - infostream<<"Server: Deinitializing scripting"<<std::endl; + infostream << "Server: Deinitializing scripting" << std::endl; delete m_script; // Delete detached inventories @@ -433,7 +423,7 @@ void Server::start(Address bind_addr) m_thread->start(); // ASCII art for the win! - actionstream + rawstream <<" .__ __ __ "<<std::endl <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl @@ -599,7 +589,7 @@ void Server::AsyncRunStep(bool initial_step) ScopeProfiler sp(g_profiler, "Server: liquid transform"); std::map<v3s16, MapBlock*> modified_blocks; - m_env->getMap().transformLiquids(modified_blocks); + m_env->getMap().transformLiquids(modified_blocks, m_env); #if 0 /* Update lighting @@ -1070,30 +1060,45 @@ void Server::Receive() DSTACK(FUNCTION_NAME); SharedBuffer<u8> data; u16 peer_id; - try { - NetworkPacket pkt; - m_con.Receive(&pkt); - peer_id = pkt.getPeerId(); - ProcessData(&pkt); - } - catch(con::InvalidIncomingDataException &e) { - infostream<<"Server::Receive(): " - "InvalidIncomingDataException: what()=" - <<e.what()<<std::endl; - } - catch(SerializationError &e) { - infostream<<"Server::Receive(): " - "SerializationError: what()=" - <<e.what()<<std::endl; - } - catch(ClientStateError &e) { - errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl; - DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect." - L"Try reconnecting or updating your client"); - } - catch(con::PeerNotFoundException &e) { - // Do nothing - } + bool first = true; + NetworkPacket pkt; + for (;;) { + pkt.clear(); + peer_id = 0; + try { + /* + In the first iteration *wait* for a packet, afterwards process + all packets that are immediately available (no waiting). + */ + if (first) { + m_con.Receive(&pkt); + first = false; + } else { + if (!m_con.TryReceive(&pkt)) + return; + } + + peer_id = pkt.getPeerId(); + ProcessData(&pkt); + + } catch (const con::InvalidIncomingDataException &e) { + infostream << "Server::Receive(): InvalidIncomingDataException: what()=" + << e.what() << std::endl; + } catch (const SerializationError &e) { + infostream << "Server::Receive(): SerializationError: what()=" + << e.what() << std::endl; + } catch (const ClientStateError &e) { + errorstream << "ProcessData: peer=" << peer_id << " what()=" + << e.what() << std::endl; + DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect." + L"Try reconnecting or updating your client"); + } catch (const con::PeerNotFoundException &e) { + // Do nothing + } catch (const con::NoIncomingDataException &e) { + return; + } + } + } PlayerSAO* Server::StageTwoClientInit(u16 peer_id) @@ -1668,15 +1673,20 @@ void Server::SendInventory(PlayerSAO* playerSAO) void Server::SendChatMessage(u16 peer_id, const std::wstring &message) { DSTACK(FUNCTION_NAME); + if (peer_id != PEER_ID_INEXISTENT) { + NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id); - NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id); - pkt << message; + if (m_clients.getProtocolVersion(peer_id) < 27) + pkt << unescape_enriched(message); + else + pkt << message; - if (peer_id != PEER_ID_INEXISTENT) { Send(&pkt); - } - else { - m_clients.sendToAll(&pkt); + } else { + std::vector<u16> clients = m_clients.getClientIDs(); + for (std::vector<u16>::iterator it = clients.begin(); + it != clients.end(); ++it) + SendChatMessage(*it, message); } } @@ -1706,13 +1716,25 @@ void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version, const struct TileAnimationParams &animation, u8 glow) { DSTACK(FUNCTION_NAME); + static const float radius = + g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS; + if (peer_id == PEER_ID_INEXISTENT) { - // This sucks and should be replaced by a better solution in a refactor: std::vector<u16> clients = m_clients.getClientIDs(); + for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) { RemotePlayer *player = m_env->getPlayer(*i); if (!player) continue; + + PlayerSAO *sao = player->getPlayerSAO(); + if (!sao) + continue; + + // Do not send to distant clients + if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius) + continue; + SendSpawnParticle(*i, player->protocol_version, pos, velocity, acceleration, expirationtime, size, collisiondetection, @@ -1870,7 +1892,8 @@ void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value) } void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor, - const std::string &type, const std::vector<std::string> ¶ms) + const std::string &type, const std::vector<std::string> ¶ms, + bool &clouds) { NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id); pkt << bgcolor << type << (u16) params.size(); @@ -1878,6 +1901,22 @@ void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor, for(size_t i=0; i<params.size(); i++) pkt << params[i]; + pkt << clouds; + + Send(&pkt); +} + +void Server::SendCloudParams(u16 peer_id, float density, + const video::SColor &color_bright, + const video::SColor &color_ambient, + float height, + float thickness, + const v2f &speed) +{ + NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id); + pkt << density << color_bright << color_ambient + << height << thickness << speed; + Send(&pkt); } @@ -2085,15 +2124,23 @@ s32 Server::playSound(const SimpleSoundSpec &spec, m_playing_sounds[id] = ServerPlayingSound(); ServerPlayingSound &psound = m_playing_sounds[id]; psound.params = params; + psound.spec = spec; + float gain = params.gain * spec.gain; NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0); - pkt << id << spec.name << (float) (spec.gain * params.gain) - << (u8) params.type << pos << params.object << params.loop; + pkt << id << spec.name << gain + << (u8) params.type << pos << params.object + << params.loop << params.fade; + + // Backwards compability + bool play_sound = gain > 0; - for(std::vector<u16>::iterator i = dst_clients.begin(); + for (std::vector<u16>::iterator i = dst_clients.begin(); i != dst_clients.end(); ++i) { - psound.clients.insert(*i); - m_clients.send(*i, 0, &pkt, true); + if (play_sound || m_clients.getProtocolVersion(*i) >= 32) { + psound.clients.insert(*i); + m_clients.send(*i, 0, &pkt, true); + } } return id; } @@ -2117,6 +2164,52 @@ void Server::stopSound(s32 handle) m_playing_sounds.erase(i); } +void Server::fadeSound(s32 handle, float step, float gain) +{ + // Get sound reference + UNORDERED_MAP<s32, ServerPlayingSound>::iterator i = + m_playing_sounds.find(handle); + if (i == m_playing_sounds.end()) + return; + + ServerPlayingSound &psound = i->second; + psound.params.gain = gain; + + NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4); + pkt << handle << step << gain; + + // Backwards compability + bool play_sound = gain > 0; + ServerPlayingSound compat_psound = psound; + compat_psound.clients.clear(); + + NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4); + compat_pkt << handle; + + for (UNORDERED_SET<u16>::iterator it = psound.clients.begin(); + it != psound.clients.end();) { + if (m_clients.getProtocolVersion(*it) >= 32) { + // Send as reliable + m_clients.send(*it, 0, &pkt, true); + ++it; + } else { + compat_psound.clients.insert(*it); + // Stop old sound + m_clients.send(*it, 0, &compat_pkt, true); + psound.clients.erase(it++); + } + } + + // Remove sound reference + if (!play_sound || psound.clients.size() == 0) + m_playing_sounds.erase(i); + + if (play_sound && compat_psound.clients.size() > 0) { + // Play new sound volume on older clients + playSound(compat_psound.spec, compat_psound.params); + } +} + void Server::sendRemoveNode(v3s16 p, u16 ignore_id, std::vector<u16> *far_players, float far_d_nodes) { @@ -2618,9 +2711,8 @@ void Server::RespawnPlayer(u16 peer_id) bool repositioned = m_script->on_respawnplayer(playersao); if (!repositioned) { - v3f pos = findSpawnPos(); // setPos will send the new position to client - playersao->setPos(pos); + playersao->setPos(findSpawnPos()); } SendPlayerHP(peer_id); @@ -2813,12 +2905,15 @@ void Server::handleChatInterfaceEvent(ChatEvent *evt) } std::wstring Server::handleChat(const std::string &name, const std::wstring &wname, - const std::wstring &wmessage, bool check_shout_priv, RemotePlayer *player) + std::wstring wmessage, bool check_shout_priv, RemotePlayer *player) { // If something goes wrong, this player is to blame RollbackScopeActor rollback_scope(m_rollback, std::string("player:") + name); + if (g_settings->getBool("strip_color_codes")) + wmessage = unescape_enriched(wmessage); + if (player) { switch (player->canSendChatMessage()) { case RPLAYER_CHATRESULT_FLOODING: { @@ -2873,7 +2968,7 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna /* Send the message to others */ - actionstream << "CHAT: " << wide_to_narrow(line) << std::endl; + actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl; std::vector<u16> clients = m_clients.getClientIDs(); @@ -3186,13 +3281,30 @@ bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third) } bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor, - const std::string &type, const std::vector<std::string> ¶ms) + const std::string &type, const std::vector<std::string> ¶ms, + bool &clouds) { if (!player) return false; - player->setSky(bgcolor, type, params); - SendSetSky(player->peer_id, bgcolor, type, params); + player->setSky(bgcolor, type, params, clouds); + SendSetSky(player->peer_id, bgcolor, type, params, clouds); + return true; +} + +bool Server::setClouds(RemotePlayer *player, float density, + const video::SColor &color_bright, + const video::SColor &color_ambient, + float height, + float thickness, + const v2f &speed) +{ + if (!player) + return false; + + SendCloudParams(player->peer_id, density, + color_bright, color_ambient, height, + thickness, speed); return true; } @@ -3436,14 +3548,16 @@ v3f Server::findSpawnPos() } bool is_good = false; + // Limit spawn range to mapgen edges (determined by 'mapgen_limit') + s32 range_max = map.getMapgenParams()->getSpawnRangeMax(); // Try to find a good place a few times for(s32 i = 0; i < 4000 && !is_good; i++) { - s32 range = 1 + i; + s32 range = MYMIN(1 + i, range_max); // We're going to try to throw the player to this position v2s16 nodepos2d = v2s16( - -range + (myrand() % (range * 2)), - -range + (myrand() % (range * 2))); + -range + (myrand() % (range * 2)), + -range + (myrand() % (range * 2))); // Get spawn level at point s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d); @@ -3479,9 +3593,16 @@ v3f Server::findSpawnPos() void Server::requestShutdown(const std::string &msg, bool reconnect, float delay) { + m_shutdown_timer = delay; + m_shutdown_msg = msg; + m_shutdown_ask_reconnect = reconnect; + if (delay == 0.0f) { // No delay, shutdown immediately m_shutdown_requested = true; + // only print to the infostream, a chat message saying + // "Server Shutting Down" is sent when the server destructs. + infostream << "*** Immediate Server shutdown requested." << std::endl; } else if (delay < 0.0f && m_shutdown_timer > 0.0f) { // Negative delay, cancel shutdown if requested m_shutdown_timer = 0.0f; @@ -3495,10 +3616,7 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay infostream << wide_to_utf8(ws.str()).c_str() << std::endl; SendChatMessage(PEER_ID_INEXISTENT, ws.str()); } else if (delay > 0.0f) { - // Positive delay, delay the shutdown - m_shutdown_timer = delay; - m_shutdown_msg = msg; - m_shutdown_ask_reconnect = reconnect; + // Positive delay, tell the clients when the server will shut down std::wstringstream ws; ws << L"*** Server shutting down in " @@ -3512,8 +3630,6 @@ void Server::requestShutdown(const std::string &msg, bool reconnect, float delay PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version) { - bool newplayer = false; - /* Try to get an existing player */ @@ -3534,44 +3650,18 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version return NULL; } - // Create a new player active object - PlayerSAO *playersao = new PlayerSAO(m_env, peer_id, isSingleplayer()); - player = m_env->loadPlayer(name, playersao); - - // Create player if it doesn't exist if (!player) { - newplayer = true; - player = new RemotePlayer(name, this->idef()); - // Set player position - infostream<<"Server: Finding spawn place for player \"" - <<name<<"\""<<std::endl; - playersao->setBasePosition(findSpawnPos()); - - // Make sure the player is saved - player->setModified(true); - - // Add player to environment - m_env->addPlayer(player); - } else { - // If the player exists, ensure that they respawn inside legal bounds - // This fixes an assert crash when the player can't be added - // to the environment - if (objectpos_over_limit(playersao->getBasePosition())) { - actionstream << "Respawn position for player \"" - << name << "\" outside limits, resetting" << std::endl; - playersao->setBasePosition(findSpawnPos()); - } + player = new RemotePlayer(name, idef()); } - playersao->initialize(player, getPlayerEffectivePrivs(player->getName())); - - player->protocol_version = proto_version; + bool newplayer = false; - /* Clean up old HUD elements from previous sessions */ - player->clearHud(); + // Load player + PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer()); - /* Add object to environment */ - m_env->addActiveObject(playersao); + // Complete init with server parts + playersao->finalize(player, getPlayerEffectivePrivs(player->getName())); + player->protocol_version = proto_version; /* Run scripts */ if (newplayer) { @@ -3615,6 +3705,11 @@ void dedicated_server_loop(Server &server, bool &kill) static const float profiler_print_interval = g_settings->getFloat("profiler_print_interval"); + /* + * The dedicated server loop only does time-keeping (in Server::step) and + * provides a way to main.cpp to kill the server externally (bool &kill). + */ + for(;;) { // This is kind of a hack but can be done like this // because server.step() is very light |