Apply physics overrides correctly during anticheat calculations (#6970)
[oweals/minetest.git] / src / server.cpp
index a3b686c257050027afc6672d8f6a7d86afd31626..d011089c3d3a6a4e1c9ad8017484ec81ec916485 100644 (file)
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <iostream>
 #include <queue>
 #include <algorithm>
 #include <iostream>
 #include <queue>
 #include <algorithm>
+#include "network/connection.h"
 #include "network/networkprotocol.h"
 #include "network/serveropcodes.h"
 #include "ban.h"
 #include "network/networkprotocol.h"
 #include "network/serveropcodes.h"
 #include "ban.h"
@@ -38,23 +39,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "profiler.h"
 #include "log.h"
 #include "settings.h"
 #include "profiler.h"
 #include "log.h"
-#include "scripting_game.h"
+#include "scripting_server.h"
 #include "nodedef.h"
 #include "itemdef.h"
 #include "craftdef.h"
 #include "emerge.h"
 #include "nodedef.h"
 #include "itemdef.h"
 #include "craftdef.h"
 #include "emerge.h"
-#include "mapgen.h"
-#include "mg_biome.h"
+#include "mapgen/mapgen.h"
+#include "mapgen/mg_biome.h"
 #include "content_mapnode.h"
 #include "content_nodemeta.h"
 #include "content_mapnode.h"
 #include "content_nodemeta.h"
-#include "content_abm.h"
 #include "content_sao.h"
 #include "mods.h"
 #include "content_sao.h"
 #include "mods.h"
-#include "sound.h" // dummySoundManager
 #include "event_manager.h"
 #include "event_manager.h"
+#include "modchannels.h"
 #include "serverlist.h"
 #include "util/string.h"
 #include "serverlist.h"
 #include "util/string.h"
-#include "util/mathconstants.h"
 #include "rollback.h"
 #include "util/serialize.h"
 #include "util/thread.h"
 #include "rollback.h"
 #include "util/serialize.h"
 #include "util/thread.h"
@@ -62,6 +61,10 @@ 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 "util/base64.h"
 #include "util/sha1.h"
 #include "util/hex.h"
+#include "database/database.h"
+#include "chatmessage.h"
+#include "chat_interface.h"
+#include "remoteplayer.h"
 
 class ClientNotFoundException : public BaseException
 {
 
 class ClientNotFoundException : public BaseException
 {
@@ -88,15 +91,12 @@ private:
 
 void *ServerThread::run()
 {
 
 void *ServerThread::run()
 {
-       DSTACK(FUNCTION_NAME);
        BEGIN_DEBUG_EXCEPTION_HANDLER
 
        m_server->AsyncRunStep(true);
 
        while (!stopRequested()) {
                try {
        BEGIN_DEBUG_EXCEPTION_HANDLER
 
        m_server->AsyncRunStep(true);
 
        while (!stopRequested()) {
                try {
-                       //TimeTaker timer("AsyncRunStep() + Receive()");
-
                        m_server->AsyncRunStep();
 
                        m_server->Receive();
                        m_server->AsyncRunStep();
 
                        m_server->Receive();
@@ -108,13 +108,14 @@ void *ServerThread::run()
                } catch (con::ConnectionBindFailed &e) {
                        m_server->setAsyncFatalError(e.what());
                } catch (LuaError &e) {
                } catch (con::ConnectionBindFailed &e) {
                        m_server->setAsyncFatalError(e.what());
                } catch (LuaError &e) {
-                       m_server->setAsyncFatalError("Lua: " + std::string(e.what()));
+                       m_server->setAsyncFatalError(
+                                       "ServerThread::run Lua: " + std::string(e.what()));
                }
        }
 
        END_DEBUG_EXCEPTION_HANDLER
 
                }
        }
 
        END_DEBUG_EXCEPTION_HANDLER
 
-       return NULL;
+       return nullptr;
 }
 
 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
 }
 
 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
@@ -148,52 +149,33 @@ Server::Server(
                const std::string &path_world,
                const SubgameSpec &gamespec,
                bool simple_singleplayer_mode,
                const std::string &path_world,
                const SubgameSpec &gamespec,
                bool simple_singleplayer_mode,
-               bool ipv6,
+               Address bind_addr,
+               bool dedicated,
                ChatInterface *iface
        ):
                ChatInterface *iface
        ):
+       m_bind_addr(bind_addr),
        m_path_world(path_world),
        m_gamespec(gamespec),
        m_simple_singleplayer_mode(simple_singleplayer_mode),
        m_path_world(path_world),
        m_gamespec(gamespec),
        m_simple_singleplayer_mode(simple_singleplayer_mode),
+       m_dedicated(dedicated),
        m_async_fatal_error(""),
        m_async_fatal_error(""),
-       m_env(NULL),
-       m_con(PROTOCOL_ID,
+       m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
                        512,
                        CONNECTION_TIMEOUT,
                        512,
                        CONNECTION_TIMEOUT,
-                       ipv6,
-                       this),
-       m_banmanager(NULL),
-       m_rollback(NULL),
-       m_enable_rollback_recording(false),
-       m_emerge(NULL),
-       m_script(NULL),
+                       m_bind_addr.isIPv6(),
+                       this)),
        m_itemdef(createItemDefManager()),
        m_nodedef(createNodeDefManager()),
        m_craftdef(createCraftDefManager()),
        m_event(new EventManager()),
        m_itemdef(createItemDefManager()),
        m_nodedef(createNodeDefManager()),
        m_craftdef(createCraftDefManager()),
        m_event(new EventManager()),
-       m_thread(NULL),
-       m_time_of_day_send_timer(0),
        m_uptime(0),
        m_uptime(0),
-       m_clients(&m_con),
-       m_shutdown_requested(false),
-       m_shutdown_ask_reconnect(false),
+       m_clients(m_con),
        m_admin_chat(iface),
        m_admin_chat(iface),
-       m_ignore_map_edit_events(false),
-       m_ignore_map_edit_events_peer_id(0),
-       m_next_sound_id(0)
-
+       m_modchannel_mgr(new ModChannelMgr())
 {
 {
-       m_liquid_transform_timer = 0.0;
-       m_liquid_transform_every = 1.0;
-       m_print_info_timer = 0.0;
-       m_masterserver_timer = 0.0;
-       m_objectdata_timer = 0.0;
-       m_emergethread_trigger_timer = 0.0;
-       m_savemap_timer = 0.0;
-
-       m_step_dtime = 0.0;
        m_lag = g_settings->getFloat("dedicated_server_step");
 
        m_lag = g_settings->getFloat("dedicated_server_step");
 
-       if(path_world == "")
+       if (path_world.empty())
                throw ServerError("Supplied empty world path");
 
        if(!gamespec.isValid())
                throw ServerError("Supplied empty world path");
 
        if(!gamespec.isValid())
@@ -221,82 +203,42 @@ Server::Server(
        std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
        m_banmanager = new BanManager(ban_path);
 
        std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
        m_banmanager = new BanManager(ban_path);
 
-       ModConfiguration modconf(m_path_world);
+       ServerModConfiguration modconf(m_path_world);
        m_mods = modconf.getMods();
        std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
        // complain about mods with unsatisfied dependencies
        m_mods = modconf.getMods();
        std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
        // complain about mods with unsatisfied dependencies
-       if(!modconf.isConsistent()) {
-               for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
-                       it != unsatisfied_mods.end(); ++it) {
-                       ModSpec mod = *it;
-                       errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
-                       for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
-                               dep_it != mod.unsatisfied_depends.end(); ++dep_it)
-                               errorstream << " \"" << *dep_it << "\"";
-                       errorstream << std::endl;
-               }
-       }
-
-       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;
+       if (!modconf.isConsistent()) {
+               modconf.printUnsatisfiedModsError();
        }
 
        //lock environment
        MutexAutoLock envlock(m_env_mutex);
 
        }
 
        //lock environment
        MutexAutoLock envlock(m_env_mutex);
 
-       // Load mapgen params from Settings
-       m_emerge->loadMapgenParams();
-
        // Create the Map (loads map_meta.txt, overriding configured mapgen params)
        ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
 
        // Initialize scripting
        infostream<<"Server: Initializing Lua"<<std::endl;
 
        // Create the Map (loads map_meta.txt, overriding configured mapgen params)
        ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
 
        // Initialize scripting
        infostream<<"Server: Initializing Lua"<<std::endl;
 
-       m_script = new GameScripting(this);
-
-       std::string script_path = getBuiltinLuaPath() + DIR_DELIM "init.lua";
+       m_script = new ServerScripting(this);
 
 
-       m_script->loadMod(script_path, BUILTIN_MOD_NAME);
+       m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
 
        // Print mods
        infostream << "Server: Loading mods: ";
 
        // Print mods
        infostream << "Server: Loading mods: ";
-       for(std::vector<ModSpec>::iterator i = m_mods.begin();
+       for (std::vector<ModSpec>::const_iterator i = m_mods.begin();
                        i != m_mods.end(); ++i) {
                        i != m_mods.end(); ++i) {
-               const ModSpec &mod = *i;
-               infostream << mod.name << " ";
+               infostream << (*i).name << " ";
        }
        infostream << std::endl;
        // Load and run "mod" scripts
        }
        infostream << std::endl;
        // Load and run "mod" scripts
-       for (std::vector<ModSpec>::iterator it = m_mods.begin();
+       for (std::vector<ModSpec>::const_iterator it = m_mods.begin();
                        it != m_mods.end(); ++it) {
                const ModSpec &mod = *it;
                if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
                        throw ModError("Error loading mod \"" + mod.name +
                                "\": Mod name does not follow naming conventions: "
                        it != m_mods.end(); ++it) {
                const ModSpec &mod = *it;
                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) << "] [\""
                }
                std::string script_path = mod.path + DIR_DELIM + "init.lua";
                infostream << "  [" << padStringRight(mod.name, 12) << "] [\""
@@ -311,9 +253,11 @@ Server::Server(
        m_nodedef->updateAliases(m_itemdef);
 
        // Apply texture overrides from texturepack/override.txt
        m_nodedef->updateAliases(m_itemdef);
 
        // Apply texture overrides from texturepack/override.txt
-       std::string texture_path = g_settings->get("texture_path");
-       if (texture_path != "" && fs::IsDir(texture_path))
-               m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
+       std::vector<std::string> paths;
+       fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
+       fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
+       for (const std::string &path : paths)
+               m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
 
        m_nodedef->setNodeRegistrationStatus(true);
 
 
        m_nodedef->setNodeRegistrationStatus(true);
 
@@ -331,8 +275,11 @@ Server::Server(
 
        m_clients.setEnv(m_env);
 
 
        m_clients.setEnv(m_env);
 
+       if (!servermap->settings_mgr.makeMapgenParams())
+               FATAL_ERROR("Couldn't create any mapgen type");
+
        // Initialize mapgens
        // Initialize mapgens
-       m_emerge->initMapgens();
+       m_emerge->initMapgens(servermap->getMapgenParams());
 
        m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
        if (m_enable_rollback_recording) {
 
        m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
        if (m_enable_rollback_recording) {
@@ -354,25 +301,23 @@ Server::Server(
                m_env->loadDefaultMeta();
        }
 
                m_env->loadDefaultMeta();
        }
 
-       // Add some test ActiveBlockModifiers to environment
-       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");
+       m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
+       m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
+       m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
 }
 
 Server::~Server()
 {
 }
 
 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");
+       SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
+                       L"*** Server shutting down"));
 
        {
                MutexAutoLock envlock(m_env_mutex);
 
        {
                MutexAutoLock envlock(m_env_mutex);
-
-               // Execute script shutdown hooks
-               m_script->on_shutdown();
-
+               
                infostream << "Server: Saving players" << std::endl;
                m_env->saveLoadedPlayers();
 
                infostream << "Server: Saving players" << std::endl;
                m_env->saveLoadedPlayers();
 
@@ -383,11 +328,25 @@ Server::~Server()
                        reconnect = m_shutdown_ask_reconnect;
                        kick_msg = m_shutdown_msg;
                }
                        reconnect = m_shutdown_ask_reconnect;
                        kick_msg = m_shutdown_msg;
                }
-               if (kick_msg == "") {
+               if (kick_msg.empty()) {
                        kick_msg = g_settings->get("kick_msg_shutdown");
                }
                m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
                        kick_msg, reconnect);
                        kick_msg = g_settings->get("kick_msg_shutdown");
                }
                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();
 
                infostream << "Server: Saving environment metadata" << std::endl;
                m_env->saveMeta();
@@ -397,16 +356,9 @@ Server::~Server()
        stop();
        delete m_thread;
 
        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 things in the reverse order of creation
-       delete m_env;
-
-       // N.B. the EmergeManager should be deleted after the Environment since Map
-       // depends on EmergeManager to write its current params to the map meta
        delete m_emerge;
        delete m_emerge;
+       delete m_env;
        delete m_rollback;
        delete m_banmanager;
        delete m_event;
        delete m_rollback;
        delete m_banmanager;
        delete m_event;
@@ -415,54 +367,46 @@ Server::~Server()
        delete m_craftdef;
 
        // Deinitialize scripting
        delete m_craftdef;
 
        // Deinitialize scripting
-       infostream<<"Server: Deinitializing scripting"<<std::endl;
+       infostream << "Server: Deinitializing scripting" << std::endl;
        delete m_script;
 
        // Delete detached inventories
        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 (auto &detached_inventory : m_detached_inventories) {
+               delete detached_inventory.second;
        }
 }
 
        }
 }
 
-void Server::start(Address bind_addr)
+void Server::start()
 {
 {
-       DSTACK(FUNCTION_NAME);
-
-       m_bind_addr = bind_addr;
-
-       infostream<<"Starting server on "
-                       << bind_addr.serializeString() <<"..."<<std::endl;
+       infostream << "Starting server on " << m_bind_addr.serializeString()
+                       << "..." << std::endl;
 
        // Stop thread if already running
        m_thread->stop();
 
        // Initialize connection
 
        // Stop thread if already running
        m_thread->stop();
 
        // Initialize connection
-       m_con.SetTimeoutMs(30);
-       m_con.Serve(bind_addr);
+       m_con->SetTimeoutMs(30);
+       m_con->Serve(m_bind_addr);
 
        // Start thread
        m_thread->start();
 
        // ASCII art for the win!
        actionstream
 
        // Start thread
        m_thread->start();
 
        // ASCII art for the win!
        actionstream
-       <<"        .__               __                   __   "<<std::endl
-       <<"  _____ |__| ____   _____/  |_  ____   _______/  |_ "<<std::endl
-       <<" /     \\|  |/    \\_/ __ \\   __\\/ __ \\ /  ___/\\   __\\"<<std::endl
-       <<"|  Y Y  \\  |   |  \\  ___/|  | \\  ___/ \\___ \\  |  |  "<<std::endl
-       <<"|__|_|  /__|___|  /\\___  >__|  \\___  >____  > |__|  "<<std::endl
-       <<"      \\/        \\/     \\/          \\/     \\/        "<<std::endl;
-       actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
-       actionstream<<"Server for gameid=\""<<m_gamespec.id
-                       <<"\" listening on "<<bind_addr.serializeString()<<":"
-                       <<bind_addr.getPort() << "."<<std::endl;
+               << "        .__               __                   __   " << std::endl
+               << "  _____ |__| ____   _____/  |_  ____   _______/  |_ " << std::endl
+               << " /     \\|  |/    \\_/ __ \\   __\\/ __ \\ /  ___/\\   __\\" << std::endl
+               << "|  Y Y  \\  |   |  \\  ___/|  | \\  ___/ \\___ \\  |  |  " << std::endl
+               << "|__|_|  /__|___|  /\\___  >__|  \\___  >____  > |__|  " << std::endl
+               << "      \\/        \\/     \\/          \\/     \\/        " << std::endl;
+       actionstream << "World at [" << m_path_world << "]" << std::endl;
+       actionstream << "Server for gameid=\"" << m_gamespec.id
+                       << "\" listening on " << m_bind_addr.serializeString() << ":"
+                       << m_bind_addr.getPort() << "." << std::endl;
 }
 
 void Server::stop()
 {
 }
 
 void Server::stop()
 {
-       DSTACK(FUNCTION_NAME);
-
        infostream<<"Server: Stopping and waiting threads"<<std::endl;
 
        // Stop threads (set run=false first so both start stopping)
        infostream<<"Server: Stopping and waiting threads"<<std::endl;
 
        // Stop threads (set run=false first so both start stopping)
@@ -476,7 +420,6 @@ void Server::stop()
 
 void Server::step(float dtime)
 {
 
 void Server::step(float dtime)
 {
-       DSTACK(FUNCTION_NAME);
        // Limit a bit
        if (dtime > 2.0)
                dtime = 2.0;
        // Limit a bit
        if (dtime > 2.0)
                dtime = 2.0;
@@ -492,14 +435,12 @@ void Server::step(float dtime)
                                g_settings->get("kick_msg_crash"),
                                g_settings->getBool("ask_reconnect_on_crash"));
                }
                                g_settings->get("kick_msg_crash"),
                                g_settings->getBool("ask_reconnect_on_crash"));
                }
