]> git.lizzy.rs Git - minetest.git/blobdiff - src/client/client.cpp
Dual wielding
[minetest.git] / src / client / client.cpp
index 923369afeee348f21b2d83b92de1140eb8e06369..8760b416da43c922ecbdd6f9d170db6813ebba9b 100644 (file)
@@ -41,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "filesys.h"
 #include "mapblock_mesh.h"
 #include "mapblock.h"
+#include "mapsector.h"
 #include "minimap.h"
 #include "modchannels.h"
 #include "content/mods.h"
@@ -50,6 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "clientmap.h"
 #include "clientmedia.h"
 #include "version.h"
+#include "database/database-files.h"
 #include "database/database-sqlite3.h"
 #include "serialization.h"
 #include "guiscalingfilter.h"
@@ -57,6 +59,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "game.h"
 #include "chatmessage.h"
 #include "translation.h"
+#include "content/mod_configuration.h"
 
 extern gui::IGUIEnvironment* guienv;
 
@@ -99,7 +102,8 @@ Client::Client(
                MtEventManager *event,
                RenderingEngine *rendering_engine,
                bool ipv6,
-               GameUI *game_ui
+               GameUI *game_ui,
+               ELoginRegister allow_login_or_register
 ):
        m_tsrc(tsrc),
        m_shsrc(shsrc),
