X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fserver.cpp;h=95d2371ff1153591957ab906f54f3edfa4e441ff;hb=4faaadc8d50d6ab7a19d22bd5a760c4b8321a51f;hp=224af47a7c294ffd0e04f16dc54a805ad1d71b23;hpb=351cc2e79a7d78f7ec97ff9b33e4f0bad4042b19;p=dragonfireclient.git diff --git a/src/server.cpp b/src/server.cpp index 224af47a7..95d2371ff 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "profiler.h" #include "log.h" -#include "serverscripting.h" +#include "scripting_server.h" #include "nodedef.h" #include "itemdef.h" #include "craftdef.h" @@ -47,7 +47,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_biome.h" #include "content_mapnode.h" #include "content_nodemeta.h" -#include "content_abm.h" #include "content_sao.h" #include "mods.h" #include "event_manager.h" @@ -60,6 +59,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/base64.h" #include "util/sha1.h" #include "util/hex.h" +#include "database.h" class ClientNotFoundException : public BaseException { @@ -156,40 +156,19 @@ Server::Server( m_simple_singleplayer_mode(simple_singleplayer_mode), m_dedicated(dedicated), m_async_fatal_error(""), - m_env(NULL), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this), - m_banmanager(NULL), - m_rollback(NULL), - m_enable_rollback_recording(false), - m_emerge(NULL), - m_script(NULL), 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_clients(&m_con), - m_shutdown_requested(false), - m_shutdown_ask_reconnect(false), - m_admin_chat(iface), - m_ignore_map_edit_events(false), - m_ignore_map_edit_events_peer_id(0), - m_next_sound_id(0), - m_mod_storage_save_timer(10.0f) -{ - m_liquid_transform_timer = 0.0; - m_liquid_transform_every = 1.0; - m_masterserver_timer = 0.0; - m_emergethread_trigger_timer = 0.0; - m_savemap_timer = 0.0; - - m_step_dtime = 0.0; + m_admin_chat(iface) +{ m_lag = g_settings->getFloat("dedicated_server_step"); if(path_world == "") @@ -228,32 +207,6 @@ Server::Server( modconf.printUnsatisfiedModsError(); } - Settings worldmt_settings; - std::string worldmt = m_path_world + DIR_DELIM + "world.mt"; - worldmt_settings.readConfigFile(worldmt.c_str()); - std::vector names = worldmt_settings.getNames(); - std::set load_mod_names; - for(std::vector::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::iterator it = m_mods.begin(); - it != m_mods.end(); ++it) - load_mod_names.erase((*it).name); - for(std::vector::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::iterator it = load_mod_names.begin(); - it != load_mod_names.end(); ++it) - errorstream << " \"" << (*it) << "\""; - errorstream << std::endl; - } - //lock environment MutexAutoLock envlock(m_env_mutex); @@ -281,7 +234,7 @@ Server::Server( if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) { throw ModError("Error loading mod \"" + mod.name + "\": Mod name does not follow naming conventions: " - "Only chararacters [a-z0-9_] are allowed."); + "Only characters [a-z0-9_] are allowed."); } std::string script_path = mod.path + DIR_DELIM + "init.lua"; infostream << " [" << padStringRight(mod.name, 12) << "] [\"" @@ -342,9 +295,6 @@ Server::Server( 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_max_chatmessage_length = g_settings->getU16("chat_message_max_size"); } @@ -598,7 +548,7 @@ void Server::AsyncRunStep(bool initial_step) ScopeProfiler sp(g_profiler, "Server: liquid transform"); std::map modified_blocks; - m_env->getMap().transformLiquids(modified_blocks); + m_env->getMap().transformLiquids(modified_blocks, m_env); #if 0 /* Update lighting @@ -658,23 +608,24 @@ void Server::AsyncRunStep(bool initial_step) MutexAutoLock envlock(m_env_mutex); m_clients.lock(); - UNORDERED_MAP clients = m_clients.getClientList(); + RemoteClientMap clients = m_clients.getClientList(); 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 - 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"); - 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; - for (UNORDERED_MAP::iterator i = clients.begin(); + for (RemoteClientMap::iterator i = clients.begin(); i != clients.end(); ++i) { RemoteClient *client = i->second; @@ -785,7 +736,7 @@ void Server::AsyncRunStep(bool initial_step) 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 (UNORDERED_MAP::const_iterator + for (std::unordered_map::const_iterator it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) { if (it->second->isModified()) { it->second->save(getModStoragePath()); @@ -803,7 +754,7 @@ void Server::AsyncRunStep(bool initial_step) // Key = object id // Value = data sent by object - UNORDERED_MAP* > buffered_messages; + std::unordered_map*> buffered_messages; // Get active object messages from environment for(;;) { @@ -812,7 +763,7 @@ void Server::AsyncRunStep(bool initial_step) break; std::vector* message_list = NULL; - UNORDERED_MAP* >::iterator n; + std::unordered_map* >::iterator n; n = buffered_messages.find(aom.id); if (n == buffered_messages.end()) { message_list = new std::vector; @@ -825,15 +776,15 @@ void Server::AsyncRunStep(bool initial_step) } m_clients.lock(); - UNORDERED_MAP clients = m_clients.getClientList(); + RemoteClientMap clients = m_clients.getClientList(); // Route data to every client - for (UNORDERED_MAP::iterator i = clients.begin(); - i != clients.end(); ++i) { + for (std::unordered_map::iterator i = clients.begin(); + i != clients.end(); ++i) { RemoteClient *client = i->second; std::string reliable_data; std::string unreliable_data; // Go through all objects in message buffer - for (UNORDERED_MAP* >::iterator + for (std::unordered_map* >::iterator j = buffered_messages.begin(); j != buffered_messages.end(); ++j) { // If object is not known by client, skip it @@ -877,7 +828,7 @@ void Server::AsyncRunStep(bool initial_step) m_clients.unlock(); // Clear buffered_messages - for (UNORDERED_MAP* >::iterator + for (std::unordered_map* >::iterator i = buffered_messages.begin(); i != buffered_messages.end(); ++i) { delete i->second; @@ -1006,7 +957,7 @@ void Server::AsyncRunStep(bool initial_step) { 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; @@ -1029,6 +980,39 @@ void Server::AsyncRunStep(bool initial_step) 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() @@ -1634,15 +1618,18 @@ void Server::SendInventory(PlayerSAO* playerSAO) void Server::SendChatMessage(u16 peer_id, const std::wstring &message) { DSTACK(FUNCTION_NAME); + if (peer_id != PEER_ID_INEXISTENT) { + NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id); - NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id); - pkt << message; + if (m_clients.getProtocolVersion(peer_id) < 27) + pkt << unescape_enriched(message); + else + pkt << message; - if (peer_id != PEER_ID_INEXISTENT) { Send(&pkt); - } - else { - m_clients.sendToAll(0, &pkt, true); + } else { + for (u16 id : m_clients.getClientIDs()) + SendChatMessage(id, message); } } @@ -1672,13 +1659,25 @@ void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version, 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) { - // This sucks and should be replaced by a better solution in a refactor: std::vector clients = m_clients.getClientIDs(); + for (std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { RemotePlayer *player = m_env->getPlayer(*i); if (!player) continue; + + PlayerSAO *sao = player->getPlayerSAO(); + if (!sao) + continue; + + // Do not send to distant clients + if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius) + continue; + SendSpawnParticle(*i, player->protocol_version, pos, velocity, acceleration, expirationtime, size, collisiondetection, @@ -1761,7 +1760,7 @@ void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id) Send(&pkt); } else { - m_clients.sendToAll(0, &pkt, true); + m_clients.sendToAll(&pkt); } } @@ -1836,7 +1835,8 @@ void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value) } void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor, - const std::string &type, const std::vector ¶ms) + const std::string &type, const std::vector ¶ms, + bool &clouds) { NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id); pkt << bgcolor << type << (u16) params.size(); @@ -1844,6 +1844,22 @@ void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor, for(size_t i=0; i 0; - for(std::vector::iterator i = dst_clients.begin(); + for (std::vector::iterator i = dst_clients.begin(); i != dst_clients.end(); ++i) { - psound.clients.insert(*i); - m_clients.send(*i, 0, &pkt, true); + if (play_sound || m_clients.getProtocolVersion(*i) >= 32) { + psound.clients.insert(*i); + m_clients.send(*i, 0, &pkt, true); + } } return id; } void Server::stopSound(s32 handle) { // Get sound reference - UNORDERED_MAP::iterator i = m_playing_sounds.find(handle); + std::unordered_map::iterator i = + m_playing_sounds.find(handle); if (i == m_playing_sounds.end()) return; ServerPlayingSound &psound = i->second; @@ -2074,15 +2099,61 @@ void Server::stopSound(s32 handle) NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4); pkt << handle; - for (UNORDERED_SET::iterator i = psound.clients.begin(); - i != psound.clients.end(); ++i) { + for (std::unordered_set::const_iterator si = psound.clients.begin(); + si != psound.clients.end(); ++si) { // 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); } +void Server::fadeSound(s32 handle, float step, float gain) +{ + // Get sound reference + std::unordered_map::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::iterator it = psound.clients.begin(); + it != psound.clients.end();) { + if (m_clients.getProtocolVersion(*it) >= 32) { + // Send as reliable + m_clients.send(*it, 0, &pkt, true); + ++it; + } else { + compat_psound.clients.insert(*it); + // Stop old sound + m_clients.send(*it, 0, &compat_pkt, true); + psound.clients.erase(it++); + } + } + + // Remove sound reference + if (!play_sound || psound.clients.size() == 0) + m_playing_sounds.erase(i); + + if (play_sound && compat_psound.clients.size() > 0) { + // Play new sound volume on older clients + playSound(compat_psound.spec, compat_psound.params); + } +} + void Server::sendRemoveNode(v3s16 p, u16 ignore_id, std::vector *far_players, float far_d_nodes) { @@ -2368,7 +2439,7 @@ void Server::sendMediaAnnouncement(u16 peer_id) NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id); pkt << (u16) m_media.size(); - for (UNORDERED_MAP::iterator i = m_media.begin(); + for (std::unordered_map::iterator i = m_media.begin(); i != m_media.end(); ++i) { pkt << i->first << i->second.sha1_digest; } @@ -2519,7 +2590,7 @@ void Server::sendDetachedInventory(const std::string &name, u16 peer_id) const std::string &check = m_detached_inventories_player[name]; if (peer_id == PEER_ID_INEXISTENT) { if (check == "") - return m_clients.sendToAll(0, &pkt, true); + return m_clients.sendToAll(&pkt); RemotePlayer *p = m_env->getPlayer(check.c_str()); if (p) m_clients.send(p->peer_id, 0, &pkt, true); @@ -2584,9 +2655,8 @@ void Server::RespawnPlayer(u16 peer_id) bool repositioned = m_script->on_respawnplayer(playersao); if (!repositioned) { - v3f pos = findSpawnPos(); // setPos will send the new position to client - playersao->setPos(pos); + playersao->setPos(findSpawnPos()); } SendPlayerHP(peer_id); @@ -2678,7 +2748,7 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason) /* Clear references to playing sounds */ - for (UNORDERED_MAP::iterator + for (std::unordered_map::iterator i = m_playing_sounds.begin(); i != m_playing_sounds.end();) { ServerPlayingSound &psound = i->second; psound.clients.erase(peer_id); @@ -2695,6 +2765,12 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason) 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(¬ice); + // run scripts m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT); playersao->disconnected(); @@ -2779,12 +2855,15 @@ void Server::handleChatInterfaceEvent(ChatEvent *evt) } std::wstring Server::handleChat(const std::string &name, const std::wstring &wname, - const std::wstring &wmessage, bool check_shout_priv, RemotePlayer *player) + std::wstring wmessage, bool check_shout_priv, RemotePlayer *player) { // If something goes wrong, this player is to blame RollbackScopeActor rollback_scope(m_rollback, std::string("player:") + name); + if (g_settings->getBool("strip_color_codes")) + wmessage = unescape_enriched(wmessage); + if (player) { switch (player->canSendChatMessage()) { case RPLAYER_CHATRESULT_FLOODING: { @@ -2820,25 +2899,14 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna // Whether to send line to the player that sent the message, or to all players bool broadcast_line = true; - // 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; - if (wcmd.length() == 0) - line += L"-!- Empty command"; - else - line += L"-!- Invalid command: " + str_split(wcmd, L' ')[0]; } 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; } /* @@ -2850,7 +2918,7 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna /* Send the message to others */ - actionstream << "CHAT: " << wide_to_narrow(line) << std::endl; + actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl; std::vector clients = m_clients.getClientIDs(); @@ -3163,13 +3231,30 @@ bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third) } bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor, - const std::string &type, const std::vector ¶ms) + const std::string &type, const std::vector ¶ms, + bool &clouds) +{ + if (!player) + return false; + + player->setSky(bgcolor, type, params, clouds); + SendSetSky(player->peer_id, bgcolor, type, params, clouds); + return true; +} + +bool Server::setClouds(RemotePlayer *player, float density, + const video::SColor &color_bright, + const video::SColor &color_ambient, + float height, + float thickness, + const v2f &speed) { if (!player) return false; - player->setSky(bgcolor, type, params); - SendSetSky(player->peer_id, bgcolor, type, params); + SendCloudParams(player->peer_id, density, + color_bright, color_ambient, height, + thickness, speed); return true; } @@ -3413,14 +3498,16 @@ v3f Server::findSpawnPos() } bool is_good = false; + // Limit spawn range to mapgen edges (determined by 'mapgen_limit') + s32 range_max = map.getMapgenParams()->getSpawnRangeMax(); // Try to find a good place a few times for(s32 i = 0; i < 4000 && !is_good; i++) { - s32 range = 1 + i; + s32 range = MYMIN(1 + i, range_max); // We're going to try to throw the player to this position v2s16 nodepos2d = v2s16( - -range + (myrand() % (range * 2)), - -range + (myrand() % (range * 2))); + -range + (myrand() % (range * 2)), + -range + (myrand() % (range * 2))); // Get spawn level at point s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d); @@ -3454,10 +3541,45 @@ v3f Server::findSpawnPos() 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, u16 peer_id, u16 proto_version) +{ /* Try to get an existing player */ @@ -3478,44 +3600,18 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version return NULL; } - // Create a new player active object - PlayerSAO *playersao = new PlayerSAO(m_env, peer_id, isSingleplayer()); - player = m_env->loadPlayer(name, playersao); - - // Create player if it doesn't exist if (!player) { - newplayer = true; - player = new RemotePlayer(name, this->idef()); - // Set player position - infostream<<"Server: Finding spawn place for player \"" - <setBasePosition(findSpawnPos()); - - // Make sure the player is saved - player->setModified(true); - - // Add player to environment - m_env->addPlayer(player); - } else { - // If the player exists, ensure that they respawn inside legal bounds - // This fixes an assert crash when the player can't be added - // to the environment - if (objectpos_over_limit(playersao->getBasePosition())) { - actionstream << "Respawn position for player \"" - << name << "\" outside limits, resetting" << std::endl; - playersao->setBasePosition(findSpawnPos()); - } + player = new RemotePlayer(name, idef()); } - playersao->initialize(player, getPlayerEffectivePrivs(player->getName())); - - player->protocol_version = proto_version; + bool newplayer = false; - /* Clean up old HUD elements from previous sessions */ - player->clearHud(); + // Load player + PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer()); - /* Add object to environment */ - m_env->addActiveObject(playersao); + // Complete init with server parts + playersao->finalize(player, getPlayerEffectivePrivs(player->getName())); + player->protocol_version = proto_version; /* Run scripts */ if (newplayer) { @@ -3539,7 +3635,7 @@ bool Server::registerModStorage(ModMetadata *storage) void Server::unregisterModStorage(const std::string &name) { - UNORDERED_MAP::const_iterator it = m_mod_storages.find(name); + std::unordered_map::const_iterator it = m_mod_storages.find(name); if (it != m_mod_storages.end()) { // Save unconditionaly on unregistration it->second->save(getModStoragePath()); @@ -3555,8 +3651,9 @@ void dedicated_server_loop(Server &server, bool &kill) 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(;;) {