3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "network/connection.h"
25 #include "network/networkprotocol.h"
26 #include "network/serveropcodes.h"
28 #include "environment.h"
30 #include "threading/mutex_auto_lock.h"
31 #include "constants.h"
37 #include "server/serveractiveobject.h"
41 #include "scripting_server.h"
46 #include "mapgen/mapgen.h"
47 #include "mapgen/mg_biome.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content/mods.h"
51 #include "modchannels.h"
52 #include "serverlist.h"
53 #include "util/string.h"
55 #include "util/serialize.h"
56 #include "util/thread.h"
57 #include "defaultsettings.h"
58 #include "server/mods.h"
59 #include "util/base64.h"
60 #include "util/sha1.h"
62 #include "database/database.h"
63 #include "chatmessage.h"
64 #include "chat_interface.h"
65 #include "remoteplayer.h"
66 #include "server/player_sao.h"
67 #include "server/serverinventorymgr.h"
68 #include "translation.h"
69 #include "database/database-sqlite3.h"
70 #include "database/database-files.h"
71 #include "database/database-dummy.h"
72 #include "gameparams.h"
74 class ClientNotFoundException : public BaseException
77 ClientNotFoundException(const char *s):
82 class ServerThread : public Thread
86 ServerThread(Server *server):
97 void *ServerThread::run()
99 BEGIN_DEBUG_EXCEPTION_HANDLER
102 * The real business of the server happens on the ServerThread.
104 * AsyncRunStep() runs an actual server step as soon as enough time has
105 * passed (dedicated_server_loop keeps track of that).
106 * Receive() blocks at least(!) 30ms waiting for a packet (so this loop
107 * doesn't busy wait) and will process any remaining packets.
111 m_server->AsyncRunStep(true);
112 } catch (con::ConnectionBindFailed &e) {
113 m_server->setAsyncFatalError(e.what());
114 } catch (LuaError &e) {
115 m_server->setAsyncFatalError(e);
118 while (!stopRequested()) {
120 m_server->AsyncRunStep();
124 } catch (con::PeerNotFoundException &e) {
125 infostream<<"Server: PeerNotFoundException"<<std::endl;
126 } catch (ClientNotFoundException &e) {
127 } catch (con::ConnectionBindFailed &e) {
128 m_server->setAsyncFatalError(e.what());
129 } catch (LuaError &e) {
130 m_server->setAsyncFatalError(e);
134 END_DEBUG_EXCEPTION_HANDLER
139 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
141 if(pos_exists) *pos_exists = false;
146 if(pos_exists) *pos_exists = true;
151 ServerActiveObject *sao = env->getActiveObject(object);
154 if(pos_exists) *pos_exists = true;
155 return sao->getBasePosition(); }
160 void Server::ShutdownState::reset()
164 should_reconnect = false;
165 is_requested = false;
168 void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
172 should_reconnect = reconnect;
175 void Server::ShutdownState::tick(float dtime, Server *server)
181 static const float shutdown_msg_times[] =
183 1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
186 // Automated messages
187 if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
188 for (float t : shutdown_msg_times) {
189 // If shutdown timer matches an automessage, shot it
190 if (m_timer > t && m_timer - dtime < t) {
191 std::wstring periodicMsg = getShutdownTimerMessage();
193 infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
194 server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
201 if (m_timer < 0.0f) {
207 std::wstring Server::ShutdownState::getShutdownTimerMessage() const
209 std::wstringstream ws;
210 ws << L"*** Server shutting down in "
211 << duration_to_string(myround(m_timer)).c_str() << ".";
220 const std::string &path_world,
221 const SubgameSpec &gamespec,
222 bool simple_singleplayer_mode,
225 ChatInterface *iface,
226 std::string *on_shutdown_errmsg
228 m_bind_addr(bind_addr),
229 m_path_world(path_world),
230 m_gamespec(gamespec),
231 m_simple_singleplayer_mode(simple_singleplayer_mode),
232 m_dedicated(dedicated),
233 m_async_fatal_error(""),
234 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
237 m_bind_addr.isIPv6(),
239 m_itemdef(createItemDefManager()),
240 m_nodedef(createNodeDefManager()),
241 m_craftdef(createCraftDefManager()),
242 m_thread(new ServerThread(this)),
245 m_on_shutdown_errmsg(on_shutdown_errmsg),
246 m_modchannel_mgr(new ModChannelMgr())
248 if (m_path_world.empty())
249 throw ServerError("Supplied empty world path");
251 if (!gamespec.isValid())
252 throw ServerError("Supplied invalid gamespec");
255 m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
257 m_metrics_backend = std::make_unique<MetricsBackend>();
260 m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
261 m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
263 m_timeofday_gauge = m_metrics_backend->addGauge(
264 "minetest_core_timeofday",
265 "Time of day value");
267 m_lag_gauge = m_metrics_backend->addGauge(
268 "minetest_core_latency",
269 "Latency value (in seconds)");
272 const std::string aom_types[] = {"reliable", "unreliable"};
273 for (u32 i = 0; i < ARRLEN(aom_types); i++) {
274 std::string help_str("Number of active object messages generated (");
275 help_str.append(aom_types[i]).append(")");
276 m_aom_buffer_counter[i] = m_metrics_backend->addCounter(
277 "minetest_core_aom_generated_count", help_str,
278 {{"type", aom_types[i]}});
281 m_packet_recv_counter = m_metrics_backend->addCounter(
282 "minetest_core_server_packet_recv",
283 "Processable packets received");
285 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
286 "minetest_core_server_packet_recv_processed",
287 "Valid received packets processed");
289 m_map_edit_event_counter = m_metrics_backend->addCounter(
290 "minetest_core_map_edit_events",
291 "Number of map edit events");
293 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
299 // Send shutdown message
300 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
301 L"*** Server shutting down"));
304 MutexAutoLock envlock(m_env_mutex);
306 infostream << "Server: Saving players" << std::endl;
307 m_env->saveLoadedPlayers();
309 infostream << "Server: Kicking players" << std::endl;
310 std::string kick_msg;
311 bool reconnect = false;
312 if (isShutdownRequested()) {
313 reconnect = m_shutdown_state.should_reconnect;
314 kick_msg = m_shutdown_state.message;
316 if (kick_msg.empty()) {
317 kick_msg = g_settings->get("kick_msg_shutdown");
319 m_env->saveLoadedPlayers(true);
320 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
321 kick_msg, reconnect);
324 actionstream << "Server: Shutting down" << std::endl;
326 // Do this before stopping the server in case mapgen callbacks need to access
327 // server-controlled resources (like ModStorages). Also do them before
328 // shutdown callbacks since they may modify state that is finalized in a
331 m_emerge->stopThreads();
334 MutexAutoLock envlock(m_env_mutex);
336 // Execute script shutdown hooks
337 infostream << "Executing shutdown hooks" << std::endl;
339 m_script->on_shutdown();
340 } catch (ModError &e) {
341 errorstream << "ModError: " << e.what() << std::endl;
342 if (m_on_shutdown_errmsg) {
343 if (m_on_shutdown_errmsg->empty()) {
344 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
346 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
351 infostream << "Server: Saving environment metadata" << std::endl;
361 // Write any changes before deletion.
362 if (m_mod_storage_database)
363 m_mod_storage_database->endSave();
365 // Delete things in the reverse order of creation
369 delete m_mod_storage_database;
375 // Deinitialize scripting
376 infostream << "Server: Deinitializing scripting" << std::endl;
378 delete m_startup_server_map; // if available
379 delete m_game_settings;
381 while (!m_unsent_map_edit_queue.empty()) {
382 delete m_unsent_map_edit_queue.front();
383 m_unsent_map_edit_queue.pop();
389 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
390 if (m_simple_singleplayer_mode)
391 infostream << " in simple singleplayer mode" << std::endl;
393 infostream << std::endl;
394 infostream << "- world: " << m_path_world << std::endl;
395 infostream << "- game: " << m_gamespec.path << std::endl;
397 m_game_settings = Settings::createLayer(SL_GAME);
399 // Create world if it doesn't exist
401 loadGameConfAndInitWorld(m_path_world,
402 fs::GetFilenameFromPath(m_path_world.c_str()),
404 } catch (const BaseException &e) {
405 throw ServerError(std::string("Failed to initialize world: ") + e.what());
408 // Create emerge manager
409 m_emerge = new EmergeManager(this, m_metrics_backend.get());
411 // Create ban manager
412 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
413 m_banmanager = new BanManager(ban_path);
415 // Create mod storage database and begin a save for later
416 m_mod_storage_database = openModStorageDatabase(m_path_world);
417 m_mod_storage_database->beginSave();
419 m_modmgr = std::make_unique<ServerModManager>(m_path_world);
420 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
421 // complain about mods with unsatisfied dependencies
422 if (!m_modmgr->isConsistent()) {
423 m_modmgr->printUnsatisfiedModsError();
427 MutexAutoLock envlock(m_env_mutex);
429 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
430 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
431 m_startup_server_map = servermap;
433 // Initialize scripting
434 infostream << "Server: Initializing Lua" << std::endl;
436 m_script = new ServerScripting(this);
438 // Must be created before mod loading because we have some inventory creation
439 m_inventory_mgr = std::make_unique<ServerInventoryManager>();
441 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
443 m_gamespec.checkAndLog();
444 m_modmgr->loadMods(m_script);
446 // Read Textures and calculate sha1 sums
449 // Apply item aliases in the node definition manager
450 m_nodedef->updateAliases(m_itemdef);
452 // Apply texture overrides from texturepack/override.txt
453 std::vector<std::string> paths;
454 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
455 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
456 for (const std::string &path : paths) {
457 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
458 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
459 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
462 m_nodedef->setNodeRegistrationStatus(true);
464 // Perform pending node name resolutions
465 m_nodedef->runNodeResolveCallbacks();
467 // unmap node names in cross-references
468 m_nodedef->resolveCrossrefs();
470 // init the recipe hashes to speed up crafting
471 m_craftdef->initHashes(this);
473 // Initialize Environment
474 m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
475 m_env = new ServerEnvironment(servermap, m_script, this,
476 m_path_world, m_metrics_backend.get());
478 m_inventory_mgr->setEnv(m_env);
479 m_clients.setEnv(m_env);
481 if (!servermap->settings_mgr.makeMapgenParams())
482 FATAL_ERROR("Couldn't create any mapgen type");
484 // Initialize mapgens
485 m_emerge->initMapgens(servermap->getMapgenParams());
487 if (g_settings->getBool("enable_rollback_recording")) {
488 // Create rollback manager
489 m_rollback = new RollbackManager(m_path_world, this);
492 // Give environment reference to scripting api
493 m_script->initializeEnvironment(m_env);
495 // Do this after regular script init is done
496 m_script->initAsync();
498 // Register us to receive map edit events
499 servermap->addEventReceiver(this);
503 // Those settings can be overwritten in world.mt, they are
504 // intended to be cached after environment loading.
505 m_liquid_transform_every = g_settings->getFloat("liquid_update");
506 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
507 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
508 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
515 infostream << "Starting server on " << m_bind_addr.serializeString()
516 << "..." << std::endl;
518 // Stop thread if already running
521 // Initialize connection
522 m_con->SetTimeoutMs(30);
523 m_con->Serve(m_bind_addr);
528 // ASCII art for the win!
530 << " __. __. __. " << std::endl
531 << " _____ |__| ____ _____ / |_ _____ _____ / |_ " << std::endl
532 << " / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\" << std::endl
533 << "| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | " << std::endl
534 << "|__|_| / |___| /\\______> | \\______>_____/| | " << std::endl
535 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
536 actionstream << "World at [" << m_path_world << "]" << std::endl;
537 actionstream << "Server for gameid=\"" << m_gamespec.id
538 << "\" listening on ";
539 m_bind_addr.print(actionstream);
540 actionstream << "." << std::endl;
545 infostream<<"Server: Stopping and waiting threads"<<std::endl;
547 // Stop threads (set run=false first so both start stopping)
551 infostream<<"Server: Threads stopped"<<std::endl;
554 void Server::step(float dtime)
560 MutexAutoLock lock(m_step_dtime_mutex);
561 m_step_dtime += dtime;
563 // Throw if fatal error occurred in thread
564 std::string async_err = m_async_fatal_error.get();
565 if (!async_err.empty()) {
566 if (!m_simple_singleplayer_mode) {
567 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
568 g_settings->get("kick_msg_crash"),
569 g_settings->getBool("ask_reconnect_on_crash"));
571 throw ServerError("AsyncErr: " + async_err);
575 void Server::AsyncRunStep(bool initial_step)
580 MutexAutoLock lock1(m_step_dtime_mutex);
581 dtime = m_step_dtime;
585 // Send blocks to clients
589 if((dtime < 0.001) && !initial_step)
592 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
595 MutexAutoLock lock1(m_step_dtime_mutex);
596 m_step_dtime -= dtime;
602 m_uptime_counter->increment(dtime);
607 Update time of day and overall game time
609 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
612 Send to clients at constant intervals
615 m_time_of_day_send_timer -= dtime;
616 if (m_time_of_day_send_timer < 0.0) {
617 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
618 u16 time = m_env->getTimeOfDay();
619 float time_speed = g_settings->getFloat("time_speed");
620 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
622 m_timeofday_gauge->set(time);
626 MutexAutoLock lock(m_env_mutex);
627 // Figure out and report maximum lag to environment
628 float max_lag = m_env->getMaxLagEstimate();
629 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
631 if(dtime > 0.1 && dtime > max_lag * 2.0)
632 infostream<<"Server: Maximum lag peaked to "<<dtime
636 m_env->reportMaxLagEstimate(max_lag);
642 static const float map_timer_and_unload_dtime = 2.92;
643 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
645 MutexAutoLock lock(m_env_mutex);
646 // Run Map's timers and unload unused data
647 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
648 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
649 g_settings->getFloat("server_unload_unused_data_timeout"),
654 Listen to the admin chat, if available
657 if (!m_admin_chat->command_queue.empty()) {
658 MutexAutoLock lock(m_env_mutex);
659 while (!m_admin_chat->command_queue.empty()) {
660 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
661 handleChatInterfaceEvent(evt);
665 m_admin_chat->outgoing_queue.push_back(
666 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
673 /* Transform liquids */
674 m_liquid_transform_timer += dtime;
675 if(m_liquid_transform_timer >= m_liquid_transform_every)
677 m_liquid_transform_timer -= m_liquid_transform_every;
679 MutexAutoLock lock(m_env_mutex);
681 ScopeProfiler sp(g_profiler, "Server: liquid transform");
683 std::map<v3s16, MapBlock*> modified_blocks;
684 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
687 Set the modified blocks unsent for all the clients
689 if (!modified_blocks.empty()) {
690 SetBlocksNotSent(modified_blocks);
693 m_clients.step(dtime);
695 // increase/decrease lag gauge gradually
696 if (m_lag_gauge->get() > dtime) {
697 m_lag_gauge->decrement(dtime/100);
699 m_lag_gauge->increment(dtime/100);
703 float &counter = m_step_pending_dyn_media_timer;
705 if (counter >= 5.0f) {
706 stepPendingDynMediaCallbacks(counter);
713 // send masterserver announce
715 float &counter = m_masterserver_timer;
716 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
717 g_settings->getBool("server_announce")) {
718 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
719 ServerList::AA_START,
720 m_bind_addr.getPort(),
721 m_clients.getPlayerNames(),
722 m_uptime_counter->get(),
723 m_env->getGameTime(),
726 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
736 Check added and deleted active objects
739 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
740 MutexAutoLock envlock(m_env_mutex);
743 ClientInterface::AutoLock clientlock(m_clients);
744 const RemoteClientMap &clients = m_clients.getClientList();
745 ScopeProfiler sp(g_profiler, "Server: update objects within range");
747 m_player_gauge->set(clients.size());
748 for (const auto &client_it : clients) {
749 RemoteClient *client = client_it.second;
751 if (client->getState() < CS_DefinitionsSent)
754 // This can happen if the client times out somehow
755 if (!m_env->getPlayer(client->peer_id))
758 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
762 SendActiveObjectRemoveAdd(client, playersao);
766 // Write changes to the mod storage
767 m_mod_storage_save_timer -= dtime;
768 if (m_mod_storage_save_timer <= 0.0f) {
769 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
770 m_mod_storage_database->endSave();
771 m_mod_storage_database->beginSave();
779 MutexAutoLock envlock(m_env_mutex);
780 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
783 // Value = data sent by object
784 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
786 // Get active object messages from environment
787 ActiveObjectMessage aom(0);
788 u32 count_reliable = 0, count_unreliable = 0;
790 if (!m_env->getActiveObjectMessage(&aom))
797 std::vector<ActiveObjectMessage>* message_list = nullptr;
798 auto n = buffered_messages.find(aom.id);
799 if (n == buffered_messages.end()) {
800 message_list = new std::vector<ActiveObjectMessage>;
801 buffered_messages[aom.id] = message_list;
803 message_list = n->second;
805 message_list->push_back(std::move(aom));
808 m_aom_buffer_counter[0]->increment(count_reliable);
809 m_aom_buffer_counter[1]->increment(count_unreliable);
812 ClientInterface::AutoLock clientlock(m_clients);
813 const RemoteClientMap &clients = m_clients.getClientList();
814 // Route data to every client
815 std::string reliable_data, unreliable_data;
816 for (const auto &client_it : clients) {
817 reliable_data.clear();
818 unreliable_data.clear();
819 RemoteClient *client = client_it.second;
820 PlayerSAO *player = getPlayerSAO(client->peer_id);
821 // Go through all objects in message buffer
822 for (const auto &buffered_message : buffered_messages) {
823 // If object does not exist or is not known by client, skip it
824 u16 id = buffered_message.first;
825 ServerActiveObject *sao = m_env->getActiveObject(id);
826 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
829 // Get message list of object
830 std::vector<ActiveObjectMessage>* list = buffered_message.second;
831 // Go through every message
832 for (const ActiveObjectMessage &aom : *list) {
833 // Send position updates to players who do not see the attachment
834 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
835 if (sao->getId() == player->getId())
838 // Do not send position updates for attached players
839 // as long the parent is known to the client
840 ServerActiveObject *parent = sao->getParent();
841 if (parent && client->m_known_objects.find(parent->getId()) !=
842 client->m_known_objects.end())
846 // Add full new data to appropriate buffer
847 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
849 writeU16((u8*) idbuf, aom.id);
852 buffer.append(idbuf, sizeof(idbuf));
853 buffer.append(serializeString16(aom.datastring));
857 reliable_data and unreliable_data are now ready.
860 if (!reliable_data.empty()) {
861 SendActiveObjectMessages(client->peer_id, reliable_data);
864 if (!unreliable_data.empty()) {
865 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
870 // Clear buffered_messages
871 for (auto &buffered_message : buffered_messages) {
872 delete buffered_message.second;
877 Send queued-for-sending map edit events.
880 // We will be accessing the environment
881 MutexAutoLock lock(m_env_mutex);
883 // Single change sending is disabled if queue size is big
884 bool disable_single_change_sending = false;
885 if(m_unsent_map_edit_queue.size() >= 4)
886 disable_single_change_sending = true;
888 const auto event_count = m_unsent_map_edit_queue.size();
889 m_map_edit_event_counter->increment(event_count);
891 // We'll log the amount of each
894 std::list<v3s16> node_meta_updates;
896 while (!m_unsent_map_edit_queue.empty()) {
897 MapEditEvent* event = m_unsent_map_edit_queue.front();
898 m_unsent_map_edit_queue.pop();
900 // Players far away from the change are stored here.
901 // Instead of sending the changes, MapBlocks are set not sent
903 std::unordered_set<u16> far_players;
905 switch (event->type) {
908 prof.add("MEET_ADDNODE", 1);
909 sendAddNode(event->p, event->n, &far_players,
910 disable_single_change_sending ? 5 : 30,
911 event->type == MEET_ADDNODE);
913 case MEET_REMOVENODE:
914 prof.add("MEET_REMOVENODE", 1);
915 sendRemoveNode(event->p, &far_players,
916 disable_single_change_sending ? 5 : 30);
918 case MEET_BLOCK_NODE_METADATA_CHANGED: {
919 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
920 if (!event->is_private_change) {
921 // Don't send the change yet. Collect them to eliminate dupes.
922 node_meta_updates.remove(event->p);
923 node_meta_updates.push_back(event->p);
926 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
927 getNodeBlockPos(event->p))) {
928 block->raiseModified(MOD_STATE_WRITE_NEEDED,
929 MOD_REASON_REPORT_META_CHANGE);
934 prof.add("MEET_OTHER", 1);
935 for (const v3s16 &modified_block : event->modified_blocks) {
936 m_clients.markBlockposAsNotSent(modified_block);
940 prof.add("unknown", 1);
941 warningstream << "Server: Unknown MapEditEvent "
942 << ((u32)event->type) << std::endl;
947 Set blocks not sent to far players
949 if (!far_players.empty()) {
950 // Convert list format to that wanted by SetBlocksNotSent
951 std::map<v3s16, MapBlock*> modified_blocks2;
952 for (const v3s16 &modified_block : event->modified_blocks) {
953 modified_blocks2[modified_block] =
954 m_env->getMap().getBlockNoCreateNoEx(modified_block);
957 // Set blocks not sent
958 for (const u16 far_player : far_players) {
959 if (RemoteClient *client = getClient(far_player))
960 client->SetBlocksNotSent(modified_blocks2);
967 if (event_count >= 5) {
968 infostream << "Server: MapEditEvents:" << std::endl;
969 prof.print(infostream);
970 } else if (event_count != 0) {
971 verbosestream << "Server: MapEditEvents:" << std::endl;
972 prof.print(verbosestream);
975 // Send all metadata updates
976 if (node_meta_updates.size())
977 sendMetadataChanged(node_meta_updates);
981 Trigger emerge thread
982 Doing this every 2s is left over from old code, unclear if this is still needed.
985 float &counter = m_emergethread_trigger_timer;
987 if (counter <= 0.0f) {
990 m_emerge->startThreads();
994 // Save map, players and auth stuff
996 float &counter = m_savemap_timer;
998 static thread_local const float save_interval =
999 g_settings->getFloat("server_map_save_interval");
1000 if (counter >= save_interval) {
1002 MutexAutoLock lock(m_env_mutex);
1004 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
1007 if (m_banmanager->isModified()) {
1008 m_banmanager->save();
1011 // Save changed parts of map
1012 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1015 m_env->saveLoadedPlayers();
1017 // Save environment metadata
1022 m_shutdown_state.tick(dtime, this);
1025 void Server::Receive()
1035 In the first iteration *wait* for a packet, afterwards process
1036 all packets that are immediately available (no waiting).
1039 m_con->Receive(&pkt);
1042 if (!m_con->TryReceive(&pkt))
1046 peer_id = pkt.getPeerId();
1047 m_packet_recv_counter->increment();
1049 m_packet_recv_processed_counter->increment();
1050 } catch (const con::InvalidIncomingDataException &e) {
1051 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1052 << e.what() << std::endl;
1053 } catch (const SerializationError &e) {
1054 infostream << "Server::Receive(): SerializationError: what()="
1055 << e.what() << std::endl;
1056 } catch (const ClientStateError &e) {
1057 errorstream << "ProcessData: peer=" << peer_id << " what()="
1058 << e.what() << std::endl;
1059 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1060 } catch (const con::PeerNotFoundException &e) {
1062 } catch (const con::NoIncomingDataException &e) {
1068 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1070 std::string playername;
1071 PlayerSAO *playersao = NULL;
1073 ClientInterface::AutoLock clientlock(m_clients);
1074 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1076 playername = client->getName();
1077 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1081 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1083 // If failed, cancel
1084 if (!playersao || !player) {
1085 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1086 actionstream << "Server: Failed to emerge player \"" << playername
1087 << "\" (player allocated to an another client)" << std::endl;
1088 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1090 errorstream << "Server: " << playername << ": Failed to emerge player"
1092 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1098 Send complete position information
1100 SendMovePlayer(peer_id);
1103 SendPlayerPrivileges(peer_id);
1105 // Send inventory formspec
1106 SendPlayerInventoryFormspec(peer_id);
1109 SendInventory(playersao, false);
1112 SendPlayerHP(playersao);
1114 // Send death screen
1115 if (playersao->isDead())
1116 SendDeathscreen(peer_id, false, v3f(0,0,0));
1119 SendPlayerBreath(playersao);
1122 Update player list and print action
1125 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1126 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1127 m_clients.sendToAll(¬ice_pkt);
1130 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1131 const auto &names = m_clients.getPlayerNames();
1133 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1134 for (const std::string &name : names)
1135 actionstream << name << " ";
1136 actionstream << player->getName() << std::endl;
1141 inline void Server::handleCommand(NetworkPacket *pkt)
1143 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1144 (this->*opHandle.handler)(pkt);
1147 void Server::ProcessData(NetworkPacket *pkt)
1149 // Environment is locked first.
1150 MutexAutoLock envlock(m_env_mutex);
1152 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1153 u32 peer_id = pkt->getPeerId();
1156 Address address = getPeerAddress(peer_id);
1157 std::string addr_s = address.serializeString();
1159 // FIXME: Isn't it a bit excessive to check this for every packet?
1160 if (m_banmanager->isIpBanned(addr_s)) {
1161 std::string ban_name = m_banmanager->getBanName(addr_s);
1162 infostream << "Server: A banned client tried to connect from "
1163 << addr_s << "; banned name was " << ban_name << std::endl;
1164 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1165 "Your IP is banned. Banned name was " + ban_name);
1168 } catch (con::PeerNotFoundException &e) {
1170 * no peer for this packet found
1171 * most common reason is peer timeout, e.g. peer didn't
1172 * respond for some time, your server was overloaded or
1175 infostream << "Server::ProcessData(): Canceling: peer "
1176 << peer_id << " not found" << std::endl;
1181 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1183 // Command must be handled into ToServerCommandHandler
1184 if (command >= TOSERVER_NUM_MSG_TYPES) {
1185 infostream << "Server: Ignoring unknown command "
1186 << command << std::endl;
1190 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1195 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1197 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1198 errorstream << "Server::ProcessData(): Cancelling: Peer"
1199 " serialization format invalid or not initialized."
1200 " Skipping incoming command=" << command << std::endl;
1204 /* Handle commands related to client startup */
1205 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1210 if (m_clients.getClientState(peer_id) < CS_Active) {
1211 if (command == TOSERVER_PLAYERPOS) return;
1213 errorstream << "Got packet command: " << command << " for peer id "
1214 << peer_id << " but client isn't active yet. Dropping packet "
1220 } catch (SendFailedException &e) {
1221 errorstream << "Server::ProcessData(): SendFailedException: "
1222 << "what=" << e.what()
1224 } catch (PacketError &e) {
1225 actionstream << "Server::ProcessData(): PacketError: "
1226 << "what=" << e.what()
1231 void Server::setTimeOfDay(u32 time)
1233 m_env->setTimeOfDay(time);
1234 m_time_of_day_send_timer = 0;
1237 void Server::onMapEditEvent(const MapEditEvent &event)
1239 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1242 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1245 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1247 std::vector<session_t> clients = m_clients.getClientIDs();
1248 ClientInterface::AutoLock clientlock(m_clients);
1249 // Set the modified blocks unsent for all the clients
1250 for (const session_t client_id : clients) {
1251 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1252 client->SetBlocksNotSent(block);
1256 void Server::peerAdded(con::Peer *peer)
1258 verbosestream<<"Server::peerAdded(): peer->id="
1259 <<peer->id<<std::endl;
1261 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1264 void Server::deletingPeer(con::Peer *peer, bool timeout)
1266 verbosestream<<"Server::deletingPeer(): peer->id="
1267 <<peer->id<<", timeout="<<timeout<<std::endl;
1269 m_clients.event(peer->id, CSE_Disconnect);
1270 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1273 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1275 *retval = m_con->getPeerStat(peer_id,type);
1276 return *retval != -1;
1279 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1281 ClientInterface::AutoLock clientlock(m_clients);
1282 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1287 ret.state = client->getState();
1288 ret.addr = client->getAddress();
1289 ret.uptime = client->uptime();
1290 ret.ser_vers = client->serialization_version;
1291 ret.prot_vers = client->net_proto_version;
1293 ret.major = client->getMajor();
1294 ret.minor = client->getMinor();
1295 ret.patch = client->getPatch();
1296 ret.vers_string = client->getFullVer();
1298 ret.lang_code = client->getLangCode();
1303 void Server::handlePeerChanges()
1305 while(!m_peer_change_queue.empty())
1307 con::PeerChange c = m_peer_change_queue.front();
1308 m_peer_change_queue.pop();
1310 verbosestream<<"Server: Handling peer change: "
1311 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1316 case con::PEER_ADDED:
1317 m_clients.CreateClient(c.peer_id);
1320 case con::PEER_REMOVED:
1321 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1325 FATAL_ERROR("Invalid peer change event received!");
1331 void Server::printToConsoleOnly(const std::string &text)
1334 m_admin_chat->outgoing_queue.push_back(
1335 new ChatEventChat("", utf8_to_wide(text)));
1337 std::cout << text << std::endl;
1341 void Server::Send(NetworkPacket *pkt)
1343 Send(pkt->getPeerId(), pkt);
1346 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1348 m_clients.send(peer_id,
1349 clientCommandFactoryTable[pkt->getCommand()].channel,
1351 clientCommandFactoryTable[pkt->getCommand()].reliable);
1354 void Server::SendMovement(session_t peer_id)
1356 std::ostringstream os(std::ios_base::binary);
1358 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1360 pkt << g_settings->getFloat("movement_acceleration_default");
1361 pkt << g_settings->getFloat("movement_acceleration_air");
1362 pkt << g_settings->getFloat("movement_acceleration_fast");
1363 pkt << g_settings->getFloat("movement_speed_walk");
1364 pkt << g_settings->getFloat("movement_speed_crouch");
1365 pkt << g_settings->getFloat("movement_speed_fast");
1366 pkt << g_settings->getFloat("movement_speed_climb");
1367 pkt << g_settings->getFloat("movement_speed_jump");
1368 pkt << g_settings->getFloat("movement_liquid_fluidity");
1369 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1370 pkt << g_settings->getFloat("movement_liquid_sink");
1371 pkt << g_settings->getFloat("movement_gravity");
1376 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1378 m_script->player_event(playersao, "health_changed");
1379 SendPlayerHP(playersao);
1381 // Send to other clients
1382 playersao->sendPunchCommand();
1384 if (playersao->isDead())
1385 HandlePlayerDeath(playersao, reason);
1388 void Server::SendPlayerHP(PlayerSAO *playersao)
1390 SendHP(playersao->getPeerID(), playersao->getHP());
1393 void Server::SendHP(session_t peer_id, u16 hp)
1395 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1400 void Server::SendBreath(session_t peer_id, u16 breath)
1402 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1403 pkt << (u16) breath;
1407 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1408 const std::string &custom_reason, bool reconnect)
1410 assert(reason < SERVER_ACCESSDENIED_MAX);
1412 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1414 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1415 pkt << custom_reason;
1416 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1417 reason == SERVER_ACCESSDENIED_CRASH)
1418 pkt << custom_reason << (u8)reconnect;
1422 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1423 v3f camera_point_target)
1425 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1426 pkt << set_camera_point_target << camera_point_target;
1430 void Server::SendItemDef(session_t peer_id,
1431 IItemDefManager *itemdef, u16 protocol_version)
1433 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1437 u32 length of the next item
1438 zlib-compressed serialized ItemDefManager
1440 std::ostringstream tmp_os(std::ios::binary);
1441 itemdef->serialize(tmp_os, protocol_version);
1442 std::ostringstream tmp_os2(std::ios::binary);
1443 compressZlib(tmp_os.str(), tmp_os2);
1444 pkt.putLongString(tmp_os2.str());
1447 verbosestream << "Server: Sending item definitions to id(" << peer_id
1448 << "): size=" << pkt.getSize() << std::endl;
1453 void Server::SendNodeDef(session_t peer_id,
1454 const NodeDefManager *nodedef, u16 protocol_version)
1456 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1460 u32 length of the next item
1461 zlib-compressed serialized NodeDefManager
1463 std::ostringstream tmp_os(std::ios::binary);
1464 nodedef->serialize(tmp_os, protocol_version);
1465 std::ostringstream tmp_os2(std::ios::binary);
1466 compressZlib(tmp_os.str(), tmp_os2);
1468 pkt.putLongString(tmp_os2.str());
1471 verbosestream << "Server: Sending node definitions to id(" << peer_id
1472 << "): size=" << pkt.getSize() << std::endl;
1478 Non-static send methods
1481 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1483 RemotePlayer *player = sao->getPlayer();
1485 // Do not send new format to old clients
1486 incremental &= player->protocol_version >= 38;
1488 UpdateCrafting(player);
1494 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1496 std::ostringstream os(std::ios::binary);
1497 sao->getInventory()->serialize(os, incremental);
1498 sao->getInventory()->setModified(false);
1499 player->setModified(true);
1501 const std::string &s = os.str();
1502 pkt.putRawString(s.c_str(), s.size());
1506 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1508 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1510 u8 type = message.type;
1511 pkt << version << type << message.sender << message.message
1512 << static_cast<u64>(message.timestamp);
1514 if (peer_id != PEER_ID_INEXISTENT) {
1515 RemotePlayer *player = m_env->getPlayer(peer_id);
1521 m_clients.sendToAll(&pkt);
1525 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1526 const std::string &formname)
1528 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1529 if (formspec.empty()){
1530 //the client should close the formspec
1531 //but make sure there wasn't another one open in meantime
1532 const auto it = m_formspec_state_data.find(peer_id);
1533 if (it != m_formspec_state_data.end() && it->second == formname) {
1534 m_formspec_state_data.erase(peer_id);
1536 pkt.putLongString("");
1538 m_formspec_state_data[peer_id] = formname;
1539 pkt.putLongString(formspec);
1546 // Spawns a particle on peer with peer_id
1547 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1548 const ParticleParameters &p)
1550 static thread_local const float radius =
1551 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1553 if (peer_id == PEER_ID_INEXISTENT) {
1554 std::vector<session_t> clients = m_clients.getClientIDs();
1555 const v3f pos = p.pos * BS;
1556 const float radius_sq = radius * radius;
1558 for (const session_t client_id : clients) {
1559 RemotePlayer *player = m_env->getPlayer(client_id);
1563 PlayerSAO *sao = player->getPlayerSAO();
1567 // Do not send to distant clients
1568 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1571 SendSpawnParticle(client_id, player->protocol_version, p);
1575 assert(protocol_version != 0);
1577 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1580 // NetworkPacket and iostreams are incompatible...
1581 std::ostringstream oss(std::ios_base::binary);
1582 p.serialize(oss, protocol_version);
1583 pkt.putRawString(oss.str());
1589 // Adds a ParticleSpawner on peer with peer_id
1590 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1591 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1593 static thread_local const float radius =
1594 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1596 if (peer_id == PEER_ID_INEXISTENT) {
1597 std::vector<session_t> clients = m_clients.getClientIDs();
1598 const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS;
1599 const float radius_sq = radius * radius;
1600 /* Don't send short-lived spawners to distant players.
1601 * This could be replaced with proper tracking at some point. */
1602 const bool distance_check = !attached_id && p.time <= 1.0f;
1604 for (const session_t client_id : clients) {
1605 RemotePlayer *player = m_env->getPlayer(client_id);
1609 if (distance_check) {
1610 PlayerSAO *sao = player->getPlayerSAO();
1613 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1617 SendAddParticleSpawner(client_id, player->protocol_version,
1618 p, attached_id, id);
1622 assert(protocol_version != 0);
1624 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1626 pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel
1627 << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime
1628 << p.minsize << p.maxsize << p.collisiondetection;
1630 pkt.putLongString(p.texture);
1632 pkt << id << p.vertical << p.collision_removal << attached_id;
1634 std::ostringstream os(std::ios_base::binary);
1635 p.animation.serialize(os, protocol_version);
1636 pkt.putRawString(os.str());
1638 pkt << p.glow << p.object_collision;
1639 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1644 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1646 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1650 if (peer_id != PEER_ID_INEXISTENT)
1653 m_clients.sendToAll(&pkt);
1657 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1659 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1661 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1662 << form->text << form->number << form->item << form->dir
1663 << form->align << form->offset << form->world_pos << form->size
1664 << form->z_index << form->text2 << form->style;
1669 void Server::SendHUDRemove(session_t peer_id, u32 id)
1671 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1676 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1678 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1679 pkt << id << (u8) stat;
1683 case HUD_STAT_SCALE:
1684 case HUD_STAT_ALIGN:
1685 case HUD_STAT_OFFSET:
1686 pkt << *(v2f *) value;
1690 case HUD_STAT_TEXT2:
1691 pkt << *(std::string *) value;
1693 case HUD_STAT_WORLD_POS:
1694 pkt << *(v3f *) value;
1697 pkt << *(v2s32 *) value;
1699 default: // all other types
1700 pkt << *(u32 *) value;
1707 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1709 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1711 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1713 pkt << flags << mask;
1718 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1720 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1721 pkt << param << value;
1725 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1727 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1729 // Handle prior clients here
1730 if (m_clients.getProtocolVersion(peer_id) < 39) {
1731 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1733 for (const std::string& texture : params.textures)
1736 pkt << params.clouds;
1737 } else { // Handle current clients and future clients
1738 pkt << params.bgcolor << params.type
1739 << params.clouds << params.fog_sun_tint
1740 << params.fog_moon_tint << params.fog_tint_type;
1742 if (params.type == "skybox") {
1743 pkt << (u16) params.textures.size();
1744 for (const std::string &texture : params.textures)
1746 } else if (params.type == "regular") {
1747 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1748 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1749 << params.sky_color.night_sky << params.sky_color.night_horizon
1750 << params.sky_color.indoors;
1757 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1759 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1760 pkt << params.visible << params.texture
1761 << params.tonemap << params.sunrise
1762 << params.sunrise_visible << params.scale;
1766 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1768 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1770 pkt << params.visible << params.texture
1771 << params.tonemap << params.scale;
1775 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1777 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1779 pkt << params.visible << params.count
1780 << params.starcolor << params.scale;
1785 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1787 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1788 pkt << params.density << params.color_bright << params.color_ambient
1789 << params.height << params.thickness << params.speed;
1793 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1796 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1799 pkt << do_override << (u16) (ratio * 65535);
1804 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1806 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1809 pkt << lighting.shadow_intensity;
1814 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1816 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1817 pkt << time << time_speed;
1819 if (peer_id == PEER_ID_INEXISTENT) {
1820 m_clients.sendToAll(&pkt);
1827 void Server::SendPlayerBreath(PlayerSAO *sao)
1831 m_script->player_event(sao, "breath_changed");
1832 SendBreath(sao->getPeerID(), sao->getBreath());
1835 void Server::SendMovePlayer(session_t peer_id)
1837 RemotePlayer *player = m_env->getPlayer(peer_id);
1839 PlayerSAO *sao = player->getPlayerSAO();
1842 // Send attachment updates instantly to the client prior updating position
1843 sao->sendOutdatedData();
1845 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1846 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1849 v3f pos = sao->getBasePosition();
1850 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1851 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1852 << " pitch=" << sao->getLookPitch()
1853 << " yaw=" << sao->getRotation().Y
1860 void Server::SendPlayerFov(session_t peer_id)
1862 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1864 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1865 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1870 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1871 f32 animation_speed)
1873 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1876 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1877 << animation_frames[3] << animation_speed;
1882 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1884 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1885 pkt << first << third;
1889 void Server::SendPlayerPrivileges(session_t peer_id)
1891 RemotePlayer *player = m_env->getPlayer(peer_id);
1893 if(player->getPeerId() == PEER_ID_INEXISTENT)
1896 std::set<std::string> privs;
1897 m_script->getAuth(player->getName(), NULL, &privs);
1899 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1900 pkt << (u16) privs.size();
1902 for (const std::string &priv : privs) {
1909 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1911 RemotePlayer *player = m_env->getPlayer(peer_id);
1913 if (player->getPeerId() == PEER_ID_INEXISTENT)
1916 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1917 pkt.putLongString(player->inventory_formspec);
1922 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1924 RemotePlayer *player = m_env->getPlayer(peer_id);
1926 if (player->getPeerId() == PEER_ID_INEXISTENT)
1929 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1930 pkt << player->formspec_prepend;
1934 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1936 // Radius inside which objects are active
1937 static thread_local const s16 radius =
1938 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1940 // Radius inside which players are active
1941 static thread_local const bool is_transfer_limited =
1942 g_settings->exists("unlimited_player_transfer_distance") &&
1943 !g_settings->getBool("unlimited_player_transfer_distance");
1945 static thread_local const s16 player_transfer_dist =
1946 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
1948 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
1949 radius : player_transfer_dist;
1951 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
1955 std::queue<u16> removed_objects, added_objects;
1956 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
1957 client->m_known_objects, removed_objects);
1958 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
1959 client->m_known_objects, added_objects);
1961 int removed_count = removed_objects.size();
1962 int added_count = added_objects.size();
1964 if (removed_objects.empty() && added_objects.empty())
1970 // Handle removed objects
1971 writeU16((u8*)buf, removed_objects.size());
1972 data.append(buf, 2);
1973 while (!removed_objects.empty()) {
1975 u16 id = removed_objects.front();
1976 ServerActiveObject* obj = m_env->getActiveObject(id);
1978 // Add to data buffer for sending
1979 writeU16((u8*)buf, id);
1980 data.append(buf, 2);
1982 // Remove from known objects
1983 client->m_known_objects.erase(id);
1985 if (obj && obj->m_known_by_count > 0)
1986 obj->m_known_by_count--;
1988 removed_objects.pop();
1991 // Handle added objects
1992 writeU16((u8*)buf, added_objects.size());
1993 data.append(buf, 2);
1994 while (!added_objects.empty()) {
1996 u16 id = added_objects.front();
1997 ServerActiveObject *obj = m_env->getActiveObject(id);
1998 added_objects.pop();
2001 warningstream << FUNCTION_NAME << ": NULL object id="
2002 << (int)id << std::endl;
2007 u8 type = obj->getSendType();
2009 // Add to data buffer for sending
2010 writeU16((u8*)buf, id);
2011 data.append(buf, 2);
2012 writeU8((u8*)buf, type);
2013 data.append(buf, 1);
2015 data.append(serializeString32(
2016 obj->getClientInitializationData(client->net_proto_version)));
2018 // Add to known objects
2019 client->m_known_objects.insert(id);
2021 obj->m_known_by_count++;
2024 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2025 pkt.putRawString(data.c_str(), data.size());
2028 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2029 << removed_count << " removed, " << added_count << " added, "
2030 << "packet size is " << pkt.getSize() << std::endl;
2033 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2036 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2037 datas.size(), peer_id);
2039 pkt.putRawString(datas.c_str(), datas.size());
2041 m_clients.send(pkt.getPeerId(),
2042 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2046 void Server::SendCSMRestrictionFlags(session_t peer_id)
2048 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2049 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2050 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2054 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2056 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2061 inline s32 Server::nextSoundId()
2063 s32 ret = m_next_sound_id;
2064 if (m_next_sound_id == INT32_MAX)
2065 m_next_sound_id = 0; // signed overflow is undefined
2071 s32 Server::playSound(const SimpleSoundSpec &spec,
2072 const ServerSoundParams ¶ms, bool ephemeral)
2074 // Find out initial position of sound
2075 bool pos_exists = false;
2076 v3f pos = params.getPos(m_env, &pos_exists);
2077 // If position is not found while it should be, cancel sound
2078 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2081 // Filter destination clients
2082 std::vector<session_t> dst_clients;
2083 if (!params.to_player.empty()) {
2084 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2086 infostream<<"Server::playSound: Player \""<<params.to_player
2087 <<"\" not found"<<std::endl;
2090 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2091 infostream<<"Server::playSound: Player \""<<params.to_player
2092 <<"\" not connected"<<std::endl;
2095 dst_clients.push_back(player->getPeerId());
2097 std::vector<session_t> clients = m_clients.getClientIDs();
2099 for (const session_t client_id : clients) {
2100 RemotePlayer *player = m_env->getPlayer(client_id);
2103 if (!params.exclude_player.empty() &&
2104 params.exclude_player == player->getName())
2107 PlayerSAO *sao = player->getPlayerSAO();
2112 if(sao->getBasePosition().getDistanceFrom(pos) >
2113 params.max_hear_distance)
2116 dst_clients.push_back(client_id);
2120 if(dst_clients.empty())
2125 ServerPlayingSound *psound = nullptr;
2127 id = -1; // old clients will still use this, so pick a reserved ID
2130 // The sound will exist as a reference in m_playing_sounds
2131 m_playing_sounds[id] = ServerPlayingSound();
2132 psound = &m_playing_sounds[id];
2133 psound->params = params;
2134 psound->spec = spec;
2137 float gain = params.gain * spec.gain;
2138 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2139 pkt << id << spec.name << gain
2140 << (u8) params.type << pos << params.object
2141 << params.loop << params.fade << params.pitch
2144 bool as_reliable = !ephemeral;
2146 for (const u16 dst_client : dst_clients) {
2148 psound->clients.insert(dst_client);
2149 m_clients.send(dst_client, 0, &pkt, as_reliable);
2153 void Server::stopSound(s32 handle)
2155 // Get sound reference
2156 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2157 m_playing_sounds.find(handle);
2158 if (i == m_playing_sounds.end())
2160 ServerPlayingSound &psound = i->second;
2162 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2165 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2166 si != psound.clients.end(); ++si) {
2168 m_clients.send(*si, 0, &pkt, true);
2170 // Remove sound reference
2171 m_playing_sounds.erase(i);
2174 void Server::fadeSound(s32 handle, float step, float gain)
2176 // Get sound reference
2177 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2178 m_playing_sounds.find(handle);
2179 if (i == m_playing_sounds.end())
2182 ServerPlayingSound &psound = i->second;
2183 psound.params.gain = gain;
2185 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2186 pkt << handle << step << gain;
2188 // Backwards compability
2189 bool play_sound = gain > 0;
2190 ServerPlayingSound compat_psound = psound;
2191 compat_psound.clients.clear();
2193 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2194 compat_pkt << handle;
2196 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2197 it != psound.clients.end();) {
2198 if (m_clients.getProtocolVersion(*it) >= 32) {
2200 m_clients.send(*it, 0, &pkt, true);
2203 compat_psound.clients.insert(*it);
2205 m_clients.send(*it, 0, &compat_pkt, true);
2206 psound.clients.erase(it++);
2210 // Remove sound reference
2211 if (!play_sound || psound.clients.empty())
2212 m_playing_sounds.erase(i);
2214 if (play_sound && !compat_psound.clients.empty()) {
2215 // Play new sound volume on older clients
2216 playSound(compat_psound.spec, compat_psound.params);
2220 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2223 float maxd = far_d_nodes * BS;
2224 v3f p_f = intToFloat(p, BS);
2225 v3s16 block_pos = getNodeBlockPos(p);
2227 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2230 std::vector<session_t> clients = m_clients.getClientIDs();
2231 ClientInterface::AutoLock clientlock(m_clients);
2233 for (session_t client_id : clients) {
2234 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2238 RemotePlayer *player = m_env->getPlayer(client_id);
2239 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2241 // If player is far away, only set modified blocks not sent
2242 if (!client->isBlockSent(block_pos) || (sao &&
2243 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2245 far_players->emplace(client_id);
2247 client->SetBlockNotSent(block_pos);
2252 m_clients.send(client_id, 0, &pkt, true);
2256 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2257 float far_d_nodes, bool remove_metadata)
2259 float maxd = far_d_nodes * BS;
2260 v3f p_f = intToFloat(p, BS);
2261 v3s16 block_pos = getNodeBlockPos(p);
2263 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2264 pkt << p << n.param0 << n.param1 << n.param2
2265 << (u8) (remove_metadata ? 0 : 1);
2267 std::vector<session_t> clients = m_clients.getClientIDs();
2268 ClientInterface::AutoLock clientlock(m_clients);
2270 for (session_t client_id : clients) {
2271 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2275 RemotePlayer *player = m_env->getPlayer(client_id);
2276 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2278 // If player is far away, only set modified blocks not sent
2279 if (!client->isBlockSent(block_pos) || (sao &&
2280 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2282 far_players->emplace(client_id);
2284 client->SetBlockNotSent(block_pos);
2289 m_clients.send(client_id, 0, &pkt, true);
2293 void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes)
2295 float maxd = far_d_nodes * BS;
2296 NodeMetadataList meta_updates_list(false);
2297 std::vector<session_t> clients = m_clients.getClientIDs();
2299 ClientInterface::AutoLock clientlock(m_clients);
2301 for (session_t i : clients) {
2302 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2306 ServerActiveObject *player = m_env->getActiveObject(i);
2307 v3f player_pos = player ? player->getBasePosition() : v3f();
2309 for (const v3s16 &pos : meta_updates) {
2310 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2315 v3s16 block_pos = getNodeBlockPos(pos);
2316 if (!client->isBlockSent(block_pos) || (player &&
2317 player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) {
2318 client->SetBlockNotSent(block_pos);
2322 // Add the change to send list
2323 meta_updates_list.set(pos, meta);
2325 if (meta_updates_list.size() == 0)
2328 // Send the meta changes
2329 std::ostringstream os(std::ios::binary);
2330 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2331 std::ostringstream oss(std::ios::binary);
2332 compressZlib(os.str(), oss);
2334 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0);
2335 pkt.putLongString(oss.str());
2336 m_clients.send(i, 0, &pkt, true);
2338 meta_updates_list.clear();
2342 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2343 u16 net_proto_version, SerializedBlockCache *cache)
2345 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2346 std::string s, *sptr = nullptr;
2349 auto it = cache->find({block->getPos(), ver});
2350 if (it != cache->end())
2354 // Serialize the block in the right format
2356 std::ostringstream os(std::ios_base::binary);
2357 block->serialize(os, ver, false, net_compression_level);
2358 block->serializeNetworkSpecific(os);
2363 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2364 pkt << block->getPos();
2365 pkt.putRawString(*sptr);
2368 // Store away in cache
2369 if (cache && sptr == &s)
2370 (*cache)[{block->getPos(), ver}] = std::move(s);
2373 void Server::SendBlocks(float dtime)
2375 MutexAutoLock envlock(m_env_mutex);
2376 //TODO check if one big lock could be faster then multiple small ones
2378 std::vector<PrioritySortedBlockTransfer> queue;
2380 u32 total_sending = 0, unique_clients = 0;
2383 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2385 std::vector<session_t> clients = m_clients.getClientIDs();
2387 ClientInterface::AutoLock clientlock(m_clients);
2388 for (const session_t client_id : clients) {
2389 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2394 total_sending += client->getSendingCount();
2395 const auto old_count = queue.size();
2396 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2397 unique_clients += queue.size() > old_count ? 1 : 0;
2402 // Lowest priority number comes first.
2403 // Lowest is most important.
2404 std::sort(queue.begin(), queue.end());
2406 ClientInterface::AutoLock clientlock(m_clients);
2408 // Maximal total count calculation
2409 // The per-client block sends is halved with the maximal online users
2410 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2411 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2413 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2414 Map &map = m_env->getMap();
2416 SerializedBlockCache cache, *cache_ptr = nullptr;
2417 if (unique_clients > 1) {
2418 // caching is pointless with a single client
2422 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2423 if (total_sending >= max_blocks_to_send)
2426 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2430 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2435 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2436 client->net_proto_version, cache_ptr);
2438 client->SentBlock(block_to_send.pos);
2443 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2445 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2449 ClientInterface::AutoLock clientlock(m_clients);
2450 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2451 if (!client || client->isBlockSent(blockpos))
2453 SendBlockNoLock(peer_id, block, client->serialization_version,
2454 client->net_proto_version);
2459 bool Server::addMediaFile(const std::string &filename,
2460 const std::string &filepath, std::string *filedata_to,
2461 std::string *digest_to)
2463 // If name contains illegal characters, ignore the file
2464 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2465 infostream << "Server: ignoring illegal file name: \""
2466 << filename << "\"" << std::endl;
2469 // If name is not in a supported format, ignore it
2470 const char *supported_ext[] = {
2471 ".png", ".jpg", ".bmp", ".tga",
2473 ".x", ".b3d", ".obj",
2474 // Custom translation file format
2478 if (removeStringEnd(filename, supported_ext).empty()) {
2479 infostream << "Server: ignoring unsupported file extension: \""
2480 << filename << "\"" << std::endl;
2483 // Ok, attempt to load the file and add to cache
2486 std::string filedata;
2487 if (!fs::ReadFile(filepath, filedata)) {
2488 errorstream << "Server::addMediaFile(): Failed to open \""
2489 << filename << "\" for reading" << std::endl;
2493 if (filedata.empty()) {
2494 errorstream << "Server::addMediaFile(): Empty file \""
2495 << filepath << "\"" << std::endl;
2500 sha1.addBytes(filedata.c_str(), filedata.length());
2502 unsigned char *digest = sha1.getDigest();
2503 std::string sha1_base64 = base64_encode(digest, 20);
2504 std::string sha1_hex = hex_encode((char*) digest, 20);
2506 *digest_to = std::string((char*) digest, 20);
2510 m_media[filename] = MediaInfo(filepath, sha1_base64);
2511 verbosestream << "Server: " << sha1_hex << " is " << filename
2515 *filedata_to = std::move(filedata);
2519 void Server::fillMediaCache()
2521 infostream << "Server: Calculating media file checksums" << std::endl;
2523 // Collect all media file paths
2524 std::vector<std::string> paths;
2526 // ordered in descending priority
2527 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2528 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2529 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2530 m_modmgr->getModsMediaPaths(paths);
2532 // Collect media file information from paths into cache
2533 for (const std::string &mediapath : paths) {
2534 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2535 for (const fs::DirListNode &dln : dirlist) {
2536 if (dln.dir) // Ignore dirs (already in paths)
2539 const std::string &filename = dln.name;
2540 if (m_media.find(filename) != m_media.end()) // Do not override
2543 std::string filepath = mediapath;
2544 filepath.append(DIR_DELIM).append(filename);
2545 addMediaFile(filename, filepath);
2549 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2552 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2555 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2558 std::string lang_suffix;
2559 lang_suffix.append(".").append(lang_code).append(".tr");
2560 for (const auto &i : m_media) {
2561 if (i.second.no_announce)
2563 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2570 for (const auto &i : m_media) {
2571 if (i.second.no_announce)
2573 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2575 pkt << i.first << i.second.sha1_digest;
2578 pkt << g_settings->get("remote_media");
2581 verbosestream << "Server: Announcing files to id(" << peer_id
2582 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2585 struct SendableMedia
2591 SendableMedia(const std::string &name, const std::string &path,
2592 std::string &&data):
2593 name(name), path(path), data(std::move(data))
2597 void Server::sendRequestedMedia(session_t peer_id,
2598 const std::vector<std::string> &tosend)
2600 verbosestream<<"Server::sendRequestedMedia(): "
2601 <<"Sending files to client"<<std::endl;
2605 // Put 5kB in one bunch (this is not accurate)
2606 u32 bytes_per_bunch = 5000;
2608 std::vector< std::vector<SendableMedia> > file_bunches;
2609 file_bunches.emplace_back();
2611 u32 file_size_bunch_total = 0;
2613 for (const std::string &name : tosend) {
2614 if (m_media.find(name) == m_media.end()) {
2615 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2616 <<"unknown file \""<<(name)<<"\""<<std::endl;
2620 const auto &m = m_media[name];
2624 if (!fs::ReadFile(m.path, data)) {
2625 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2626 << name << "\"" << std::endl;
2629 file_size_bunch_total += data.size();
2632 file_bunches.back().emplace_back(name, m.path, std::move(data));
2634 // Start next bunch if got enough data
2635 if(file_size_bunch_total >= bytes_per_bunch) {
2636 file_bunches.emplace_back();
2637 file_size_bunch_total = 0;
2642 /* Create and send packets */
2644 u16 num_bunches = file_bunches.size();
2645 for (u16 i = 0; i < num_bunches; i++) {
2648 u16 total number of texture bunches
2649 u16 index of this bunch
2650 u32 number of files in this bunch
2659 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2660 pkt << num_bunches << i << (u32) file_bunches[i].size();
2662 for (const SendableMedia &j : file_bunches[i]) {
2664 pkt.putLongString(j.data);
2667 verbosestream << "Server::sendRequestedMedia(): bunch "
2668 << i << "/" << num_bunches
2669 << " files=" << file_bunches[i].size()
2670 << " size=" << pkt.getSize() << std::endl;
2675 void Server::stepPendingDynMediaCallbacks(float dtime)
2677 MutexAutoLock lock(m_env_mutex);
2679 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2680 it->second.expiry_timer -= dtime;
2681 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2688 const auto &name = it->second.filename;
2689 if (!name.empty()) {
2690 assert(m_media.count(name));
2691 // if no_announce isn't set we're definitely deleting the wrong file!
2692 sanity_check(m_media[name].no_announce);
2694 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2695 m_media.erase(name);
2697 getScriptIface()->freeDynamicMediaCallback(it->first);
2698 it = m_pending_dyn_media.erase(it);
2702 void Server::SendMinimapModes(session_t peer_id,
2703 std::vector<MinimapMode> &modes, size_t wanted_mode)
2705 RemotePlayer *player = m_env->getPlayer(peer_id);
2707 if (player->getPeerId() == PEER_ID_INEXISTENT)
2710 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2711 pkt << (u16)modes.size() << (u16)wanted_mode;
2713 for (auto &mode : modes)
2714 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2719 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2721 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2725 pkt << false; // Remove inventory
2727 pkt << true; // Update inventory
2729 // Serialization & NetworkPacket isn't a love story
2730 std::ostringstream os(std::ios_base::binary);
2731 inventory->serialize(os);
2732 inventory->setModified(false);
2734 const std::string &os_str = os.str();
2735 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2736 pkt.putRawString(os_str);
2739 if (peer_id == PEER_ID_INEXISTENT)
2740 m_clients.sendToAll(&pkt);
2745 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2747 // Lookup player name, to filter detached inventories just after
2748 std::string peer_name;
2749 if (peer_id != PEER_ID_INEXISTENT) {
2750 peer_name = getClient(peer_id, CS_Created)->getName();
2753 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2754 sendDetachedInventory(inv, name, peer_id);
2757 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2764 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2766 infostream << "Server::DiePlayer(): Player "
2767 << playersao->getPlayer()->getName()
2768 << " dies" << std::endl;
2770 playersao->clearParentAttachment();
2772 // Trigger scripted stuff
2773 m_script->on_dieplayer(playersao, reason);
2775 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2778 void Server::RespawnPlayer(session_t peer_id)
2780 PlayerSAO *playersao = getPlayerSAO(peer_id);
2783 infostream << "Server::RespawnPlayer(): Player "
2784 << playersao->getPlayer()->getName()
2785 << " respawns" << std::endl;
2787 const auto *prop = playersao->accessObjectProperties();
2788 playersao->setHP(prop->hp_max,
2789 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2790 playersao->setBreath(prop->breath_max);
2792 bool repositioned = m_script->on_respawnplayer(playersao);
2793 if (!repositioned) {
2794 // setPos will send the new position to client
2795 playersao->setPos(findSpawnPos());
2800 void Server::DenySudoAccess(session_t peer_id)
2802 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2807 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2808 const std::string &custom_reason, bool reconnect)
2810 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2811 m_clients.event(peer_id, CSE_SetDenied);
2812 DisconnectPeer(peer_id);
2815 void Server::DisconnectPeer(session_t peer_id)
2817 m_modchannel_mgr->leaveAllChannels(peer_id);
2818 m_con->DisconnectPeer(peer_id);
2821 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2824 RemoteClient* client = getClient(peer_id, CS_Invalid);
2826 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2828 // Right now, the auth mechs don't change between login and sudo mode.
2829 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2830 client->allowed_sudo_mechs = sudo_auth_mechs;
2832 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2833 << g_settings->getFloat("dedicated_server_step")
2837 m_clients.event(peer_id, CSE_AuthAccept);
2839 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2841 // We only support SRP right now
2842 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2844 resp_pkt << sudo_auth_mechs;
2846 m_clients.event(peer_id, CSE_SudoSuccess);
2850 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2852 std::wstring message;
2855 Clear references to playing sounds
2857 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2858 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2859 ServerPlayingSound &psound = i->second;
2860 psound.clients.erase(peer_id);
2861 if (psound.clients.empty())
2862 m_playing_sounds.erase(i++);
2867 // clear formspec info so the next client can't abuse the current state
2868 m_formspec_state_data.erase(peer_id);
2870 RemotePlayer *player = m_env->getPlayer(peer_id);
2872 /* Run scripts and remove from environment */
2874 PlayerSAO *playersao = player->getPlayerSAO();
2877 playersao->clearChildAttachments();
2878 playersao->clearParentAttachment();
2880 // inform connected clients
2881 const std::string &player_name = player->getName();
2882 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2883 // (u16) 1 + std::string represents a vector serialization representation
2884 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2885 m_clients.sendToAll(¬ice);
2887 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2889 playersao->disconnected();
2896 if (player && reason != CDR_DENY) {
2897 std::ostringstream os(std::ios_base::binary);
2898 std::vector<session_t> clients = m_clients.getClientIDs();
2900 for (const session_t client_id : clients) {
2902 RemotePlayer *player = m_env->getPlayer(client_id);
2906 // Get name of player
2907 os << player->getName() << " ";
2910 std::string name = player->getName();
2911 actionstream << name << " "
2912 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2913 << " List of players: " << os.str() << std::endl;
2915 m_admin_chat->outgoing_queue.push_back(
2916 new ChatEventNick(CET_NICK_REMOVE, name));
2920 MutexAutoLock env_lock(m_env_mutex);
2921 m_clients.DeleteClient(peer_id);
2925 // Send leave chat message to all remaining clients
2926 if (!message.empty()) {
2927 SendChatMessage(PEER_ID_INEXISTENT,
2928 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2932 void Server::UpdateCrafting(RemotePlayer *player)
2934 InventoryList *clist = player->inventory.getList("craft");
2935 if (!clist || clist->getSize() == 0)
2938 if (!clist->checkModified())
2941 // Get a preview for crafting
2943 InventoryLocation loc;
2944 loc.setPlayer(player->getName());
2945 std::vector<ItemStack> output_replacements;
2946 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2947 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2950 InventoryList *plist = player->inventory.getList("craftpreview");
2951 if (plist && plist->getSize() >= 1) {
2952 // Put the new preview in
2953 plist->changeItem(0, preview);
2957 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2959 if (evt->type == CET_NICK_ADD) {
2960 // The terminal informed us of its nick choice
2961 m_admin_nick = ((ChatEventNick *)evt)->nick;
2962 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2963 errorstream << "You haven't set up an account." << std::endl
2964 << "Please log in using the client as '"
2965 << m_admin_nick << "' with a secure password." << std::endl
2966 << "Until then, you can't execute admin tasks via the console," << std::endl
2967 << "and everybody can claim the user account instead of you," << std::endl
2968 << "giving them full control over this server." << std::endl;
2971 assert(evt->type == CET_CHAT);
2972 handleAdminChat((ChatEventChat *)evt);
2976 std::wstring Server::handleChat(const std::string &name,
2977 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2979 // If something goes wrong, this player is to blame
2980 RollbackScopeActor rollback_scope(m_rollback,
2981 std::string("player:") + name);
2983 if (g_settings->getBool("strip_color_codes"))
2984 wmessage = unescape_enriched(wmessage);
2987 switch (player->canSendChatMessage()) {
2988 case RPLAYER_CHATRESULT_FLOODING: {
2989 std::wstringstream ws;
2990 ws << L"You cannot send more messages. You are limited to "
2991 << g_settings->getFloat("chat_message_limit_per_10sec")
2992 << L" messages per 10 seconds.";
2995 case RPLAYER_CHATRESULT_KICK:
2996 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
2997 "You have been kicked due to message flooding.");
2999 case RPLAYER_CHATRESULT_OK:
3002 FATAL_ERROR("Unhandled chat filtering result found.");
3006 if (m_max_chatmessage_length > 0
3007 && wmessage.length() > m_max_chatmessage_length) {
3008 return L"Your message exceed the maximum chat message limit set on the server. "
3009 L"It was refused. Send a shorter message";
3012 auto message = trim(wide_to_utf8(wmessage));
3013 if (message.empty())
3016 if (message.find_first_of("\n\r") != std::wstring::npos) {
3017 return L"Newlines are not permitted in chat messages";
3020 // Run script hook, exit if script ate the chat message
3021 if (m_script->on_chat_message(name, message))
3026 // Whether to send line to the player that sent the message, or to all players
3027 bool broadcast_line = true;
3029 if (check_shout_priv && !checkPriv(name, "shout")) {
3030 line += L"-!- You don't have permission to shout.";
3031 broadcast_line = false;
3034 Workaround for fixing chat on Android. Lua doesn't handle
3035 the Cyrillic alphabet and some characters on older Android devices
3038 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3040 line += utf8_to_wide(m_script->formatChatMessage(name,
3041 wide_to_utf8(wmessage)));
3046 Tell calling method to send the message to sender
3048 if (!broadcast_line)
3052 Send the message to others
3054 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3056 ChatMessage chatmsg(line);
3058 std::vector<session_t> clients = m_clients.getClientIDs();
3059 for (u16 cid : clients)
3060 SendChatMessage(cid, chatmsg);
3065 void Server::handleAdminChat(const ChatEventChat *evt)
3067 std::string name = evt->nick;
3068 std::wstring wmessage = evt->evt_msg;
3070 std::wstring answer = handleChat(name, wmessage);
3072 // If asked to send answer to sender
3073 if (!answer.empty()) {
3074 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3078 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3080 RemoteClient *client = getClientNoEx(peer_id,state_min);
3082 throw ClientNotFoundException("Client not found");
3086 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3088 return m_clients.getClientNoEx(peer_id, state_min);
3091 std::string Server::getPlayerName(session_t peer_id)
3093 RemotePlayer *player = m_env->getPlayer(peer_id);
3095 return "[id="+itos(peer_id)+"]";
3096 return player->getName();
3099 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3101 RemotePlayer *player = m_env->getPlayer(peer_id);
3104 return player->getPlayerSAO();
3107 std::string Server::getStatusString()
3109 std::ostringstream os(std::ios_base::binary);
3112 os << "version: " << g_version_string;
3114 os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3116 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3118 os << " | max lag: " << std::setprecision(3);
3119 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3121 // Information about clients
3123 os << " | clients: ";
3125 std::vector<session_t> clients = m_clients.getClientIDs();
3126 for (session_t client_id : clients) {
3127 RemotePlayer *player = m_env->getPlayer(client_id);
3129 // Get name of player
3130 const char *name = player ? player->getName() : "<unknown>";
3132 // Add name to information string
3141 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3142 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3144 if (!g_settings->get("motd").empty())
3145 os << std::endl << "# Server: " << g_settings->get("motd");
3150 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3152 std::set<std::string> privs;
3153 m_script->getAuth(name, NULL, &privs);
3157 bool Server::checkPriv(const std::string &name, const std::string &priv)
3159 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3160 return (privs.count(priv) != 0);
3163 void Server::reportPrivsModified(const std::string &name)
3166 std::vector<session_t> clients = m_clients.getClientIDs();
3167 for (const session_t client_id : clients) {
3168 RemotePlayer *player = m_env->getPlayer(client_id);
3169 reportPrivsModified(player->getName());
3172 RemotePlayer *player = m_env->getPlayer(name.c_str());
3175 SendPlayerPrivileges(player->getPeerId());
3176 PlayerSAO *sao = player->getPlayerSAO();
3179 sao->updatePrivileges(
3180 getPlayerEffectivePrivs(name),
3185 void Server::reportInventoryFormspecModified(const std::string &name)
3187 RemotePlayer *player = m_env->getPlayer(name.c_str());
3190 SendPlayerInventoryFormspec(player->getPeerId());
3193 void Server::reportFormspecPrependModified(const std::string &name)
3195 RemotePlayer *player = m_env->getPlayer(name.c_str());
3198 SendPlayerFormspecPrepend(player->getPeerId());
3201 void Server::setIpBanned(const std::string &ip, const std::string &name)
3203 m_banmanager->add(ip, name);
3206 void Server::unsetIpBanned(const std::string &ip_or_name)
3208 m_banmanager->remove(ip_or_name);
3211 std::string Server::getBanDescription(const std::string &ip_or_name)
3213 return m_banmanager->getBanDescription(ip_or_name);
3216 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3218 // m_env will be NULL if the server is initializing
3222 if (m_admin_nick == name && !m_admin_nick.empty()) {
3223 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3226 RemotePlayer *player = m_env->getPlayer(name);
3231 if (player->getPeerId() == PEER_ID_INEXISTENT)
3234 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3237 bool Server::showFormspec(const char *playername, const std::string &formspec,
3238 const std::string &formname)
3240 // m_env will be NULL if the server is initializing
3244 RemotePlayer *player = m_env->getPlayer(playername);
3248 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3252 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3257 u32 id = player->addHud(form);
3259 SendHUDAdd(player->getPeerId(), id, form);
3264 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3268 HudElement* todel = player->removeHud(id);
3275 SendHUDRemove(player->getPeerId(), id);
3279 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3284 SendHUDChange(player->getPeerId(), id, stat, data);
3288 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3293 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3294 if (new_hud_flags == player->hud_flags) // no change
3297 SendHUDSetFlags(player->getPeerId(), flags, mask);
3298 player->hud_flags = new_hud_flags;
3300 PlayerSAO* playersao = player->getPlayerSAO();
3305 m_script->player_event(playersao, "hud_changed");
3309 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3314 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3317 player->setHotbarItemcount(hotbar_itemcount);
3318 std::ostringstream os(std::ios::binary);
3319 writeS32(os, hotbar_itemcount);
3320 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3324 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3329 player->setHotbarImage(name);
3330 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3333 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3338 player->setHotbarSelectedImage(name);
3339 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3342 Address Server::getPeerAddress(session_t peer_id)
3344 // Note that this is only set after Init was received in Server::handleCommand_Init
3345 return getClient(peer_id, CS_Invalid)->getAddress();
3348 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3349 v2s32 animation_frames[4], f32 frame_speed)
3351 sanity_check(player);
3352 player->setLocalAnimations(animation_frames, frame_speed);
3353 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3356 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3358 sanity_check(player);
3359 player->eye_offset_first = first;
3360 player->eye_offset_third = third;
3361 SendEyeOffset(player->getPeerId(), first, third);
3364 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3366 sanity_check(player);
3367 player->setSky(params);
3368 SendSetSky(player->getPeerId(), params);
3371 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3373 sanity_check(player);
3374 player->setSun(params);
3375 SendSetSun(player->getPeerId(), params);
3378 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3380 sanity_check(player);
3381 player->setMoon(params);
3382 SendSetMoon(player->getPeerId(), params);
3385 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3387 sanity_check(player);
3388 player->setStars(params);
3389 SendSetStars(player->getPeerId(), params);
3392 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3394 sanity_check(player);
3395 player->setCloudParams(params);
3396 SendCloudParams(player->getPeerId(), params);
3399 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3402 sanity_check(player);
3403 player->overrideDayNightRatio(do_override, ratio);
3404 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3407 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3409 sanity_check(player);
3410 player->setLighting(lighting);
3411 SendSetLighting(player->getPeerId(), lighting);
3414 void Server::notifyPlayers(const std::wstring &msg)
3416 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3419 void Server::spawnParticle(const std::string &playername,
3420 const ParticleParameters &p)
3422 // m_env will be NULL if the server is initializing
3426 session_t peer_id = PEER_ID_INEXISTENT;
3428 if (!playername.empty()) {
3429 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3432 peer_id = player->getPeerId();
3433 proto_ver = player->protocol_version;
3436 SendSpawnParticle(peer_id, proto_ver, p);
3439 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3440 ServerActiveObject *attached, const std::string &playername)
3442 // m_env will be NULL if the server is initializing
3446 session_t peer_id = PEER_ID_INEXISTENT;
3448 if (!playername.empty()) {
3449 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3452 peer_id = player->getPeerId();
3453 proto_ver = player->protocol_version;
3456 u16 attached_id = attached ? attached->getId() : 0;
3459 if (attached_id == 0)
3460 id = m_env->addParticleSpawner(p.time);
3462 id = m_env->addParticleSpawner(p.time, attached_id);
3464 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3468 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3470 // m_env will be NULL if the server is initializing
3472 throw ServerError("Can't delete particle spawners during initialisation!");
3474 session_t peer_id = PEER_ID_INEXISTENT;
3475 if (!playername.empty()) {
3476 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3479 peer_id = player->getPeerId();
3482 m_env->deleteParticleSpawner(id);
3483 SendDeleteParticleSpawner(peer_id, id);
3486 bool Server::dynamicAddMedia(std::string filepath,
3487 const u32 token, const std::string &to_player, bool ephemeral)
3489 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3490 auto it = m_media.find(filename);
3491 if (it != m_media.end()) {
3492 // Allow the same path to be "added" again in certain conditions
3493 if (ephemeral || it->second.path != filepath) {
3494 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3495 << "\" already exists in media cache" << std::endl;
3500 // Load the file and add it to our media cache
3501 std::string filedata, raw_hash;
3502 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3507 // Create a copy of the file and swap out the path, this removes the
3508 // requirement that mods keep the file accessible at the original path.
3509 filepath = fs::CreateTempFile();
3510 bool ok = ([&] () -> bool {
3511 if (filepath.empty())
3513 std::ofstream os(filepath.c_str(), std::ios::binary);
3521 errorstream << "Server: failed to create a copy of media file "
3522 << "\"" << filename << "\"" << std::endl;
3523 m_media.erase(filename);
3526 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3527 << filepath << std::endl;
3529 m_media[filename].path = filepath;
3530 m_media[filename].no_announce = true;
3531 // stepPendingDynMediaCallbacks will clean this up later.
3532 } else if (!to_player.empty()) {
3533 m_media[filename].no_announce = true;
3536 // Push file to existing clients
3537 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3538 pkt << raw_hash << filename << (bool)ephemeral;
3540 NetworkPacket legacy_pkt = pkt;
3542 // Newer clients get asked to fetch the file (asynchronous)
3544 // Older clients have an awful hack that just throws the data at them
3545 legacy_pkt.putLongString(filedata);
3547 std::unordered_set<session_t> delivered, waiting;
3549 ClientInterface::AutoLock clientlock(m_clients);
3550 for (auto &pair : m_clients.getClientList()) {
3551 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3553 If a client is in the DefinitionsSent state it is too late to
3554 transfer the file via sendMediaAnnouncement() but at the same
3555 time the client cannot accept a media push yet.
3556 Short of artificially delaying the joining process there is no
3557 way for the server to resolve this so we (currently) opt not to.
3559 warningstream << "The media \"" << filename << "\" (dynamic) could "
3560 "not be delivered to " << pair.second->getName()
3561 << " due to a race condition." << std::endl;
3564 if (pair.second->getState() < CS_Active)
3567 const auto proto_ver = pair.second->net_proto_version;
3571 const session_t peer_id = pair.second->peer_id;
3572 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3575 if (proto_ver < 40) {
3576 delivered.emplace(peer_id);
3578 The network layer only guarantees ordered delivery inside a channel.
3579 Since the very next packet could be one that uses the media, we have
3580 to push the media over ALL channels to ensure it is processed before
3581 it is used. In practice this means channels 1 and 0.
3583 m_clients.send(peer_id, 1, &legacy_pkt, true);
3584 m_clients.send(peer_id, 0, &legacy_pkt, true);
3586 waiting.emplace(peer_id);
3587 Send(peer_id, &pkt);
3592 // Run callback for players that already had the file delivered (legacy-only)
3593 for (session_t peer_id : delivered) {
3594 if (auto player = m_env->getPlayer(peer_id))
3595 getScriptIface()->on_dynamic_media_added(token, player->getName());
3598 // Save all others in our pending state
3599 auto &state = m_pending_dyn_media[token];
3600 state.waiting_players = std::move(waiting);
3601 // regardless of success throw away the callback after a while
3602 state.expiry_timer = 60.0f;
3604 state.filename = filename;
3609 // actions: time-reversed list
3610 // Return value: success/failure
3611 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3612 std::list<std::string> *log)
3614 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3615 ServerMap *map = (ServerMap*)(&m_env->getMap());
3617 // Fail if no actions to handle
3618 if (actions.empty()) {
3620 log->push_back("Nothing to do.");
3627 for (const RollbackAction &action : actions) {
3629 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3632 std::ostringstream os;
3633 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3634 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3636 log->push_back(os.str());
3638 std::ostringstream os;
3639 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3640 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3642 log->push_back(os.str());
3646 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3647 <<" failed"<<std::endl;
3649 // Call it done if less than half failed
3650 return num_failed <= num_tried/2;
3653 // IGameDef interface
3655 IItemDefManager *Server::getItemDefManager()
3660 const NodeDefManager *Server::getNodeDefManager()
3665 ICraftDefManager *Server::getCraftDefManager()
3670 u16 Server::allocateUnknownNodeId(const std::string &name)
3672 return m_nodedef->allocateDummy(name);
3675 IWritableItemDefManager *Server::getWritableItemDefManager()
3680 NodeDefManager *Server::getWritableNodeDefManager()
3685 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3690 const std::vector<ModSpec> & Server::getMods() const
3692 return m_modmgr->getMods();
3695 const ModSpec *Server::getModSpec(const std::string &modname) const
3697 return m_modmgr->getModSpec(modname);
3700 std::string Server::getBuiltinLuaPath()
3702 return porting::path_share + DIR_DELIM + "builtin";
3705 v3f Server::findSpawnPos()
3707 ServerMap &map = m_env->getServerMap();
3709 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3710 return nodeposf * BS;
3712 bool is_good = false;
3713 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3714 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3716 // Try to find a good place a few times
3717 for (s32 i = 0; i < 4000 && !is_good; i++) {
3718 s32 range = MYMIN(1 + i, range_max);
3719 // We're going to try to throw the player to this position
3720 v2s16 nodepos2d = v2s16(
3721 -range + (myrand() % (range * 2)),
3722 -range + (myrand() % (range * 2)));
3723 // Get spawn level at point
3724 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3725 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3726 // signify an unsuitable spawn position, or if outside limits.
3727 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3728 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3731 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3732 // Consecutive empty nodes
3735 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3736 // avoid obstructions in already-generated mapblocks.
3737 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3738 // no obstructions, but mapgen decorations are generated after spawn so
3739 // the player may end up inside one.
3740 for (s32 i = 0; i < 8; i++) {
3741 v3s16 blockpos = getNodeBlockPos(nodepos);
3742 map.emergeBlock(blockpos, true);
3743 content_t c = map.getNode(nodepos).getContent();
3745 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3746 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3747 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3749 if (air_count >= 2) {
3750 // Spawn in lower empty node
3752 nodeposf = intToFloat(nodepos, BS);
3753 // Don't spawn the player outside map boundaries
3754 if (objectpos_over_limit(nodeposf))
3755 // Exit this loop, positions above are probably over limit
3758 // Good position found, cause an exit from main loop
3772 // No suitable spawn point found, return fallback 0,0,0
3773 return v3f(0.0f, 0.0f, 0.0f);
3776 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3778 if (delay == 0.0f) {
3779 // No delay, shutdown immediately
3780 m_shutdown_state.is_requested = true;
3781 // only print to the infostream, a chat message saying
3782 // "Server Shutting Down" is sent when the server destructs.
3783 infostream << "*** Immediate Server shutdown requested." << std::endl;
3784 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3785 // Negative delay, cancel shutdown if requested
3786 m_shutdown_state.reset();
3787 std::wstringstream ws;
3789 ws << L"*** Server shutdown canceled.";
3791 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3792 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3793 // m_shutdown_* are already handled, skip.
3795 } else if (delay > 0.0f) {
3796 // Positive delay, tell the clients when the server will shut down
3797 std::wstringstream ws;
3799 ws << L"*** Server shutting down in "
3800 << duration_to_string(myround(delay)).c_str()
3803 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3804 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3807 m_shutdown_state.trigger(delay, msg, reconnect);
3810 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3813 Try to get an existing player
3815 RemotePlayer *player = m_env->getPlayer(name);
3817 // If player is already connected, cancel
3818 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3819 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3824 If player with the wanted peer_id already exists, cancel.
3826 if (m_env->getPlayer(peer_id)) {
3827 infostream<<"emergePlayer(): Player with wrong name but same"
3828 " peer_id already exists"<<std::endl;
3833 player = new RemotePlayer(name, idef());
3836 bool newplayer = false;
3839 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3841 // Complete init with server parts
3842 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3843 player->protocol_version = proto_version;
3847 m_script->on_newplayer(playersao);
3853 bool Server::registerModStorage(ModMetadata *storage)
3855 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3856 errorstream << "Unable to register same mod storage twice. Storage name: "
3857 << storage->getModName() << std::endl;
3861 m_mod_storages[storage->getModName()] = storage;
3865 void Server::unregisterModStorage(const std::string &name)
3867 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3868 if (it != m_mod_storages.end())
3869 m_mod_storages.erase(name);
3872 void dedicated_server_loop(Server &server, bool &kill)
3874 verbosestream<<"dedicated_server_loop()"<<std::endl;
3876 IntervalLimiter m_profiler_interval;
3878 static thread_local const float steplen =
3879 g_settings->getFloat("dedicated_server_step");
3880 static thread_local const float profiler_print_interval =
3881 g_settings->getFloat("profiler_print_interval");
3884 * The dedicated server loop only does time-keeping (in Server::step) and
3885 * provides a way to main.cpp to kill the server externally (bool &kill).
3889 // This is kind of a hack but can be done like this
3890 // because server.step() is very light
3891 sleep_ms((int)(steplen*1000.0));
3892 server.step(steplen);
3894 if (server.isShutdownRequested() || kill)
3900 if (profiler_print_interval != 0) {
3901 if(m_profiler_interval.step(steplen, profiler_print_interval))
3903 infostream<<"Profiler:"<<std::endl;
3904 g_profiler->print(infostream);
3905 g_profiler->clear();
3910 infostream << "Dedicated server quitting" << std::endl;
3912 if (g_settings->getBool("server_announce"))
3913 ServerList::sendAnnounce(ServerList::AA_DELETE,
3914 server.m_bind_addr.getPort());
3923 bool Server::joinModChannel(const std::string &channel)
3925 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3926 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3929 bool Server::leaveModChannel(const std::string &channel)
3931 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3934 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3936 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3939 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3943 ModChannel* Server::getModChannel(const std::string &channel)
3945 return m_modchannel_mgr->getModChannel(channel);
3948 void Server::broadcastModChannelMessage(const std::string &channel,
3949 const std::string &message, session_t from_peer)
3951 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3955 if (message.size() > STRING_MAX_LEN) {
3956 warningstream << "ModChannel message too long, dropping before sending "
3957 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3958 << channel << ")" << std::endl;
3963 if (from_peer != PEER_ID_SERVER) {
3964 sender = getPlayerName(from_peer);
3967 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3968 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3969 resp_pkt << channel << sender << message;
3970 for (session_t peer_id : peers) {
3972 if (peer_id == from_peer)
3975 Send(peer_id, &resp_pkt);
3978 if (from_peer != PEER_ID_SERVER) {
3979 m_script->on_modchannel_message(channel, sender, message);
3983 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3985 if (lang_code.empty())
3988 auto it = server_translations.find(lang_code);
3989 if (it != server_translations.end())
3990 return &it->second; // Already loaded
3992 // [] will create an entry
3993 auto *translations = &server_translations[lang_code];
3995 std::string suffix = "." + lang_code + ".tr";
3996 for (const auto &i : m_media) {
3997 if (str_ends_with(i.first, suffix)) {
3999 if (fs::ReadFile(i.second.path, data)) {
4000 translations->loadTranslation(data);
4005 return translations;
4008 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
4010 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4012 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4013 throw BaseException("Cannot read world.mt!");
4015 std::string backend = world_mt.exists("mod_storage_backend") ?
4016 world_mt.get("mod_storage_backend") : "files";
4017 if (backend == "files")
4018 warningstream << "/!\\ You are using the old mod storage files backend. "
4019 << "This backend is deprecated and may be removed in a future release /!\\"
4020 << std::endl << "Switching to SQLite3 is advised, "
4021 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4023 return openModStorageDatabase(backend, world_path, world_mt);
4026 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
4027 const std::string &world_path, const Settings &world_mt)
4029 if (backend == "sqlite3")
4030 return new ModMetadataDatabaseSQLite3(world_path);
4032 if (backend == "files")
4033 return new ModMetadataDatabaseFiles(world_path);
4035 if (backend == "dummy")
4036 return new Database_Dummy();
4038 throw BaseException("Mod storage database backend " + backend + " not supported");
4041 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4043 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4045 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4046 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4047 errorstream << "Cannot read world.mt!" << std::endl;
4051 std::string backend = world_mt.exists("mod_storage_backend") ?
4052 world_mt.get("mod_storage_backend") : "files";
4053 if (backend == migrate_to) {
4054 errorstream << "Cannot migrate: new backend is same"
4055 << " as the old one" << std::endl;
4059 ModMetadataDatabase *srcdb = nullptr;
4060 ModMetadataDatabase *dstdb = nullptr;
4062 bool succeeded = false;
4065 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4066 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4070 std::vector<std::string> mod_list;
4071 srcdb->listMods(&mod_list);
4072 for (const std::string &modname : mod_list) {
4074 srcdb->getModEntries(modname, &meta);
4075 for (const auto &pair : meta) {
4076 dstdb->setModEntry(modname, pair.first, pair.second);
4084 actionstream << "Successfully migrated the metadata of "
4085 << mod_list.size() << " mods" << std::endl;
4086 world_mt.set("mod_storage_backend", migrate_to);
4087 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4088 errorstream << "Failed to update world.mt!" << std::endl;
4090 actionstream << "world.mt updated" << std::endl;
4092 } catch (BaseException &e) {
4093 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4099 if (succeeded && backend == "files") {
4101 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4102 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4103 if (!fs::Rename(storage_path, backup_path))
4104 warningstream << "After migration, " << storage_path
4105 << " could not be renamed to " << backup_path << std::endl;