-               throw ServerError(async_err);
+               throw ServerError("AsyncErr: " + async_err);
        }
 }
 
 void Server::AsyncRunStep(bool initial_step)
 {
        }
 }
 
 void Server::AsyncRunStep(bool initial_step)
 {
-       DSTACK(FUNCTION_NAME);
-
        g_profiler->add("Server::AsyncRunStep (num)", 1);
 
        float dtime;
        g_profiler->add("Server::AsyncRunStep (num)", 1);
 
        float dtime;
@@ -513,7 +454,7 @@ void Server::AsyncRunStep(bool initial_step)
                SendBlocks(dtime);
        }
 
                SendBlocks(dtime);
        }
 
-       if((dtime < 0.001) && (initial_step == false))
+       if((dtime < 0.001) && !initial_step)
                return;
 
        g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
                return;
 
        g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
@@ -612,24 +553,8 @@ void Server::AsyncRunStep(bool initial_step)
                ScopeProfiler sp(g_profiler, "Server: liquid transform");
 
                std::map<v3s16, MapBlock*> modified_blocks;
                ScopeProfiler sp(g_profiler, "Server: liquid transform");
 
                std::map<v3s16, MapBlock*> modified_blocks;
-               m_env->getMap().transformLiquids(modified_blocks);
-#if 0
-               /*
-                       Update lighting
-               */
-               core::map<v3s16, MapBlock*> lighting_modified_blocks;
-               ServerMap &map = ((ServerMap&)m_env->getMap());
-               map.updateLighting(modified_blocks, lighting_modified_blocks);
-
-               // Add blocks modified by lighting to modified_blocks
-               for(core::map<v3s16, MapBlock*>::Iterator
-                               i = lighting_modified_blocks.getIterator();
-                               i.atEnd() == false; i++)
-               {
-                       MapBlock *block = i.getNode()->getValue();
-                       modified_blocks.insert(block->getPos(), block);
-               }
-#endif
+               m_env->getMap().transformLiquids(modified_blocks, m_env);
+
                /*
                        Set the modified blocks unsent for all the clients
                */
                /*
                        Set the modified blocks unsent for all the clients
                */
@@ -645,18 +570,19 @@ void Server::AsyncRunStep(bool initial_step)
        // send masterserver announce
        {
                float &counter = m_masterserver_timer;
        // send masterserver announce
        {
                float &counter = m_masterserver_timer;
-               if(!isSingleplayer() && (!counter || counter >= 300.0) &&
-                               g_settings->getBool("server_announce"))
-               {
-                       ServerList::sendAnnounce(counter ? "update" : "start",
+               if (!isSingleplayer() && (!counter || counter >= 300.0) &&
+                               g_settings->getBool("server_announce")) {
+                       ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
+                                               ServerList::AA_START,
                                        m_bind_addr.getPort(),
                                        m_clients.getPlayerNames(),
                                        m_uptime.get(),
                                        m_env->getGameTime(),
                                        m_lag,
                                        m_gamespec.id,
                                        m_bind_addr.getPort(),
                                        m_clients.getPlayerNames(),
                                        m_uptime.get(),
                                        m_env->getGameTime(),
                                        m_lag,
                                        m_gamespec.id,
-                                       m_emerge->params.mg_name,
-                                       m_mods);
+                                       Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
+                                       m_mods,
+                                       m_dedicated);
                        counter = 0.01;
                }
                counter += dtime;
                        counter = 0.01;
                }
                counter += dtime;
@@ -671,46 +597,50 @@ void Server::AsyncRunStep(bool initial_step)
                MutexAutoLock envlock(m_env_mutex);
 
                m_clients.lock();
                MutexAutoLock envlock(m_env_mutex);
 
                m_clients.lock();
-               std::map<u16, RemoteClient*> clients = m_clients.getClientList();
+               const RemoteClientMap &clients = m_clients.getClientList();
                ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
 
                // Radius inside which objects are active
                ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
 
                // Radius inside which objects are active
-               static const s16 radius =
+               static thread_local const s16 radius =
                        g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
 
                // Radius inside which players are active
                        g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
 
                // Radius inside which players are active
-               static const bool is_transfer_limited =
+               static thread_local const bool is_transfer_limited =
                        g_settings->exists("unlimited_player_transfer_distance") &&
                        !g_settings->getBool("unlimited_player_transfer_distance");
                        g_settings->exists("unlimited_player_transfer_distance") &&
                        !g_settings->getBool("unlimited_player_transfer_distance");
-               static const s16 player_transfer_dist = g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
+               static thread_local const s16 player_transfer_dist =
+                       g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
                s16 player_radius = player_transfer_dist;
                if (player_radius == 0 && is_transfer_limited)
                        player_radius = radius;
 
                s16 player_radius = player_transfer_dist;
                if (player_radius == 0 && is_transfer_limited)
                        player_radius = radius;
 
-               for (std::map<u16, RemoteClient*>::iterator
-                       i = clients.begin();
-                       i != clients.end(); ++i) {
-                       RemoteClient *client = i->second;
+               for (const auto &client_it : clients) {
+                       RemoteClient *client = client_it.second;
 
                        // If definitions and textures have not been sent, don't
                        // send objects either
                        if (client->getState() < CS_DefinitionsSent)
                                continue;
 
 
                        // If definitions and textures have not been sent, don't
                        // send objects either
                        if (client->getState() < CS_DefinitionsSent)
                                continue;
 
-                       Player *player = m_env->getPlayer(client->peer_id);
-                       if(player == NULL) {
+                       RemotePlayer *player = m_env->getPlayer(client->peer_id);
+                       if (!player) {
                                // This can happen if the client timeouts somehow
                                // This can happen if the client timeouts somehow
-                               /*warningstream<<FUNCTION_NAME<<": Client "
-                                               <<client->peer_id
-                                               <<" has no associated player"<<std::endl;*/
                                continue;
                        }
 
                                continue;
                        }
 
+                       PlayerSAO *playersao = player->getPlayerSAO();
+                       if (!playersao)
+                               continue;
+
+                       s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
+                       if (my_radius <= 0) my_radius = radius;
+                       //infostream << "Server: Active Radius " << my_radius << std::endl;
+
                        std::queue<u16> removed_objects;
                        std::queue<u16> added_objects;
                        std::queue<u16> removed_objects;
                        std::queue<u16> added_objects;
-                       m_env->getRemovedActiveObjects(playerradius, player_radius,
+                       m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
                                        client->m_known_objects, removed_objects);
                                        client->m_known_objects, removed_objects);
-                       m_env->getAddedActiveObjects(playerradius, player_radius,
+                       m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
                                        client->m_known_objects, added_objects);
 
                        // Ignore if nothing happened
                                        client->m_known_objects, added_objects);
 
                        // Ignore if nothing happened
@@ -752,9 +682,8 @@ void Server::AsyncRunStep(bool initial_step)
 
                                // Get object type
                                u8 type = ACTIVEOBJECT_TYPE_INVALID;
 
                                // Get object type
                                u8 type = ACTIVEOBJECT_TYPE_INVALID;
-                               if(obj == NULL)
-                                       warningstream<<FUNCTION_NAME
-                                                       <<": NULL object"<<std::endl;
+                               if (!obj)
+                                       warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
                                else
                                        type = obj->getSendType();
 
                                else
                                        type = obj->getSendType();
 
@@ -786,6 +715,18 @@ void Server::AsyncRunStep(bool initial_step)
                                        << "packet size is " << pktSize << std::endl;
                }
                m_clients.unlock();
                                        << "packet size is " << pktSize << std::endl;
                }
                m_clients.unlock();
+
+               m_mod_storage_save_timer -= dtime;
+               if (m_mod_storage_save_timer <= 0.0f) {
+                       infostream << "Saving registered mod storages." << std::endl;
+                       m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
+                       for (std::unordered_map<std::string, ModMetadata *>::const_iterator
+                               it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
+                               if (it->second->isModified()) {
+                                       it->second->save(getModStoragePath());
+                               }
+                       }
+               }
        }
 
        /*
        }
 
        /*
@@ -797,7 +738,7 @@ void Server::AsyncRunStep(bool initial_step)
 
                // Key = object id
                // Value = data sent by object
 
                // Key = object id
                // Value = data sent by object
-               std::map<u16, std::vector<ActiveObjectMessage>* > buffered_messages;
+               std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
 
                // Get active object messages from environment
                for(;;) {
 
                // Get active object messages from environment
                for(;;) {
@@ -805,8 +746,8 @@ void Server::AsyncRunStep(bool initial_step)
                        if (aom.id == 0)
                                break;
 
                        if (aom.id == 0)
                                break;
 
-                       std::vector<ActiveObjectMessage>* message_list = NULL;
-                       std::map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
+                       std::vector<ActiveObjectMessage>* message_list = nullptr;
+                       std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
                        n = buffered_messages.find(aom.id);
                        if (n == buffered_messages.end()) {
                                message_list = new std::vector<ActiveObjectMessage>;
                        n = buffered_messages.find(aom.id);
                        if (n == buffered_messages.end()) {
                                message_list = new std::vector<ActiveObjectMessage>;
@@ -819,30 +760,24 @@ void Server::AsyncRunStep(bool initial_step)
                }
 
                m_clients.lock();
                }
 
                m_clients.lock();
-               std::map<u16, RemoteClient*> clients = m_clients.getClientList();
+               const RemoteClientMap &clients = m_clients.getClientList();
                // Route data to every client
                // Route data to every client
-               for (std::map<u16, RemoteClient*>::iterator
-                       i = clients.begin();
-                       i != clients.end(); ++i) {
-                       RemoteClient *client = i->second;
+               for (const auto &client_it : clients) {
+                       RemoteClient *client = client_it.second;
                        std::string reliable_data;
                        std::string unreliable_data;
                        // Go through all objects in message buffer
                        std::string reliable_data;
                        std::string unreliable_data;
                        // Go through all objects in message buffer
-                       for (std::map<u16, std::vector<ActiveObjectMessage>* >::iterator
-                                       j = buffered_messages.begin();
-                                       j != buffered_messages.end(); ++j) {
+                       for (const auto &buffered_message : buffered_messages) {
                                // If object is not known by client, skip it
                                // If object is not known by client, skip it
-                               u16 id = j->first;
+                               u16 id = buffered_message.first;
                                if (client->m_known_objects.find(id) == client->m_known_objects.end())
                                        continue;
 
                                // Get message list of object
                                if (client->m_known_objects.find(id) == client->m_known_objects.end())
                                        continue;
 
                                // Get message list of object
-                               std::vector<ActiveObjectMessage>* list = j->second;
+                               std::vector<ActiveObjectMessage>* list = buffered_message.second;
                                // Go through every message
                                // Go through every message
-                               for (std::vector<ActiveObjectMessage>::iterator
-                                               k = list->begin(); k != list->end(); ++k) {
+                               for (const ActiveObjectMessage &aom : *list) {
                                        // Compose the full new data with header
                                        // Compose the full new data with header
-                                       ActiveObjectMessage aom = *k;
                                        std::string new_data;
                                        // Add object id
                                        char buf[2];
                                        std::string new_data;
                                        // Add object id
                                        char buf[2];
@@ -851,7 +786,7 @@ void Server::AsyncRunStep(bool initial_step)
                                        // Add data
                                        new_data += serializeString(aom.datastring);
                                        // Add data to buffer
                                        // Add data
                                        new_data += serializeString(aom.datastring);
                                        // Add data to buffer
-                                       if(aom.reliable)
+                                       if (aom.reliable)
                                                reliable_data += new_data;
                                        else
                                                unreliable_data += new_data;
                                                reliable_data += new_data;
                                        else
                                                unreliable_data += new_data;
@@ -861,21 +796,19 @@ void Server::AsyncRunStep(bool initial_step)
                                reliable_data and unreliable_data are now ready.
                                Send them.
                        */
                                reliable_data and unreliable_data are now ready.
                                Send them.
                        */
-                       if(reliable_data.size() > 0) {
+                       if (!reliable_data.empty()) {
                                SendActiveObjectMessages(client->peer_id, reliable_data);
                        }
 
                                SendActiveObjectMessages(client->peer_id, reliable_data);
                        }
 
-                       if(unreliable_data.size() > 0) {
+                       if (!unreliable_data.empty()) {
                                SendActiveObjectMessages(client->peer_id, unreliable_data, false);
                        }
                }
                m_clients.unlock();
 
                // Clear buffered_messages
                                SendActiveObjectMessages(client->peer_id, unreliable_data, false);
                        }
                }
                m_clients.unlock();
 
                // Clear buffered_messages
-               for(std::map<u16, std::vector<ActiveObjectMessage>* >::iterator
-                               i = buffered_messages.begin();
-                               i != buffered_messages.end(); ++i) {
-                       delete i->second;
+               for (auto &buffered_message : buffered_messages) {
+                       delete buffered_message.second;
                }
        }
 
                }
        }
 
@@ -899,8 +832,7 @@ void Server::AsyncRunStep(bool initial_step)
                // We'll log the amount of each
                Profiler prof;
 
                // We'll log the amount of each
                Profiler prof;
 
-               while(m_unsent_map_edit_queue.size() != 0)
-               {
+               while (!m_unsent_map_edit_queue.empty()) {
                        MapEditEvent* event = m_unsent_map_edit_queue.front();
                        m_unsent_map_edit_queue.pop();
 
                        MapEditEvent* event = m_unsent_map_edit_queue.front();
                        m_unsent_map_edit_queue.pop();
 
@@ -930,10 +862,8 @@ void Server::AsyncRunStep(bool initial_step)
                        case MEET_OTHER:
                                infostream << "Server: MEET_OTHER" << std::endl;
                                prof.add("MEET_OTHER", 1);
                        case MEET_OTHER:
                                infostream << "Server: MEET_OTHER" << std::endl;
                                prof.add("MEET_OTHER", 1);
-                               for(std::set<v3s16>::iterator
-                                               i = event->modified_blocks.begin();
-                                               i != event->modified_blocks.end(); ++i) {
-                                       setBlockNotSent(*i);
+                               for (const v3s16 &modified_block : event->modified_blocks) {
+                                       setBlockNotSent(modified_block);
                                }
                                break;
                        default:
                                }
                                break;
                        default:
@@ -946,38 +876,29 @@ void Server::AsyncRunStep(bool initial_step)
                        /*
                                Set blocks not sent to far players
                        */
                        /*
                                Set blocks not sent to far players
                        */
-                       if(!far_players.empty()) {
+                       if (!far_players.empty()) {
                                // Convert list format to that wanted by SetBlocksNotSent
                                std::map<v3s16, MapBlock*> modified_blocks2;
                                // Convert list format to that wanted by SetBlocksNotSent
                                std::map<v3s16, MapBlock*> modified_blocks2;
-                               for(std::set<v3s16>::iterator
-                                               i = event->modified_blocks.begin();
-                                               i != event->modified_blocks.end(); ++i) {
-                                       modified_blocks2[*i] =
-                                                       m_env->getMap().getBlockNoCreateNoEx(*i);
+                               for (const v3s16 &modified_block : event->modified_blocks) {
+                                       modified_blocks2[modified_block] =
+                                                       m_env->getMap().getBlockNoCreateNoEx(modified_block);
                                }
 
                                // Set blocks not sent
                                }
 
                                // Set blocks not sent
-                               for(std::vector<u16>::iterator
-                                               i = far_players.begin();
-                                               i != far_players.end(); ++i) {
-                                       if(RemoteClient *client = getClient(*i))
+                               for (const u16 far_player : far_players) {
+                                       if (RemoteClient *client = getClient(far_player))
                                                client->SetBlocksNotSent(modified_blocks2);
                                }
                        }
 
                        delete event;
                                                client->SetBlocksNotSent(modified_blocks2);
                                }
                        }
 
                        delete event;
-
-                       /*// Don't send too many at a time
-                       count++;
-                       if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
-                               break;*/
                }
 
                }
 
