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 ServerPlayingSound::getPos(ServerEnvironment *env, bool *pos_exists) const
145 case SoundLocation::Local:
147 case SoundLocation::Position:
151 case SoundLocation::Object:
155 ServerActiveObject *sao = env->getActiveObject(object);
160 return sao->getBasePosition();
167 void Server::ShutdownState::reset()
171 should_reconnect = false;
172 is_requested = false;
175 void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
179 should_reconnect = reconnect;
182 void Server::ShutdownState::tick(float dtime, Server *server)
188 static const float shutdown_msg_times[] =
190 1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
193 // Automated messages
194 if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
195 for (float t : shutdown_msg_times) {
196 // If shutdown timer matches an automessage, shot it
197 if (m_timer > t && m_timer - dtime < t) {
198 std::wstring periodicMsg = getShutdownTimerMessage();
200 infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
201 server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
208 if (m_timer < 0.0f) {
214 std::wstring Server::ShutdownState::getShutdownTimerMessage() const
216 std::wstringstream ws;
217 ws << L"*** Server shutting down in "
218 << duration_to_string(myround(m_timer)).c_str() << ".";
227 const std::string &path_world,
228 const SubgameSpec &gamespec,
229 bool simple_singleplayer_mode,
232 ChatInterface *iface,
233 std::string *on_shutdown_errmsg
235 m_bind_addr(bind_addr),
236 m_path_world(path_world),
237 m_gamespec(gamespec),
238 m_simple_singleplayer_mode(simple_singleplayer_mode),
239 m_dedicated(dedicated),
240 m_async_fatal_error(""),
241 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
244 m_bind_addr.isIPv6(),
246 m_itemdef(createItemDefManager()),
247 m_nodedef(createNodeDefManager()),
248 m_craftdef(createCraftDefManager()),
249 m_thread(new ServerThread(this)),
252 m_on_shutdown_errmsg(on_shutdown_errmsg),
253 m_modchannel_mgr(new ModChannelMgr())
255 if (m_path_world.empty())
256 throw ServerError("Supplied empty world path");
258 if (!gamespec.isValid())
259 throw ServerError("Supplied invalid gamespec");
262 m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
264 m_metrics_backend = std::make_unique<MetricsBackend>();
267 m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
268 m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
270 m_timeofday_gauge = m_metrics_backend->addGauge(
271 "minetest_core_timeofday",
272 "Time of day value");
274 m_lag_gauge = m_metrics_backend->addGauge(
275 "minetest_core_latency",
276 "Latency value (in seconds)");
279 const std::string aom_types[] = {"reliable", "unreliable"};
280 for (u32 i = 0; i < ARRLEN(aom_types); i++) {
281 std::string help_str("Number of active object messages generated (");
282 help_str.append(aom_types[i]).append(")");
283 m_aom_buffer_counter[i] = m_metrics_backend->addCounter(
284 "minetest_core_aom_generated_count", help_str,
285 {{"type", aom_types[i]}});
288 m_packet_recv_counter = m_metrics_backend->addCounter(
289 "minetest_core_server_packet_recv",
290 "Processable packets received");
292 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
293 "minetest_core_server_packet_recv_processed",
294 "Valid received packets processed");
296 m_map_edit_event_counter = m_metrics_backend->addCounter(
297 "minetest_core_map_edit_events",
298 "Number of map edit events");
300 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
306 // Send shutdown message
307 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
308 L"*** Server shutting down"));
311 MutexAutoLock envlock(m_env_mutex);
313 infostream << "Server: Saving players" << std::endl;
314 m_env->saveLoadedPlayers();
316 infostream << "Server: Kicking players" << std::endl;
317 std::string kick_msg;
318 bool reconnect = false;
319 if (isShutdownRequested()) {
320 reconnect = m_shutdown_state.should_reconnect;
321 kick_msg = m_shutdown_state.message;
323 if (kick_msg.empty()) {
324 kick_msg = g_settings->get("kick_msg_shutdown");
326 m_env->saveLoadedPlayers(true);
327 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
328 kick_msg, reconnect);
331 actionstream << "Server: Shutting down" << std::endl;
333 // Do this before stopping the server in case mapgen callbacks need to access
334 // server-controlled resources (like ModStorages). Also do them before
335 // shutdown callbacks since they may modify state that is finalized in a
338 m_emerge->stopThreads();
341 MutexAutoLock envlock(m_env_mutex);
343 // Execute script shutdown hooks
344 infostream << "Executing shutdown hooks" << std::endl;
346 m_script->on_shutdown();
347 } catch (ModError &e) {
348 errorstream << "ModError: " << e.what() << std::endl;
349 if (m_on_shutdown_errmsg) {
350 if (m_on_shutdown_errmsg->empty()) {
351 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
353 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
358 infostream << "Server: Saving environment metadata" << std::endl;
368 // Write any changes before deletion.
369 if (m_mod_storage_database)
370 m_mod_storage_database->endSave();
372 // Delete things in the reverse order of creation
376 delete m_mod_storage_database;
382 // Deinitialize scripting
383 infostream << "Server: Deinitializing scripting" << std::endl;
385 delete m_startup_server_map; // if available
386 delete m_game_settings;
388 while (!m_unsent_map_edit_queue.empty()) {
389 delete m_unsent_map_edit_queue.front();
390 m_unsent_map_edit_queue.pop();
396 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
397 if (m_simple_singleplayer_mode)
398 infostream << " in simple singleplayer mode" << std::endl;
400 infostream << std::endl;
401 infostream << "- world: " << m_path_world << std::endl;
402 infostream << "- game: " << m_gamespec.path << std::endl;
404 m_game_settings = Settings::createLayer(SL_GAME);
406 // Create world if it doesn't exist
408 loadGameConfAndInitWorld(m_path_world,
409 fs::GetFilenameFromPath(m_path_world.c_str()),
411 } catch (const BaseException &e) {
412 throw ServerError(std::string("Failed to initialize world: ") + e.what());
415 // Create emerge manager
416 m_emerge = new EmergeManager(this, m_metrics_backend.get());
418 // Create ban manager
419 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
420 m_banmanager = new BanManager(ban_path);
422 // Create mod storage database and begin a save for later
423 m_mod_storage_database = openModStorageDatabase(m_path_world);
424 m_mod_storage_database->beginSave();
426 m_modmgr = std::make_unique<ServerModManager>(m_path_world);
428 // complain about mods with unsatisfied dependencies
429 if (!m_modmgr->isConsistent()) {
430 std::string error = m_modmgr->getUnsatisfiedModsError();
431 throw ServerError(error);
435 MutexAutoLock envlock(m_env_mutex);
437 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
438 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
439 m_startup_server_map = servermap;
441 // Initialize scripting
442 infostream << "Server: Initializing Lua" << std::endl;
444 m_script = new ServerScripting(this);
446 // Must be created before mod loading because we have some inventory creation
447 m_inventory_mgr = std::make_unique<ServerInventoryManager>();
449 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
451 m_gamespec.checkAndLog();
452 m_modmgr->loadMods(m_script);
454 // Read Textures and calculate sha1 sums
457 // Apply item aliases in the node definition manager
458 m_nodedef->updateAliases(m_itemdef);
460 // Apply texture overrides from texturepack/override.txt
461 std::vector<std::string> paths;
462 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
463 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
464 for (const std::string &path : paths) {
465 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
466 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
467 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
470 m_nodedef->setNodeRegistrationStatus(true);
472 // Perform pending node name resolutions
473 m_nodedef->runNodeResolveCallbacks();
475 // unmap node names in cross-references
476 m_nodedef->resolveCrossrefs();
478 // init the recipe hashes to speed up crafting
479 m_craftdef->initHashes(this);
481 // Initialize Environment
482 m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
483 m_env = new ServerEnvironment(servermap, m_script, this,
484 m_path_world, m_metrics_backend.get());
487 m_inventory_mgr->setEnv(m_env);
488 m_clients.setEnv(m_env);
490 if (!servermap->settings_mgr.makeMapgenParams())
491 FATAL_ERROR("Couldn't create any mapgen type");
493 // Initialize mapgens
494 m_emerge->initMapgens(servermap->getMapgenParams());
496 if (g_settings->getBool("enable_rollback_recording")) {
497 // Create rollback manager
498 m_rollback = new RollbackManager(m_path_world, this);
501 // Give environment reference to scripting api
502 m_script->initializeEnvironment(m_env);
504 // Do this after regular script init is done
505 m_script->initAsync();
507 // Register us to receive map edit events
508 servermap->addEventReceiver(this);
512 // Those settings can be overwritten in world.mt, they are
513 // intended to be cached after environment loading.
514 m_liquid_transform_every = g_settings->getFloat("liquid_update");
515 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
516 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
517 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
524 infostream << "Starting server on " << m_bind_addr.serializeString()
525 << "..." << std::endl;
527 // Stop thread if already running
530 // Initialize connection
531 m_con->SetTimeoutMs(30);
532 m_con->Serve(m_bind_addr);
537 // ASCII art for the win!
539 << " __. __. __. " << std::endl
540 << " _____ |__| ____ _____ / |_ _____ _____ / |_ " << std::endl
541 << " / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\" << std::endl
542 << "| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | " << std::endl
543 << "|__|_| / |___| /\\______> | \\______>_____/| | " << std::endl
544 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
545 actionstream << "World at [" << m_path_world << "]" << std::endl;
546 actionstream << "Server for gameid=\"" << m_gamespec.id
547 << "\" listening on ";
548 m_bind_addr.print(actionstream);
549 actionstream << "." << std::endl;
554 infostream<<"Server: Stopping and waiting threads"<<std::endl;
556 // Stop threads (set run=false first so both start stopping)
560 infostream<<"Server: Threads stopped"<<std::endl;
563 void Server::step(float dtime)
569 MutexAutoLock lock(m_step_dtime_mutex);
570 m_step_dtime += dtime;
572 // Throw if fatal error occurred in thread
573 std::string async_err = m_async_fatal_error.get();
574 if (!async_err.empty()) {
575 if (!m_simple_singleplayer_mode) {
576 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
577 g_settings->get("kick_msg_crash"),
578 g_settings->getBool("ask_reconnect_on_crash"));
580 throw ServerError("AsyncErr: " + async_err);
584 void Server::AsyncRunStep(bool initial_step)
589 MutexAutoLock lock1(m_step_dtime_mutex);
590 dtime = m_step_dtime;
594 // Send blocks to clients
598 if((dtime < 0.001) && !initial_step)
601 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
604 MutexAutoLock lock1(m_step_dtime_mutex);
605 m_step_dtime -= dtime;
611 m_uptime_counter->increment(dtime);
616 Update time of day and overall game time
618 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
621 Send to clients at constant intervals
624 m_time_of_day_send_timer -= dtime;
625 if (m_time_of_day_send_timer < 0.0) {
626 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
627 u16 time = m_env->getTimeOfDay();
628 float time_speed = g_settings->getFloat("time_speed");
629 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
631 m_timeofday_gauge->set(time);
635 MutexAutoLock lock(m_env_mutex);
636 // Figure out and report maximum lag to environment
637 float max_lag = m_env->getMaxLagEstimate();
638 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
640 if(dtime > 0.1 && dtime > max_lag * 2.0)
641 infostream<<"Server: Maximum lag peaked to "<<dtime
645 m_env->reportMaxLagEstimate(max_lag);
651 static const float map_timer_and_unload_dtime = 2.92;
652 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
654 MutexAutoLock lock(m_env_mutex);
655 // Run Map's timers and unload unused data
656 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
657 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
658 std::max(g_settings->getFloat("server_unload_unused_data_timeout"), 0.0f),
663 Listen to the admin chat, if available
666 if (!m_admin_chat->command_queue.empty()) {
667 MutexAutoLock lock(m_env_mutex);
668 while (!m_admin_chat->command_queue.empty()) {
669 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
670 handleChatInterfaceEvent(evt);
674 m_admin_chat->outgoing_queue.push_back(
675 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
682 /* Transform liquids */
683 m_liquid_transform_timer += dtime;
684 if(m_liquid_transform_timer >= m_liquid_transform_every)
686 m_liquid_transform_timer -= m_liquid_transform_every;
688 MutexAutoLock lock(m_env_mutex);
690 ScopeProfiler sp(g_profiler, "Server: liquid transform");
692 std::map<v3s16, MapBlock*> modified_blocks;
693 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
696 Set the modified blocks unsent for all the clients
698 if (!modified_blocks.empty()) {
699 SetBlocksNotSent(modified_blocks);
702 m_clients.step(dtime);
704 // increase/decrease lag gauge gradually
705 if (m_lag_gauge->get() > dtime) {
706 m_lag_gauge->decrement(dtime/100);
708 m_lag_gauge->increment(dtime/100);
712 float &counter = m_step_pending_dyn_media_timer;
714 if (counter >= 5.0f) {
715 stepPendingDynMediaCallbacks(counter);
722 // send masterserver announce
724 float &counter = m_masterserver_timer;
725 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
726 g_settings->getBool("server_announce")) {
727 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
728 ServerList::AA_START,
729 m_bind_addr.getPort(),
730 m_clients.getPlayerNames(),
731 m_uptime_counter->get(),
732 m_env->getGameTime(),
735 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
745 Check added and deleted active objects
748 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
749 MutexAutoLock envlock(m_env_mutex);
752 ClientInterface::AutoLock clientlock(m_clients);
753 const RemoteClientMap &clients = m_clients.getClientList();
754 ScopeProfiler sp(g_profiler, "Server: update objects within range");
756 m_player_gauge->set(clients.size());
757 for (const auto &client_it : clients) {
758 RemoteClient *client = client_it.second;
760 if (client->getState() < CS_DefinitionsSent)
763 // This can happen if the client times out somehow
764 if (!m_env->getPlayer(client->peer_id))
767 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
771 SendActiveObjectRemoveAdd(client, playersao);
775 // Write changes to the mod storage
776 m_mod_storage_save_timer -= dtime;
777 if (m_mod_storage_save_timer <= 0.0f) {
778 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
779 m_mod_storage_database->endSave();
780 m_mod_storage_database->beginSave();
788 MutexAutoLock envlock(m_env_mutex);
789 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
792 // Value = data sent by object
793 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
795 // Get active object messages from environment
796 ActiveObjectMessage aom(0);
797 u32 count_reliable = 0, count_unreliable = 0;
799 if (!m_env->getActiveObjectMessage(&aom))
806 std::vector<ActiveObjectMessage>* message_list = nullptr;
807 auto n = buffered_messages.find(aom.id);
808 if (n == buffered_messages.end()) {
809 message_list = new std::vector<ActiveObjectMessage>;
810 buffered_messages[aom.id] = message_list;
812 message_list = n->second;
814 message_list->push_back(std::move(aom));
817 m_aom_buffer_counter[0]->increment(count_reliable);
818 m_aom_buffer_counter[1]->increment(count_unreliable);
821 ClientInterface::AutoLock clientlock(m_clients);
822 const RemoteClientMap &clients = m_clients.getClientList();
823 // Route data to every client
824 std::string reliable_data, unreliable_data;
825 for (const auto &client_it : clients) {
826 reliable_data.clear();
827 unreliable_data.clear();
828 RemoteClient *client = client_it.second;
829 PlayerSAO *player = getPlayerSAO(client->peer_id);
830 // Go through all objects in message buffer
831 for (const auto &buffered_message : buffered_messages) {
832 // If object does not exist or is not known by client, skip it
833 u16 id = buffered_message.first;
834 ServerActiveObject *sao = m_env->getActiveObject(id);
835 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
838 // Get message list of object
839 std::vector<ActiveObjectMessage>* list = buffered_message.second;
840 // Go through every message
841 for (const ActiveObjectMessage &aom : *list) {
842 // Send position updates to players who do not see the attachment
843 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
844 if (sao->getId() == player->getId())
847 // Do not send position updates for attached players
848 // as long the parent is known to the client
849 ServerActiveObject *parent = sao->getParent();
850 if (parent && client->m_known_objects.find(parent->getId()) !=
851 client->m_known_objects.end())
855 // Add full new data to appropriate buffer
856 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
858 writeU16((u8*) idbuf, aom.id);
861 buffer.append(idbuf, sizeof(idbuf));
862 buffer.append(serializeString16(aom.datastring));
866 reliable_data and unreliable_data are now ready.
869 if (!reliable_data.empty()) {
870 SendActiveObjectMessages(client->peer_id, reliable_data);
873 if (!unreliable_data.empty()) {
874 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
879 // Clear buffered_messages
880 for (auto &buffered_message : buffered_messages) {
881 delete buffered_message.second;
886 Send queued-for-sending map edit events.
889 // We will be accessing the environment
890 MutexAutoLock lock(m_env_mutex);
892 // Single change sending is disabled if queue size is big
893 bool disable_single_change_sending = false;
894 if(m_unsent_map_edit_queue.size() >= 4)
895 disable_single_change_sending = true;
897 const auto event_count = m_unsent_map_edit_queue.size();
898 m_map_edit_event_counter->increment(event_count);
900 // We'll log the amount of each
903 std::unordered_set<v3s16> node_meta_updates;
905 while (!m_unsent_map_edit_queue.empty()) {
906 MapEditEvent* event = m_unsent_map_edit_queue.front();
907 m_unsent_map_edit_queue.pop();
909 // Players far away from the change are stored here.
910 // Instead of sending the changes, MapBlocks are set not sent
912 std::unordered_set<u16> far_players;
914 switch (event->type) {
917 prof.add("MEET_ADDNODE", 1);
918 sendAddNode(event->p, event->n, &far_players,
919 disable_single_change_sending ? 5 : 30,
920 event->type == MEET_ADDNODE);
922 case MEET_REMOVENODE:
923 prof.add("MEET_REMOVENODE", 1);
924 sendRemoveNode(event->p, &far_players,
925 disable_single_change_sending ? 5 : 30);
927 case MEET_BLOCK_NODE_METADATA_CHANGED: {
928 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
929 if (!event->is_private_change) {
930 node_meta_updates.emplace(event->p);
933 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
934 getNodeBlockPos(event->p))) {
935 block->raiseModified(MOD_STATE_WRITE_NEEDED,
936 MOD_REASON_REPORT_META_CHANGE);
941 prof.add("MEET_OTHER", 1);
942 for (const v3s16 &modified_block : event->modified_blocks) {
943 m_clients.markBlockposAsNotSent(modified_block);
947 prof.add("unknown", 1);
948 warningstream << "Server: Unknown MapEditEvent "
949 << ((u32)event->type) << std::endl;
954 Set blocks not sent to far players
956 if (!far_players.empty()) {
957 // Convert list format to that wanted by SetBlocksNotSent
958 std::map<v3s16, MapBlock*> modified_blocks2;
959 for (const v3s16 &modified_block : event->modified_blocks) {
960 modified_blocks2[modified_block] =
961 m_env->getMap().getBlockNoCreateNoEx(modified_block);
964 // Set blocks not sent
965 for (const u16 far_player : far_players) {
966 if (RemoteClient *client = getClient(far_player))
967 client->SetBlocksNotSent(modified_blocks2);
974 if (event_count >= 5) {
975 infostream << "Server: MapEditEvents:" << std::endl;
976 prof.print(infostream);
977 } else if (event_count != 0) {
978 verbosestream << "Server: MapEditEvents:" << std::endl;
979 prof.print(verbosestream);
982 // Send all metadata updates
983 if (!node_meta_updates.empty())
984 sendMetadataChanged(node_meta_updates);
988 Trigger emerge thread
989 Doing this every 2s is left over from old code, unclear if this is still needed.
992 float &counter = m_emergethread_trigger_timer;
994 if (counter <= 0.0f) {
997 m_emerge->startThreads();
1001 // Save map, players and auth stuff
1003 float &counter = m_savemap_timer;
1005 static thread_local const float save_interval =
1006 g_settings->getFloat("server_map_save_interval");
1007 if (counter >= save_interval) {
1009 MutexAutoLock lock(m_env_mutex);
1011 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
1014 if (m_banmanager->isModified()) {
1015 m_banmanager->save();
1018 // Save changed parts of map
1019 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1022 m_env->saveLoadedPlayers();
1024 // Save environment metadata
1029 m_shutdown_state.tick(dtime, this);
1032 void Server::Receive()
1042 In the first iteration *wait* for a packet, afterwards process
1043 all packets that are immediately available (no waiting).
1046 m_con->Receive(&pkt);
1049 if (!m_con->TryReceive(&pkt))
1053 peer_id = pkt.getPeerId();
1054 m_packet_recv_counter->increment();
1056 m_packet_recv_processed_counter->increment();
1057 } catch (const con::InvalidIncomingDataException &e) {
1058 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1059 << e.what() << std::endl;
1060 } catch (const SerializationError &e) {
1061 infostream << "Server::Receive(): SerializationError: what()="
1062 << e.what() << std::endl;
1063 } catch (const ClientStateError &e) {
1064 errorstream << "ProcessData: peer=" << peer_id << " what()="
1065 << e.what() << std::endl;
1066 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1067 } catch (const con::PeerNotFoundException &e) {
1069 } catch (const con::NoIncomingDataException &e) {
1075 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1077 std::string playername;
1078 PlayerSAO *playersao = NULL;
1080 ClientInterface::AutoLock clientlock(m_clients);
1081 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1083 playername = client->getName();
1084 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1088 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1090 // If failed, cancel
1091 if (!playersao || !player) {
1092 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1093 actionstream << "Server: Failed to emerge player \"" << playername
1094 << "\" (player allocated to an another client)" << std::endl;
1095 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1097 errorstream << "Server: " << playername << ": Failed to emerge player"
1099 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1105 Send complete position information
1107 SendMovePlayer(peer_id);
1110 SendPlayerPrivileges(peer_id);
1112 // Send inventory formspec
1113 SendPlayerInventoryFormspec(peer_id);
1116 SendInventory(playersao, false);
1119 SendPlayerHP(playersao, false);
1121 // Send death screen
1122 if (playersao->isDead())
1123 SendDeathscreen(peer_id, false, v3f(0,0,0));
1126 SendPlayerBreath(playersao);
1129 Update player list and print action
1132 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1133 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1134 m_clients.sendToAll(¬ice_pkt);
1137 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1138 const auto &names = m_clients.getPlayerNames();
1140 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1141 for (const std::string &name : names)
1142 actionstream << name << " ";
1143 actionstream << player->getName() << std::endl;
1148 inline void Server::handleCommand(NetworkPacket *pkt)
1150 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1151 (this->*opHandle.handler)(pkt);
1154 void Server::ProcessData(NetworkPacket *pkt)
1156 // Environment is locked first.
1157 MutexAutoLock envlock(m_env_mutex);
1159 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1160 u32 peer_id = pkt->getPeerId();
1163 Address address = getPeerAddress(peer_id);
1164 std::string addr_s = address.serializeString();
1166 // FIXME: Isn't it a bit excessive to check this for every packet?
1167 if (m_banmanager->isIpBanned(addr_s)) {
1168 std::string ban_name = m_banmanager->getBanName(addr_s);
1169 infostream << "Server: A banned client tried to connect from "
1170 << addr_s << "; banned name was " << ban_name << std::endl;
1171 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1172 "Your IP is banned. Banned name was " + ban_name);
1175 } catch (con::PeerNotFoundException &e) {
1177 * no peer for this packet found
1178 * most common reason is peer timeout, e.g. peer didn't
1179 * respond for some time, your server was overloaded or
1182 infostream << "Server::ProcessData(): Canceling: peer "
1183 << peer_id << " not found" << std::endl;
1188 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1190 // Command must be handled into ToServerCommandHandler
1191 if (command >= TOSERVER_NUM_MSG_TYPES) {
1192 infostream << "Server: Ignoring unknown command "
1193 << command << std::endl;
1197 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1202 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1204 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1205 errorstream << "Server::ProcessData(): Cancelling: Peer"
1206 " serialization format invalid or not initialized."
1207 " Skipping incoming command=" << command << std::endl;
1211 /* Handle commands related to client startup */
1212 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1217 if (m_clients.getClientState(peer_id) < CS_Active) {
1218 if (command == TOSERVER_PLAYERPOS) return;
1220 errorstream << "Got packet command: " << command << " for peer id "
1221 << peer_id << " but client isn't active yet. Dropping packet "
1227 } catch (SendFailedException &e) {
1228 errorstream << "Server::ProcessData(): SendFailedException: "
1229 << "what=" << e.what()
1231 } catch (PacketError &e) {
1232 actionstream << "Server::ProcessData(): PacketError: "
1233 << "what=" << e.what()
1238 void Server::setTimeOfDay(u32 time)
1240 m_env->setTimeOfDay(time);
1241 m_time_of_day_send_timer = 0;
1244 void Server::onMapEditEvent(const MapEditEvent &event)
1246 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1249 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1252 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1254 std::vector<session_t> clients = m_clients.getClientIDs();
1255 ClientInterface::AutoLock clientlock(m_clients);
1256 // Set the modified blocks unsent for all the clients
1257 for (const session_t client_id : clients) {
1258 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1259 client->SetBlocksNotSent(block);
1263 void Server::peerAdded(con::Peer *peer)
1265 verbosestream<<"Server::peerAdded(): peer->id="
1266 <<peer->id<<std::endl;
1268 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1271 void Server::deletingPeer(con::Peer *peer, bool timeout)
1273 verbosestream<<"Server::deletingPeer(): peer->id="
1274 <<peer->id<<", timeout="<<timeout<<std::endl;
1276 m_clients.event(peer->id, CSE_Disconnect);
1277 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1280 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1282 *retval = m_con->getPeerStat(peer_id,type);
1283 return *retval != -1;
1286 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1288 ClientInterface::AutoLock clientlock(m_clients);
1289 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1294 ret.state = client->getState();
1295 ret.addr = client->getAddress();
1296 ret.uptime = client->uptime();
1297 ret.ser_vers = client->serialization_version;
1298 ret.prot_vers = client->net_proto_version;
1300 ret.major = client->getMajor();
1301 ret.minor = client->getMinor();
1302 ret.patch = client->getPatch();
1303 ret.vers_string = client->getFullVer();
1305 ret.lang_code = client->getLangCode();
1310 void Server::handlePeerChanges()
1312 while(!m_peer_change_queue.empty())
1314 con::PeerChange c = m_peer_change_queue.front();
1315 m_peer_change_queue.pop();
1317 verbosestream<<"Server: Handling peer change: "
1318 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1323 case con::PEER_ADDED:
1324 m_clients.CreateClient(c.peer_id);
1327 case con::PEER_REMOVED:
1328 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1332 FATAL_ERROR("Invalid peer change event received!");
1338 void Server::printToConsoleOnly(const std::string &text)
1341 m_admin_chat->outgoing_queue.push_back(
1342 new ChatEventChat("", utf8_to_wide(text)));
1344 std::cout << text << std::endl;
1348 void Server::Send(NetworkPacket *pkt)
1350 Send(pkt->getPeerId(), pkt);
1353 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1355 m_clients.send(peer_id,
1356 clientCommandFactoryTable[pkt->getCommand()].channel,
1358 clientCommandFactoryTable[pkt->getCommand()].reliable);
1361 void Server::SendMovement(session_t peer_id)
1363 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1365 pkt << g_settings->getFloat("movement_acceleration_default");
1366 pkt << g_settings->getFloat("movement_acceleration_air");
1367 pkt << g_settings->getFloat("movement_acceleration_fast");
1368 pkt << g_settings->getFloat("movement_speed_walk");
1369 pkt << g_settings->getFloat("movement_speed_crouch");
1370 pkt << g_settings->getFloat("movement_speed_fast");
1371 pkt << g_settings->getFloat("movement_speed_climb");
1372 pkt << g_settings->getFloat("movement_speed_jump");
1373 pkt << g_settings->getFloat("movement_liquid_fluidity");
1374 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1375 pkt << g_settings->getFloat("movement_liquid_sink");
1376 pkt << g_settings->getFloat("movement_gravity");
1381 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1383 m_script->player_event(playersao, "health_changed");
1384 SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX);
1386 // Send to other clients
1387 playersao->sendPunchCommand();
1389 if (playersao->isDead())
1390 HandlePlayerDeath(playersao, reason);
1393 void Server::SendPlayerHP(PlayerSAO *playersao, bool effect)
1395 SendHP(playersao->getPeerID(), playersao->getHP(), effect);
1398 void Server::SendHP(session_t peer_id, u16 hp, bool effect)
1400 NetworkPacket pkt(TOCLIENT_HP, 3, peer_id);
1401 pkt << hp << effect;
1405 void Server::SendBreath(session_t peer_id, u16 breath)
1407 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1408 pkt << (u16) breath;
1412 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1413 const std::string &custom_reason, bool reconnect)
1415 assert(reason < SERVER_ACCESSDENIED_MAX);
1417 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1419 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1420 pkt << custom_reason;
1421 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1422 reason == SERVER_ACCESSDENIED_CRASH)
1423 pkt << custom_reason << (u8)reconnect;
1427 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1428 v3f camera_point_target)
1430 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1431 pkt << set_camera_point_target << camera_point_target;
1435 void Server::SendItemDef(session_t peer_id,
1436 IItemDefManager *itemdef, u16 protocol_version)
1438 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1442 u32 length of the next item
1443 zlib-compressed serialized ItemDefManager
1445 std::ostringstream tmp_os(std::ios::binary);
1446 itemdef->serialize(tmp_os, protocol_version);
1447 std::ostringstream tmp_os2(std::ios::binary);
1448 compressZlib(tmp_os.str(), tmp_os2);
1449 pkt.putLongString(tmp_os2.str());
1452 verbosestream << "Server: Sending item definitions to id(" << peer_id
1453 << "): size=" << pkt.getSize() << std::endl;
1458 void Server::SendNodeDef(session_t peer_id,
1459 const NodeDefManager *nodedef, u16 protocol_version)
1461 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1465 u32 length of the next item
1466 zlib-compressed serialized NodeDefManager
1468 std::ostringstream tmp_os(std::ios::binary);
1469 nodedef->serialize(tmp_os, protocol_version);
1470 std::ostringstream tmp_os2(std::ios::binary);
1471 compressZlib(tmp_os.str(), tmp_os2);
1473 pkt.putLongString(tmp_os2.str());
1476 verbosestream << "Server: Sending node definitions to id(" << peer_id
1477 << "): size=" << pkt.getSize() << std::endl;
1483 Non-static send methods
1486 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1488 RemotePlayer *player = sao->getPlayer();
1490 // Do not send new format to old clients
1491 incremental &= player->protocol_version >= 38;
1493 UpdateCrafting(player);
1499 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1501 std::ostringstream os(std::ios::binary);
1502 sao->getInventory()->serialize(os, incremental);
1503 sao->getInventory()->setModified(false);
1504 player->setModified(true);
1506 const std::string &s = os.str();
1507 pkt.putRawString(s.c_str(), s.size());
1511 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1513 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1515 u8 type = message.type;
1516 pkt << version << type << message.sender << message.message
1517 << static_cast<u64>(message.timestamp);
1519 if (peer_id != PEER_ID_INEXISTENT) {
1520 RemotePlayer *player = m_env->getPlayer(peer_id);
1526 m_clients.sendToAll(&pkt);
1530 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1531 const std::string &formname)
1533 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1534 if (formspec.empty()){
1535 //the client should close the formspec
1536 //but make sure there wasn't another one open in meantime
1537 const auto it = m_formspec_state_data.find(peer_id);
1538 if (it != m_formspec_state_data.end() && it->second == formname) {
1539 m_formspec_state_data.erase(peer_id);
1541 pkt.putLongString("");
1543 m_formspec_state_data[peer_id] = formname;
1544 pkt.putLongString(formspec);
1551 // Spawns a particle on peer with peer_id
1552 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1553 const ParticleParameters &p)
1555 static thread_local const float radius =
1556 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1558 if (peer_id == PEER_ID_INEXISTENT) {
1559 std::vector<session_t> clients = m_clients.getClientIDs();
1560 const v3f pos = p.pos * BS;
1561 const float radius_sq = radius * radius;
1563 for (const session_t client_id : clients) {
1564 RemotePlayer *player = m_env->getPlayer(client_id);
1568 PlayerSAO *sao = player->getPlayerSAO();
1572 // Do not send to distant clients
1573 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1576 SendSpawnParticle(client_id, player->protocol_version, p);
1580 assert(protocol_version != 0);
1582 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1585 // NetworkPacket and iostreams are incompatible...
1586 std::ostringstream oss(std::ios_base::binary);
1587 p.serialize(oss, protocol_version);
1588 pkt.putRawString(oss.str());
1594 // Adds a ParticleSpawner on peer with peer_id
1595 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1596 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1598 static thread_local const float radius =
1599 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1601 if (peer_id == PEER_ID_INEXISTENT) {
1602 std::vector<session_t> clients = m_clients.getClientIDs();
1604 p.pos.start.min.val +
1605 p.pos.start.max.val +
1609 const float radius_sq = radius * radius;
1610 /* Don't send short-lived spawners to distant players.
1611 * This could be replaced with proper tracking at some point. */
1612 const bool distance_check = !attached_id && p.time <= 1.0f;
1614 for (const session_t client_id : clients) {
1615 RemotePlayer *player = m_env->getPlayer(client_id);
1619 if (distance_check) {
1620 PlayerSAO *sao = player->getPlayerSAO();
1623 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1627 SendAddParticleSpawner(client_id, player->protocol_version,
1628 p, attached_id, id);
1632 assert(protocol_version != 0);
1634 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1636 pkt << p.amount << p.time;
1637 { // serialize legacy fields
1638 std::ostringstream os(std::ios_base::binary);
1639 p.pos.start.legacySerialize(os);
1640 p.vel.start.legacySerialize(os);
1641 p.acc.start.legacySerialize(os);
1642 p.exptime.start.legacySerialize(os);
1643 p.size.start.legacySerialize(os);
1644 pkt.putRawString(os.str());
1646 pkt << p.collisiondetection;
1648 pkt.putLongString(p.texture.string);
1650 pkt << id << p.vertical << p.collision_removal << attached_id;
1652 std::ostringstream os(std::ios_base::binary);
1653 p.animation.serialize(os, protocol_version);
1654 pkt.putRawString(os.str());
1656 pkt << p.glow << p.object_collision;
1657 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1659 { // serialize new fields
1660 // initial bias for older properties
1661 pkt << p.pos.start.bias
1664 << p.exptime.start.bias
1665 << p.size.start.bias;
1667 std::ostringstream os(std::ios_base::binary);
1669 // final tween frames of older properties
1670 p.pos.end.serialize(os);
1671 p.vel.end.serialize(os);
1672 p.acc.end.serialize(os);
1673 p.exptime.end.serialize(os);
1674 p.size.end.serialize(os);
1676 // properties for legacy texture field
1677 p.texture.serialize(os, protocol_version, true);
1680 p.drag.serialize(os);
1681 p.jitter.serialize(os);
1682 p.bounce.serialize(os);
1683 ParticleParamTypes::serializeParameterValue(os, p.attractor_kind);
1684 if (p.attractor_kind != ParticleParamTypes::AttractorKind::none) {
1685 p.attract.serialize(os);
1686 p.attractor_origin.serialize(os);
1687 writeU16(os, p.attractor_attachment); /* object ID */
1688 writeU8(os, p.attractor_kill);
1689 if (p.attractor_kind != ParticleParamTypes::AttractorKind::point) {
1690 p.attractor_direction.serialize(os);
1691 writeU16(os, p.attractor_direction_attachment);
1694 p.radius.serialize(os);
1696 ParticleParamTypes::serializeParameterValue(os, (u16)p.texpool.size());
1697 for (const auto& tex : p.texpool) {
1698 tex.serialize(os, protocol_version);
1701 pkt.putRawString(os.str());
1707 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1709 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1713 if (peer_id != PEER_ID_INEXISTENT)
1716 m_clients.sendToAll(&pkt);
1720 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1722 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1724 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1725 << form->text << form->number << form->item << form->dir
1726 << form->align << form->offset << form->world_pos << form->size
1727 << form->z_index << form->text2 << form->style;
1732 void Server::SendHUDRemove(session_t peer_id, u32 id)
1734 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1739 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1741 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1742 pkt << id << (u8) stat;
1746 case HUD_STAT_SCALE:
1747 case HUD_STAT_ALIGN:
1748 case HUD_STAT_OFFSET:
1749 pkt << *(v2f *) value;
1753 case HUD_STAT_TEXT2:
1754 pkt << *(std::string *) value;
1756 case HUD_STAT_WORLD_POS:
1757 pkt << *(v3f *) value;
1760 pkt << *(v2s32 *) value;
1762 default: // all other types
1763 pkt << *(u32 *) value;
1770 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1772 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1774 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1776 pkt << flags << mask;
1781 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1783 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1784 pkt << param << value;
1788 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1790 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1792 // Handle prior clients here
1793 if (m_clients.getProtocolVersion(peer_id) < 39) {
1794 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1796 for (const std::string& texture : params.textures)
1799 pkt << params.clouds;
1800 } else { // Handle current clients and future clients
1801 pkt << params.bgcolor << params.type
1802 << params.clouds << params.fog_sun_tint
1803 << params.fog_moon_tint << params.fog_tint_type;
1805 if (params.type == "skybox") {
1806 pkt << (u16) params.textures.size();
1807 for (const std::string &texture : params.textures)
1809 } else if (params.type == "regular") {
1810 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1811 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1812 << params.sky_color.night_sky << params.sky_color.night_horizon
1813 << params.sky_color.indoors;
1820 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1822 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1823 pkt << params.visible << params.texture
1824 << params.tonemap << params.sunrise
1825 << params.sunrise_visible << params.scale;
1829 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1831 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1833 pkt << params.visible << params.texture
1834 << params.tonemap << params.scale;
1838 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1840 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1842 pkt << params.visible << params.count
1843 << params.starcolor << params.scale
1844 << params.day_opacity;
1849 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1851 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1852 pkt << params.density << params.color_bright << params.color_ambient
1853 << params.height << params.thickness << params.speed;
1857 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1860 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1863 pkt << do_override << (u16) (ratio * 65535);
1868 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1870 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1873 pkt << lighting.shadow_intensity;
1878 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1880 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1881 pkt << time << time_speed;
1883 if (peer_id == PEER_ID_INEXISTENT) {
1884 m_clients.sendToAll(&pkt);
1891 void Server::SendPlayerBreath(PlayerSAO *sao)
1895 m_script->player_event(sao, "breath_changed");
1896 SendBreath(sao->getPeerID(), sao->getBreath());
1899 void Server::SendMovePlayer(session_t peer_id)
1901 RemotePlayer *player = m_env->getPlayer(peer_id);
1903 PlayerSAO *sao = player->getPlayerSAO();
1906 // Send attachment updates instantly to the client prior updating position
1907 sao->sendOutdatedData();
1909 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1910 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1913 v3f pos = sao->getBasePosition();
1914 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1915 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1916 << " pitch=" << sao->getLookPitch()
1917 << " yaw=" << sao->getRotation().Y
1924 void Server::SendPlayerFov(session_t peer_id)
1926 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1928 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1929 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1934 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1935 f32 animation_speed)
1937 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1940 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1941 << animation_frames[3] << animation_speed;
1946 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1948 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1949 pkt << first << third;
1953 void Server::SendPlayerPrivileges(session_t peer_id)
1955 RemotePlayer *player = m_env->getPlayer(peer_id);
1957 if(player->getPeerId() == PEER_ID_INEXISTENT)
1960 std::set<std::string> privs;
1961 m_script->getAuth(player->getName(), NULL, &privs);
1963 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1964 pkt << (u16) privs.size();
1966 for (const std::string &priv : privs) {
1973 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1975 RemotePlayer *player = m_env->getPlayer(peer_id);
1977 if (player->getPeerId() == PEER_ID_INEXISTENT)
1980 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1981 pkt.putLongString(player->inventory_formspec);
1986 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1988 RemotePlayer *player = m_env->getPlayer(peer_id);
1990 if (player->getPeerId() == PEER_ID_INEXISTENT)
1993 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1994 pkt << player->formspec_prepend;
1998 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
2000 // Radius inside which objects are active
2001 static thread_local const s16 radius =
2002 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
2004 // Radius inside which players are active
2005 static thread_local const bool is_transfer_limited =
2006 g_settings->exists("unlimited_player_transfer_distance") &&
2007 !g_settings->getBool("unlimited_player_transfer_distance");
2009 static thread_local const s16 player_transfer_dist =
2010 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
2012 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
2013 radius : player_transfer_dist;
2015 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
2019 std::queue<u16> removed_objects, added_objects;
2020 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
2021 client->m_known_objects, removed_objects);
2022 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
2023 client->m_known_objects, added_objects);
2025 int removed_count = removed_objects.size();
2026 int added_count = added_objects.size();
2028 if (removed_objects.empty() && added_objects.empty())
2034 // Handle removed objects
2035 writeU16((u8*)buf, removed_objects.size());
2036 data.append(buf, 2);
2037 while (!removed_objects.empty()) {
2039 u16 id = removed_objects.front();
2040 ServerActiveObject* obj = m_env->getActiveObject(id);
2042 // Add to data buffer for sending
2043 writeU16((u8*)buf, id);
2044 data.append(buf, 2);
2046 // Remove from known objects
2047 client->m_known_objects.erase(id);
2049 if (obj && obj->m_known_by_count > 0)
2050 obj->m_known_by_count--;
2052 removed_objects.pop();
2055 // Handle added objects
2056 writeU16((u8*)buf, added_objects.size());
2057 data.append(buf, 2);
2058 while (!added_objects.empty()) {
2060 u16 id = added_objects.front();
2061 ServerActiveObject *obj = m_env->getActiveObject(id);
2062 added_objects.pop();
2065 warningstream << FUNCTION_NAME << ": NULL object id="
2066 << (int)id << std::endl;
2071 u8 type = obj->getSendType();
2073 // Add to data buffer for sending
2074 writeU16((u8*)buf, id);
2075 data.append(buf, 2);
2076 writeU8((u8*)buf, type);
2077 data.append(buf, 1);
2079 data.append(serializeString32(
2080 obj->getClientInitializationData(client->net_proto_version)));
2082 // Add to known objects
2083 client->m_known_objects.insert(id);
2085 obj->m_known_by_count++;
2088 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2089 pkt.putRawString(data.c_str(), data.size());
2092 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2093 << removed_count << " removed, " << added_count << " added, "
2094 << "packet size is " << pkt.getSize() << std::endl;
2097 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2100 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2101 datas.size(), peer_id);
2103 pkt.putRawString(datas.c_str(), datas.size());
2105 m_clients.send(pkt.getPeerId(),
2106 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2110 void Server::SendCSMRestrictionFlags(session_t peer_id)
2112 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2113 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2114 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2118 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2120 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2125 inline s32 Server::nextSoundId()
2127 s32 ret = m_next_sound_id;
2128 if (m_next_sound_id == INT32_MAX)
2129 m_next_sound_id = 0; // signed overflow is undefined
2135 s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
2137 // Find out initial position of sound
2138 bool pos_exists = false;
2139 const v3f pos = params.getPos(m_env, &pos_exists);
2140 // If position is not found while it should be, cancel sound
2141 if(pos_exists != (params.type != SoundLocation::Local))
2144 // Filter destination clients
2145 std::vector<session_t> dst_clients;
2146 if (!params.to_player.empty()) {
2147 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2149 infostream<<"Server::playSound: Player \""<<params.to_player
2150 <<"\" not found"<<std::endl;
2153 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2154 infostream<<"Server::playSound: Player \""<<params.to_player
2155 <<"\" not connected"<<std::endl;
2158 dst_clients.push_back(player->getPeerId());
2160 std::vector<session_t> clients = m_clients.getClientIDs();
2162 for (const session_t client_id : clients) {
2163 RemotePlayer *player = m_env->getPlayer(client_id);
2166 if (!params.exclude_player.empty() &&
2167 params.exclude_player == player->getName())
2170 PlayerSAO *sao = player->getPlayerSAO();
2175 if(sao->getBasePosition().getDistanceFrom(pos) >
2176 params.max_hear_distance)
2179 dst_clients.push_back(client_id);
2183 if(dst_clients.empty())
2186 // old clients will still use this, so pick a reserved ID (-1)
2187 const s32 id = ephemeral ? -1 : nextSoundId();
2189 float gain = params.gain * params.spec.gain;
2190 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2191 pkt << id << params.spec.name << gain
2192 << (u8) params.type << pos << params.object
2193 << params.spec.loop << params.spec.fade << params.spec.pitch
2196 bool as_reliable = !ephemeral;
2198 for (const session_t peer_id : dst_clients) {
2200 params.clients.insert(peer_id);
2201 m_clients.send(peer_id, 0, &pkt, as_reliable);
2205 m_playing_sounds[id] = std::move(params);
2208 void Server::stopSound(s32 handle)
2210 auto it = m_playing_sounds.find(handle);
2211 if (it == m_playing_sounds.end())
2214 ServerPlayingSound &psound = it->second;
2216 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2219 for (session_t peer_id : psound.clients) {
2221 m_clients.send(peer_id, 0, &pkt, true);
2224 // Remove sound reference
2225 m_playing_sounds.erase(it);
2228 void Server::fadeSound(s32 handle, float step, float gain)
2230 auto it = m_playing_sounds.find(handle);
2231 if (it == m_playing_sounds.end())
2234 ServerPlayingSound &psound = it->second;
2235 psound.gain = gain; // destination gain
2237 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2238 pkt << handle << step << gain;
2240 for (session_t peer_id : psound.clients) {
2242 m_clients.send(peer_id, 0, &pkt, true);
2245 // Remove sound reference
2246 if (gain <= 0 || psound.clients.empty())
2247 m_playing_sounds.erase(it);
2250 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2253 float maxd = far_d_nodes * BS;
2254 v3f p_f = intToFloat(p, BS);
2255 v3s16 block_pos = getNodeBlockPos(p);
2257 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2260 std::vector<session_t> clients = m_clients.getClientIDs();
2261 ClientInterface::AutoLock clientlock(m_clients);
2263 for (session_t client_id : clients) {
2264 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2268 RemotePlayer *player = m_env->getPlayer(client_id);
2269 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2271 // If player is far away, only set modified blocks not sent
2272 if (!client->isBlockSent(block_pos) || (sao &&
2273 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2275 far_players->emplace(client_id);
2277 client->SetBlockNotSent(block_pos);
2282 m_clients.send(client_id, 0, &pkt, true);
2286 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2287 float far_d_nodes, bool remove_metadata)
2289 float maxd = far_d_nodes * BS;
2290 v3f p_f = intToFloat(p, BS);
2291 v3s16 block_pos = getNodeBlockPos(p);
2293 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2294 pkt << p << n.param0 << n.param1 << n.param2
2295 << (u8) (remove_metadata ? 0 : 1);
2297 std::vector<session_t> clients = m_clients.getClientIDs();
2298 ClientInterface::AutoLock clientlock(m_clients);
2300 for (session_t client_id : clients) {
2301 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2305 RemotePlayer *player = m_env->getPlayer(client_id);
2306 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2308 // If player is far away, only set modified blocks not sent
2309 if (!client->isBlockSent(block_pos) || (sao &&
2310 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2312 far_players->emplace(client_id);
2314 client->SetBlockNotSent(block_pos);
2319 m_clients.send(client_id, 0, &pkt, true);
2323 void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
2325 NodeMetadataList meta_updates_list(false);
2326 std::ostringstream os(std::ios::binary);
2328 std::vector<session_t> clients = m_clients.getClientIDs();
2329 ClientInterface::AutoLock clientlock(m_clients);
2331 for (session_t i : clients) {
2332 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2336 ServerActiveObject *player = getPlayerSAO(i);
2339 player_pos = floatToInt(player->getBasePosition(), BS);
2341 for (const v3s16 pos : positions) {
2342 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2347 v3s16 block_pos = getNodeBlockPos(pos);
2348 if (!client->isBlockSent(block_pos) ||
2349 player_pos.getDistanceFrom(pos) > far_d_nodes) {
2350 client->SetBlockNotSent(block_pos);
2354 // Add the change to send list
2355 meta_updates_list.set(pos, meta);
2357 if (meta_updates_list.size() == 0)
2360 // Send the meta changes
2362 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2363 std::string raw = os.str();
2365 compressZlib(raw, os);
2367 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
2368 pkt.putLongString(os.str());
2371 meta_updates_list.clear();
2375 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2376 u16 net_proto_version, SerializedBlockCache *cache)
2378 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2379 std::string s, *sptr = nullptr;
2382 auto it = cache->find({block->getPos(), ver});
2383 if (it != cache->end())
2387 // Serialize the block in the right format
2389 std::ostringstream os(std::ios_base::binary);
2390 block->serialize(os, ver, false, net_compression_level);
2391 block->serializeNetworkSpecific(os);
2396 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2397 pkt << block->getPos();
2398 pkt.putRawString(*sptr);
2401 // Store away in cache
2402 if (cache && sptr == &s)
2403 (*cache)[{block->getPos(), ver}] = std::move(s);
2406 void Server::SendBlocks(float dtime)
2408 MutexAutoLock envlock(m_env_mutex);
2409 //TODO check if one big lock could be faster then multiple small ones
2411 std::vector<PrioritySortedBlockTransfer> queue;
2413 u32 total_sending = 0, unique_clients = 0;
2416 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2418 std::vector<session_t> clients = m_clients.getClientIDs();
2420 ClientInterface::AutoLock clientlock(m_clients);
2421 for (const session_t client_id : clients) {
2422 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2427 total_sending += client->getSendingCount();
2428 const auto old_count = queue.size();
2429 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2430 unique_clients += queue.size() > old_count ? 1 : 0;
2435 // Lowest priority number comes first.
2436 // Lowest is most important.
2437 std::sort(queue.begin(), queue.end());
2439 ClientInterface::AutoLock clientlock(m_clients);
2441 // Maximal total count calculation
2442 // The per-client block sends is halved with the maximal online users
2443 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2444 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2446 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2447 Map &map = m_env->getMap();
2449 SerializedBlockCache cache, *cache_ptr = nullptr;
2450 if (unique_clients > 1) {
2451 // caching is pointless with a single client
2455 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2456 if (total_sending >= max_blocks_to_send)
2459 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2463 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2468 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2469 client->net_proto_version, cache_ptr);
2471 client->SentBlock(block_to_send.pos);
2476 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2478 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2482 ClientInterface::AutoLock clientlock(m_clients);
2483 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2484 if (!client || client->isBlockSent(blockpos))
2486 SendBlockNoLock(peer_id, block, client->serialization_version,
2487 client->net_proto_version);
2492 bool Server::addMediaFile(const std::string &filename,
2493 const std::string &filepath, std::string *filedata_to,
2494 std::string *digest_to)
2496 // If name contains illegal characters, ignore the file
2497 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2498 infostream << "Server: ignoring illegal file name: \""
2499 << filename << "\"" << std::endl;
2502 // If name is not in a supported format, ignore it
2503 const char *supported_ext[] = {
2504 ".png", ".jpg", ".bmp", ".tga",
2506 ".x", ".b3d", ".obj",
2507 // Custom translation file format
2511 if (removeStringEnd(filename, supported_ext).empty()) {
2512 infostream << "Server: ignoring unsupported file extension: \""
2513 << filename << "\"" << std::endl;
2516 // Ok, attempt to load the file and add to cache
2519 std::string filedata;
2520 if (!fs::ReadFile(filepath, filedata)) {
2521 errorstream << "Server::addMediaFile(): Failed to open \""
2522 << filename << "\" for reading" << std::endl;
2526 if (filedata.empty()) {
2527 errorstream << "Server::addMediaFile(): Empty file \""
2528 << filepath << "\"" << std::endl;
2533 sha1.addBytes(filedata.c_str(), filedata.length());
2535 unsigned char *digest = sha1.getDigest();
2536 std::string sha1_base64 = base64_encode(digest, 20);
2537 std::string sha1_hex = hex_encode((char*) digest, 20);
2539 *digest_to = std::string((char*) digest, 20);
2543 m_media[filename] = MediaInfo(filepath, sha1_base64);
2544 verbosestream << "Server: " << sha1_hex << " is " << filename
2548 *filedata_to = std::move(filedata);
2552 void Server::fillMediaCache()
2554 infostream << "Server: Calculating media file checksums" << std::endl;
2556 // Collect all media file paths
2557 std::vector<std::string> paths;
2559 // ordered in descending priority
2560 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2561 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2562 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2563 m_modmgr->getModsMediaPaths(paths);
2565 // Collect media file information from paths into cache
2566 for (const std::string &mediapath : paths) {
2567 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2568 for (const fs::DirListNode &dln : dirlist) {
2569 if (dln.dir) // Ignore dirs (already in paths)
2572 const std::string &filename = dln.name;
2573 if (m_media.find(filename) != m_media.end()) // Do not override
2576 std::string filepath = mediapath;
2577 filepath.append(DIR_DELIM).append(filename);
2578 addMediaFile(filename, filepath);
2582 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2585 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2588 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2591 std::string lang_suffix;
2592 lang_suffix.append(".").append(lang_code).append(".tr");
2593 for (const auto &i : m_media) {
2594 if (i.second.no_announce)
2596 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2603 for (const auto &i : m_media) {
2604 if (i.second.no_announce)
2606 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2608 pkt << i.first << i.second.sha1_digest;
2611 pkt << g_settings->get("remote_media");
2614 verbosestream << "Server: Announcing files to id(" << peer_id
2615 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2618 struct SendableMedia
2624 SendableMedia(const std::string &name, const std::string &path,
2625 std::string &&data):
2626 name(name), path(path), data(std::move(data))
2630 void Server::sendRequestedMedia(session_t peer_id,
2631 const std::vector<std::string> &tosend)
2633 verbosestream<<"Server::sendRequestedMedia(): "
2634 <<"Sending files to client"<<std::endl;
2638 // Put 5kB in one bunch (this is not accurate)
2639 u32 bytes_per_bunch = 5000;
2641 std::vector< std::vector<SendableMedia> > file_bunches;
2642 file_bunches.emplace_back();
2644 u32 file_size_bunch_total = 0;
2646 for (const std::string &name : tosend) {
2647 if (m_media.find(name) == m_media.end()) {
2648 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2649 <<"unknown file \""<<(name)<<"\""<<std::endl;
2653 const auto &m = m_media[name];
2657 if (!fs::ReadFile(m.path, data)) {
2658 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2659 << name << "\"" << std::endl;
2662 file_size_bunch_total += data.size();
2665 file_bunches.back().emplace_back(name, m.path, std::move(data));
2667 // Start next bunch if got enough data
2668 if(file_size_bunch_total >= bytes_per_bunch) {
2669 file_bunches.emplace_back();
2670 file_size_bunch_total = 0;
2675 /* Create and send packets */
2677 u16 num_bunches = file_bunches.size();
2678 for (u16 i = 0; i < num_bunches; i++) {
2681 u16 total number of texture bunches
2682 u16 index of this bunch
2683 u32 number of files in this bunch
2692 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2693 pkt << num_bunches << i << (u32) file_bunches[i].size();
2695 for (const SendableMedia &j : file_bunches[i]) {
2697 pkt.putLongString(j.data);
2700 verbosestream << "Server::sendRequestedMedia(): bunch "
2701 << i << "/" << num_bunches
2702 << " files=" << file_bunches[i].size()
2703 << " size=" << pkt.getSize() << std::endl;
2708 void Server::stepPendingDynMediaCallbacks(float dtime)
2710 MutexAutoLock lock(m_env_mutex);
2712 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2713 it->second.expiry_timer -= dtime;
2714 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2721 const auto &name = it->second.filename;
2722 if (!name.empty()) {
2723 assert(m_media.count(name));
2724 // if no_announce isn't set we're definitely deleting the wrong file!
2725 sanity_check(m_media[name].no_announce);
2727 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2728 m_media.erase(name);
2730 getScriptIface()->freeDynamicMediaCallback(it->first);
2731 it = m_pending_dyn_media.erase(it);
2735 void Server::SendMinimapModes(session_t peer_id,
2736 std::vector<MinimapMode> &modes, size_t wanted_mode)
2738 RemotePlayer *player = m_env->getPlayer(peer_id);
2740 if (player->getPeerId() == PEER_ID_INEXISTENT)
2743 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2744 pkt << (u16)modes.size() << (u16)wanted_mode;
2746 for (auto &mode : modes)
2747 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2752 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2754 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2758 pkt << false; // Remove inventory
2760 pkt << true; // Update inventory
2762 // Serialization & NetworkPacket isn't a love story
2763 std::ostringstream os(std::ios_base::binary);
2764 inventory->serialize(os);
2765 inventory->setModified(false);
2767 const std::string &os_str = os.str();
2768 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2769 pkt.putRawString(os_str);
2772 if (peer_id == PEER_ID_INEXISTENT)
2773 m_clients.sendToAll(&pkt);
2778 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2780 // Lookup player name, to filter detached inventories just after
2781 std::string peer_name;
2782 if (peer_id != PEER_ID_INEXISTENT) {
2783 peer_name = getClient(peer_id, CS_Created)->getName();
2786 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2787 sendDetachedInventory(inv, name, peer_id);
2790 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2797 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2799 infostream << "Server::DiePlayer(): Player "
2800 << playersao->getPlayer()->getName()
2801 << " dies" << std::endl;
2803 playersao->clearParentAttachment();
2805 // Trigger scripted stuff
2806 m_script->on_dieplayer(playersao, reason);
2808 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2811 void Server::RespawnPlayer(session_t peer_id)
2813 PlayerSAO *playersao = getPlayerSAO(peer_id);
2816 infostream << "Server::RespawnPlayer(): Player "
2817 << playersao->getPlayer()->getName()
2818 << " respawns" << std::endl;
2820 const auto *prop = playersao->accessObjectProperties();
2821 playersao->setHP(prop->hp_max,
2822 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2823 playersao->setBreath(prop->breath_max);
2825 bool repositioned = m_script->on_respawnplayer(playersao);
2826 if (!repositioned) {
2827 // setPos will send the new position to client
2828 playersao->setPos(findSpawnPos());
2833 void Server::DenySudoAccess(session_t peer_id)
2835 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2840 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2841 const std::string &custom_reason, bool reconnect)
2843 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2844 m_clients.event(peer_id, CSE_SetDenied);
2845 DisconnectPeer(peer_id);
2848 void Server::DisconnectPeer(session_t peer_id)
2850 m_modchannel_mgr->leaveAllChannels(peer_id);
2851 m_con->DisconnectPeer(peer_id);
2854 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2857 RemoteClient* client = getClient(peer_id, CS_Invalid);
2859 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2861 // Right now, the auth mechs don't change between login and sudo mode.
2862 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2863 client->allowed_sudo_mechs = sudo_auth_mechs;
2865 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2866 << g_settings->getFloat("dedicated_server_step")
2870 m_clients.event(peer_id, CSE_AuthAccept);
2872 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2874 // We only support SRP right now
2875 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2877 resp_pkt << sudo_auth_mechs;
2879 m_clients.event(peer_id, CSE_SudoSuccess);
2883 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2885 std::wstring message;
2888 Clear references to playing sounds
2890 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2891 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2892 ServerPlayingSound &psound = i->second;
2893 psound.clients.erase(peer_id);
2894 if (psound.clients.empty())
2895 m_playing_sounds.erase(i++);
2900 // clear formspec info so the next client can't abuse the current state
2901 m_formspec_state_data.erase(peer_id);
2903 RemotePlayer *player = m_env->getPlayer(peer_id);
2905 /* Run scripts and remove from environment */
2907 PlayerSAO *playersao = player->getPlayerSAO();
2910 playersao->clearChildAttachments();
2911 playersao->clearParentAttachment();
2913 // inform connected clients
2914 const std::string &player_name = player->getName();
2915 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2916 // (u16) 1 + std::string represents a vector serialization representation
2917 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2918 m_clients.sendToAll(¬ice);
2920 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2922 playersao->disconnected();
2929 if (player && reason != CDR_DENY) {
2930 std::ostringstream os(std::ios_base::binary);
2931 std::vector<session_t> clients = m_clients.getClientIDs();
2933 for (const session_t client_id : clients) {
2935 RemotePlayer *player = m_env->getPlayer(client_id);
2939 // Get name of player
2940 os << player->getName() << " ";
2943 std::string name = player->getName();
2944 actionstream << name << " "
2945 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2946 << " List of players: " << os.str() << std::endl;
2948 m_admin_chat->outgoing_queue.push_back(
2949 new ChatEventNick(CET_NICK_REMOVE, name));
2953 MutexAutoLock env_lock(m_env_mutex);
2954 m_clients.DeleteClient(peer_id);
2958 // Send leave chat message to all remaining clients
2959 if (!message.empty()) {
2960 SendChatMessage(PEER_ID_INEXISTENT,
2961 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2965 void Server::UpdateCrafting(RemotePlayer *player)
2967 InventoryList *clist = player->inventory.getList("craft");
2968 if (!clist || clist->getSize() == 0)
2971 if (!clist->checkModified())
2974 // Get a preview for crafting
2976 InventoryLocation loc;
2977 loc.setPlayer(player->getName());
2978 std::vector<ItemStack> output_replacements;
2979 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2980 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2983 InventoryList *plist = player->inventory.getList("craftpreview");
2984 if (plist && plist->getSize() >= 1) {
2985 // Put the new preview in
2986 plist->changeItem(0, preview);
2990 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2992 if (evt->type == CET_NICK_ADD) {
2993 // The terminal informed us of its nick choice
2994 m_admin_nick = ((ChatEventNick *)evt)->nick;
2995 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2996 errorstream << "You haven't set up an account." << std::endl
2997 << "Please log in using the client as '"
2998 << m_admin_nick << "' with a secure password." << std::endl
2999 << "Until then, you can't execute admin tasks via the console," << std::endl
3000 << "and everybody can claim the user account instead of you," << std::endl
3001 << "giving them full control over this server." << std::endl;
3004 assert(evt->type == CET_CHAT);
3005 handleAdminChat((ChatEventChat *)evt);
3009 std::wstring Server::handleChat(const std::string &name,
3010 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
3012 // If something goes wrong, this player is to blame
3013 RollbackScopeActor rollback_scope(m_rollback,
3014 std::string("player:") + name);
3016 if (g_settings->getBool("strip_color_codes"))
3017 wmessage = unescape_enriched(wmessage);
3020 switch (player->canSendChatMessage()) {
3021 case RPLAYER_CHATRESULT_FLOODING: {
3022 std::wstringstream ws;
3023 ws << L"You cannot send more messages. You are limited to "
3024 << g_settings->getFloat("chat_message_limit_per_10sec")
3025 << L" messages per 10 seconds.";
3028 case RPLAYER_CHATRESULT_KICK:
3029 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
3030 "You have been kicked due to message flooding.");
3032 case RPLAYER_CHATRESULT_OK:
3035 FATAL_ERROR("Unhandled chat filtering result found.");
3039 if (m_max_chatmessage_length > 0
3040 && wmessage.length() > m_max_chatmessage_length) {
3041 return L"Your message exceed the maximum chat message limit set on the server. "
3042 L"It was refused. Send a shorter message";
3045 auto message = trim(wide_to_utf8(wmessage));
3046 if (message.empty())
3049 if (message.find_first_of("\n\r") != std::wstring::npos) {
3050 return L"Newlines are not permitted in chat messages";
3053 // Run script hook, exit if script ate the chat message
3054 if (m_script->on_chat_message(name, message))
3059 // Whether to send line to the player that sent the message, or to all players
3060 bool broadcast_line = true;
3062 if (check_shout_priv && !checkPriv(name, "shout")) {
3063 line += L"-!- You don't have permission to shout.";
3064 broadcast_line = false;
3067 Workaround for fixing chat on Android. Lua doesn't handle
3068 the Cyrillic alphabet and some characters on older Android devices
3071 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3073 line += utf8_to_wide(m_script->formatChatMessage(name,
3074 wide_to_utf8(wmessage)));
3079 Tell calling method to send the message to sender
3081 if (!broadcast_line)
3085 Send the message to others
3087 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3089 ChatMessage chatmsg(line);
3091 std::vector<session_t> clients = m_clients.getClientIDs();
3092 for (u16 cid : clients)
3093 SendChatMessage(cid, chatmsg);
3098 void Server::handleAdminChat(const ChatEventChat *evt)
3100 std::string name = evt->nick;
3101 std::wstring wmessage = evt->evt_msg;
3103 std::wstring answer = handleChat(name, wmessage);
3105 // If asked to send answer to sender
3106 if (!answer.empty()) {
3107 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3111 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3113 RemoteClient *client = getClientNoEx(peer_id,state_min);
3115 throw ClientNotFoundException("Client not found");
3119 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3121 return m_clients.getClientNoEx(peer_id, state_min);
3124 std::string Server::getPlayerName(session_t peer_id)
3126 RemotePlayer *player = m_env->getPlayer(peer_id);
3128 return "[id="+itos(peer_id)+"]";
3129 return player->getName();
3132 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3134 RemotePlayer *player = m_env->getPlayer(peer_id);
3137 return player->getPlayerSAO();
3140 std::string Server::getStatusString()
3142 std::ostringstream os(std::ios_base::binary);
3145 os << "version: " << g_version_string;
3147 os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3149 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3151 os << " | max lag: " << std::setprecision(3);
3152 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3154 // Information about clients
3156 os << " | clients: ";
3158 std::vector<session_t> clients = m_clients.getClientIDs();
3159 for (session_t client_id : clients) {
3160 RemotePlayer *player = m_env->getPlayer(client_id);
3162 // Get name of player
3163 const char *name = player ? player->getName() : "<unknown>";
3165 // Add name to information string
3174 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3175 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3177 if (!g_settings->get("motd").empty())
3178 os << std::endl << "# Server: " << g_settings->get("motd");
3183 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3185 std::set<std::string> privs;
3186 m_script->getAuth(name, NULL, &privs);
3190 bool Server::checkPriv(const std::string &name, const std::string &priv)
3192 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3193 return (privs.count(priv) != 0);
3196 void Server::reportPrivsModified(const std::string &name)
3199 std::vector<session_t> clients = m_clients.getClientIDs();
3200 for (const session_t client_id : clients) {
3201 RemotePlayer *player = m_env->getPlayer(client_id);
3202 reportPrivsModified(player->getName());
3205 RemotePlayer *player = m_env->getPlayer(name.c_str());
3208 SendPlayerPrivileges(player->getPeerId());
3209 PlayerSAO *sao = player->getPlayerSAO();
3212 sao->updatePrivileges(
3213 getPlayerEffectivePrivs(name),
3218 void Server::reportInventoryFormspecModified(const std::string &name)
3220 RemotePlayer *player = m_env->getPlayer(name.c_str());
3223 SendPlayerInventoryFormspec(player->getPeerId());
3226 void Server::reportFormspecPrependModified(const std::string &name)
3228 RemotePlayer *player = m_env->getPlayer(name.c_str());
3231 SendPlayerFormspecPrepend(player->getPeerId());
3234 void Server::setIpBanned(const std::string &ip, const std::string &name)
3236 m_banmanager->add(ip, name);
3239 void Server::unsetIpBanned(const std::string &ip_or_name)
3241 m_banmanager->remove(ip_or_name);
3244 std::string Server::getBanDescription(const std::string &ip_or_name)
3246 return m_banmanager->getBanDescription(ip_or_name);
3249 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3251 // m_env will be NULL if the server is initializing
3255 if (m_admin_nick == name && !m_admin_nick.empty()) {
3256 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3259 RemotePlayer *player = m_env->getPlayer(name);
3264 if (player->getPeerId() == PEER_ID_INEXISTENT)
3267 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3270 bool Server::showFormspec(const char *playername, const std::string &formspec,
3271 const std::string &formname)
3273 // m_env will be NULL if the server is initializing
3277 RemotePlayer *player = m_env->getPlayer(playername);
3281 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3285 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3290 u32 id = player->addHud(form);
3292 SendHUDAdd(player->getPeerId(), id, form);
3297 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3301 HudElement* todel = player->removeHud(id);
3308 SendHUDRemove(player->getPeerId(), id);
3312 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3317 SendHUDChange(player->getPeerId(), id, stat, data);
3321 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3326 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3327 if (new_hud_flags == player->hud_flags) // no change
3330 SendHUDSetFlags(player->getPeerId(), flags, mask);
3331 player->hud_flags = new_hud_flags;
3333 PlayerSAO* playersao = player->getPlayerSAO();
3338 m_script->player_event(playersao, "hud_changed");
3342 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3347 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3350 player->setHotbarItemcount(hotbar_itemcount);
3351 std::ostringstream os(std::ios::binary);
3352 writeS32(os, hotbar_itemcount);
3353 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3357 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3362 player->setHotbarImage(name);
3363 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3366 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3371 player->setHotbarSelectedImage(name);
3372 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3375 Address Server::getPeerAddress(session_t peer_id)
3377 // Note that this is only set after Init was received in Server::handleCommand_Init
3378 return getClient(peer_id, CS_Invalid)->getAddress();
3381 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3382 v2s32 animation_frames[4], f32 frame_speed)
3384 sanity_check(player);
3385 player->setLocalAnimations(animation_frames, frame_speed);
3386 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3389 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3391 sanity_check(player);
3392 player->eye_offset_first = first;
3393 player->eye_offset_third = third;
3394 SendEyeOffset(player->getPeerId(), first, third);
3397 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3399 sanity_check(player);
3400 player->setSky(params);
3401 SendSetSky(player->getPeerId(), params);
3404 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3406 sanity_check(player);
3407 player->setSun(params);
3408 SendSetSun(player->getPeerId(), params);
3411 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3413 sanity_check(player);
3414 player->setMoon(params);
3415 SendSetMoon(player->getPeerId(), params);
3418 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3420 sanity_check(player);
3421 player->setStars(params);
3422 SendSetStars(player->getPeerId(), params);
3425 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3427 sanity_check(player);
3428 player->setCloudParams(params);
3429 SendCloudParams(player->getPeerId(), params);
3432 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3435 sanity_check(player);
3436 player->overrideDayNightRatio(do_override, ratio);
3437 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3440 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3442 sanity_check(player);
3443 player->setLighting(lighting);
3444 SendSetLighting(player->getPeerId(), lighting);
3447 void Server::notifyPlayers(const std::wstring &msg)
3449 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3452 void Server::spawnParticle(const std::string &playername,
3453 const ParticleParameters &p)
3455 // m_env will be NULL if the server is initializing
3459 session_t peer_id = PEER_ID_INEXISTENT;
3461 if (!playername.empty()) {
3462 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3465 peer_id = player->getPeerId();
3466 proto_ver = player->protocol_version;
3469 SendSpawnParticle(peer_id, proto_ver, p);
3472 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3473 ServerActiveObject *attached, const std::string &playername)
3475 // m_env will be NULL if the server is initializing
3479 session_t peer_id = PEER_ID_INEXISTENT;
3481 if (!playername.empty()) {
3482 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3485 peer_id = player->getPeerId();
3486 proto_ver = player->protocol_version;
3489 u16 attached_id = attached ? attached->getId() : 0;
3492 if (attached_id == 0)
3493 id = m_env->addParticleSpawner(p.time);
3495 id = m_env->addParticleSpawner(p.time, attached_id);
3497 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3501 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3503 // m_env will be NULL if the server is initializing
3505 throw ServerError("Can't delete particle spawners during initialisation!");
3507 session_t peer_id = PEER_ID_INEXISTENT;
3508 if (!playername.empty()) {
3509 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3512 peer_id = player->getPeerId();
3515 m_env->deleteParticleSpawner(id);
3516 SendDeleteParticleSpawner(peer_id, id);
3519 bool Server::dynamicAddMedia(std::string filepath,
3520 const u32 token, const std::string &to_player, bool ephemeral)
3522 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3523 auto it = m_media.find(filename);
3524 if (it != m_media.end()) {
3525 // Allow the same path to be "added" again in certain conditions
3526 if (ephemeral || it->second.path != filepath) {
3527 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3528 << "\" already exists in media cache" << std::endl;
3533 // Load the file and add it to our media cache
3534 std::string filedata, raw_hash;
3535 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3540 // Create a copy of the file and swap out the path, this removes the
3541 // requirement that mods keep the file accessible at the original path.
3542 filepath = fs::CreateTempFile();
3543 bool ok = ([&] () -> bool {
3544 if (filepath.empty())
3546 std::ofstream os(filepath.c_str(), std::ios::binary);
3554 errorstream << "Server: failed to create a copy of media file "
3555 << "\"" << filename << "\"" << std::endl;
3556 m_media.erase(filename);
3559 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3560 << filepath << std::endl;
3562 m_media[filename].path = filepath;
3563 m_media[filename].no_announce = true;
3564 // stepPendingDynMediaCallbacks will clean this up later.
3565 } else if (!to_player.empty()) {
3566 m_media[filename].no_announce = true;
3569 // Push file to existing clients
3570 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3571 pkt << raw_hash << filename << (bool)ephemeral;
3573 NetworkPacket legacy_pkt = pkt;
3575 // Newer clients get asked to fetch the file (asynchronous)
3577 // Older clients have an awful hack that just throws the data at them
3578 legacy_pkt.putLongString(filedata);
3580 std::unordered_set<session_t> delivered, waiting;
3582 ClientInterface::AutoLock clientlock(m_clients);
3583 for (auto &pair : m_clients.getClientList()) {
3584 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3586 If a client is in the DefinitionsSent state it is too late to
3587 transfer the file via sendMediaAnnouncement() but at the same
3588 time the client cannot accept a media push yet.
3589 Short of artificially delaying the joining process there is no
3590 way for the server to resolve this so we (currently) opt not to.
3592 warningstream << "The media \"" << filename << "\" (dynamic) could "
3593 "not be delivered to " << pair.second->getName()
3594 << " due to a race condition." << std::endl;
3597 if (pair.second->getState() < CS_Active)
3600 const auto proto_ver = pair.second->net_proto_version;
3604 const session_t peer_id = pair.second->peer_id;
3605 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3608 if (proto_ver < 40) {
3609 delivered.emplace(peer_id);
3611 The network layer only guarantees ordered delivery inside a channel.
3612 Since the very next packet could be one that uses the media, we have
3613 to push the media over ALL channels to ensure it is processed before
3614 it is used. In practice this means channels 1 and 0.
3616 m_clients.send(peer_id, 1, &legacy_pkt, true);
3617 m_clients.send(peer_id, 0, &legacy_pkt, true);
3619 waiting.emplace(peer_id);
3620 Send(peer_id, &pkt);
3625 // Run callback for players that already had the file delivered (legacy-only)
3626 for (session_t peer_id : delivered) {
3627 if (auto player = m_env->getPlayer(peer_id))
3628 getScriptIface()->on_dynamic_media_added(token, player->getName());
3631 // Save all others in our pending state
3632 auto &state = m_pending_dyn_media[token];
3633 state.waiting_players = std::move(waiting);
3634 // regardless of success throw away the callback after a while
3635 state.expiry_timer = 60.0f;
3637 state.filename = filename;
3642 // actions: time-reversed list
3643 // Return value: success/failure
3644 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3645 std::list<std::string> *log)
3647 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3648 ServerMap *map = (ServerMap*)(&m_env->getMap());
3650 // Fail if no actions to handle
3651 if (actions.empty()) {
3653 log->push_back("Nothing to do.");
3660 for (const RollbackAction &action : actions) {
3662 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3665 std::ostringstream os;
3666 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3667 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3669 log->push_back(os.str());
3671 std::ostringstream os;
3672 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3673 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3675 log->push_back(os.str());
3679 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3680 <<" failed"<<std::endl;
3682 // Call it done if less than half failed
3683 return num_failed <= num_tried/2;
3686 // IGameDef interface
3688 IItemDefManager *Server::getItemDefManager()
3693 const NodeDefManager *Server::getNodeDefManager()
3698 ICraftDefManager *Server::getCraftDefManager()
3703 u16 Server::allocateUnknownNodeId(const std::string &name)
3705 return m_nodedef->allocateDummy(name);
3708 IWritableItemDefManager *Server::getWritableItemDefManager()
3713 NodeDefManager *Server::getWritableNodeDefManager()
3718 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3723 const std::vector<ModSpec> & Server::getMods() const
3725 return m_modmgr->getMods();
3728 const ModSpec *Server::getModSpec(const std::string &modname) const
3730 return m_modmgr->getModSpec(modname);
3733 std::string Server::getBuiltinLuaPath()
3735 return porting::path_share + DIR_DELIM + "builtin";
3738 v3f Server::findSpawnPos()
3740 ServerMap &map = m_env->getServerMap();
3742 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3743 return nodeposf * BS;
3745 bool is_good = false;
3746 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3747 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3749 // Try to find a good place a few times
3750 for (s32 i = 0; i < 4000 && !is_good; i++) {
3751 s32 range = MYMIN(1 + i, range_max);
3752 // We're going to try to throw the player to this position
3753 v2s16 nodepos2d = v2s16(
3754 -range + myrand_range(0, range*2),
3755 -range + myrand_range(0, range*2));
3756 // Get spawn level at point
3757 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3758 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3759 // signify an unsuitable spawn position, or if outside limits.
3760 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3761 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3764 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3765 // Consecutive empty nodes
3768 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3769 // avoid obstructions in already-generated mapblocks.
3770 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3771 // no obstructions, but mapgen decorations are generated after spawn so
3772 // the player may end up inside one.
3773 for (s32 i = 0; i < 8; i++) {
3774 v3s16 blockpos = getNodeBlockPos(nodepos);
3775 map.emergeBlock(blockpos, true);
3776 content_t c = map.getNode(nodepos).getContent();
3778 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3779 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3780 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3782 if (air_count >= 2) {
3783 // Spawn in lower empty node
3785 nodeposf = intToFloat(nodepos, BS);
3786 // Don't spawn the player outside map boundaries
3787 if (objectpos_over_limit(nodeposf))
3788 // Exit this loop, positions above are probably over limit
3791 // Good position found, cause an exit from main loop
3805 // No suitable spawn point found, return fallback 0,0,0
3806 return v3f(0.0f, 0.0f, 0.0f);
3809 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3811 if (delay == 0.0f) {
3812 // No delay, shutdown immediately
3813 m_shutdown_state.is_requested = true;
3814 // only print to the infostream, a chat message saying
3815 // "Server Shutting Down" is sent when the server destructs.
3816 infostream << "*** Immediate Server shutdown requested." << std::endl;
3817 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3818 // Negative delay, cancel shutdown if requested
3819 m_shutdown_state.reset();
3820 std::wstringstream ws;
3822 ws << L"*** Server shutdown canceled.";
3824 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3825 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3826 // m_shutdown_* are already handled, skip.
3828 } else if (delay > 0.0f) {
3829 // Positive delay, tell the clients when the server will shut down
3830 std::wstringstream ws;
3832 ws << L"*** Server shutting down in "
3833 << duration_to_string(myround(delay)).c_str()
3836 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3837 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3840 m_shutdown_state.trigger(delay, msg, reconnect);
3843 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3846 Try to get an existing player
3848 RemotePlayer *player = m_env->getPlayer(name);
3850 // If player is already connected, cancel
3851 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3852 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3857 If player with the wanted peer_id already exists, cancel.
3859 if (m_env->getPlayer(peer_id)) {
3860 infostream<<"emergePlayer(): Player with wrong name but same"
3861 " peer_id already exists"<<std::endl;
3866 player = new RemotePlayer(name, idef());
3869 bool newplayer = false;
3872 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3874 // Complete init with server parts
3875 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3876 player->protocol_version = proto_version;
3880 m_script->on_newplayer(playersao);
3886 void dedicated_server_loop(Server &server, bool &kill)
3888 verbosestream<<"dedicated_server_loop()"<<std::endl;
3890 IntervalLimiter m_profiler_interval;
3892 static thread_local const float steplen =
3893 g_settings->getFloat("dedicated_server_step");
3894 static thread_local const float profiler_print_interval =
3895 g_settings->getFloat("profiler_print_interval");
3898 * The dedicated server loop only does time-keeping (in Server::step) and
3899 * provides a way to main.cpp to kill the server externally (bool &kill).
3903 // This is kind of a hack but can be done like this
3904 // because server.step() is very light
3905 sleep_ms((int)(steplen*1000.0));
3906 server.step(steplen);
3908 if (server.isShutdownRequested() || kill)
3914 if (profiler_print_interval != 0) {
3915 if(m_profiler_interval.step(steplen, profiler_print_interval))
3917 infostream<<"Profiler:"<<std::endl;
3918 g_profiler->print(infostream);
3919 g_profiler->clear();
3924 infostream << "Dedicated server quitting" << std::endl;
3926 if (g_settings->getBool("server_announce"))
3927 ServerList::sendAnnounce(ServerList::AA_DELETE,
3928 server.m_bind_addr.getPort());
3937 bool Server::joinModChannel(const std::string &channel)
3939 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3940 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3943 bool Server::leaveModChannel(const std::string &channel)
3945 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3948 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3950 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3953 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3957 ModChannel* Server::getModChannel(const std::string &channel)
3959 return m_modchannel_mgr->getModChannel(channel);
3962 void Server::broadcastModChannelMessage(const std::string &channel,
3963 const std::string &message, session_t from_peer)
3965 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3969 if (message.size() > STRING_MAX_LEN) {
3970 warningstream << "ModChannel message too long, dropping before sending "
3971 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3972 << channel << ")" << std::endl;
3977 if (from_peer != PEER_ID_SERVER) {
3978 sender = getPlayerName(from_peer);
3981 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3982 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3983 resp_pkt << channel << sender << message;
3984 for (session_t peer_id : peers) {
3986 if (peer_id == from_peer)
3989 Send(peer_id, &resp_pkt);
3992 if (from_peer != PEER_ID_SERVER) {
3993 m_script->on_modchannel_message(channel, sender, message);
3997 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3999 if (lang_code.empty())
4002 auto it = server_translations.find(lang_code);
4003 if (it != server_translations.end())
4004 return &it->second; // Already loaded
4006 // [] will create an entry
4007 auto *translations = &server_translations[lang_code];
4009 std::string suffix = "." + lang_code + ".tr";
4010 for (const auto &i : m_media) {
4011 if (str_ends_with(i.first, suffix)) {
4013 if (fs::ReadFile(i.second.path, data)) {
4014 translations->loadTranslation(data);
4019 return translations;
4022 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
4024 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4026 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4027 throw BaseException("Cannot read world.mt!");
4029 std::string backend = world_mt.exists("mod_storage_backend") ?
4030 world_mt.get("mod_storage_backend") : "files";
4031 if (backend == "files")
4032 warningstream << "/!\\ You are using the old mod storage files backend. "
4033 << "This backend is deprecated and may be removed in a future release /!\\"
4034 << std::endl << "Switching to SQLite3 is advised, "
4035 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4037 return openModStorageDatabase(backend, world_path, world_mt);
4040 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
4041 const std::string &world_path, const Settings &world_mt)
4043 if (backend == "sqlite3")
4044 return new ModMetadataDatabaseSQLite3(world_path);
4046 if (backend == "files")
4047 return new ModMetadataDatabaseFiles(world_path);
4049 if (backend == "dummy")
4050 return new Database_Dummy();
4052 throw BaseException("Mod storage database backend " + backend + " not supported");
4055 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4057 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4059 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4060 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4061 errorstream << "Cannot read world.mt!" << std::endl;
4065 std::string backend = world_mt.exists("mod_storage_backend") ?
4066 world_mt.get("mod_storage_backend") : "files";
4067 if (backend == migrate_to) {
4068 errorstream << "Cannot migrate: new backend is same"
4069 << " as the old one" << std::endl;
4073 ModMetadataDatabase *srcdb = nullptr;
4074 ModMetadataDatabase *dstdb = nullptr;
4076 bool succeeded = false;
4079 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4080 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4084 std::vector<std::string> mod_list;
4085 srcdb->listMods(&mod_list);
4086 for (const std::string &modname : mod_list) {
4088 srcdb->getModEntries(modname, &meta);
4089 for (const auto &pair : meta) {
4090 dstdb->setModEntry(modname, pair.first, pair.second);
4098 actionstream << "Successfully migrated the metadata of "
4099 << mod_list.size() << " mods" << std::endl;
4100 world_mt.set("mod_storage_backend", migrate_to);
4101 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4102 errorstream << "Failed to update world.mt!" << std::endl;
4104 actionstream << "world.mt updated" << std::endl;
4106 } catch (BaseException &e) {
4107 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4113 if (succeeded && backend == "files") {
4115 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4116 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4117 if (!fs::Rename(storage_path, backup_path))
4118 warningstream << "After migration, " << storage_path
4119 << " could not be renamed to " << backup_path << std::endl;