@@ -108,7 +112,7 @@ Client::Client(
        m_sound(sound),
        m_event(event),
        m_rendering_engine(rendering_engine),
-       m_mesh_update_thread(this),
+       m_mesh_update_manager(this),
        m_env(
                new ClientMap(this, rendering_engine, control, 666),
                tsrc, this
@@ -116,6 +120,7 @@ Client::Client(
        m_particle_manager(&m_env),
        m_con(new con::Connection(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this)),
        m_address_name(address_name),
+       m_allow_login_or_register(allow_login_or_register),
        m_server_ser_ver(SER_FMT_VER_INVALID),
        m_last_chat_message_sent(time(NULL)),
        m_password(password),
@@ -128,11 +133,44 @@ Client::Client(
        // Add local player
        m_env.setLocalPlayer(new LocalPlayer(this, playername));
 
+       // Make the mod storage database and begin the save for later
+       m_mod_storage_database =
+                       new ModStorageDatabaseSQLite3(porting::path_user + DIR_DELIM + "client");
+       m_mod_storage_database->beginSave();
+
        if (g_settings->getBool("enable_minimap")) {
                m_minimap = new Minimap(this);
        }
 
        m_cache_save_interval = g_settings->getU16("server_map_save_interval");
+       m_mesh_grid = { g_settings->getU16("client_mesh_chunk") };
+}
+
+void Client::migrateModStorage()
+{
+       std::string mod_storage_dir = porting::path_user + DIR_DELIM + "client";
+       std::string old_mod_storage = mod_storage_dir + DIR_DELIM + "mod_storage";
+       if (fs::IsDir(old_mod_storage)) {
+               infostream << "Migrating client mod storage to SQLite3 database" << std::endl;
+               {
+                       ModStorageDatabaseFiles files_db(mod_storage_dir);
+                       std::vector<std::string> mod_list;
+                       files_db.listMods(&mod_list);
+                       for (const std::string &modname : mod_list) {
+                               infostream << "Migrating client mod storage for mod " << modname << std::endl;
+                               StringMap meta;
+                               files_db.getModEntries(modname, &meta);
+                               for (const auto &pair : meta) {
+                                       m_mod_storage_database->setModEntry(modname, pair.first, pair.second);
+                               }
+                       }
+               }
+               if (!fs::Rename(old_mod_storage, old_mod_storage + ".bak")) {
+                       // Execution cannot move forward if the migration does not complete.
+                       throw BaseException("Could not finish migrating client mod storage");
+               }
+               infostream << "Finished migration of client mod storage" << std::endl;
+       }
 }
 
 void Client::loadMods()
@@ -160,12 +198,28 @@ void Client::loadMods()
        // Load builtin
        scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath());
        m_script->loadModFromMemory(BUILTIN_MOD_NAME);
+       m_script->checkSetByBuiltin();
+
+       ModConfiguration modconf;
+       {
+               std::unordered_map<std::string, std::string> paths;
+               std::string path_user = porting::path_user + DIR_DELIM + "clientmods";
+               const auto modsPath = getClientModsLuaPath();
+               if (modsPath != path_user) {
+                       paths["share"] = modsPath;
+               }
+               paths["mods"] = path_user;
+
+               std::string settings_path = path_user + DIR_DELIM + "mods.conf";
+               modconf.addModsFromConfig(settings_path, paths);
+               modconf.checkConflictsAndDeps();
+       }
 
-       ClientModConfiguration modconf(getClientModsLuaPath());
        m_mods = modconf.getMods();
+
        // complain about mods with unsatisfied dependencies
        if (!modconf.isConsistent()) {
-               modconf.printUnsatisfiedModsError();
+               errorstream << modconf.getUnsatisfiedModsError() << std::endl;
                return;
        }
 
@@ -260,7 +314,7 @@ void Client::Stop()
        if (m_mods_loaded)
                m_script->on_shutdown();
        //request all client managed threads to stop
-       m_mesh_update_thread.stop();
+       m_mesh_update_manager.stop();
        // Save local server map
        if (m_localdb) {
                infostream << "Local map saving ended." << std::endl;
@@ -273,7 +327,7 @@ void Client::Stop()
 
 bool Client::isShutdown()
 {
-       return m_shutdown || !m_mesh_update_thread.isRunning();
+       return m_shutdown || !m_mesh_update_manager.isRunning();
 }
 
 Client::~Client()
@@ -283,14 +337,17 @@ Client::~Client()
 
        deleteAuthData();
 
-       m_mesh_update_thread.stop();
-       m_mesh_update_thread.wait();
-       while (!m_mesh_update_thread.m_queue_out.empty()) {
-               MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
+       m_mesh_update_manager.stop();
+       m_mesh_update_manager.wait();
+       
+       MeshUpdateResult r;
+       while (m_mesh_update_manager.getNextResult(r)) {
+               for (auto block : r.map_blocks)
+                       if (block)
+                               block->refDrop();
                delete r.mesh;
        }
 
-
        delete m_inventory_from_server;
 
        // Delete detached inventories
@@ -301,10 +358,17 @@ Client::~Client()
        // cleanup 3d model meshes on client shutdown
        m_rendering_engine->cleanupMeshCache();
 
+       guiScalingCacheClear();
+
        delete m_minimap;
        m_minimap = nullptr;
 
        delete m_media_downloader;
+
+       // Write the changes and delete
+       if (m_mod_storage_database)
+               m_mod_storage_database->endSave();
+       delete m_mod_storage_database;
 }
 
 void Client::connect(Address address, bool is_local_server)
@@ -356,10 +420,6 @@ void Client::step(float dtime)
                initial_step = false;
        }
        else if(m_state == LC_Created) {
-               if (m_is_registration_confirmation_state) {
-                       // Waiting confirmation
-                       return;
-               }
                float &counter = m_connection_reinit_timer;
                counter -= dtime;
                if(counter <= 0.0) {
@@ -386,7 +446,7 @@ void Client::step(float dtime)
        if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) {
                std::vector<v3s16> deleted_blocks;
                m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
-                       g_settings->getFloat("client_unload_unused_data_timeout"),
+                       std::max(g_settings->getFloat("client_unload_unused_data_timeout"), 0.0f),
                        g_settings->getS32("client_mapblock_limit"),
                        &deleted_blocks);
 
@@ -455,6 +515,7 @@ void Client::step(float dtime)
                        ClientEvent *event = new ClientEvent();
                        event->type = CE_PLAYER_DAMAGE;
                        event->player_damage.amount = damage;
+                       event->player_damage.effect = true;
                        m_client_event_queue.push(event);
                }
        }
@@ -490,23 +551,33 @@ void Client::step(float dtime)
        {
                int num_processed_meshes = 0;
                std::vector<v3s16> blocks_to_ack;
-               while (!m_mesh_update_thread.m_queue_out.empty())
+               bool force_update_shadows = false;
+               MeshUpdateResult r;
+               while (m_mesh_update_manager.getNextResult(r))
                {
                        num_processed_meshes++;
 
-                       MinimapMapblock *minimap_mapblock = NULL;
+                       std::vector<MinimapMapblock*> minimap_mapblocks;
                        bool do_mapper_update = true;
 
-                       MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
-                       MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
+                       MapSector *sector = m_env.getMap().emergeSector(v2s16(r.p.X, r.p.Z));
+
+                       MapBlock *block = sector->getBlockNoCreateNoEx(r.p.Y);
+
+                       // The block in question is not visible (perhaps it is culled at the server),
+                       // create a blank block just to hold the chunk's mesh.
+                       // If the block becomes visible later it will replace the blank block.
+                       if (!block && r.mesh)
+                               block = sector->createBlankBlock(r.p.Y);
+
                        if (block) {
                                // Delete the old mesh
                                delete block->mesh;
                                block->mesh = nullptr;
 
                                if (r.mesh) {
-                                       minimap_mapblock = r.mesh->moveMinimapMapblock();
-                                       if (minimap_mapblock == NULL)
+                                       minimap_mapblocks = r.mesh->moveMinimapMapblocks();
+                                       if (minimap_mapblocks.empty())
                                                do_mapper_update = false;
 
                                        bool is_empty = true;
@@ -516,25 +587,48 @@ void Client::step(float dtime)
 
                                        if (is_empty)
                                                delete r.mesh;
-                                       else
+                                       else {
                                                // Replace with the new mesh
                                                block->mesh = r.mesh;
+                                               if (r.urgent)
+                                                       force_update_shadows = true;
+                                       }
                                }
                        } else {
                                delete r.mesh;
                        }
 
-                       if (m_minimap && do_mapper_update)
-                               m_minimap->addBlock(r.p, minimap_mapblock);
+                       for (auto p : r.solid_sides) {
+                               auto block = m_env.getMap().getBlockNoCreateNoEx(p.first);
+                               if (block)
+                                       block->solid_sides = p.second;
+                       }
+
+                       if (m_minimap && do_mapper_update) {
+                               v3s16 ofs;
 
-                       if (r.ack_block_to_server) {
+                               // See also mapblock_mesh.cpp for the code that creates the array of minimap blocks.
+                               for (ofs.Z = 0; ofs.Z < m_mesh_grid.cell_size; ofs.Z++)
+                               for (ofs.Y = 0; ofs.Y < m_mesh_grid.cell_size; ofs.Y++)
+                               for (ofs.X = 0; ofs.X < m_mesh_grid.cell_size; ofs.X++) {
+                                       size_t i = m_mesh_grid.getOffsetIndex(ofs);
+                                       if (i < minimap_mapblocks.size() && minimap_mapblocks[i])
+                                               m_minimap->addBlock(r.p + ofs, minimap_mapblocks[i]);
+                               }
+                       }
+
+                       for (auto p : r.ack_list) {
                                if (blocks_to_ack.size() == 255) {
                                        sendGotBlocks(blocks_to_ack);
                                        blocks_to_ack.clear();
                                }
 
-                               blocks_to_ack.emplace_back(r.p);
+                               blocks_to_ack.emplace_back(p);
                        }
+
+                       for (auto block : r.map_blocks)
+                               if (block)
+                                       block->refDrop();
                }
                if (blocks_to_ack.size() > 0) {
                                // Acknowledge block(s)
@@ -543,6 +637,10 @@ void Client::step(float dtime)
 
                if (num_processed_meshes > 0)
                        g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
+
+               auto shadow_renderer = RenderingEngine::get_shadow_renderer();
+               if (shadow_renderer && force_update_shadows)
+                       shadow_renderer->setForceUpdateShadowMap();
        }
 
        /*
@@ -555,6 +653,29 @@ void Client::step(float dtime)
                        m_media_downloader = NULL;
                }
        }
+       {
+               // Acknowledge dynamic media downloads to server
+               std::vector<u32> done;
+               for (auto it = m_pending_media_downloads.begin();
+                               it != m_pending_media_downloads.end();) {
+                       assert(it->second->isStarted());
+                       it->second->step(this);
+                       if (it->second->isDone()) {
+                               done.emplace_back(it->first);
+
+                               it = m_pending_media_downloads.erase(it);
+                       } else {
+                               it++;
+                       }
+
+                       if (done.size() == 255) { // maximum in one packet
+                               sendHaveMedia(done);
+                               done.clear();
+                       }
+               }
+               if (!done.empty())
+                       sendHaveMedia(done);
+       }
 
        /*
                If the server didn't update the inventory in a while, revert
@@ -618,19 +739,12 @@ void Client::step(float dtime)
                }
        }
 
+       // Write changes to the mod storage
        m_mod_storage_save_timer -= dtime;
        if (m_mod_storage_save_timer <= 0.0f) {
                m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
-               int n = 0;
-               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());
-                               n++;
-                       }
-               }
-               if (n > 0)
-                       infostream << "Saved " << n << " modified mod storages." << std::endl;
+               m_mod_storage_database->endSave();
+               m_mod_storage_database->beginSave();
        }
 
        // Write server map
@@ -730,16 +844,18 @@ void Client::peerAdded(con::Peer *peer)
        infostream << "Client::peerAdded(): peer->id="
                        << peer->id << std::endl;
 }
+
 void Client::deletingPeer(con::Peer *peer, bool timeout)
 {
        infostream << "Client::deletingPeer(): "
                        "Server Peer is getting deleted "
                        << "(timeout=" << timeout << ")" << std::endl;
 
-       if (timeout) {
-               m_access_denied = true;
+       m_access_denied = true;
+       if (timeout)
                m_access_denied_reason = gettext("Connection timed out.");
-       }
+       else if (m_access_denied_reason.empty())
+               m_access_denied_reason = gettext("Connection aborted (protocol error?).");
 }
 
 /*
@@ -770,7 +886,8 @@ void Client::request_media(const std::vector<std::string> &file_requests)
        Send(&pkt);
 
        infostream << "Client: Sending media request list to server ("
-                       << file_requests.size() << " files. packet size)" << std::endl;
+                       << file_requests.size() << " files, packet size "
+                       << pkt.getSize() << ")" << std::endl;
 }
 
 void Client::initLocalMapSaving(const Address &address,
@@ -806,7 +923,7 @@ void Client::ReceiveAll()
 {
        NetworkPacket pkt;
        u64 start_ms = porting::getTimeMs();
-       const u64 budget = 100;
+       const u64 budget = 10;
        for(;;) {
                // Limit time even if there would be huge amounts of data to
                // process
@@ -853,7 +970,7 @@ void Client::ProcessData(NetworkPacket *pkt)
        */
        if(sender_peer_id != PEER_ID_SERVER) {
                infostream << "Client::ProcessData(): Discarding data not "
-                       "coming from server: peer_id=" << sender_peer_id
+                       "coming from server: peer_id=" << sender_peer_id << " command=" << pkt->getCommand()
                        << std::endl;
                return;
        }
@@ -904,7 +1021,7 @@ void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *
        v3f sf           = myplayer->getSpeed() * 100;
        s32 pitch        = myplayer->getPitch() * 100;
        s32 yaw          = myplayer->getYaw() * 100;
-       u32 keyPressed   = myplayer->keyPressed;
+       u32 keyPressed   = myplayer->control.getKeysPressed();
        // scaled by 80, so that pi can fit into a u8
        u8 fov           = clientMap->getCameraFov() * 80;
        u8 wanted_range  = MYMIN(255,
@@ -1012,18 +1129,6 @@ void Client::sendInit(const std::string &playerName)
        Send(&pkt);
 }
 
-void Client::promptConfirmRegistration(AuthMechanism chosen_auth_mechanism)
-{
-       m_chosen_auth_mech = chosen_auth_mechanism;
-       m_is_registration_confirmation_state = true;
-}
-
-void Client::confirmRegistration()
-{
-       m_is_registration_confirmation_state = false;
-       startAuth(m_chosen_auth_mech);
-}
-
 void Client::startAuth(AuthMechanism chosen_auth_mechanism)
 {
        m_chosen_auth_mech = chosen_auth_mechanism;
@@ -1201,7 +1306,7 @@ void Client::sendChatMessage(const std::wstring &message)
                pkt << message;
 
                Send(&pkt);
-       } else if (m_out_chat_queue.size() < (u16) max_queue_size || max_queue_size == -1) {
+       } else if (m_out_chat_queue.size() < (u16) max_queue_size || max_queue_size < 0) {
                m_out_chat_queue.push(message);
        } else {
                infostream << "Could not queue chat message because maximum out chat queue size ("
@@ -1260,22 +1365,24 @@ void Client::sendPlayerPos()
        if (!player)
                return;
 
-       ClientMap &map = m_env.getClientMap();
-       u8 camera_fov   = map.getCameraFov();
-       u8 wanted_range = map.getControl().wanted_range;
-
        // Save bandwidth by only updating position when
        // player is not dead and something changed
 
        if (m_activeobjects_received && player->isDead())
                return;
 
+       ClientMap &map = m_env.getClientMap();
+       u8 camera_fov   = map.getCameraFov();
+       u8 wanted_range = map.getControl().wanted_range;
+
+       u32 keyPressed = player->control.getKeysPressed();
+
        if (
                        player->last_position     == player->getPosition() &&
                        player->last_speed        == player->getSpeed()    &&
                        player->last_pitch        == player->getPitch()    &&
                        player->last_yaw          == player->getYaw()      &&
-                       player->last_keyPressed   == player->keyPressed    &&
+                       player->last_keyPressed   == keyPressed            &&
                        player->last_camera_fov   == camera_fov            &&
                        player->last_wanted_range == wanted_range)
                return;
@@ -1284,7 +1391,7 @@ void Client::sendPlayerPos()
        player->last_speed        = player->getSpeed();
        player->last_pitch        = player->getPitch();
        player->last_yaw          = player->getYaw();
-       player->last_keyPressed   = player->keyPressed;
+       player->last_keyPressed   = keyPressed;
        player->last_camera_fov   = camera_fov;
        player->last_wanted_range = wanted_range;
 
@@ -1295,6 +1402,30 @@ void Client::sendPlayerPos()
        Send(&pkt);
 }
 
+void Client::sendHaveMedia(const std::vector<u32> &tokens)
+{
+       NetworkPacket pkt(TOSERVER_HAVE_MEDIA, 1 + tokens.size() * 4);
+
+       sanity_check(tokens.size() < 256);
+
+       pkt << static_cast<u8>(tokens.size());
+       for (u32 token : tokens)
+               pkt << token;
+
+       Send(&pkt);
+}
+
+void Client::sendUpdateClientInfo(const ClientDynamicInfo& info)
+{
+       NetworkPacket pkt(TOSERVER_UPDATE_CLIENT_INFO, 4*2 + 4 + 4 + 4*2);
+       pkt << (u32)info.render_target_size.X << (u32)info.render_target_size.Y;
+       pkt << info.real_gui_scaling;
+       pkt << info.real_hud_scaling;
+       pkt << (f32)info.max_fs_size.X << (f32)info.max_fs_size.Y;
+
+       Send(&pkt);
+}
+
 void Client::removeNode(v3s16 p)
 {
        std::map<v3s16, MapBlock*> modified_blocks;
@@ -1404,6 +1535,8 @@ bool Client::updateWieldedItem()
                list->setModified(false);
        if (auto *list = player->inventory.getList("hand"))
                list->setModified(false);
+       if (auto *list = player->inventory.getList("offhand"))
+               list->setModified(false);
 
        return true;
 }
@@ -1569,25 +1702,12 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
        if (b == NULL)
                return;
 
-       m_mesh_update_thread.updateBlock(&m_env.getMap(), p, ack_to_server, urgent);
+       m_mesh_update_manager.updateBlock(&m_env.getMap(), p, ack_to_server, urgent);
 }
 
 void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
 {
-       try{
-               addUpdateMeshTask(blockpos, ack_to_server, urgent);
-       }
-       catch(InvalidPositionException &e){}
-
-       // Leading edge
-       for (int i=0;i<6;i++)
-       {
-               try{
-                       v3s16 p = blockpos + g_6dirs[i];
-                       addUpdateMeshTask(p, false, urgent);
-               }
-               catch(InvalidPositionException &e){}
-       }
+       m_mesh_update_manager.updateBlock(&m_env.getMap(), blockpos, ack_to_server, urgent, true);
 }
 
 void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
@@ -1599,38 +1719,16 @@ void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool ur
                                <<std::endl;
        }
 
-       v3s16 blockpos          = getNodeBlockPos(nodepos);
+       v3s16 blockpos = getNodeBlockPos(nodepos);
        v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
-
-       try{
-               addUpdateMeshTask(blockpos, ack_to_server, urgent);
-       }
-       catch(InvalidPositionException &e) {}
-
+       m_mesh_update_manager.updateBlock(&m_env.getMap(), blockpos, ack_to_server, urgent, false);
        // Leading edge
-       if(nodepos.X == blockpos_relative.X){
-               try{
-                       v3s16 p = blockpos + v3s16(-1,0,0);
-                       addUpdateMeshTask(p, false, urgent);
-               }
-               catch(InvalidPositionException &e){}
-       }
-
-       if(nodepos.Y == blockpos_relative.Y){
-               try{
-                       v3s16 p = blockpos + v3s16(0,-1,0);
-                       addUpdateMeshTask(p, false, urgent);
-               }
-               catch(InvalidPositionException &e){}
-       }
-
-       if(nodepos.Z == blockpos_relative.Z){
-               try{
-                       v3s16 p = blockpos + v3s16(0,0,-1);
-                       addUpdateMeshTask(p, false, urgent);
-               }
-               catch(InvalidPositionException &e){}
-       }
+       if (nodepos.X == blockpos_relative.X)
+               addUpdateMeshTask(blockpos + v3s16(-1, 0, 0), false, urgent);
+       if (nodepos.Y == blockpos_relative.Y)
+               addUpdateMeshTask(blockpos + v3s16(0, -1, 0), false, urgent);
+       if (nodepos.Z == blockpos_relative.Z)
+               addUpdateMeshTask(blockpos + v3s16(0, 0, -1), false, urgent);
 }
 
 ClientEvent *Client::getClientEvent()