-               if(event_count >= 5){
-                       infostream<<"Server: MapEditEvents:"<<std::endl;
+               if (event_count >= 5) {
+                       infostream << "Server: MapEditEvents:" << std::endl;
                        prof.print(infostream);
                        prof.print(infostream);
-               } else if(event_count != 0){
-                       verbosestream<<"Server: MapEditEvents:"<<std::endl;
+               } else if (event_count != 0) {
+                       verbosestream << "Server: MapEditEvents:" << std::endl;
                        prof.print(verbosestream);
                }
 
                        prof.print(verbosestream);
                }
 
@@ -1001,7 +922,7 @@ void Server::AsyncRunStep(bool initial_step)
        {
                float &counter = m_savemap_timer;
                counter += dtime;
        {
                float &counter = m_savemap_timer;
                counter += dtime;
-               static const float save_interval =
+               static thread_local const float save_interval =
                        g_settings->getFloat("server_map_save_interval");
                if (counter >= save_interval) {
                        counter = 0.0;
                        g_settings->getFloat("server_map_save_interval");
                if (counter >= save_interval) {
                        counter = 0.0;
@@ -1024,47 +945,72 @@ void Server::AsyncRunStep(bool initial_step)
                        m_env->saveMeta();
                }
        }
                        m_env->saveMeta();
                }
        }
+
+       // Timed shutdown
+       static const float shutdown_msg_times[] =
+       {
+               1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
+       };
+
+       if (m_shutdown_timer > 0.0f) {
+               // Automated messages
+               if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
+                       for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
+                               // If shutdown timer matches an automessage, shot it
+                               if (m_shutdown_timer > shutdown_msg_times[i] &&
+                                       m_shutdown_timer - dtime < shutdown_msg_times[i]) {
+                                       std::wstringstream ws;
+
+                                       ws << L"*** Server shutting down in "
+                                               << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
+                                               << ".";
+
+                                       infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
+                                       SendChatMessage(PEER_ID_INEXISTENT, ws.str());
+                                       break;
+                               }
+                       }
+               }
+
+               m_shutdown_timer -= dtime;
+               if (m_shutdown_timer < 0.0f) {
+                       m_shutdown_timer = 0.0f;
+                       m_shutdown_requested = true;
+               }
+       }
 }
 
 void Server::Receive()
 {
 }
 
 void Server::Receive()
 {
-       DSTACK(FUNCTION_NAME);
-       SharedBuffer<u8> data;
-       u16 peer_id;
+       session_t peer_id;
        try {
                NetworkPacket pkt;
        try {
                NetworkPacket pkt;
-               m_con.Receive(&pkt);
+               m_con->Receive(&pkt);
                peer_id = pkt.getPeerId();
                ProcessData(&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;
+       } 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 << e.what() << std::endl;
                DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
                                L"Try reconnecting or updating your client");
                DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
                                L"Try reconnecting or updating your client");
-       }
-       catch(con::PeerNotFoundException &e) {
+       } catch (const con::PeerNotFoundException &e) {
                // Do nothing
        }
 }
 
                // Do nothing
        }
 }
 
-PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
+PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
 {
 {
-       std::string playername = "";
+       std::string playername;
        PlayerSAO *playersao = NULL;
        m_clients.lock();
        try {
                RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
        PlayerSAO *playersao = NULL;
        m_clients.lock();
        try {
                RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
-               if (client != NULL) {
+               if (client) {
                        playername = client->getName();
                        playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
                }
                        playername = client->getName();
                        playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
                }
@@ -1074,12 +1020,11 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
        }
        m_clients.unlock();
 
        }
        m_clients.unlock();
 
-       RemotePlayer *player =
-               static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
+       RemotePlayer *player = m_env->getPlayer(playername.c_str());
 
        // If failed, cancel
 
        // If failed, cancel
-       if ((playersao == NULL) || (player == NULL)) {
-               if (player && player->peer_id != 0) {
+       if (!playersao || !player) {
+               if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
                        actionstream << "Server: Failed to emerge player \"" << playername
                                        << "\" (player allocated to an another client)" << std::endl;
                        DenyAccess_Legacy(peer_id, L"Another client is connected with this "
                        actionstream << "Server: Failed to emerge player \"" << playername
                                        << "\" (player allocated to an another client)" << std::endl;
                        DenyAccess_Legacy(peer_id, L"Another client is connected with this "
@@ -1107,52 +1052,33 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
        // Send inventory
        SendInventory(playersao);
 
        // Send inventory
        SendInventory(playersao);
 
-       // Send HP
-       SendPlayerHPOrDie(playersao);
+       // Send HP or death screen
+       if (playersao->isDead())
+               SendDeathscreen(peer_id, false, v3f(0,0,0));
+       else
+               SendPlayerHPOrDie(playersao);
 
        // Send Breath
 
        // Send Breath
-       SendPlayerBreath(peer_id);
-
-       // Show death screen if necessary
-       if(player->isDead())
-               SendDeathscreen(peer_id, false, v3f(0,0,0));
+       SendPlayerBreath(playersao);
 
        // Note things in chat if not in simple singleplayer mode
 
        // Note things in chat if not in simple singleplayer mode
-       if(!m_simple_singleplayer_mode) {
+       if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
                // Send information about server to player in chat
                // Send information about server to player in chat
-               SendChatMessage(peer_id, getStatusString());
-
-               // Send information about joining in chat
-               {
-                       std::string name = "unknown";
-                       Player *player = m_env->getPlayer(peer_id);
-                       if(player != NULL)
-                               name = player->getName();
-
-                       std::wstring message;
-                       message += L"*** ";
-                       message += narrow_to_wide(name);
-                       message += L" joined the game.";
-                       SendChatMessage(PEER_ID_INEXISTENT,message);
-                       if (m_admin_chat)
-                               m_admin_chat->outgoing_queue.push_back(
-                                       new ChatEventNick(CET_NICK_ADD, name));
-               }
+               SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
        }
        }
-       Address addr = getPeerAddress(player->peer_id);
+       Address addr = getPeerAddress(player->getPeerId());
        std::string ip_str = addr.serializeString();
        actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
        /*
                Print out action
        */
        {
        std::string ip_str = addr.serializeString();
        actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
        /*
                Print out action
        */
        {
-               std::vector<std::string> names = m_clients.getPlayerNames();
+               const std::vector<std::string> &names = m_clients.getPlayerNames();
 
 
-               actionstream<<player->getName() <<" joins game. List of players: ";
+               actionstream << player->getName() << " joins game. List of players: ";
 
 
-               for (std::vector<std::string>::iterator i = names.begin();
-                               i != names.end(); ++i) {
-                       actionstream << *i << " ";
+               for (const std::string &name : names) {
+                       actionstream << name << " ";
                }
 
                actionstream << player->getName() <<std::endl;
                }
 
                actionstream << player->getName() <<std::endl;
@@ -1168,7 +1094,6 @@ inline void Server::handleCommand(NetworkPacket* pkt)
 
 void Server::ProcessData(NetworkPacket *pkt)
 {
 
 void Server::ProcessData(NetworkPacket *pkt)
 {
-       DSTACK(FUNCTION_NAME);
        // Environment is locked first.
        MutexAutoLock envlock(m_env_mutex);
 
        // Environment is locked first.
        MutexAutoLock envlock(m_env_mutex);
 
@@ -1277,7 +1202,7 @@ Inventory* Server::getInventory(const InventoryLocation &loc)
                break;
        case InventoryLocation::PLAYER:
        {
                break;
        case InventoryLocation::PLAYER:
        {
-               Player *player = m_env->getPlayer(loc.name.c_str());
+               RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
                if(!player)
                        return NULL;
                PlayerSAO *playersao = player->getPlayerSAO();
                if(!player)
                        return NULL;
                PlayerSAO *playersao = player->getPlayerSAO();
@@ -1317,9 +1242,11 @@ void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
                if (!playerSend)
                        return;
 
                if (!playerSend)
                        return;
 
-               Player *player = m_env->getPlayer(loc.name.c_str());
-               if(!player)
+               RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
+
+               if (!player)
                        return;
                        return;
+
                PlayerSAO *playersao = player->getPlayerSAO();
                if(!playersao)
                        return;
                PlayerSAO *playersao = player->getPlayerSAO();
                if(!playersao)
                        return;
@@ -1332,7 +1259,7 @@ void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
                v3s16 blockpos = getNodeBlockPos(loc.p);
 
                MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
                v3s16 blockpos = getNodeBlockPos(loc.p);
 
                MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
-               if(block)
+               if (block)
                        block->raiseModified(MOD_STATE_WRITE_NEEDED);
 
                setBlockNotSent(blockpos);
                        block->raiseModified(MOD_STATE_WRITE_NEEDED);
 
                setBlockNotSent(blockpos);
@@ -1351,12 +1278,11 @@ void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
 
 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
 {
 
 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
 {
-       std::vector<u16> clients = m_clients.getClientIDs();
+       std::vector<session_t> clients = m_clients.getClientIDs();
        m_clients.lock();
        // Set the modified blocks unsent for all the clients
        m_clients.lock();
        // Set the modified blocks unsent for all the clients
-       for (std::vector<u16>::iterator i = clients.begin();
-                i != clients.end(); ++i) {
-                       if (RemoteClient *client = m_clients.lockedGetClientNoEx(*i))
+       for (const session_t client_id : clients) {
+                       if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
                                client->SetBlocksNotSent(block);
        }
        m_clients.unlock();
                                client->SetBlocksNotSent(block);
        }
        m_clients.unlock();
@@ -1364,40 +1290,29 @@ void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
 
 void Server::peerAdded(con::Peer *peer)
 {
 
 void Server::peerAdded(con::Peer *peer)
 {
-       DSTACK(FUNCTION_NAME);
        verbosestream<<"Server::peerAdded(): peer->id="
                        <<peer->id<<std::endl;
 
        verbosestream<<"Server::peerAdded(): peer->id="
                        <<peer->id<<std::endl;
 
-       con::PeerChange c;
-       c.type = con::PEER_ADDED;
-       c.peer_id = peer->id;
-       c.timeout = false;
-       m_peer_change_queue.push(c);
+       m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
 }
 
 void Server::deletingPeer(con::Peer *peer, bool timeout)
 {
 }
 
 void Server::deletingPeer(con::Peer *peer, bool timeout)
 {
-       DSTACK(FUNCTION_NAME);
        verbosestream<<"Server::deletingPeer(): peer->id="
                        <<peer->id<<", timeout="<<timeout<<std::endl;
 
        m_clients.event(peer->id, CSE_Disconnect);
        verbosestream<<"Server::deletingPeer(): peer->id="
                        <<peer->id<<", timeout="<<timeout<<std::endl;
 
        m_clients.event(peer->id, CSE_Disconnect);
-       con::PeerChange c;
-       c.type = con::PEER_REMOVED;
-       c.peer_id = peer->id;
-       c.timeout = timeout;
-       m_peer_change_queue.push(c);
+       m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
 }
 
 }
 
-bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
+bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
 {
 {
-       *retval = m_con.getPeerStat(peer_id,type);
-       if (*retval == -1) return false;
-       return true;
+       *retval = m_con->getPeerStat(peer_id,type);
+       return *retval != -1;
 }
 
 bool Server::getClientInfo(
 }
 
 bool Server::getClientInfo(
-               u16          peer_id,
+               session_t    peer_id,
                ClientState* state,
                u32*         uptime,
                u8*          ser_vers,
                ClientState* state,
                u32*         uptime,
                u8*          ser_vers,
@@ -1412,7 +1327,7 @@ bool Server::getClientInfo(
        m_clients.lock();
        RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
 
        m_clients.lock();
        RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
 
-       if (client == NULL) {
+       if (!client) {
                m_clients.unlock();
                return false;
        }
                m_clients.unlock();
                return false;
        }
@@ -1433,7 +1348,7 @@ bool Server::getClientInfo(
 
 void Server::handlePeerChanges()
 {
 
 void Server::handlePeerChanges()
 {
-       while(m_peer_change_queue.size() > 0)
+       while(!m_peer_change_queue.empty())
        {
                con::PeerChange c = m_peer_change_queue.front();
                m_peer_change_queue.pop();
        {
                con::PeerChange c = m_peer_change_queue.front();
                m_peer_change_queue.pop();
@@ -1469,17 +1384,21 @@ void Server::printToConsoleOnly(const std::string &text)
        }
 }
 
        }
 }
 
-void Server::Send(NetworkPacket* pkt)
+void Server::Send(NetworkPacket *pkt)
+{
+       Send(pkt->getPeerId(), pkt);
+}
+
+void Server::Send(session_t peer_id, NetworkPacket *pkt)
 {
 {
-       m_clients.send(pkt->getPeerId(),
+       m_clients.send(peer_id,
                clientCommandFactoryTable[pkt->getCommand()].channel,
                pkt,
                clientCommandFactoryTable[pkt->getCommand()].reliable);
 }
 
                clientCommandFactoryTable[pkt->getCommand()].channel,
                pkt,
                clientCommandFactoryTable[pkt->getCommand()].reliable);
 }
 
-void Server::SendMovement(u16 peer_id)
+void Server::SendMovement(session_t peer_id)
 {
 {
-       DSTACK(FUNCTION_NAME);
        std::ostringstream os(std::ios_base::binary);
 
        NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
        std::ostringstream os(std::ios_base::binary);
 
        NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
@@ -1505,7 +1424,7 @@ void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
        if (!g_settings->getBool("enable_damage"))
                return;
 
        if (!g_settings->getBool("enable_damage"))
                return;
 
-       u16 peer_id   = playersao->getPeerID();
+       session_t peer_id   = playersao->getPeerID();
        bool is_alive = playersao->getHP() > 0;
 
        if (is_alive)
        bool is_alive = playersao->getHP() > 0;
 
        if (is_alive)
@@ -1514,25 +1433,21 @@ void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
                DiePlayer(peer_id);
 }
 
                DiePlayer(peer_id);
 }
 
-void Server::SendHP(u16 peer_id, u8 hp)
+void Server::SendHP(session_t peer_id, u16 hp)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
        pkt << hp;
        Send(&pkt);
 }
 
        NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
        pkt << hp;
        Send(&pkt);
 }
 
-void Server::SendBreath(u16 peer_id, u16 breath)
+void Server::SendBreath(session_t peer_id, u16 breath)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
        pkt << (u16) breath;
        Send(&pkt);
 }
 
        NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
        pkt << (u16) breath;
        Send(&pkt);
 }
 
-void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
+void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
                const std::string &custom_reason, bool reconnect)
 {
        assert(reason < SERVER_ACCESSDENIED_MAX);
                const std::string &custom_reason, bool reconnect)
 {
        assert(reason < SERVER_ACCESSDENIED_MAX);
@@ -1547,30 +1462,24 @@ void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
        Send(&pkt);
 }
 
        Send(&pkt);
 }
 
-void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
+void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
        pkt << reason;
        Send(&pkt);
 }
 
        NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
        pkt << reason;
        Send(&pkt);
 }
 
-void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
+void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
                v3f camera_point_target)
 {
                v3f camera_point_target)
 {
-       DSTACK(FUNCTION_NAME);
-
        NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
        pkt << set_camera_point_target << camera_point_target;
        Send(&pkt);
 }
 
        NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
        pkt << set_camera_point_target << camera_point_target;
        Send(&pkt);
 }
 
-void Server::SendItemDef(u16 peer_id,
+void Server::SendItemDef(session_t peer_id,
                IItemDefManager *itemdef, u16 protocol_version)
 {
                IItemDefManager *itemdef, u16 protocol_version)
 {
-       DSTACK(FUNCTION_NAME);
-
        NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
 
        /*
        NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
 
        /*
@@ -1591,11 +1500,9 @@ void Server::SendItemDef(u16 peer_id,
        Send(&pkt);
 }
 
        Send(&pkt);
 }
 
-void Server::SendNodeDef(u16 peer_id,
+void Server::SendNodeDef(session_t peer_id,
                INodeDefManager *nodedef, u16 protocol_version)
 {
                INodeDefManager *nodedef, u16 protocol_version)
 {
-       DSTACK(FUNCTION_NAME);
-
        NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
 
        /*
        NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
 
        /*
@@ -1623,8 +1530,6 @@ void Server::SendNodeDef(u16 peer_id,
 
 void Server::SendInventory(PlayerSAO* playerSAO)
 {
 
 void Server::SendInventory(PlayerSAO* playerSAO)
 {
-       DSTACK(FUNCTION_NAME);
-
        UpdateCrafting(playerSAO->getPlayer());
 
        /*
        UpdateCrafting(playerSAO->getPlayer());
 
        /*
@@ -1642,40 +1547,73 @@ void Server::SendInventory(PlayerSAO* playerSAO)
        Send(&pkt);
 }
 
        Send(&pkt);
 }
 
-void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
+void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
        NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
-       pkt << message;
+       u8 version = 1;
+       u8 type = message.type;
+       pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
 
        if (peer_id != PEER_ID_INEXISTENT) {
 
        if (peer_id != PEER_ID_INEXISTENT) {
+               RemotePlayer *player = m_env->getPlayer(peer_id);
+               if (!player)
+                       return;
+
                Send(&pkt);
                Send(&pkt);
-       }
-       else {
-               m_clients.sendToAll(0, &pkt, true);
+       } else {
+               m_clients.sendToAll(&pkt);
        }
 }
 
        }
 }
 
-void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
+void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
                                      const std::string &formname)
 {
                                      const std::string &formname)
 {
-       DSTACK(FUNCTION_NAME);
-
        NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
        NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
-
-       pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
+       if (formspec.empty()){
+               //the client should close the formspec
+               pkt.putLongString("");
+       } else {
+               pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
+       }
        pkt << formname;
 
        Send(&pkt);
 }
 
 // Spawns a particle on peer with peer_id
        pkt << formname;
 
        Send(&pkt);
 }
 
 // Spawns a particle on peer with peer_id
-void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
+void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
+                               v3f pos, v3f velocity, v3f acceleration,
                                float expirationtime, float size, bool collisiondetection,
                                float expirationtime, float size, bool collisiondetection,
-                               bool vertical, std::string texture)
+                               bool collision_removal,
+                               bool vertical, const std::string &texture,
+                               const struct TileAnimationParams &animation, u8 glow)
 {
 {
-       DSTACK(FUNCTION_NAME);
+       static thread_local const float radius =
+                       g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
+
+       if (peer_id == PEER_ID_INEXISTENT) {
+               std::vector<session_t> clients = m_clients.getClientIDs();
+
+               for (const session_t client_id : clients) {
+                       RemotePlayer *player = m_env->getPlayer(client_id);
+                       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(client_id, player->protocol_version,
+                                       pos, velocity, acceleration,
+                                       expirationtime, size, collisiondetection,
+                                       collision_removal, vertical, texture, animation, glow);
+               }
+               return;
+       }
 
        NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
 
 
        NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
 
@@ -1683,21 +1621,39 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat
                        << size << collisiondetection;
        pkt.putLongString(texture);
        pkt << vertical;
                        << size << collisiondetection;
        pkt.putLongString(texture);
        pkt << vertical;
+       pkt << collision_removal;
+       // This is horrible but required (why are there two ways to serialize pkts?)
+       std::ostringstream os(std::ios_base::binary);
+       animation.serialize(os, protocol_version);
+       pkt.putRawString(os.str());
+       pkt << glow;
 
 
-       if (peer_id != PEER_ID_INEXISTENT) {
-               Send(&pkt);
-       }
-       else {
-               m_clients.sendToAll(0, &pkt, true);
-       }
+       Send(&pkt);
 }
 
 // Adds a ParticleSpawner on peer with peer_id
 }
 
 // Adds a ParticleSpawner on peer with peer_id
-void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
+void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
+       u16 amount, float spawntime, v3f minpos, v3f maxpos,
        v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
        v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
-       float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id)
+       float minsize, float maxsize, bool collisiondetection, bool collision_removal,
+       u16 attached_id, bool vertical, const std::string &texture, u32 id,
+       const struct TileAnimationParams &animation, u8 glow)
 {
 {
-       DSTACK(FUNCTION_NAME);
+       if (peer_id == PEER_ID_INEXISTENT) {
+               // This sucks and should be replaced:
+               std::vector<session_t> clients = m_clients.getClientIDs();
+               for (const session_t client_id : clients) {
+                       RemotePlayer *player = m_env->getPlayer(client_id);
+                       if (!player)
+                               continue;
+                       SendAddParticleSpawner(client_id, player->protocol_version,
+                                       amount, spawntime, minpos, maxpos,
+                                       minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
+                                       minsize, maxsize, collisiondetection, collision_removal,
+                                       attached_id, vertical, texture, id, animation, glow);
+               }
+               return;
+       }
 
        NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
 
 
        NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
 
@@ -1708,34 +1664,32 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3
        pkt.putLongString(texture);
 
        pkt << id << vertical;
        pkt.putLongString(texture);
 
        pkt << id << vertical;
+       pkt << collision_removal;
+       pkt << attached_id;
+       // This is horrible but required
+       std::ostringstream os(std::ios_base::binary);
+       animation.serialize(os, protocol_version);
+       pkt.putRawString(os.str());
+       pkt << glow;
 
 
-       if (peer_id != PEER_ID_INEXISTENT) {
-               Send(&pkt);
-       }
-       else {
-               m_clients.sendToAll(0, &pkt, true);
-       }
+       Send(&pkt);
 }
 
 }
 
-void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
+void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
-       NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
+       NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
 
        // Ugly error in this packet
 
        // Ugly error in this packet
-       pkt << (u16) id;
+       pkt << id;
 
 
-       if (peer_id != PEER_ID_INEXISTENT) {
+       if (peer_id != PEER_ID_INEXISTENT)
                Send(&pkt);
                Send(&pkt);
-       }
-       else {
-               m_clients.sendToAll(0, &pkt, true);
-       }
+       else
+               m_clients.sendToAll(&pkt);
 
 }
 
 
 }
 
-void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
+void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
 {
        NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
 
 {
        NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
 
@@ -1746,14 +1700,14 @@ void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
        Send(&pkt);
 }
 
        Send(&pkt);
 }
 
-void Server::SendHUDRemove(u16 peer_id, u32 id)
+void Server::SendHUDRemove(session_t peer_id, u32 id)
 {
        NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
        pkt << id;
        Send(&pkt);
 }
 
 {
        NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
        pkt << id;
        Send(&pkt);
 }
 
-void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
+void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
 {
        NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
        pkt << id << (u8) stat;
 {
        NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
        pkt << id << (u8) stat;
@@ -1786,7 +1740,7 @@ void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value
        Send(&pkt);
 }
 
        Send(&pkt);
 }
 
-void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
+void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
 {
        NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
 
 {
        NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
 
@@ -1797,26 +1751,43 @@ void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
        Send(&pkt);
 }
 
        Send(&pkt);
 }
 
-void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
+void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
 {
        NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
        pkt << param << value;
        Send(&pkt);
 }
 
 {
        NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
        pkt << param << value;
        Send(&pkt);
 }
 
-void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
-               const std::string &type, const std::vector<std::string> &params)
+void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
+               const std::string &type, const std::vector<std::string> &params,
+               bool &clouds)
 {
        NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
        pkt << bgcolor << type << (u16) params.size();
 
 {
        NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
        pkt << bgcolor << type << (u16) params.size();
 
-       for(size_t i=0; i<params.size(); i++)
-               pkt << params[i];
+       for (const std::string &param : params)
+               pkt << param;
+
+       pkt << clouds;
+
+       Send(&pkt);
+}
+
+void Server::SendCloudParams(session_t 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);
 }
 
 
        Send(&pkt);
 }
 
-void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
+void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
                float ratio)
 {
        NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
                float ratio)
 {
        NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
@@ -1827,24 +1798,21 @@ void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
        Send(&pkt);
 }
 
        Send(&pkt);
 }
 
-void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
+void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
        pkt << time << time_speed;
 
        if (peer_id == PEER_ID_INEXISTENT) {
        NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
        pkt << time << time_speed;
 
        if (peer_id == PEER_ID_INEXISTENT) {
-               m_clients.sendToAll(0, &pkt, true);
+               m_clients.sendToAll(&pkt);
        }
        else {
                Send(&pkt);
        }
 }
 
        }
        else {
                Send(&pkt);
        }
 }
 
-void Server::SendPlayerHP(u16 peer_id)
+void Server::SendPlayerHP(session_t peer_id)
 {
 {
-       DSTACK(FUNCTION_NAME);
        PlayerSAO *playersao = getPlayerSAO(peer_id);
        // In some rare case if the player is disconnected
        // while Lua call l_punch, for example, this can be NULL
        PlayerSAO *playersao = getPlayerSAO(peer_id);
        // In some rare case if the player is disconnected
        // while Lua call l_punch, for example, this can be NULL
@@ -1860,40 +1828,38 @@ void Server::SendPlayerHP(u16 peer_id)
        playersao->m_messages_out.push(aom);
 }
 
        playersao->m_messages_out.push(aom);
 }
 
-void Server::SendPlayerBreath(u16 peer_id)
+void Server::SendPlayerBreath(PlayerSAO *sao)
 {
 {
-       DSTACK(FUNCTION_NAME);
-       PlayerSAO *playersao = getPlayerSAO(peer_id);
-       assert(playersao);
+       assert(sao);
 
 
-       m_script->player_event(playersao, "breath_changed");
-       SendBreath(peer_id, playersao->getBreath());
+       m_script->player_event(sao, "breath_changed");
+       SendBreath(sao->getPeerID(), sao->getBreath());
 }
 
 }
 
-void Server::SendMovePlayer(u16 peer_id)
+void Server::SendMovePlayer(session_t peer_id)
 {
 {
-       DSTACK(FUNCTION_NAME);
-       Player *player = m_env->getPlayer(peer_id);
+       RemotePlayer *player = m_env->getPlayer(peer_id);
        assert(player);
        assert(player);
+       PlayerSAO *sao = player->getPlayerSAO();
+       assert(sao);
 
        NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
 
        NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
-       pkt << player->getPosition() << player->getPitch() << player->getYaw();
+       pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
 
        {
 
        {
-               v3f pos = player->getPosition();
-               f32 pitch = player->getPitch();
-               f32 yaw = player->getYaw();
+               v3f pos = sao->getBasePosition();
                verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
                                << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
                verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
                                << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
-                               << " pitch=" << pitch
-                               << " yaw=" << yaw
+                               << " pitch=" << sao->getPitch()
+                               << " yaw=" << sao->getYaw()
                                << std::endl;
        }
 
        Send(&pkt);
 }
 
                                << std::endl;
        }
 
        Send(&pkt);
 }
 
-void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
+void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
+               f32 animation_speed)
 {
        NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
                peer_id);
 {
        NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
                peer_id);
@@ -1904,17 +1870,18 @@ void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f
        Send(&pkt);
 }
 
        Send(&pkt);
 }
 
-void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
+void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
 {
        NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
        pkt << first << third;
        Send(&pkt);
 }
 {
        NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
        pkt << first << third;
        Send(&pkt);
 }
-void Server::SendPlayerPrivileges(u16 peer_id)
+
+void Server::SendPlayerPrivileges(session_t peer_id)
 {
 {
-       Player *player = m_env->getPlayer(peer_id);
+       RemotePlayer *player = m_env->getPlayer(peer_id);
        assert(player);
        assert(player);
-       if(player->peer_id == PEER_ID_INEXISTENT)
+       if(player->getPeerId() == PEER_ID_INEXISTENT)
                return;
 
        std::set<std::string> privs;
                return;
 
        std::set<std::string> privs;
@@ -1923,19 +1890,18 @@ void Server::SendPlayerPrivileges(u16 peer_id)
        NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
        pkt << (u16) privs.size();
 
        NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
        pkt << (u16) privs.size();
 
-       for(std::set<std::string>::const_iterator i = privs.begin();
-                       i != privs.end(); ++i) {
-               pkt << (*i);
+       for (const std::string &priv : privs) {
+               pkt << priv;
        }
 
        Send(&pkt);
 }
 
        }
 
        Send(&pkt);
 }
 
-void Server::SendPlayerInventoryFormspec(u16 peer_id)
+void Server::SendPlayerInventoryFormspec(session_t peer_id)
 {
 {
-       Player *player = m_env->getPlayer(peer_id);
+       RemotePlayer *player = m_env->getPlayer(peer_id);
        assert(player);
        assert(player);
-       if(player->peer_id == PEER_ID_INEXISTENT)
+       if(player->getPeerId() == PEER_ID_INEXISTENT)
                return;
 
        NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
                return;
 
        NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
@@ -1943,7 +1909,7 @@ void Server::SendPlayerInventoryFormspec(u16 peer_id)
        Send(&pkt);
 }
 
        Send(&pkt);
 }
 
-u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
+u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
 {
        NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
        pkt.putRawString(datas.c_str(), datas.size());
 {
        NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
        pkt.putRawString(datas.c_str(), datas.size());
@@ -1951,7 +1917,8 @@ u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
        return pkt.getSize();
 }
 
        return pkt.getSize();
 }
 
-void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
+void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
+               bool reliable)
 {
        NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
                        datas.size(), peer_id);
 {
        NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
                        datas.size(), peer_id);
@@ -1961,7 +1928,14 @@ void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, boo
        m_clients.send(pkt.getPeerId(),
                        reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
                        &pkt, reliable);
        m_clients.send(pkt.getPeerId(),
                        reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
                        &pkt, reliable);
+}
 
 
+void Server::SendCSMFlavourLimits(session_t peer_id)
+{
+       NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
+               sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
+       pkt << m_csm_flavour_limits << m_csm_noderange_limit;
+       Send(&pkt);
 }
 
 s32 Server::playSound(const SimpleSoundSpec &spec,
 }
 
 s32 Server::playSound(const SimpleSoundSpec &spec,
@@ -1975,37 +1949,38 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
                return -1;
 
        // Filter destination clients
                return -1;
 
        // Filter destination clients
-       std::vector<u16> dst_clients;
-       if(params.to_player != "")
-       {
-               Player *player = m_env->getPlayer(params.to_player.c_str());
+       std::vector<session_t> dst_clients;
+       if(!params.to_player.empty()) {
+               RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
                if(!player){
                        infostream<<"Server::playSound: Player \""<<params.to_player
                                        <<"\" not found"<<std::endl;
                        return -1;
                }
                if(!player){
                        infostream<<"Server::playSound: Player \""<<params.to_player
                                        <<"\" not found"<<std::endl;
                        return -1;
                }
-               if(player->peer_id == PEER_ID_INEXISTENT){
+               if (player->getPeerId() == PEER_ID_INEXISTENT) {
                        infostream<<"Server::playSound: Player \""<<params.to_player
                                        <<"\" not connected"<<std::endl;
                        return -1;
                }
                        infostream<<"Server::playSound: Player \""<<params.to_player
                                        <<"\" not connected"<<std::endl;
                        return -1;
                }
-               dst_clients.push_back(player->peer_id);
-       }
-       else {
-               std::vector<u16> clients = m_clients.getClientIDs();
+               dst_clients.push_back(player->getPeerId());
+       } else {
+               std::vector<session_t> clients = m_clients.getClientIDs();
+
+               for (const session_t client_id : clients) {
+                       RemotePlayer *player = m_env->getPlayer(client_id);
+                       if (!player)
+                               continue;
 
 
-               for(std::vector<u16>::iterator
-                               i = clients.begin(); i != clients.end(); ++i) {
-                       Player *player = m_env->getPlayer(*i);
-                       if(!player)
+                       PlayerSAO *sao = player->getPlayerSAO();
+                       if (!sao)
                                continue;
 
                                continue;
 
-                       if(pos_exists) {
-                               if(player->getPosition().getDistanceFrom(pos) >
+                       if (pos_exists) {
+                               if(sao->getBasePosition().getDistanceFrom(pos) >
                                                params.max_hear_distance)
                                        continue;
                        }
                                                params.max_hear_distance)
                                        continue;
                        }
-                       dst_clients.push_back(*i);
+                       dst_clients.push_back(client_id);
                }
        }
 
                }
        }
 
@@ -2018,39 +1993,92 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
        m_playing_sounds[id] = ServerPlayingSound();
        ServerPlayingSound &psound = m_playing_sounds[id];
        psound.params = params;
        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);
        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 << params.pitch;
+
+       // Backwards compability
+       bool play_sound = gain > 0;
 
 
-       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);
+       for (const u16 dst_client : dst_clients) {
+               if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
+                       psound.clients.insert(dst_client);
+                       m_clients.send(dst_client, 0, &pkt, true);
+               }
        }
        return id;
 }
 void Server::stopSound(s32 handle)
 {
        // Get sound reference
        }
        return id;
 }
 void Server::stopSound(s32 handle)
 {
        // Get sound reference
-       std::map<s32, ServerPlayingSound>::iterator i =
-                       m_playing_sounds.find(handle);
-       if(i == m_playing_sounds.end())
+       std::unordered_map<s32, ServerPlayingSound>::iterator i =
+               m_playing_sounds.find(handle);
+       if (i == m_playing_sounds.end())
                return;
        ServerPlayingSound &psound = i->second;
 
        NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
        pkt << handle;
 
                return;
        ServerPlayingSound &psound = i->second;
 
        NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
        pkt << handle;
 
-       for(std::set<u16>::iterator i = psound.clients.begin();
-                       i != psound.clients.end(); ++i) {
+       for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
+                       si != psound.clients.end(); ++si) {
                // Send as reliable
                // Send as reliable
-               m_clients.send(*i, 0, &pkt, true);
+               m_clients.send(*si, 0, &pkt, true);
        }
        // Remove sound reference
        m_playing_sounds.erase(i);
 }
 
        }
        // Remove sound reference
        m_playing_sounds.erase(i);
 }
 
+void Server::fadeSound(s32 handle, float step, float gain)
+{
+       // Get sound reference
+       std::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 (std::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.empty())
+               m_playing_sounds.erase(i);
+
+       if (play_sound && !compat_psound.clients.empty()) {
+               // 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)
 {
 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
        std::vector<u16> *far_players, float far_d_nodes)
 {
@@ -2060,23 +2088,26 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
        NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
        pkt << p;
 
        NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
        pkt << p;
 
-       std::vector<u16> clients = m_clients.getClientIDs();
-       for(std::vector<u16>::iterator i = clients.begin();
-               i != clients.end(); ++i) {
+       std::vector<session_t> clients = m_clients.getClientIDs();
+       for (session_t client_id : clients) {
                if (far_players) {
                        // Get player
                if (far_players) {
                        // Get player
-                       if(Player *player = m_env->getPlayer(*i)) {
+                       if (RemotePlayer *player = m_env->getPlayer(client_id)) {
+                               PlayerSAO *sao = player->getPlayerSAO();
+                               if (!sao)
+                                       continue;
+
                                // If player is far away, only set modified blocks not sent
                                // If player is far away, only set modified blocks not sent
-                               v3f player_pos = player->getPosition();
-                               if(player_pos.getDistanceFrom(p_f) > maxd) {
-                                       far_players->push_back(*i);
+                               v3f player_pos = sao->getBasePosition();
+                               if (player_pos.getDistanceFrom(p_f) > maxd) {
+                                       far_players->push_back(client_id);
                                        continue;
                                }
                        }
                }
 
                // Send as reliable
                                        continue;
                                }
                        }
                }
 
                // Send as reliable
-               m_clients.send(*i, 0, &pkt, true);
+               m_clients.send(client_id, 0, &pkt, true);
        }
 }
 
        }
 }
 
@@ -2087,17 +2118,19 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
        float maxd = far_d_nodes*BS;
        v3f p_f = intToFloat(p, BS);
 
        float maxd = far_d_nodes*BS;
        v3f p_f = intToFloat(p, BS);
 
-       std::vector<u16> clients = m_clients.getClientIDs();
-       for(std::vector<u16>::iterator i = clients.begin();
-                       i != clients.end(); ++i) {
-
-               if(far_players) {
+       std::vector<session_t> clients = m_clients.getClientIDs();
+       for (const session_t client_id : clients) {
+               if (far_players) {
                        // Get player
                        // Get player
-                       if(Player *player = m_env->getPlayer(*i)) {
+                       if (RemotePlayer *player = m_env->getPlayer(client_id)) {
+                               PlayerSAO *sao = player->getPlayerSAO();
+                               if (!sao)
+                                       continue;
+
                                // If player is far away, only set modified blocks not sent
                                // If player is far away, only set modified blocks not sent
-                               v3f player_pos = player->getPosition();
+                               v3f player_pos = sao->getBasePosition();
                                if(player_pos.getDistanceFrom(p_f) > maxd) {
                                if(player_pos.getDistanceFrom(p_f) > maxd) {
-                                       far_players->push_back(*i);
+                                       far_players->push_back(client_id);
                                        continue;
                                }
                        }
                                        continue;
                                }
                        }
@@ -2105,43 +2138,33 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
 
                NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
                m_clients.lock();
 
                NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
                m_clients.lock();
-               RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
-               if (client != 0) {
+               RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
+               if (client) {
                        pkt << p << n.param0 << n.param1 << n.param2
                                        << (u8) (remove_metadata ? 0 : 1);
                        pkt << p << n.param0 << n.param1 << n.param2
                                        << (u8) (remove_metadata ? 0 : 1);
-
-                       if (!remove_metadata) {
-                               if (client->net_proto_version <= 21) {
-                                       // Old clients always clear metadata; fix it
-                                       // by sending the full block again.
-                                       client->SetBlockNotSent(getNodeBlockPos(p));
-                               }
-                       }
                }
                m_clients.unlock();
 
                // Send as reliable
                if (pkt.getSize() > 0)
                }
                m_clients.unlock();
 
                // Send as reliable
                if (pkt.getSize() > 0)
-                       m_clients.send(*i, 0, &pkt, true);
+                       m_clients.send(client_id, 0, &pkt, true);
        }
 }
 
 void Server::setBlockNotSent(v3s16 p)
 {
        }
 }
 
 void Server::setBlockNotSent(v3s16 p)
 {
-       std::vector<u16> clients = m_clients.getClientIDs();
+       std::vector<session_t> clients = m_clients.getClientIDs();
        m_clients.lock();
        m_clients.lock();
-       for(std::vector<u16>::iterator i = clients.begin();
-               i != clients.end(); ++i) {
-               RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
+       for (const session_t i : clients) {
+               RemoteClient *client = m_clients.lockedGetClientNoEx(i);
                client->SetBlockNotSent(p);
        }
        m_clients.unlock();
 }
 
                client->SetBlockNotSent(p);
        }
        m_clients.unlock();
 }
 
-void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
+void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
+               u16 net_proto_version)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        v3s16 p = block->getPos();
 
        /*
        v3s16 p = block->getPos();
 
        /*
@@ -2150,7 +2173,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto
 
        std::ostringstream os(std::ios_base::binary);
        block->serialize(os, ver, false);
 
        std::ostringstream os(std::ios_base::binary);
        block->serialize(os, ver, false);
-       block->serializeNetworkSpecific(os, net_proto_version);
+       block->serializeNetworkSpecific(os);
        std::string s = os.str();
 
        NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
        std::string s = os.str();
 
        NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
@@ -2162,8 +2185,6 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto
 
 void Server::SendBlocks(float dtime)
 {
 
 void Server::SendBlocks(float dtime)
 {
-       DSTACK(FUNCTION_NAME);
-
        MutexAutoLock envlock(m_env_mutex);
        //TODO check if one big lock could be faster then multiple small ones
 
        MutexAutoLock envlock(m_env_mutex);
        //TODO check if one big lock could be faster then multiple small ones
 
@@ -2171,22 +2192,21 @@ void Server::SendBlocks(float dtime)
 
        std::vector<PrioritySortedBlockTransfer> queue;
 
 
        std::vector<PrioritySortedBlockTransfer> queue;
 
-       s32 total_sending = 0;
+       u32 total_sending = 0;
 
        {
 
        {
-               ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
+               ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
 
 
-               std::vector<u16> clients = m_clients.getClientIDs();
+               std::vector<session_t> clients = m_clients.getClientIDs();
 
                m_clients.lock();
 
                m_clients.lock();
-               for(std::vector<u16>::iterator i = clients.begin();
-                       i != clients.end(); ++i) {
-                       RemoteClient *client = m_clients.lockedGetClientNoEx(*i, CS_Active);
+               for (const session_t client_id : clients) {
+                       RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
 
 
-                       if (client == NULL)
+                       if (!client)
                                continue;
 
                                continue;
 
-                       total_sending += client->SendingCount();
+                       total_sending += client->getSendingCount();
                        client->GetNextBlocks(m_env,m_emerge, dtime, queue);
                }
                m_clients.unlock();
                        client->GetNextBlocks(m_env,m_emerge, dtime, queue);
                }
                m_clients.unlock();
@@ -2198,33 +2218,32 @@ void Server::SendBlocks(float dtime)
        std::sort(queue.begin(), queue.end());
 
        m_clients.lock();
        std::sort(queue.begin(), queue.end());
 
        m_clients.lock();
-       for(u32 i=0; i<queue.size(); i++)
-       {
-               //TODO: Calculate limit dynamically
-               if(total_sending >= g_settings->getS32
-                               ("max_simultaneous_block_sends_server_total"))
-                       break;
 
 
-               PrioritySortedBlockTransfer q = queue[i];
+       // Maximal total count calculation
+       // The per-client block sends is halved with the maximal online users
+       u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
+               g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
 
 
-               MapBlock *block = NULL;
-               try
-               {
-                       block = m_env->getMap().getBlockNoCreate(q.pos);
-               }
-               catch(InvalidPositionException &e)
-               {
+       for (const PrioritySortedBlockTransfer &block_to_send : queue) {
+               if (total_sending >= max_blocks_to_send)
+                       break;
+
+               MapBlock *block = nullptr;
+               try {
+                       block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
+               } catch (const InvalidPositionException &e) {
                        continue;
                }
 
                        continue;
                }
 
-               RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id, CS_Active);
-
-               if(!client)
+               RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
+                               CS_Active);
+               if (!client)
                        continue;
 
                        continue;
 
-               SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
+               SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
+                               client->net_proto_version);
 
 
-               client->SentBlock(q.pos);
+               client->SentBlock(block_to_send.pos);
                total_sending++;
        }
        m_clients.unlock();
                total_sending++;
        }
        m_clients.unlock();
@@ -2232,31 +2251,27 @@ void Server::SendBlocks(float dtime)
 
 void Server::fillMediaCache()
 {
 
 void Server::fillMediaCache()
 {
-       DSTACK(FUNCTION_NAME);
-
        infostream<<"Server: Calculating media file checksums"<<std::endl;
 
        // Collect all media file paths
        std::vector<std::string> paths;
        infostream<<"Server: Calculating media file checksums"<<std::endl;
 
        // Collect all media file paths
        std::vector<std::string> paths;
-       for(std::vector<ModSpec>::iterator i = m_mods.begin();
-                       i != m_mods.end(); ++i) {
-               const ModSpec &mod = *i;
+       for (const ModSpec &mod : m_mods) {
                paths.push_back(mod.path + DIR_DELIM + "textures");
                paths.push_back(mod.path + DIR_DELIM + "sounds");
                paths.push_back(mod.path + DIR_DELIM + "media");
                paths.push_back(mod.path + DIR_DELIM + "models");
                paths.push_back(mod.path + DIR_DELIM + "textures");
                paths.push_back(mod.path + DIR_DELIM + "sounds");
                paths.push_back(mod.path + DIR_DELIM + "media");
                paths.push_back(mod.path + DIR_DELIM + "models");
+               paths.push_back(mod.path + DIR_DELIM + "locale");
        }
        }
-       paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
+       fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
+       fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
 
        // Collect media file information from paths into cache
 
        // Collect media file information from paths into cache
-       for(std::vector<std::string>::iterator i = paths.begin();
-                       i != paths.end(); ++i) {
-               std::string mediapath = *i;
+       for (const std::string &mediapath : paths) {
                std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
                std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
-               for (u32 j = 0; j < dirlist.size(); j++) {
-                       if (dirlist[j].dir) // Ignode dirs
+               for (const fs::DirListNode &dln : dirlist) {
+                       if (dln.dir) // Ignode dirs
                                continue;
                                continue;
-                       std::string filename = dirlist[j].name;
+                       std::string filename = dln.name;
                        // If name contains illegal characters, ignore the file
                        if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
                                infostream<<"Server: ignoring illegal file name: \""
                        // If name contains illegal characters, ignore the file
                        if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
                                infostream<<"Server: ignoring illegal file name: \""
@@ -2269,15 +2284,19 @@ void Server::fillMediaCache()
                                ".pcx", ".ppm", ".psd", ".wal", ".rgb",
                                ".ogg",
                                ".x", ".b3d", ".md2", ".obj",
                                ".pcx", ".ppm", ".psd", ".wal", ".rgb",
                                ".ogg",
                                ".x", ".b3d", ".md2", ".obj",
+                               // Custom translation file format
+                               ".tr",
                                NULL
                        };
                                NULL
                        };
-                       if (removeStringEnd(filename, supported_ext) == ""){
+                       if (removeStringEnd(filename, supported_ext).empty()){
                                infostream << "Server: ignoring unsupported file extension: \""
                                                << filename << "\"" << std::endl;
                                continue;
                        }
                        // Ok, attempt to load the file and add to cache
                                infostream << "Server: ignoring unsupported file extension: \""
                                                << filename << "\"" << std::endl;
                                continue;
                        }
                        // Ok, attempt to load the file and add to cache
-                       std::string filepath = mediapath + DIR_DELIM + filename;
+                       std::string filepath;
+                       filepath.append(mediapath).append(DIR_DELIM).append(filename);
+
                        // Read data
                        std::ifstream fis(filepath.c_str(), std::ios_base::binary);
                        if (!fis.good()) {
                        // Read data
                        std::ifstream fis(filepath.c_str(), std::ios_base::binary);
                        if (!fis.good()) {
@@ -2326,22 +2345,29 @@ void Server::fillMediaCache()
        }
 }
 
        }
 }
 
-void Server::sendMediaAnnouncement(u16 peer_id)
+void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        verbosestream << "Server: Announcing files to id(" << peer_id << ")"
                << std::endl;
 
        // Make packet
        verbosestream << "Server: Announcing files to id(" << peer_id << ")"
                << std::endl;
 
        // Make packet
-       std::ostringstream os(std::ios_base::binary);
-
        NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
        NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
-       pkt << (u16) m_media.size();
 
 
-       for (std::map<std::string, MediaInfo>::iterator i = m_media.begin();
-                       i != m_media.end(); ++i) {
-               pkt << i->first << i->second.sha1_digest;
+       u16 media_sent = 0;
+       std::string lang_suffix;
+       lang_suffix.append(".").append(lang_code).append(".tr");
+       for (const auto &i : m_media) {
+               if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
+                       continue;
+               media_sent++;
+       }
+
+       pkt << media_sent;
+
+       for (const auto &i : m_media) {
+               if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
+                       continue;
+               pkt << i.first << i.second.sha1_digest;
        }
 
        pkt << g_settings->get("remote_media");
        }
 
        pkt << g_settings->get("remote_media");
@@ -2362,11 +2388,9 @@ struct SendableMedia
        {}
 };
 
        {}
 };
 
-void Server::sendRequestedMedia(u16 peer_id,
+void Server::sendRequestedMedia(session_t peer_id,
                const std::vector<std::string> &tosend)
 {
                const std::vector<std::string> &tosend)
 {
-       DSTACK(FUNCTION_NAME);
-
        verbosestream<<"Server::sendRequestedMedia(): "
                        <<"Sending files to client"<<std::endl;
 
        verbosestream<<"Server::sendRequestedMedia(): "
                        <<"Sending files to client"<<std::endl;
 
@@ -2376,15 +2400,12 @@ void Server::sendRequestedMedia(u16 peer_id,
        u32 bytes_per_bunch = 5000;
 
        std::vector< std::vector<SendableMedia> > file_bunches;
        u32 bytes_per_bunch = 5000;
 
        std::vector< std::vector<SendableMedia> > file_bunches;
-       file_bunches.push_back(std::vector<SendableMedia>());
+       file_bunches.emplace_back();
 
        u32 file_size_bunch_total = 0;
 
 
        u32 file_size_bunch_total = 0;
 
-       for(std::vector<std::string>::const_iterator i = tosend.begin();
-                       i != tosend.end(); ++i) {
-               const std::string &name = *i;
-
-               if(m_media.find(name) == m_media.end()) {
+       for (const std::string &name : tosend) {
+               if (m_media.find(name) == m_media.end()) {
                        errorstream<<"Server::sendRequestedMedia(): Client asked for "
                                        <<"unknown file \""<<(name)<<"\""<<std::endl;
                        continue;
                        errorstream<<"Server::sendRequestedMedia(): Client asked for "
                                        <<"unknown file \""<<(name)<<"\""<<std::endl;
                        continue;
@@ -2395,7 +2416,7 @@ void Server::sendRequestedMedia(u16 peer_id,
 
                // Read data
                std::ifstream fis(tpath.c_str(), std::ios_base::binary);
 
                // Read data
                std::ifstream fis(tpath.c_str(), std::ios_base::binary);
-               if(fis.good() == false){
+               if(!fis.good()){
                        errorstream<<"Server::sendRequestedMedia(): Could not open \""
                                        <<tpath<<"\" for reading"<<std::endl;
                        continue;
                        errorstream<<"Server::sendRequestedMedia(): Could not open \""
                                        <<tpath<<"\" for reading"<<std::endl;
                        continue;
@@ -2415,7 +2436,7 @@ void Server::sendRequestedMedia(u16 peer_id,
                                break;
                        }
                }
                                break;
                        }
                }
-               if(bad) {
+               if (bad) {
                        errorstream<<"Server::sendRequestedMedia(): Failed to read \""
                                        <<name<<"\""<<std::endl;
                        continue;
                        errorstream<<"Server::sendRequestedMedia(): Failed to read \""
                                        <<name<<"\""<<std::endl;
                        continue;
@@ -2423,12 +2444,11 @@ void Server::sendRequestedMedia(u16 peer_id,
                /*infostream<<"Server::sendRequestedMedia(): Loaded \""
                                <<tname<<"\""<<std::endl;*/
                // Put in list
                /*infostream<<"Server::sendRequestedMedia(): Loaded \""
                                <<tname<<"\""<<std::endl;*/
                // Put in list
-               file_bunches[file_bunches.size()-1].push_back(
-                               SendableMedia(name, tpath, tmp_os.str()));
+               file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
 
                // Start next bunch if got enough data
                if(file_size_bunch_total >= bytes_per_bunch) {
 
                // Start next bunch if got enough data
                if(file_size_bunch_total >= bytes_per_bunch) {
-                       file_bunches.push_back(std::vector<SendableMedia>());
+                       file_bunches.emplace_back();
                        file_size_bunch_total = 0;
                }
 
                        file_size_bunch_total = 0;
                }
 
@@ -2437,7 +2457,7 @@ void Server::sendRequestedMedia(u16 peer_id,
        /* Create and send packets */
 
        u16 num_bunches = file_bunches.size();
        /* Create and send packets */
 
        u16 num_bunches = file_bunches.size();
-       for(u16 i = 0; i < num_bunches; i++) {
+       for (u16 i = 0; i < num_bunches; i++) {
                /*
                        u16 command
                        u16 total number of texture bunches
                /*
                        u16 command
                        u16 total number of texture bunches
@@ -2454,11 +2474,9 @@ void Server::sendRequestedMedia(u16 peer_id,
                NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
                pkt << num_bunches << i << (u32) file_bunches[i].size();
 
                NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
                pkt << num_bunches << i << (u32) file_bunches[i].size();
 
-               for(std::vector<SendableMedia>::iterator
-                               j = file_bunches[i].begin();
-                               j != file_bunches[i].end(); ++j) {
-                       pkt << j->name;
-                       pkt.putLongString(j->data);
+               for (const SendableMedia &j : file_bunches[i]) {
+                       pkt << j.name;
+                       pkt.putLongString(j.data);
                }
 
                verbosestream << "Server::sendRequestedMedia(): bunch "
                }
 
                verbosestream << "Server::sendRequestedMedia(): bunch "
@@ -2469,7 +2487,7 @@ void Server::sendRequestedMedia(u16 peer_id,
        }
 }
 
        }
 }
 
-void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
+void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
 {
        if(m_detached_inventories.count(name) == 0) {
                errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
 {
        if(m_detached_inventories.count(name) == 0) {
                errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
@@ -2487,22 +2505,23 @@ void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
        NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
        pkt.putRawString(s.c_str(), s.size());
 
        NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
        pkt.putRawString(s.c_str(), s.size());
 
-       if (peer_id != PEER_ID_INEXISTENT) {
-               Send(&pkt);
-       }
-       else {
-               m_clients.sendToAll(0, &pkt, true);
+       const std::string &check = m_detached_inventories_player[name];
+       if (peer_id == PEER_ID_INEXISTENT) {
+               if (check.empty())
+                       return m_clients.sendToAll(&pkt);
+               RemotePlayer *p = m_env->getPlayer(check.c_str());
+               if (p)
+                       m_clients.send(p->getPeerId(), 0, &pkt, true);
+       } else {
+               if (check.empty() || getPlayerName(peer_id) == check)
+                       Send(&pkt);
        }
 }
 
        }
 }
 
-void Server::sendDetachedInventories(u16 peer_id)
+void Server::sendDetachedInventories(session_t peer_id)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
-       for(std::map<std::string, Inventory*>::iterator
-                       i = m_detached_inventories.begin();
-                       i != m_detached_inventories.end(); ++i) {
-               const std::string &name = i->first;
+       for (const auto &detached_inventory : m_detached_inventories) {
+               const std::string &name = detached_inventory.first;
                //Inventory *inv = i->second;
                sendDetachedInventory(name, peer_id);
        }
                //Inventory *inv = i->second;
                sendDetachedInventory(name, peer_id);
        }
@@ -2512,9 +2531,8 @@ void Server::sendDetachedInventories(u16 peer_id)
        Something random
 */
 
        Something random
 */
 
-void Server::DiePlayer(u16 peer_id)
+void Server::DiePlayer(session_t peer_id)
 {
 {
-       DSTACK(FUNCTION_NAME);
        PlayerSAO *playersao = getPlayerSAO(peer_id);
        // In some rare cases this can be NULL -- if the player is disconnected
        // when a Lua function modifies l_punch, for example
        PlayerSAO *playersao = getPlayerSAO(peer_id);
        // In some rare cases this can be NULL -- if the player is disconnected
        // when a Lua function modifies l_punch, for example
@@ -2534,10 +2552,8 @@ void Server::DiePlayer(u16 peer_id)
        SendDeathscreen(peer_id, false, v3f(0,0,0));
 }
 
        SendDeathscreen(peer_id, false, v3f(0,0,0));
 }
 
-void Server::RespawnPlayer(u16 peer_id)
+void Server::RespawnPlayer(session_t peer_id)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        PlayerSAO *playersao = getPlayerSAO(peer_id);
        assert(playersao);
 
        PlayerSAO *playersao = getPlayerSAO(peer_id);
        assert(playersao);
 
@@ -2545,71 +2561,61 @@ void Server::RespawnPlayer(u16 peer_id)
                        << playersao->getPlayer()->getName()
                        << " respawns" << std::endl;
 
                        << playersao->getPlayer()->getName()
                        << " respawns" << std::endl;
 
-       playersao->setHP(PLAYER_MAX_HP);
-       playersao->setBreath(PLAYER_MAX_BREATH);
-
-       SendPlayerHP(peer_id);
-       SendPlayerBreath(peer_id);
+       playersao->setHP(playersao->accessObjectProperties()->hp_max);
+       playersao->setBreath(playersao->accessObjectProperties()->breath_max);
 
        bool repositioned = m_script->on_respawnplayer(playersao);
 
        bool repositioned = m_script->on_respawnplayer(playersao);
-       if(!repositioned){
-               v3f pos = findSpawnPos();
+       if (!repositioned) {
                // setPos will send the new position to client
                // setPos will send the new position to client
-               playersao->setPos(pos);
+               playersao->setPos(findSpawnPos());
        }
        }
+
+       SendPlayerHP(peer_id);
 }
 
 
 }
 
 
-void Server::DenySudoAccess(u16 peer_id)
+void Server::DenySudoAccess(session_t peer_id)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
        Send(&pkt);
 }
 
 
        NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
        Send(&pkt);
 }
 
 
-void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
+void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
                const std::string &str_reason, bool reconnect)
 {
                const std::string &str_reason, bool reconnect)
 {
-       if (proto_ver >= 25) {
-               SendAccessDenied(peer_id, reason, str_reason, reconnect);
-       } else {
-               std::wstring wreason = utf8_to_wide(
-                       reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
-                       accessDeniedStrings[(u8)reason]);
-               SendAccessDenied_Legacy(peer_id, wreason);
-       }
+       SendAccessDenied(peer_id, reason, str_reason, reconnect);
 
        m_clients.event(peer_id, CSE_SetDenied);
 
        m_clients.event(peer_id, CSE_SetDenied);
-       m_con.DisconnectPeer(peer_id);
+       DisconnectPeer(peer_id);
 }
 
 
 }
 
 
-void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
+void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
+               const std::string &custom_reason)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        SendAccessDenied(peer_id, reason, custom_reason);
        m_clients.event(peer_id, CSE_SetDenied);
        SendAccessDenied(peer_id, reason, custom_reason);
        m_clients.event(peer_id, CSE_SetDenied);
-       m_con.DisconnectPeer(peer_id);
+       DisconnectPeer(peer_id);
 }
 
 // 13/03/15: remove this function when protocol version 25 will become
 // the minimum version for MT users, maybe in 1 year
 }
 
 // 13/03/15: remove this function when protocol version 25 will become
 // the minimum version for MT users, maybe in 1 year
-void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
+void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        SendAccessDenied_Legacy(peer_id, reason);
        m_clients.event(peer_id, CSE_SetDenied);
        SendAccessDenied_Legacy(peer_id, reason);
        m_clients.event(peer_id, CSE_SetDenied);
-       m_con.DisconnectPeer(peer_id);
+       DisconnectPeer(peer_id);
 }
 
 }
 
-void Server::acceptAuth(u16 peer_id, bool forSudoMode)
+void Server::DisconnectPeer(session_t peer_id)
 {
 {
-       DSTACK(FUNCTION_NAME);
+       m_modchannel_mgr->leaveAllChannels(peer_id);
+       m_con->DisconnectPeer(peer_id);
+}
 
 
+void Server::acceptAuth(session_t peer_id, bool forSudoMode)
+{
        if (!forSudoMode) {
                RemoteClient* client = getClient(peer_id, CS_Invalid);
 
        if (!forSudoMode) {
                RemoteClient* client = getClient(peer_id, CS_Invalid);
 
@@ -2637,67 +2643,53 @@ void Server::acceptAuth(u16 peer_id, bool forSudoMode)
        }
 }
 
        }
 }
 
-void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
+void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
 {
 {
-       DSTACK(FUNCTION_NAME);
        std::wstring message;
        {
                /*
                        Clear references to playing sounds
                */
        std::wstring message;
        {
                /*
                        Clear references to playing sounds
                */
-               for(std::map<s32, ServerPlayingSound>::iterator
-                               i = m_playing_sounds.begin();
-                               i != m_playing_sounds.end();)
-               {
+               for (std::unordered_map<s32, ServerPlayingSound>::iterator
+                                i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
                        ServerPlayingSound &psound = i->second;
                        psound.clients.erase(peer_id);
                        ServerPlayingSound &psound = i->second;
                        psound.clients.erase(peer_id);
-                       if(psound.clients.empty())
+                       if (psound.clients.empty())
                                m_playing_sounds.erase(i++);
                        else
                                ++i;
                }
 
                                m_playing_sounds.erase(i++);
                        else
                                ++i;
                }
 
-               Player *player = m_env->getPlayer(peer_id);
-
-               // Collect information about leaving in chat
-               {
-                       if(player != NULL && reason != CDR_DENY)
-                       {
-                               std::wstring name = narrow_to_wide(player->getName());
-                               message += L"*** ";
-                               message += name;
-                               message += L" left the game.";
-                               if(reason == CDR_TIMEOUT)
-                                       message += L" (timed out)";
-                       }
-               }
+               RemotePlayer *player = m_env->getPlayer(peer_id);
 
                /* Run scripts and remove from environment */
 
                /* Run scripts and remove from environment */
-               {
-                       if(player != NULL)
-                       {
-                               PlayerSAO *playersao = player->getPlayerSAO();
-                               assert(playersao);
-
-                               m_script->on_leaveplayer(playersao);
-
-                               playersao->disconnected();
-                       }
+               if (player) {
+                       PlayerSAO *playersao = player->getPlayerSAO();
+                       assert(playersao);
+
+                       // inform connected clients
+                       NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
+                       // (u16) 1 + std::string represents a vector serialization representation
+                       notice << (u8) PLAYER_LIST_REMOVE  << (u16) 1 << std::string(playersao->getPlayer()->getName());
+                       m_clients.sendToAll(&notice);
+                       // run scripts
+                       m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
+
+                       playersao->disconnected();
                }
 
                /*
                        Print out action
                */
                {
                }
 
                /*
                        Print out action
                */
                {
-                       if(player != NULL && reason != CDR_DENY) {
+                       if (player && reason != CDR_DENY) {
                                std::ostringstream os(std::ios_base::binary);
                                std::ostringstream os(std::ios_base::binary);
-                               std::vector<u16> clients = m_clients.getClientIDs();
+                               std::vector<session_t> clients = m_clients.getClientIDs();
 
 
-                               for(std::vector<u16>::iterator i = clients.begin();
-                                       i != clients.end(); ++i) {
+                               for (const session_t client_id : clients) {
                                        // Get player
                                        // Get player
-                                       Player *player = m_env->getPlayer(*i);
-                                       if(!player)
+                                       RemotePlayer *player = m_env->getPlayer(client_id);
+                                       if (!player)
                                                continue;
 
                                        // Get name of player
                                                continue;
 
                                        // Get name of player
@@ -2720,21 +2712,22 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
        }
 
        // Send leave chat message to all remaining clients
        }
 
        // Send leave chat message to all remaining clients
-       if(message.length() != 0)
-               SendChatMessage(PEER_ID_INEXISTENT,message);
+       if (!message.empty()) {
+               SendChatMessage(PEER_ID_INEXISTENT,
+                               ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
+       }
 }
 
 }
 
-void Server::UpdateCrafting(Player* player)
+void Server::UpdateCrafting(RemotePlayer *player)
 {
 {
-       DSTACK(FUNCTION_NAME);
-
        // Get a preview for crafting
        ItemStack preview;
        InventoryLocation loc;
        loc.setPlayer(player->getName());
        std::vector<ItemStack> output_replacements;
        getCraftingResult(&player->inventory, preview, output_replacements, false, this);
        // Get a preview for crafting
        ItemStack preview;
        InventoryLocation loc;
        loc.setPlayer(player->getName());
        std::vector<ItemStack> output_replacements;
        getCraftingResult(&player->inventory, preview, output_replacements, false, this);
-       m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
+       m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
+                       (&player->inventory)->getList("craft"), loc);
 
        // Put the new preview in
        InventoryList *plist = player->inventory.getList("craftpreview");
 
        // Put the new preview in
        InventoryList *plist = player->inventory.getList("craftpreview");
@@ -2763,64 +2756,87 @@ void Server::handleChatInterfaceEvent(ChatEvent *evt)
 }
 
 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
 }
 
 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
-       const std::wstring &wmessage, bool check_shout_priv,
-       u16 peer_id_to_avoid_sending)
+       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 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: {
+                               std::wstringstream ws;
+                               ws << L"You cannot send more messages. You are limited to "
+                                  << g_settings->getFloat("chat_message_limit_per_10sec")
+                                  << L" messages per 10 seconds.";
+                               return ws.str();
+                       }
+                       case RPLAYER_CHATRESULT_KICK:
+                               DenyAccess_Legacy(player->getPeerId(),
+                                               L"You have been kicked due to message flooding.");
+                               return L"";
+                       case RPLAYER_CHATRESULT_OK:
+                               break;
+                       default:
+                               FATAL_ERROR("Unhandled chat filtering result found.");
+               }
+       }
+
+       if (m_max_chatmessage_length > 0
+                       && wmessage.length() > m_max_chatmessage_length) {
+               return L"Your message exceed the maximum chat message limit set on the server. "
+                               L"It was refused. Send a shorter message";
+       }
+
+       // Run script hook, exit if script ate the chat message
+       if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
+               return L"";
+
        // Line to send
        std::wstring line;
        // Whether to send line to the player that sent the message, or to all players
        bool broadcast_line = true;
 
        // Line to send
        std::wstring line;
        // Whether to send line to the player that sent the message, or to all players
        bool broadcast_line = true;
 
-       // Run script hook
-       bool ate = m_script->on_chat_message(name,
-               wide_to_utf8(wmessage));
-       // If script ate the message, don't proceed
-       if (ate)
-               return L"";
-
-       // Commands are implemented in Lua, so only catch invalid
-       // commands that were not "eaten" and send an error back
-       if (wmessage[0] == L'/') {
-               std::wstring wcmd = wmessage.substr(1);
+       if (check_shout_priv && !checkPriv(name, "shout")) {
+               line += L"-!- You don't have permission to shout.";
                broadcast_line = false;
                broadcast_line = false;
-               if (wcmd.length() == 0)
-                       line += L"-!- Empty command";
-               else
-                       line += L"-!- Invalid command: " + str_split(wcmd, L' ')[0];
        } else {
        } else {
-               if (check_shout_priv && !checkPriv(name, "shout")) {
-                       line += L"-!- You don't have permission to shout.";
-                       broadcast_line = false;
-               } else {
-                       line += L"<";
-                       line += wname;
-                       line += L"> ";
-                       line += wmessage;
-               }
+               line += L"<";
+               line += wname;
+               line += L"> ";
+               line += wmessage;
        }
 
        /*
                Tell calling method to send the message to sender
        */
        }
 
        /*
                Tell calling method to send the message to sender
        */
-       if (!broadcast_line) {
+       if (!broadcast_line)
                return line;
                return line;
-       } else {
-               /*
-                       Send the message to others
-               */
-               actionstream << "CHAT: " << wide_to_narrow(line) << std::endl;
 
 
-               std::vector<u16> clients = m_clients.getClientIDs();
+       /*
+               Send the message to others
+       */
+       actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
 
 
-               for (u16 i = 0; i < clients.size(); i++) {
-                       u16 cid = clients[i];
-                       if (cid != peer_id_to_avoid_sending)
-                               SendChatMessage(cid, line);
-               }
+       std::vector<session_t> clients = m_clients.getClientIDs();
+
+       /*
+               Send the message back to the inital sender
+               if they are using protocol version >= 29
+       */
+
+       session_t peer_id_to_avoid_sending =
+               (player ? player->getPeerId() : PEER_ID_INEXISTENT);
+
+       if (player && player->protocol_version >= 29)
+               peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
+
+       for (u16 cid : clients) {
+               if (cid != peer_id_to_avoid_sending)
+                       SendChatMessage(cid, ChatMessage(line));
        }
        return L"";
 }
        }
        return L"";
 }
@@ -2839,7 +2855,7 @@ void Server::handleAdminChat(const ChatEventChat *evt)
        }
 }
 
        }
 }
 
-RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
+RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
 {
        RemoteClient *client = getClientNoEx(peer_id,state_min);
        if(!client)
 {
        RemoteClient *client = getClientNoEx(peer_id,state_min);
        if(!client)
@@ -2847,23 +2863,23 @@ RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
 
        return client;
 }
 
        return client;
 }
-RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
+RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
 {
        return m_clients.getClientNoEx(peer_id, state_min);
 }
 
 {
        return m_clients.getClientNoEx(peer_id, state_min);
 }
 
-std::string Server::getPlayerName(u16 peer_id)
+std::string Server::getPlayerName(session_t peer_id)
 {
 {
-       Player *player = m_env->getPlayer(peer_id);
-       if(player == NULL)
+       RemotePlayer *player = m_env->getPlayer(peer_id);
+       if (!player)
                return "[id="+itos(peer_id)+"]";
        return player->getName();
 }
 
                return "[id="+itos(peer_id)+"]";
        return player->getName();
 }
 
-PlayerSAO* Server::getPlayerSAO(u16 peer_id)
+PlayerSAO *Server::getPlayerSAO(session_t peer_id)
 {
 {
-       Player *player = m_env->getPlayer(peer_id);
-       if(player == NULL)
+       RemotePlayer *player = m_env->getPlayer(peer_id);
+       if (!player)
                return NULL;
        return player->getPlayerSAO();
 }
                return NULL;
        return player->getPlayerSAO();
 }