@@ -1656,20 +1754,20 @@ float Client::mediaReceiveProgress()
        return 1.0; // downloader only exists when not yet done
 }
 
-typedef struct TextureUpdateArgs {
+struct TextureUpdateArgs {
        gui::IGUIEnvironment *guienv;
        u64 last_time_ms;
        u16 last_percent;
        const wchar_t* text_base;
        ITextureSource *tsrc;
-} TextureUpdateArgs;
+};
 
 void Client::showUpdateProgressTexture(void *args, u32 progress, u32 max_progress)
 {
                TextureUpdateArgs* targs = (TextureUpdateArgs*) args;
                u16 cur_percent = ceil(progress / (double) max_progress * 100.);
 
-               // update the loading menu -- if neccessary
+               // update the loading menu -- if necessary
                bool do_draw = false;
                u64 time_ms = targs->last_time_ms;
                if (cur_percent != targs->last_percent) {
@@ -1681,8 +1779,8 @@ void Client::showUpdateProgressTexture(void *args, u32 progress, u32 max_progres
 
                if (do_draw) {
                        targs->last_time_ms = time_ms;
-                       std::basic_stringstream<wchar_t> strm;
-                       strm << targs->text_base << " " << targs->last_percent << "%...";
+                       std::wostringstream strm;
+                       strm << targs->text_base << L" " << targs->last_percent << L"%...";
                        m_rendering_engine->draw_load_screen(strm.str(), targs->guienv, targs->tsrc, 0,
                                72 + (u16) ((18. / 100.) * (double) targs->last_percent), true);
                }
@@ -1735,14 +1833,14 @@ void Client::afterContentReceived()
        tu_args.guienv = guienv;
        tu_args.last_time_ms = porting::getTimeMs();
        tu_args.last_percent = 0;
-       tu_args.text_base =  wgettext("Initializing nodes");
+       tu_args.text_base = wgettext("Initializing nodes");
        tu_args.tsrc = m_tsrc;
        m_nodedef->updateTextures(this, &tu_args);
        delete[] tu_args.text_base;
 
        // Start mesh update thread after setting up content definitions
        infostream<<"- Starting mesh update thread"<<std::endl;
-       m_mesh_update_thread.start();
+       m_mesh_update_manager.start();
 
        m_state = LC_Ready;
        sendReady();
@@ -1775,11 +1873,10 @@ void Client::makeScreenshot()
        if (!raw_image)
                return;
 
-       time_t t = time(NULL);
-       struct tm *tm = localtime(&t);
+       const struct tm tm = mt_localtime();
 
        char timetstamp_c[64];
-       strftime(timetstamp_c, sizeof(timetstamp_c), "%Y%m%d_%H%M%S", tm);
+       strftime(timetstamp_c, sizeof(timetstamp_c), "%Y%m%d_%H%M%S", &tm);
 
        std::string screenshot_dir;
 
@@ -1942,34 +2039,6 @@ const std::string* Client::getModFile(std::string filename)
        return &it->second;
 }
 
-bool Client::registerModStorage(ModMetadata *storage)
-{
-       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 Client::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);
-       }
-}
-
-std::string Client::getModStoragePath() const
-{
-       return porting::path_user + DIR_DELIM + "client" + DIR_DELIM + "mod_storage";
-}
-
 /*
  * Mod channels
  */