@@ -2881,14 +2897,13 @@ std::wstring Server::getStatusString()
        // Information about clients
        bool first = true;
        os<<L", clients={";
        // Information about clients
        bool first = true;
        os<<L", clients={";
-       std::vector<u16> clients = m_clients.getClientIDs();
-       for(std::vector<u16>::iterator i = clients.begin();
-               i != clients.end(); ++i) {
+       std::vector<session_t> clients = m_clients.getClientIDs();
+       for (session_t client_id : clients) {
                // Get player
                // Get player
-               Player *player = m_env->getPlayer(*i);
+               RemotePlayer *player = m_env->getPlayer(client_id);
                // Get name of player
                std::wstring name = L"unknown";
                // Get name of player
                std::wstring name = L"unknown";
-               if(player != NULL)
+               if (player)
                        name = narrow_to_wide(player->getName());
                // Add name to information string
                if(!first)
                        name = narrow_to_wide(player->getName());
                // Add name to information string
                if(!first)
@@ -2898,9 +2913,11 @@ std::wstring Server::getStatusString()
                os << name;
        }
        os << L"}";
                os << name;
        }
        os << L"}";
-       if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
+
+       if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
                os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
                os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
-       if(g_settings->get("motd") != "")
+
+       if (!g_settings->get("motd").empty())
                os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
        return os.str();
 }
                os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
        return os.str();
 }
@@ -2920,18 +2937,17 @@ bool Server::checkPriv(const std::string &name, const std::string &priv)
 
 void Server::reportPrivsModified(const std::string &name)
 {
 
 void Server::reportPrivsModified(const std::string &name)
 {
-       if(name == "") {
-               std::vector<u16> clients = m_clients.getClientIDs();
-               for(std::vector<u16>::iterator i = clients.begin();
-                               i != clients.end(); ++i) {
-                       Player *player = m_env->getPlayer(*i);
+       if (name.empty()) {
+               std::vector<session_t> clients = m_clients.getClientIDs();
+               for (const session_t client_id : clients) {
+                       RemotePlayer *player = m_env->getPlayer(client_id);
                        reportPrivsModified(player->getName());
                }
        } else {
                        reportPrivsModified(player->getName());
                }
        } else {
-               Player *player = m_env->getPlayer(name.c_str());
-               if(!player)
+               RemotePlayer *player = m_env->getPlayer(name.c_str());
+               if (!player)
                        return;
                        return;
-               SendPlayerPrivileges(player->peer_id);
+               SendPlayerPrivileges(player->getPeerId());
                PlayerSAO *sao = player->getPlayerSAO();
                if(!sao)
                        return;
                PlayerSAO *sao = player->getPlayerSAO();
                if(!sao)
                        return;
@@ -2943,10 +2959,10 @@ void Server::reportPrivsModified(const std::string &name)
 
 void Server::reportInventoryFormspecModified(const std::string &name)
 {
 
 void Server::reportInventoryFormspecModified(const std::string &name)
 {
-       Player *player = m_env->getPlayer(name.c_str());
-       if(!player)
+       RemotePlayer *player = m_env->getPlayer(name.c_str());
+       if (!player)
                return;
                return;
-       SendPlayerInventoryFormspec(player->peer_id);
+       SendPlayerInventoryFormspec(player->getPeerId());
 }
 
 void Server::setIpBanned(const std::string &ip, const std::string &name)
 }
 
 void Server::setIpBanned(const std::string &ip, const std::string &name)
@@ -2974,15 +2990,15 @@ void Server::notifyPlayer(const char *name, const std::wstring &msg)
                m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
        }
 
                m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
        }
 
-       Player *player = m_env->getPlayer(name);
+       RemotePlayer *player = m_env->getPlayer(name);
        if (!player) {
                return;
        }
 
        if (!player) {
                return;
        }
 
-       if (player->peer_id == PEER_ID_INEXISTENT)
+       if (player->getPeerId() == PEER_ID_INEXISTENT)
                return;
 
                return;
 
-       SendChatMessage(player->peer_id, msg);
+       SendChatMessage(player->getPeerId(), ChatMessage(msg));
 }
 
 bool Server::showFormspec(const char *playername, const std::string &formspec,
 }
 
 bool Server::showFormspec(const char *playername, const std::string &formspec,
@@ -2992,27 +3008,27 @@ bool Server::showFormspec(const char *playername, const std::string &formspec,
        if (!m_env)
                return false;
 
        if (!m_env)
                return false;
 
-       Player *player = m_env->getPlayer(playername);
+       RemotePlayer *player = m_env->getPlayer(playername);
        if (!player)
                return false;
 
        if (!player)
                return false;
 
-       SendShowFormspecMessage(player->peer_id, formspec, formname);
+       SendShowFormspecMessage(player->getPeerId(), formspec, formname);
        return true;
 }
 
        return true;
 }
 
-u32 Server::hudAdd(Player *player, HudElement *form)
+u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
 {
        if (!player)
                return -1;
 
        u32 id = player->addHud(form);
 
 {
        if (!player)
                return -1;
 
        u32 id = player->addHud(form);
 
-       SendHUDAdd(player->peer_id, id, form);
+       SendHUDAdd(player->getPeerId(), id, form);
 
        return id;
 }
 
 
        return id;
 }
 
-bool Server::hudRemove(Player *player, u32 id) {
+bool Server::hudRemove(RemotePlayer *player, u32 id) {
        if (!player)
                return false;
 
        if (!player)
                return false;
 
@@ -3023,184 +3039,219 @@ bool Server::hudRemove(Player *player, u32 id) {
 
        delete todel;
 
 
        delete todel;
 
-       SendHUDRemove(player->peer_id, id);
+       SendHUDRemove(player->getPeerId(), id);
        return true;
 }
 
        return true;
 }
 
-bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data)
+bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
 {
        if (!player)
                return false;
 
 {
        if (!player)
                return false;
 
-       SendHUDChange(player->peer_id, id, stat, data);
+       SendHUDChange(player->getPeerId(), id, stat, data);
        return true;
 }
 
        return true;
 }
 
-bool Server::hudSetFlags(Player *player, u32 flags, u32 mask)
+bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
 {
        if (!player)
                return false;
 
 {
        if (!player)
                return false;
 
-       SendHUDSetFlags(player->peer_id, flags, mask);
+       SendHUDSetFlags(player->getPeerId(), flags, mask);
        player->hud_flags &= ~mask;
        player->hud_flags |= flags;
 
        PlayerSAO* playersao = player->getPlayerSAO();
 
        player->hud_flags &= ~mask;
        player->hud_flags |= flags;
 
        PlayerSAO* playersao = player->getPlayerSAO();
 
-       if (playersao == NULL)
+       if (!playersao)
                return false;
 
        m_script->player_event(playersao, "hud_changed");
        return true;
 }
 
                return false;
 
        m_script->player_event(playersao, "hud_changed");
        return true;
 }
 
-bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount)
+bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
 {
        if (!player)
                return false;
 {
        if (!player)
                return false;
+
        if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
                return false;
 
        player->setHotbarItemcount(hotbar_itemcount);
        std::ostringstream os(std::ios::binary);
        writeS32(os, hotbar_itemcount);
        if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
                return false;
 
        player->setHotbarItemcount(hotbar_itemcount);
        std::ostringstream os(std::ios::binary);
        writeS32(os, hotbar_itemcount);
-       SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
+       SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
        return true;
 }
 
        return true;
 }
 
-s32 Server::hudGetHotbarItemcount(Player *player)
+s32 Server::hudGetHotbarItemcount(RemotePlayer *player) const
 {
 {
-       if (!player)
-               return 0;
        return player->getHotbarItemcount();
 }
 
        return player->getHotbarItemcount();
 }
 
-void Server::hudSetHotbarImage(Player *player, std::string name)
+void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
 {
        if (!player)
                return;
 
        player->setHotbarImage(name);
 {
        if (!player)
                return;
 
        player->setHotbarImage(name);
-       SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
+       SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
 }
 
 }
 
-std::string Server::hudGetHotbarImage(Player *player)
+std::string Server::hudGetHotbarImage(RemotePlayer *player)
 {
        if (!player)
                return "";
        return player->getHotbarImage();
 }
 
 {
        if (!player)
                return "";
        return player->getHotbarImage();
 }
 
-void Server::hudSetHotbarSelectedImage(Player *player, std::string name)
+void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
 {
        if (!player)
                return;
 
        player->setHotbarSelectedImage(name);
 {
        if (!player)
                return;
 
        player->setHotbarSelectedImage(name);
-       SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
+       SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
 }
 
 }
 
-std::string Server::hudGetHotbarSelectedImage(Player *player)
+const std::string& Server::hudGetHotbarSelectedImage(RemotePlayer *player) const
 {
 {
-       if (!player)
-               return "";
-
        return player->getHotbarSelectedImage();
 }
 
        return player->getHotbarSelectedImage();
 }
 
-bool Server::setLocalPlayerAnimations(Player *player,
-       v2s32 animation_frames[4], f32 frame_speed)
+Address Server::getPeerAddress(session_t peer_id)
+{
+       return m_con->GetPeerAddress(peer_id);
+}
+
+bool Server::setLocalPlayerAnimations(RemotePlayer *player,
+               v2s32 animation_frames[4], f32 frame_speed)
 {
        if (!player)
                return false;
 
        player->setLocalAnimations(animation_frames, frame_speed);
 {
        if (!player)
                return false;
 
        player->setLocalAnimations(animation_frames, frame_speed);
-       SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
+       SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
        return true;
 }
 
        return true;
 }
 
-bool Server::setPlayerEyeOffset(Player *player, v3f first, v3f third)
+bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
 {
        if (!player)
                return false;
 
        player->eye_offset_first = first;
        player->eye_offset_third = third;
 {
        if (!player)
                return false;
 
        player->eye_offset_first = first;
        player->eye_offset_third = third;
-       SendEyeOffset(player->peer_id, first, third);
+       SendEyeOffset(player->getPeerId(), first, third);
        return true;
 }
 
        return true;
 }
 
-bool Server::setSky(Player *player, const video::SColor &bgcolor,
-       const std::string &type, const std::vector<std::string> &params)
+bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
+       const std::string &type, const std::vector<std::string> &params,
+       bool &clouds)
 {
        if (!player)
                return false;
 
 {
        if (!player)
                return false;
 
-       player->setSky(bgcolor, type, params);
-       SendSetSky(player->peer_id, bgcolor, type, params);
+       player->setSky(bgcolor, type, params, clouds);
+       SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
        return true;
 }
 
        return true;
 }
 
-bool Server::overrideDayNightRatio(Player *player, bool do_override,
+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->getPeerId(), density,
+                       color_bright, color_ambient, height,
+                       thickness, speed);
+       return true;
+}
+
+bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
        float ratio)
 {
        if (!player)
                return false;
 
        player->overrideDayNightRatio(do_override, ratio);
        float ratio)
 {
        if (!player)
                return false;
 
        player->overrideDayNightRatio(do_override, ratio);
-       SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
+       SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
        return true;
 }
 
 void Server::notifyPlayers(const std::wstring &msg)
 {
        return true;
 }
 
 void Server::notifyPlayers(const std::wstring &msg)
 {
-       SendChatMessage(PEER_ID_INEXISTENT,msg);
+       SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
 }
 
 void Server::spawnParticle(const std::string &playername, v3f pos,
        v3f velocity, v3f acceleration,
        float expirationtime, float size, bool
 }
 
 void Server::spawnParticle(const std::string &playername, v3f pos,
        v3f velocity, v3f acceleration,
        float expirationtime, float size, bool
-       collisiondetection, bool vertical, const std::string &texture)
+       collisiondetection, bool collision_removal,
+       bool vertical, const std::string &texture,
+       const struct TileAnimationParams &animation, u8 glow)
 {
        // m_env will be NULL if the server is initializing
        if (!m_env)
                return;
 
 {
        // m_env will be NULL if the server is initializing
        if (!m_env)
                return;
 
-       u16 peer_id = PEER_ID_INEXISTENT;
-       if (playername != "") {
-               Player* player = m_env->getPlayer(playername.c_str());
+       session_t peer_id = PEER_ID_INEXISTENT;
+       u16 proto_ver = 0;
+       if (!playername.empty()) {
+               RemotePlayer *player = m_env->getPlayer(playername.c_str());
                if (!player)
                        return;
                if (!player)
                        return;
-               peer_id = player->peer_id;
+               peer_id = player->getPeerId();
+               proto_ver = player->protocol_version;
        }
 
        }
 
-       SendSpawnParticle(peer_id, pos, velocity, acceleration,
-                       expirationtime, size, collisiondetection, vertical, texture);
+       SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
+                       expirationtime, size, collisiondetection,
+                       collision_removal, vertical, texture, animation, glow);
 }
 
 u32 Server::addParticleSpawner(u16 amount, float spawntime,
        v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
        float minexptime, float maxexptime, float minsize, float maxsize,
 }
 
 u32 Server::addParticleSpawner(u16 amount, float spawntime,
        v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
        float minexptime, float maxexptime, float minsize, float maxsize,
-       bool collisiondetection, bool vertical, const std::string &texture,
-       const std::string &playername)
+       bool collisiondetection, bool collision_removal,
+       ServerActiveObject *attached, bool vertical, const std::string &texture,
+       const std::string &playername, const struct TileAnimationParams &animation,
+       u8 glow)
 {
        // m_env will be NULL if the server is initializing
        if (!m_env)
                return -1;
 
 {
        // m_env will be NULL if the server is initializing
        if (!m_env)
                return -1;
 
-       u16 peer_id = PEER_ID_INEXISTENT;
-       if (playername != "") {
-               Player* player = m_env->getPlayer(playername.c_str());
+       session_t peer_id = PEER_ID_INEXISTENT;
+       u16 proto_ver = 0;
+       if (!playername.empty()) {
+               RemotePlayer *player = m_env->getPlayer(playername.c_str());
                if (!player)
                        return -1;
                if (!player)
                        return -1;
-               peer_id = player->peer_id;
+               peer_id = player->getPeerId();
+               proto_ver = player->protocol_version;
        }
 
        }
 
-       u32 id = m_env->addParticleSpawner(spawntime);
-       SendAddParticleSpawner(peer_id, amount, spawntime,
+       u16 attached_id = attached ? attached->getId() : 0;
+
+       u32 id;
+       if (attached_id == 0)
+               id = m_env->addParticleSpawner(spawntime);
+       else
+               id = m_env->addParticleSpawner(spawntime, attached_id);
+
+       SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
                minpos, maxpos, minvel, maxvel, minacc, maxacc,
                minexptime, maxexptime, minsize, maxsize,
                minpos, maxpos, minvel, maxvel, minacc, maxacc,
                minexptime, maxexptime, minsize, maxsize,
-               collisiondetection, vertical, texture, id);
+               collisiondetection, collision_removal, attached_id, vertical,
+               texture, id, animation, glow);
 
        return id;
 }
 
        return id;
 }
@@ -3211,25 +3262,19 @@ void Server::deleteParticleSpawner(const std::string &playername, u32 id)
        if (!m_env)
                throw ServerError("Can't delete particle spawners during initialisation!");
 
        if (!m_env)
                throw ServerError("Can't delete particle spawners during initialisation!");
 
-       u16 peer_id = PEER_ID_INEXISTENT;
-       if (playername != "") {
-               Player* player = m_env->getPlayer(playername.c_str());
+       session_t peer_id = PEER_ID_INEXISTENT;
+       if (!playername.empty()) {
+               RemotePlayer *player = m_env->getPlayer(playername.c_str());
                if (!player)
                        return;
                if (!player)
                        return;
-               peer_id = player->peer_id;
+               peer_id = player->getPeerId();
        }
 
        m_env->deleteParticleSpawner(id);
        SendDeleteParticleSpawner(peer_id, id);
 }
 
        }
 
        m_env->deleteParticleSpawner(id);
        SendDeleteParticleSpawner(peer_id, id);
 }
 
-void Server::deleteParticleSpawnerAll(u32 id)
-{
-       m_env->deleteParticleSpawner(id);
-       SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
-}
-
-Inventory* Server::createDetachedInventory(const std::string &name)
+Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
 {
        if(m_detached_inventories.count(name) > 0){
                infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
 {
        if(m_detached_inventories.count(name) > 0){
                infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
@@ -3240,6 +3285,7 @@ Inventory* Server::createDetachedInventory(const std::string &name)
        Inventory *inv = new Inventory(m_itemdef);
        sanity_check(inv);
        m_detached_inventories[name] = inv;
        Inventory *inv = new Inventory(m_itemdef);
        sanity_check(inv);
        m_detached_inventories[name] = inv;
+       m_detached_inventories_player[name] = player;
        //TODO find a better way to do this
        sendDetachedInventory(name,PEER_ID_INEXISTENT);
        return inv;
        //TODO find a better way to do this
        sendDetachedInventory(name,PEER_ID_INEXISTENT);
        return inv;
@@ -3262,11 +3308,7 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
        int num_tried = 0;
        int num_failed = 0;
 
        int num_tried = 0;
        int num_failed = 0;
 
-       for(std::list<RollbackAction>::const_iterator
-                       i = actions.begin();
-                       i != actions.end(); ++i)
-       {
-               const RollbackAction &action = *i;
+       for (const RollbackAction &action : actions) {
                num_tried++;
                bool success = action.applyRevert(map, this, this);
                if(!success){
                num_tried++;
                bool success = action.applyRevert(map, this, this);
                if(!success){
@@ -3274,13 +3316,13 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
                        std::ostringstream os;
                        os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
                        infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
                        std::ostringstream os;
                        os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
                        infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
-                       if(log)
+                       if (log)
                                log->push_back(os.str());
                }else{
                        std::ostringstream os;
                        os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
                        infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
                                log->push_back(os.str());
                }else{
                        std::ostringstream os;
                        os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
                        infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
-                       if(log)
+                       if (log)
                                log->push_back(os.str());
                }
        }
                                log->push_back(os.str());
                }
        }
@@ -3308,29 +3350,12 @@ ICraftDefManager *Server::getCraftDefManager()
 {
        return m_craftdef;
 }
 {
        return m_craftdef;
 }
-ITextureSource *Server::getTextureSource()
-{
-       return NULL;
-}
-IShaderSource *Server::getShaderSource()
-{
-       return NULL;
-}
-scene::ISceneManager *Server::getSceneManager()
-{
-       return NULL;
-}
 
 u16 Server::allocateUnknownNodeId(const std::string &name)
 {
        return m_nodedef->allocateDummy(name);
 }
 
 
 u16 Server::allocateUnknownNodeId(const std::string &name)
 {
        return m_nodedef->allocateDummy(name);
 }
 
-ISoundManager *Server::getSoundManager()
-{
-       return &dummySoundManager;
-}
-
 MtEventManager *Server::getEventManager()
 {
        return m_event;
 MtEventManager *Server::getEventManager()
 {
        return m_event;
@@ -3374,6 +3399,11 @@ std::string Server::getBuiltinLuaPath()
        return porting::path_share + DIR_DELIM + "builtin";
 }
 
        return porting::path_share + DIR_DELIM + "builtin";
 }
 
+std::string Server::getModStoragePath() const
+{
+       return m_path_world + DIR_DELIM + "mod_storage";
+}
+
 v3f Server::findSpawnPos()
 {
        ServerMap &map = m_env->getServerMap();
 v3f Server::findSpawnPos()
 {
        ServerMap &map = m_env->getServerMap();
@@ -3383,14 +3413,16 @@ v3f Server::findSpawnPos()
        }
 
        bool is_good = false;
        }
 
        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++) {
 
        // 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(
                // 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);
 
                // Get spawn level at point
                s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
@@ -3424,18 +3456,52 @@ v3f Server::findSpawnPos()
        return nodeposf;
 }
 
        return nodeposf;
 }
 
-PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
+void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
 {
 {
-       bool newplayer = false;
+       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;
+               m_shutdown_msg = "";
+               m_shutdown_ask_reconnect = false;
+               m_shutdown_requested = false;
+               std::wstringstream ws;
+
+               ws << L"*** Server shutdown canceled.";
+
+               infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
+               SendChatMessage(PEER_ID_INEXISTENT, ws.str());
+       } else if (delay > 0.0f) {
+       // Positive delay, tell the clients when the server will shut down
+               std::wstringstream ws;
+
+               ws << L"*** Server shutting down in "
+                               << duration_to_string(myround(m_shutdown_timer)).c_str()
+                               << ".";
 
 
+               infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
+               SendChatMessage(PEER_ID_INEXISTENT, ws.str());
+       }
+}
+
+PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
+{
        /*
                Try to get an existing player
        */
        /*
                Try to get an existing player
        */
-       RemotePlayer *player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
+       RemotePlayer *player = m_env->getPlayer(name);
 
        // If player is already connected, cancel
 
        // If player is already connected, cancel
-       if(player != NULL && player->peer_id != 0)
-       {
+       if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
                infostream<<"emergePlayer(): Player already connected"<<std::endl;
                return NULL;
        }
                infostream<<"emergePlayer(): Player already connected"<<std::endl;
                return NULL;
        }
@@ -3443,58 +3509,25 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version
        /*
                If player with the wanted peer_id already exists, cancel.
        */
        /*
                If player with the wanted peer_id already exists, cancel.
        */
-       if(m_env->getPlayer(peer_id) != NULL)
-       {
+       if (m_env->getPlayer(peer_id)) {
                infostream<<"emergePlayer(): Player with wrong name but same"
                                " peer_id already exists"<<std::endl;
                return NULL;
        }
 
                infostream<<"emergePlayer(): Player with wrong name but same"
                                " peer_id already exists"<<std::endl;
                return NULL;
        }
 
-       // Load player if it isn't already loaded
        if (!player) {
        if (!player) {
-               player = static_cast<RemotePlayer*>(m_env->loadPlayer(name));
+               player = new RemotePlayer(name, idef());
        }
 
        }
 
-       // Create player if it doesn't exist
-       if (!player) {
-               newplayer = true;
-               player = new RemotePlayer(this, name);
-               // Set player position
-               infostream<<"Server: Finding spawn place for player \""
-                               <<name<<"\""<<std::endl;
-               v3f pos = findSpawnPos();
-               player->setPosition(pos);
-
-               // 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(player->getPosition())) {
-                       actionstream << "Respawn position for player \""
-                               << name << "\" outside limits, resetting" << std::endl;
-                       v3f pos = findSpawnPos();
-                       player->setPosition(pos);
-               }
-       }
+       bool newplayer = false;
 
 
-       // Create a new player active object
-       PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
-                       getPlayerEffectivePrivs(player->getName()),
-                       isSingleplayer());
+       // Load player
+       PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
 
 
+       // Complete init with server parts
+       playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
        player->protocol_version = proto_version;
 
        player->protocol_version = proto_version;
 
-       /* Clean up old HUD elements from previous sessions */
-       player->clearHud();
-
-       /* Add object to environment */
-       m_env->addActiveObject(playersao);
-
        /* Run scripts */
        if (newplayer) {
                m_script->on_newplayer(playersao);
        /* Run scripts */
        if (newplayer) {
                m_script->on_newplayer(playersao);
@@ -3503,16 +3536,37 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version
        return playersao;
 }
 
        return playersao;
 }
 
-void dedicated_server_loop(Server &server, bool &kill)
+bool Server::registerModStorage(ModMetadata *storage)
 {
 {
-       DSTACK(FUNCTION_NAME);
+       if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
+               errorstream << "Unable to register same mod storage twice. Storage name: "
+                               << storage->getModName() << std::endl;
+               return false;
+       }
 
 
+       m_mod_storages[storage->getModName()] = storage;
+       return true;
+}
+
+void Server::unregisterModStorage(const std::string &name)
+{
+       std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
+       if (it != m_mod_storages.end()) {
+               // Save unconditionaly on unregistration
+               it->second->save(getModStoragePath());
+               m_mod_storages.erase(name);
+       }
+}
+
+void dedicated_server_loop(Server &server, bool &kill)
+{
        verbosestream<<"dedicated_server_loop()"<<std::endl;
 
        IntervalLimiter m_profiler_interval;
 
        verbosestream<<"dedicated_server_loop()"<<std::endl;
 
        IntervalLimiter m_profiler_interval;
 
-       static const float steplen = g_settings->getFloat("dedicated_server_step");
-       static const float profiler_print_interval =
+       static thread_local const float steplen =
+                       g_settings->getFloat("dedicated_server_step");
+       static thread_local const float profiler_print_interval =
                        g_settings->getFloat("profiler_print_interval");
 
        for(;;) {
                        g_settings->getFloat("profiler_print_interval");
 
        for(;;) {
@@ -3524,15 +3578,8 @@ void dedicated_server_loop(Server &server, bool &kill)
                }
                server.step(steplen);
 
                }
                server.step(steplen);
 
-               if(server.getShutdownRequested() || kill)
-               {
-                       infostream<<"Dedicated server quitting"<<std::endl;
-#if USE_CURL
-                       if(g_settings->getBool("server_announce"))
-                               ServerList::sendAnnounce("delete", server.m_bind_addr.getPort());
-#endif
+               if (server.getShutdownRequested() || kill)
                        break;
                        break;
-               }
 
                /*
                        Profiler
 
                /*
                        Profiler
@@ -3546,4 +3593,76 @@ void dedicated_server_loop(Server &server, bool &kill)
                        }
                }
        }
                        }
                }
        }
+
+       infostream << "Dedicated server quitting" << std::endl;
+#if USE_CURL
+       if (g_settings->getBool("server_announce"))
+               ServerList::sendAnnounce(ServerList::AA_DELETE,
+                       server.m_bind_addr.getPort());
+#endif
+}
+
+/*
+ * Mod channels
+ */
+
+
+bool Server::joinModChannel(const std::string &channel)
+{
+       return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
+                       m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
+}
+
+bool Server::leaveModChannel(const std::string &channel)
+{
+       return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
+}
+
+bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
+{
+       if (!m_modchannel_mgr->canWriteOnChannel(channel))
+               return false;
+
+       broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
+       return true;
+}
+
+ModChannel* Server::getModChannel(const std::string &channel)
+{
+       return m_modchannel_mgr->getModChannel(channel);
+}
+
+void Server::broadcastModChannelMessage(const std::string &channel,
+               const std::string &message, session_t from_peer)
+{
+       const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
+       if (peers.empty())
+               return;
+
+       if (message.size() > STRING_MAX_LEN) {
+               warningstream << "ModChannel message too long, dropping before sending "
+                               << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
+                               << channel << ")" << std::endl;
+               return;
+       }
+
+       std::string sender;
+       if (from_peer != PEER_ID_SERVER) {
+               sender = getPlayerName(from_peer);
+       }
+
+       NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
+                       2 + channel.size() + 2 + sender.size() + 2 + message.size());
+       resp_pkt << channel << sender << message;
+       for (session_t peer_id : peers) {
+               // Ignore sender
+               if (peer_id == from_peer)
+                       continue;
+
+               Send(peer_id, &resp_pkt);
+       }
+
+       if (from_peer != PEER_ID_SERVER) {
+               m_script->on_modchannel_message(channel, sender, message);
+       }
 }
 }