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);
427 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
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());
486 m_inventory_mgr->setEnv(m_env);
487 m_clients.setEnv(m_env);
489 if (!servermap->settings_mgr.makeMapgenParams())
490 FATAL_ERROR("Couldn't create any mapgen type");
492 // Initialize mapgens
493 m_emerge->initMapgens(servermap->getMapgenParams());
495 if (g_settings->getBool("enable_rollback_recording")) {
496 // Create rollback manager
497 m_rollback = new RollbackManager(m_path_world, this);
500 // Give environment reference to scripting api
501 m_script->initializeEnvironment(m_env);
503 // Do this after regular script init is done
504 m_script->initAsync();
506 // Register us to receive map edit events
507 servermap->addEventReceiver(this);
511 // Those settings can be overwritten in world.mt, they are
512 // intended to be cached after environment loading.
513 m_liquid_transform_every = g_settings->getFloat("liquid_update");
514 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
515 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
516 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
523 infostream << "Starting server on " << m_bind_addr.serializeString()
524 << "..." << std::endl;
526 // Stop thread if already running
529 // Initialize connection
530 m_con->SetTimeoutMs(30);
531 m_con->Serve(m_bind_addr);
536 // ASCII art for the win!
538 << " __. __. __. " << std::endl
539 << " _____ |__| ____ _____ / |_ _____ _____ / |_ " << std::endl
540 << " / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\" << std::endl
541 << "| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | " << std::endl
542 << "|__|_| / |___| /\\______> | \\______>_____/| | " << std::endl
543 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
544 actionstream << "World at [" << m_path_world << "]" << std::endl;
545 actionstream << "Server for gameid=\"" << m_gamespec.id
546 << "\" listening on ";
547 m_bind_addr.print(actionstream);
548 actionstream << "." << std::endl;
553 infostream<<"Server: Stopping and waiting threads"<<std::endl;
555 // Stop threads (set run=false first so both start stopping)
559 infostream<<"Server: Threads stopped"<<std::endl;
562 void Server::step(float dtime)
568 MutexAutoLock lock(m_step_dtime_mutex);
569 m_step_dtime += dtime;
571 // Throw if fatal error occurred in thread
572 std::string async_err = m_async_fatal_error.get();
573 if (!async_err.empty()) {
574 if (!m_simple_singleplayer_mode) {
575 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
576 g_settings->get("kick_msg_crash"),
577 g_settings->getBool("ask_reconnect_on_crash"));
579 throw ServerError("AsyncErr: " + async_err);
583 void Server::AsyncRunStep(bool initial_step)
588 MutexAutoLock lock1(m_step_dtime_mutex);
589 dtime = m_step_dtime;
593 // Send blocks to clients
597 if((dtime < 0.001) && !initial_step)
600 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
603 MutexAutoLock lock1(m_step_dtime_mutex);
604 m_step_dtime -= dtime;
610 m_uptime_counter->increment(dtime);
615 Update time of day and overall game time
617 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
620 Send to clients at constant intervals
623 m_time_of_day_send_timer -= dtime;
624 if (m_time_of_day_send_timer < 0.0) {
625 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
626 u16 time = m_env->getTimeOfDay();
627 float time_speed = g_settings->getFloat("time_speed");
628 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
630 m_timeofday_gauge->set(time);
634 MutexAutoLock lock(m_env_mutex);
635 // Figure out and report maximum lag to environment
636 float max_lag = m_env->getMaxLagEstimate();
637 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
639 if(dtime > 0.1 && dtime > max_lag * 2.0)
640 infostream<<"Server: Maximum lag peaked to "<<dtime
644 m_env->reportMaxLagEstimate(max_lag);
650 static const float map_timer_and_unload_dtime = 2.92;
651 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
653 MutexAutoLock lock(m_env_mutex);
654 // Run Map's timers and unload unused data
655 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
656 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
657 std::max(g_settings->getFloat("server_unload_unused_data_timeout"), 0.0f),
662 Listen to the admin chat, if available
665 if (!m_admin_chat->command_queue.empty()) {
666 MutexAutoLock lock(m_env_mutex);
667 while (!m_admin_chat->command_queue.empty()) {
668 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
669 handleChatInterfaceEvent(evt);
673 m_admin_chat->outgoing_queue.push_back(
674 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
681 /* Transform liquids */
682 m_liquid_transform_timer += dtime;
683 if(m_liquid_transform_timer >= m_liquid_transform_every)
685 m_liquid_transform_timer -= m_liquid_transform_every;
687 MutexAutoLock lock(m_env_mutex);
689 ScopeProfiler sp(g_profiler, "Server: liquid transform");
691 std::map<v3s16, MapBlock*> modified_blocks;
692 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
695 Set the modified blocks unsent for all the clients
697 if (!modified_blocks.empty()) {
698 SetBlocksNotSent(modified_blocks);
701 m_clients.step(dtime);
703 // increase/decrease lag gauge gradually
704 if (m_lag_gauge->get() > dtime) {
705 m_lag_gauge->decrement(dtime/100);
707 m_lag_gauge->increment(dtime/100);
711 float &counter = m_step_pending_dyn_media_timer;
713 if (counter >= 5.0f) {
714 stepPendingDynMediaCallbacks(counter);
721 // send masterserver announce
723 float &counter = m_masterserver_timer;
724 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
725 g_settings->getBool("server_announce")) {
726 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
727 ServerList::AA_START,
728 m_bind_addr.getPort(),
729 m_clients.getPlayerNames(),
730 m_uptime_counter->get(),
731 m_env->getGameTime(),
734 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
744 Check added and deleted active objects
747 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
748 MutexAutoLock envlock(m_env_mutex);
751 ClientInterface::AutoLock clientlock(m_clients);
752 const RemoteClientMap &clients = m_clients.getClientList();
753 ScopeProfiler sp(g_profiler, "Server: update objects within range");
755 m_player_gauge->set(clients.size());
756 for (const auto &client_it : clients) {
757 RemoteClient *client = client_it.second;
759 if (client->getState() < CS_DefinitionsSent)
762 // This can happen if the client times out somehow
763 if (!m_env->getPlayer(client->peer_id))
766 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
770 SendActiveObjectRemoveAdd(client, playersao);
774 // Write changes to the mod storage
775 m_mod_storage_save_timer -= dtime;
776 if (m_mod_storage_save_timer <= 0.0f) {
777 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
778 m_mod_storage_database->endSave();
779 m_mod_storage_database->beginSave();
787 MutexAutoLock envlock(m_env_mutex);
788 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
791 // Value = data sent by object
792 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
794 // Get active object messages from environment
795 ActiveObjectMessage aom(0);
796 u32 count_reliable = 0, count_unreliable = 0;
798 if (!m_env->getActiveObjectMessage(&aom))
805 std::vector<ActiveObjectMessage>* message_list = nullptr;
806 auto n = buffered_messages.find(aom.id);
807 if (n == buffered_messages.end()) {
808 message_list = new std::vector<ActiveObjectMessage>;
809 buffered_messages[aom.id] = message_list;
811 message_list = n->second;
813 message_list->push_back(std::move(aom));
816 m_aom_buffer_counter[0]->increment(count_reliable);
817 m_aom_buffer_counter[1]->increment(count_unreliable);
820 ClientInterface::AutoLock clientlock(m_clients);
821 const RemoteClientMap &clients = m_clients.getClientList();
822 // Route data to every client
823 std::string reliable_data, unreliable_data;
824 for (const auto &client_it : clients) {
825 reliable_data.clear();
826 unreliable_data.clear();
827 RemoteClient *client = client_it.second;
828 PlayerSAO *player = getPlayerSAO(client->peer_id);
829 // Go through all objects in message buffer
830 for (const auto &buffered_message : buffered_messages) {
831 // If object does not exist or is not known by client, skip it
832 u16 id = buffered_message.first;
833 ServerActiveObject *sao = m_env->getActiveObject(id);
834 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
837 // Get message list of object
838 std::vector<ActiveObjectMessage>* list = buffered_message.second;
839 // Go through every message
840 for (const ActiveObjectMessage &aom : *list) {
841 // Send position updates to players who do not see the attachment
842 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
843 if (sao->getId() == player->getId())
846 // Do not send position updates for attached players
847 // as long the parent is known to the client
848 ServerActiveObject *parent = sao->getParent();
849 if (parent && client->m_known_objects.find(parent->getId()) !=
850 client->m_known_objects.end())
854 // Add full new data to appropriate buffer
855 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
857 writeU16((u8*) idbuf, aom.id);
860 buffer.append(idbuf, sizeof(idbuf));
861 buffer.append(serializeString16(aom.datastring));
865 reliable_data and unreliable_data are now ready.
868 if (!reliable_data.empty()) {
869 SendActiveObjectMessages(client->peer_id, reliable_data);
872 if (!unreliable_data.empty()) {
873 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
878 // Clear buffered_messages
879 for (auto &buffered_message : buffered_messages) {
880 delete buffered_message.second;
885 Send queued-for-sending map edit events.
888 // We will be accessing the environment
889 MutexAutoLock lock(m_env_mutex);
891 // Single change sending is disabled if queue size is big
892 bool disable_single_change_sending = false;
893 if(m_unsent_map_edit_queue.size() >= 4)
894 disable_single_change_sending = true;
896 const auto event_count = m_unsent_map_edit_queue.size();
897 m_map_edit_event_counter->increment(event_count);
899 // We'll log the amount of each
902 std::unordered_set<v3s16> node_meta_updates;
904 while (!m_unsent_map_edit_queue.empty()) {
905 MapEditEvent* event = m_unsent_map_edit_queue.front();
906 m_unsent_map_edit_queue.pop();
908 // Players far away from the change are stored here.
909 // Instead of sending the changes, MapBlocks are set not sent
911 std::unordered_set<u16> far_players;
913 switch (event->type) {
916 prof.add("MEET_ADDNODE", 1);
917 sendAddNode(event->p, event->n, &far_players,
918 disable_single_change_sending ? 5 : 30,
919 event->type == MEET_ADDNODE);
921 case MEET_REMOVENODE:
922 prof.add("MEET_REMOVENODE", 1);
923 sendRemoveNode(event->p, &far_players,
924 disable_single_change_sending ? 5 : 30);
926 case MEET_BLOCK_NODE_METADATA_CHANGED: {
927 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
928 if (!event->is_private_change) {
929 node_meta_updates.emplace(event->p);
932 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
933 getNodeBlockPos(event->p))) {
934 block->raiseModified(MOD_STATE_WRITE_NEEDED,
935 MOD_REASON_REPORT_META_CHANGE);
940 prof.add("MEET_OTHER", 1);
941 for (const v3s16 &modified_block : event->modified_blocks) {
942 m_clients.markBlockposAsNotSent(modified_block);
946 prof.add("unknown", 1);
947 warningstream << "Server: Unknown MapEditEvent "
948 << ((u32)event->type) << std::endl;
953 Set blocks not sent to far players
955 if (!far_players.empty()) {
956 // Convert list format to that wanted by SetBlocksNotSent
957 std::map<v3s16, MapBlock*> modified_blocks2;
958 for (const v3s16 &modified_block : event->modified_blocks) {
959 modified_blocks2[modified_block] =
960 m_env->getMap().getBlockNoCreateNoEx(modified_block);
963 // Set blocks not sent
964 for (const u16 far_player : far_players) {
965 if (RemoteClient *client = getClient(far_player))
966 client->SetBlocksNotSent(modified_blocks2);
973 if (event_count >= 5) {
974 infostream << "Server: MapEditEvents:" << std::endl;
975 prof.print(infostream);
976 } else if (event_count != 0) {
977 verbosestream << "Server: MapEditEvents:" << std::endl;
978 prof.print(verbosestream);
981 // Send all metadata updates
982 if (!node_meta_updates.empty())
983 sendMetadataChanged(node_meta_updates);
987 Trigger emerge thread
988 Doing this every 2s is left over from old code, unclear if this is still needed.
991 float &counter = m_emergethread_trigger_timer;
993 if (counter <= 0.0f) {
996 m_emerge->startThreads();
1000 // Save map, players and auth stuff
1002 float &counter = m_savemap_timer;
1004 static thread_local const float save_interval =
1005 g_settings->getFloat("server_map_save_interval");
1006 if (counter >= save_interval) {
1008 MutexAutoLock lock(m_env_mutex);
1010 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
1013 if (m_banmanager->isModified()) {
1014 m_banmanager->save();
1017 // Save changed parts of map
1018 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1021 m_env->saveLoadedPlayers();
1023 // Save environment metadata
1028 m_shutdown_state.tick(dtime, this);
1031 void Server::Receive()
1041 In the first iteration *wait* for a packet, afterwards process
1042 all packets that are immediately available (no waiting).
1045 m_con->Receive(&pkt);
1048 if (!m_con->TryReceive(&pkt))
1052 peer_id = pkt.getPeerId();
1053 m_packet_recv_counter->increment();
1055 m_packet_recv_processed_counter->increment();
1056 } catch (const con::InvalidIncomingDataException &e) {
1057 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1058 << e.what() << std::endl;
1059 } catch (const SerializationError &e) {
1060 infostream << "Server::Receive(): SerializationError: what()="
1061 << e.what() << std::endl;
1062 } catch (const ClientStateError &e) {
1063 errorstream << "ProcessData: peer=" << peer_id << " what()="
1064 << e.what() << std::endl;
1065 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1066 } catch (const con::PeerNotFoundException &e) {
1068 } catch (const con::NoIncomingDataException &e) {
1074 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1076 std::string playername;
1077 PlayerSAO *playersao = NULL;
1079 ClientInterface::AutoLock clientlock(m_clients);
1080 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1082 playername = client->getName();
1083 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1087 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1089 // If failed, cancel
1090 if (!playersao || !player) {
1091 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1092 actionstream << "Server: Failed to emerge player \"" << playername
1093 << "\" (player allocated to an another client)" << std::endl;
1094 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1096 errorstream << "Server: " << playername << ": Failed to emerge player"
1098 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1104 Send complete position information
1106 SendMovePlayer(peer_id);
1109 SendPlayerPrivileges(peer_id);
1111 // Send inventory formspec
1112 SendPlayerInventoryFormspec(peer_id);
1115 SendInventory(playersao, false);
1118 SendPlayerHP(playersao, false);
1120 // Send death screen
1121 if (playersao->isDead())
1122 SendDeathscreen(peer_id, false, v3f(0,0,0));
1125 SendPlayerBreath(playersao);
1128 Update player list and print action
1131 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1132 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1133 m_clients.sendToAll(¬ice_pkt);
1136 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1137 const auto &names = m_clients.getPlayerNames();
1139 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1140 for (const std::string &name : names)
1141 actionstream << name << " ";
1142 actionstream << player->getName() << std::endl;
1147 inline void Server::handleCommand(NetworkPacket *pkt)
1149 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1150 (this->*opHandle.handler)(pkt);
1153 void Server::ProcessData(NetworkPacket *pkt)
1155 // Environment is locked first.
1156 MutexAutoLock envlock(m_env_mutex);
1158 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1159 u32 peer_id = pkt->getPeerId();
1162 Address address = getPeerAddress(peer_id);
1163 std::string addr_s = address.serializeString();
1165 // FIXME: Isn't it a bit excessive to check this for every packet?
1166 if (m_banmanager->isIpBanned(addr_s)) {
1167 std::string ban_name = m_banmanager->getBanName(addr_s);
1168 infostream << "Server: A banned client tried to connect from "
1169 << addr_s << "; banned name was " << ban_name << std::endl;
1170 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1171 "Your IP is banned. Banned name was " + ban_name);
1174 } catch (con::PeerNotFoundException &e) {
1176 * no peer for this packet found
1177 * most common reason is peer timeout, e.g. peer didn't
1178 * respond for some time, your server was overloaded or
1181 infostream << "Server::ProcessData(): Canceling: peer "
1182 << peer_id << " not found" << std::endl;
1187 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1189 // Command must be handled into ToServerCommandHandler
1190 if (command >= TOSERVER_NUM_MSG_TYPES) {
1191 infostream << "Server: Ignoring unknown command "
1192 << command << std::endl;
1196 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1201 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1203 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1204 errorstream << "Server::ProcessData(): Cancelling: Peer"
1205 " serialization format invalid or not initialized."
1206 " Skipping incoming command=" << command << std::endl;
1210 /* Handle commands related to client startup */
1211 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1216 if (m_clients.getClientState(peer_id) < CS_Active) {
1217 if (command == TOSERVER_PLAYERPOS) return;
1219 errorstream << "Got packet command: " << command << " for peer id "
1220 << peer_id << " but client isn't active yet. Dropping packet "
1226 } catch (SendFailedException &e) {
1227 errorstream << "Server::ProcessData(): SendFailedException: "
1228 << "what=" << e.what()
1230 } catch (PacketError &e) {
1231 actionstream << "Server::ProcessData(): PacketError: "
1232 << "what=" << e.what()
1237 void Server::setTimeOfDay(u32 time)
1239 m_env->setTimeOfDay(time);
1240 m_time_of_day_send_timer = 0;
1243 void Server::onMapEditEvent(const MapEditEvent &event)
1245 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1248 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1251 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1253 std::vector<session_t> clients = m_clients.getClientIDs();
1254 ClientInterface::AutoLock clientlock(m_clients);
1255 // Set the modified blocks unsent for all the clients
1256 for (const session_t client_id : clients) {
1257 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1258 client->SetBlocksNotSent(block);
1262 void Server::peerAdded(con::Peer *peer)
1264 verbosestream<<"Server::peerAdded(): peer->id="
1265 <<peer->id<<std::endl;
1267 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1270 void Server::deletingPeer(con::Peer *peer, bool timeout)
1272 verbosestream<<"Server::deletingPeer(): peer->id="
1273 <<peer->id<<", timeout="<<timeout<<std::endl;
1275 m_clients.event(peer->id, CSE_Disconnect);
1276 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1279 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1281 *retval = m_con->getPeerStat(peer_id,type);
1282 return *retval != -1;
1285 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1287 ClientInterface::AutoLock clientlock(m_clients);
1288 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1293 ret.state = client->getState();
1294 ret.addr = client->getAddress();
1295 ret.uptime = client->uptime();
1296 ret.ser_vers = client->serialization_version;
1297 ret.prot_vers = client->net_proto_version;
1299 ret.major = client->getMajor();
1300 ret.minor = client->getMinor();
1301 ret.patch = client->getPatch();
1302 ret.vers_string = client->getFullVer();
1304 ret.lang_code = client->getLangCode();
1309 void Server::handlePeerChanges()
1311 while(!m_peer_change_queue.empty())
1313 con::PeerChange c = m_peer_change_queue.front();
1314 m_peer_change_queue.pop();
1316 verbosestream<<"Server: Handling peer change: "
1317 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1322 case con::PEER_ADDED:
1323 m_clients.CreateClient(c.peer_id);
1326 case con::PEER_REMOVED:
1327 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1331 FATAL_ERROR("Invalid peer change event received!");
1337 void Server::printToConsoleOnly(const std::string &text)
1340 m_admin_chat->outgoing_queue.push_back(
1341 new ChatEventChat("", utf8_to_wide(text)));
1343 std::cout << text << std::endl;
1347 void Server::Send(NetworkPacket *pkt)
1349 Send(pkt->getPeerId(), pkt);
1352 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1354 m_clients.send(peer_id,
1355 clientCommandFactoryTable[pkt->getCommand()].channel,
1357 clientCommandFactoryTable[pkt->getCommand()].reliable);
1360 void Server::SendMovement(session_t peer_id)
1362 std::ostringstream os(std::ios_base::binary);
1364 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1366 pkt << g_settings->getFloat("movement_acceleration_default");
1367 pkt << g_settings->getFloat("movement_acceleration_air");
1368 pkt << g_settings->getFloat("movement_acceleration_fast");
1369 pkt << g_settings->getFloat("movement_speed_walk");
1370 pkt << g_settings->getFloat("movement_speed_crouch");
1371 pkt << g_settings->getFloat("movement_speed_fast");
1372 pkt << g_settings->getFloat("movement_speed_climb");
1373 pkt << g_settings->getFloat("movement_speed_jump");
1374 pkt << g_settings->getFloat("movement_liquid_fluidity");
1375 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1376 pkt << g_settings->getFloat("movement_liquid_sink");
1377 pkt << g_settings->getFloat("movement_gravity");
1382 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1384 m_script->player_event(playersao, "health_changed");
1385 SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX);
1387 // Send to other clients
1388 playersao->sendPunchCommand();
1390 if (playersao->isDead())
1391 HandlePlayerDeath(playersao, reason);
1394 void Server::SendPlayerHP(PlayerSAO *playersao, bool effect)
1396 SendHP(playersao->getPeerID(), playersao->getHP(), effect);
1399 void Server::SendHP(session_t peer_id, u16 hp, bool effect)
1401 NetworkPacket pkt(TOCLIENT_HP, 3, peer_id);
1402 pkt << hp << effect;
1406 void Server::SendBreath(session_t peer_id, u16 breath)
1408 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1409 pkt << (u16) breath;
1413 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1414 const std::string &custom_reason, bool reconnect)
1416 assert(reason < SERVER_ACCESSDENIED_MAX);
1418 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1420 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1421 pkt << custom_reason;
1422 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1423 reason == SERVER_ACCESSDENIED_CRASH)
1424 pkt << custom_reason << (u8)reconnect;
1428 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1429 v3f camera_point_target)
1431 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1432 pkt << set_camera_point_target << camera_point_target;
1436 void Server::SendItemDef(session_t peer_id,
1437 IItemDefManager *itemdef, u16 protocol_version)
1439 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1443 u32 length of the next item
1444 zlib-compressed serialized ItemDefManager
1446 std::ostringstream tmp_os(std::ios::binary);
1447 itemdef->serialize(tmp_os, protocol_version);
1448 std::ostringstream tmp_os2(std::ios::binary);
1449 compressZlib(tmp_os.str(), tmp_os2);
1450 pkt.putLongString(tmp_os2.str());
1453 verbosestream << "Server: Sending item definitions to id(" << peer_id
1454 << "): size=" << pkt.getSize() << std::endl;
1459 void Server::SendNodeDef(session_t peer_id,
1460 const NodeDefManager *nodedef, u16 protocol_version)
1462 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1466 u32 length of the next item
1467 zlib-compressed serialized NodeDefManager
1469 std::ostringstream tmp_os(std::ios::binary);
1470 nodedef->serialize(tmp_os, protocol_version);
1471 std::ostringstream tmp_os2(std::ios::binary);
1472 compressZlib(tmp_os.str(), tmp_os2);
1474 pkt.putLongString(tmp_os2.str());
1477 verbosestream << "Server: Sending node definitions to id(" << peer_id
1478 << "): size=" << pkt.getSize() << std::endl;
1484 Non-static send methods
1487 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1489 RemotePlayer *player = sao->getPlayer();
1491 // Do not send new format to old clients
1492 incremental &= player->protocol_version >= 38;
1494 UpdateCrafting(player);
1500 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1502 std::ostringstream os(std::ios::binary);
1503 sao->getInventory()->serialize(os, incremental);
1504 sao->getInventory()->setModified(false);
1505 player->setModified(true);
1507 const std::string &s = os.str();
1508 pkt.putRawString(s.c_str(), s.size());
1512 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1514 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1516 u8 type = message.type;
1517 pkt << version << type << message.sender << message.message
1518 << static_cast<u64>(message.timestamp);
1520 if (peer_id != PEER_ID_INEXISTENT) {
1521 RemotePlayer *player = m_env->getPlayer(peer_id);
1527 m_clients.sendToAll(&pkt);
1531 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1532 const std::string &formname)
1534 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1535 if (formspec.empty()){
1536 //the client should close the formspec
1537 //but make sure there wasn't another one open in meantime
1538 const auto it = m_formspec_state_data.find(peer_id);
1539 if (it != m_formspec_state_data.end() && it->second == formname) {
1540 m_formspec_state_data.erase(peer_id);
1542 pkt.putLongString("");
1544 m_formspec_state_data[peer_id] = formname;
1545 pkt.putLongString(formspec);
1552 // Spawns a particle on peer with peer_id
1553 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1554 const ParticleParameters &p)
1556 static thread_local const float radius =
1557 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1559 if (peer_id == PEER_ID_INEXISTENT) {
1560 std::vector<session_t> clients = m_clients.getClientIDs();
1561 const v3f pos = p.pos * BS;
1562 const float radius_sq = radius * radius;
1564 for (const session_t client_id : clients) {
1565 RemotePlayer *player = m_env->getPlayer(client_id);
1569 PlayerSAO *sao = player->getPlayerSAO();
1573 // Do not send to distant clients
1574 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1577 SendSpawnParticle(client_id, player->protocol_version, p);
1581 assert(protocol_version != 0);
1583 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1586 // NetworkPacket and iostreams are incompatible...
1587 std::ostringstream oss(std::ios_base::binary);
1588 p.serialize(oss, protocol_version);
1589 pkt.putRawString(oss.str());
1595 // Adds a ParticleSpawner on peer with peer_id
1596 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1597 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1599 static thread_local const float radius =
1600 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1602 if (peer_id == PEER_ID_INEXISTENT) {
1603 std::vector<session_t> clients = m_clients.getClientIDs();
1605 p.pos.start.min.val +
1606 p.pos.start.max.val +
1610 const float radius_sq = radius * radius;
1611 /* Don't send short-lived spawners to distant players.
1612 * This could be replaced with proper tracking at some point. */
1613 const bool distance_check = !attached_id && p.time <= 1.0f;
1615 for (const session_t client_id : clients) {
1616 RemotePlayer *player = m_env->getPlayer(client_id);
1620 if (distance_check) {
1621 PlayerSAO *sao = player->getPlayerSAO();
1624 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1628 SendAddParticleSpawner(client_id, player->protocol_version,
1629 p, attached_id, id);
1633 assert(protocol_version != 0);
1635 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1637 pkt << p.amount << p.time;
1638 { // serialize legacy fields
1639 std::ostringstream os(std::ios_base::binary);
1640 p.pos.start.legacySerialize(os);
1641 p.vel.start.legacySerialize(os);
1642 p.acc.start.legacySerialize(os);
1643 p.exptime.start.legacySerialize(os);
1644 p.size.start.legacySerialize(os);
1645 pkt.putRawString(os.str());
1647 pkt << p.collisiondetection;
1649 pkt.putLongString(p.texture.string);
1651 pkt << id << p.vertical << p.collision_removal << attached_id;
1653 std::ostringstream os(std::ios_base::binary);
1654 p.animation.serialize(os, protocol_version);
1655 pkt.putRawString(os.str());
1657 pkt << p.glow << p.object_collision;
1658 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1660 { // serialize new fields
1661 // initial bias for older properties
1662 pkt << p.pos.start.bias
1665 << p.exptime.start.bias
1666 << p.size.start.bias;
1668 std::ostringstream os(std::ios_base::binary);
1670 // final tween frames of older properties
1671 p.pos.end.serialize(os);
1672 p.vel.end.serialize(os);
1673 p.acc.end.serialize(os);
1674 p.exptime.end.serialize(os);
1675 p.size.end.serialize(os);
1677 // properties for legacy texture field
1678 p.texture.serialize(os, protocol_version, true);
1681 p.drag.serialize(os);
1682 p.jitter.serialize(os);
1683 p.bounce.serialize(os);
1684 ParticleParamTypes::serializeParameterValue(os, p.attractor_kind);
1685 if (p.attractor_kind != ParticleParamTypes::AttractorKind::none) {
1686 p.attract.serialize(os);
1687 p.attractor_origin.serialize(os);
1688 writeU16(os, p.attractor_attachment); /* object ID */
1689 writeU8(os, p.attractor_kill);
1690 if (p.attractor_kind != ParticleParamTypes::AttractorKind::point) {
1691 p.attractor_direction.serialize(os);
1692 writeU16(os, p.attractor_direction_attachment);
1695 p.radius.serialize(os);
1697 ParticleParamTypes::serializeParameterValue(os, (u16)p.texpool.size());
1698 for (const auto& tex : p.texpool) {
1699 tex.serialize(os, protocol_version);
1702 pkt.putRawString(os.str());
1708 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1710 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1714 if (peer_id != PEER_ID_INEXISTENT)
1717 m_clients.sendToAll(&pkt);
1721 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1723 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1725 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1726 << form->text << form->number << form->item << form->dir
1727 << form->align << form->offset << form->world_pos << form->size
1728 << form->z_index << form->text2 << form->style;
1733 void Server::SendHUDRemove(session_t peer_id, u32 id)
1735 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1740 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1742 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1743 pkt << id << (u8) stat;
1747 case HUD_STAT_SCALE:
1748 case HUD_STAT_ALIGN:
1749 case HUD_STAT_OFFSET:
1750 pkt << *(v2f *) value;
1754 case HUD_STAT_TEXT2:
1755 pkt << *(std::string *) value;
1757 case HUD_STAT_WORLD_POS:
1758 pkt << *(v3f *) value;
1761 pkt << *(v2s32 *) value;
1763 default: // all other types
1764 pkt << *(u32 *) value;
1771 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1773 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1775 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1777 pkt << flags << mask;
1782 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1784 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1785 pkt << param << value;
1789 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1791 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1793 // Handle prior clients here
1794 if (m_clients.getProtocolVersion(peer_id) < 39) {
1795 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1797 for (const std::string& texture : params.textures)
1800 pkt << params.clouds;
1801 } else { // Handle current clients and future clients
1802 pkt << params.bgcolor << params.type
1803 << params.clouds << params.fog_sun_tint
1804 << params.fog_moon_tint << params.fog_tint_type;
1806 if (params.type == "skybox") {
1807 pkt << (u16) params.textures.size();
1808 for (const std::string &texture : params.textures)
1810 } else if (params.type == "regular") {
1811 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1812 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1813 << params.sky_color.night_sky << params.sky_color.night_horizon
1814 << params.sky_color.indoors;
1821 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1823 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1824 pkt << params.visible << params.texture
1825 << params.tonemap << params.sunrise
1826 << params.sunrise_visible << params.scale;
1830 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1832 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1834 pkt << params.visible << params.texture
1835 << params.tonemap << params.scale;
1839 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1841 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1843 pkt << params.visible << params.count
1844 << params.starcolor << params.scale
1845 << params.day_opacity;
1850 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1852 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1853 pkt << params.density << params.color_bright << params.color_ambient
1854 << params.height << params.thickness << params.speed;
1858 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1861 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1864 pkt << do_override << (u16) (ratio * 65535);
1869 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1871 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1874 pkt << lighting.shadow_intensity;
1879 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1881 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1882 pkt << time << time_speed;
1884 if (peer_id == PEER_ID_INEXISTENT) {
1885 m_clients.sendToAll(&pkt);
1892 void Server::SendPlayerBreath(PlayerSAO *sao)
1896 m_script->player_event(sao, "breath_changed");
1897 SendBreath(sao->getPeerID(), sao->getBreath());
1900 void Server::SendMovePlayer(session_t peer_id)
1902 RemotePlayer *player = m_env->getPlayer(peer_id);
1904 PlayerSAO *sao = player->getPlayerSAO();
1907 // Send attachment updates instantly to the client prior updating position
1908 sao->sendOutdatedData();
1910 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1911 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1914 v3f pos = sao->getBasePosition();
1915 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1916 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1917 << " pitch=" << sao->getLookPitch()
1918 << " yaw=" << sao->getRotation().Y
1925 void Server::SendPlayerFov(session_t peer_id)
1927 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1929 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1930 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1935 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1936 f32 animation_speed)
1938 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1941 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1942 << animation_frames[3] << animation_speed;
1947 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1949 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1950 pkt << first << third;
1954 void Server::SendPlayerPrivileges(session_t peer_id)
1956 RemotePlayer *player = m_env->getPlayer(peer_id);
1958 if(player->getPeerId() == PEER_ID_INEXISTENT)
1961 std::set<std::string> privs;
1962 m_script->getAuth(player->getName(), NULL, &privs);
1964 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1965 pkt << (u16) privs.size();
1967 for (const std::string &priv : privs) {
1974 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1976 RemotePlayer *player = m_env->getPlayer(peer_id);
1978 if (player->getPeerId() == PEER_ID_INEXISTENT)
1981 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1982 pkt.putLongString(player->inventory_formspec);
1987 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1989 RemotePlayer *player = m_env->getPlayer(peer_id);
1991 if (player->getPeerId() == PEER_ID_INEXISTENT)
1994 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1995 pkt << player->formspec_prepend;
1999 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
2001 // Radius inside which objects are active
2002 static thread_local const s16 radius =
2003 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
2005 // Radius inside which players are active
2006 static thread_local const bool is_transfer_limited =
2007 g_settings->exists("unlimited_player_transfer_distance") &&
2008 !g_settings->getBool("unlimited_player_transfer_distance");
2010 static thread_local const s16 player_transfer_dist =
2011 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
2013 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
2014 radius : player_transfer_dist;
2016 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
2020 std::queue<u16> removed_objects, added_objects;
2021 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
2022 client->m_known_objects, removed_objects);
2023 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
2024 client->m_known_objects, added_objects);
2026 int removed_count = removed_objects.size();
2027 int added_count = added_objects.size();
2029 if (removed_objects.empty() && added_objects.empty())
2035 // Handle removed objects
2036 writeU16((u8*)buf, removed_objects.size());
2037 data.append(buf, 2);
2038 while (!removed_objects.empty()) {
2040 u16 id = removed_objects.front();
2041 ServerActiveObject* obj = m_env->getActiveObject(id);
2043 // Add to data buffer for sending
2044 writeU16((u8*)buf, id);
2045 data.append(buf, 2);
2047 // Remove from known objects
2048 client->m_known_objects.erase(id);
2050 if (obj && obj->m_known_by_count > 0)
2051 obj->m_known_by_count--;
2053 removed_objects.pop();
2056 // Handle added objects
2057 writeU16((u8*)buf, added_objects.size());
2058 data.append(buf, 2);
2059 while (!added_objects.empty()) {
2061 u16 id = added_objects.front();
2062 ServerActiveObject *obj = m_env->getActiveObject(id);
2063 added_objects.pop();
2066 warningstream << FUNCTION_NAME << ": NULL object id="
2067 << (int)id << std::endl;
2072 u8 type = obj->getSendType();
2074 // Add to data buffer for sending
2075 writeU16((u8*)buf, id);
2076 data.append(buf, 2);
2077 writeU8((u8*)buf, type);
2078 data.append(buf, 1);
2080 data.append(serializeString32(
2081 obj->getClientInitializationData(client->net_proto_version)));
2083 // Add to known objects
2084 client->m_known_objects.insert(id);
2086 obj->m_known_by_count++;
2089 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2090 pkt.putRawString(data.c_str(), data.size());
2093 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2094 << removed_count << " removed, " << added_count << " added, "
2095 << "packet size is " << pkt.getSize() << std::endl;
2098 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2101 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2102 datas.size(), peer_id);
2104 pkt.putRawString(datas.c_str(), datas.size());
2106 m_clients.send(pkt.getPeerId(),
2107 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2111 void Server::SendCSMRestrictionFlags(session_t peer_id)
2113 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2114 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2115 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2119 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2121 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2126 inline s32 Server::nextSoundId()
2128 s32 ret = m_next_sound_id;
2129 if (m_next_sound_id == INT32_MAX)
2130 m_next_sound_id = 0; // signed overflow is undefined
2136 s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
2138 // Find out initial position of sound
2139 bool pos_exists = false;
2140 const v3f pos = params.getPos(m_env, &pos_exists);
2141 // If position is not found while it should be, cancel sound
2142 if(pos_exists != (params.type != SoundLocation::Local))
2145 // Filter destination clients
2146 std::vector<session_t> dst_clients;
2147 if (!params.to_player.empty()) {
2148 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2150 infostream<<"Server::playSound: Player \""<<params.to_player
2151 <<"\" not found"<<std::endl;
2154 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2155 infostream<<"Server::playSound: Player \""<<params.to_player
2156 <<"\" not connected"<<std::endl;
2159 dst_clients.push_back(player->getPeerId());
2161 std::vector<session_t> clients = m_clients.getClientIDs();
2163 for (const session_t client_id : clients) {
2164 RemotePlayer *player = m_env->getPlayer(client_id);
2167 if (!params.exclude_player.empty() &&
2168 params.exclude_player == player->getName())
2171 PlayerSAO *sao = player->getPlayerSAO();
2176 if(sao->getBasePosition().getDistanceFrom(pos) >
2177 params.max_hear_distance)
2180 dst_clients.push_back(client_id);
2184 if(dst_clients.empty())
2187 // old clients will still use this, so pick a reserved ID (-1)
2188 const s32 id = ephemeral ? -1 : nextSoundId();
2190 float gain = params.gain * params.spec.gain;
2191 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2192 pkt << id << params.spec.name << gain
2193 << (u8) params.type << pos << params.object
2194 << params.spec.loop << params.spec.fade << params.spec.pitch
2197 bool as_reliable = !ephemeral;
2199 for (const session_t peer_id : dst_clients) {
2201 params.clients.insert(peer_id);
2202 m_clients.send(peer_id, 0, &pkt, as_reliable);
2206 m_playing_sounds[id] = std::move(params);
2209 void Server::stopSound(s32 handle)
2211 auto it = m_playing_sounds.find(handle);
2212 if (it == m_playing_sounds.end())
2215 ServerPlayingSound &psound = it->second;
2217 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2220 for (session_t peer_id : psound.clients) {
2222 m_clients.send(peer_id, 0, &pkt, true);
2225 // Remove sound reference
2226 m_playing_sounds.erase(it);
2229 void Server::fadeSound(s32 handle, float step, float gain)
2231 auto it = m_playing_sounds.find(handle);
2232 if (it == m_playing_sounds.end())
2235 ServerPlayingSound &psound = it->second;
2236 psound.gain = gain; // destination gain
2238 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2239 pkt << handle << step << gain;
2241 for (session_t peer_id : psound.clients) {
2243 m_clients.send(peer_id, 0, &pkt, true);
2246 // Remove sound reference
2247 if (gain <= 0 || psound.clients.empty())
2248 m_playing_sounds.erase(it);
2251 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2254 float maxd = far_d_nodes * BS;
2255 v3f p_f = intToFloat(p, BS);
2256 v3s16 block_pos = getNodeBlockPos(p);
2258 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2261 std::vector<session_t> clients = m_clients.getClientIDs();
2262 ClientInterface::AutoLock clientlock(m_clients);
2264 for (session_t client_id : clients) {
2265 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2269 RemotePlayer *player = m_env->getPlayer(client_id);
2270 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2272 // If player is far away, only set modified blocks not sent
2273 if (!client->isBlockSent(block_pos) || (sao &&
2274 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2276 far_players->emplace(client_id);
2278 client->SetBlockNotSent(block_pos);
2283 m_clients.send(client_id, 0, &pkt, true);
2287 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2288 float far_d_nodes, bool remove_metadata)
2290 float maxd = far_d_nodes * BS;
2291 v3f p_f = intToFloat(p, BS);
2292 v3s16 block_pos = getNodeBlockPos(p);
2294 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2295 pkt << p << n.param0 << n.param1 << n.param2
2296 << (u8) (remove_metadata ? 0 : 1);
2298 std::vector<session_t> clients = m_clients.getClientIDs();
2299 ClientInterface::AutoLock clientlock(m_clients);
2301 for (session_t client_id : clients) {
2302 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2306 RemotePlayer *player = m_env->getPlayer(client_id);
2307 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2309 // If player is far away, only set modified blocks not sent
2310 if (!client->isBlockSent(block_pos) || (sao &&
2311 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2313 far_players->emplace(client_id);
2315 client->SetBlockNotSent(block_pos);
2320 m_clients.send(client_id, 0, &pkt, true);
2324 void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
2326 NodeMetadataList meta_updates_list(false);
2327 std::ostringstream os(std::ios::binary);
2329 std::vector<session_t> clients = m_clients.getClientIDs();
2330 ClientInterface::AutoLock clientlock(m_clients);
2332 for (session_t i : clients) {
2333 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2337 ServerActiveObject *player = getPlayerSAO(i);
2340 player_pos = floatToInt(player->getBasePosition(), BS);
2342 for (const v3s16 pos : positions) {
2343 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2348 v3s16 block_pos = getNodeBlockPos(pos);
2349 if (!client->isBlockSent(block_pos) ||
2350 player_pos.getDistanceFrom(pos) > far_d_nodes) {
2351 client->SetBlockNotSent(block_pos);
2355 // Add the change to send list
2356 meta_updates_list.set(pos, meta);
2358 if (meta_updates_list.size() == 0)
2361 // Send the meta changes
2363 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2364 std::string raw = os.str();
2366 compressZlib(raw, os);
2368 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
2369 pkt.putLongString(os.str());
2372 meta_updates_list.clear();
2376 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2377 u16 net_proto_version, SerializedBlockCache *cache)
2379 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2380 std::string s, *sptr = nullptr;
2383 auto it = cache->find({block->getPos(), ver});
2384 if (it != cache->end())
2388 // Serialize the block in the right format
2390 std::ostringstream os(std::ios_base::binary);
2391 block->serialize(os, ver, false, net_compression_level);
2392 block->serializeNetworkSpecific(os);
2397 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2398 pkt << block->getPos();
2399 pkt.putRawString(*sptr);
2402 // Store away in cache
2403 if (cache && sptr == &s)
2404 (*cache)[{block->getPos(), ver}] = std::move(s);
2407 void Server::SendBlocks(float dtime)
2409 MutexAutoLock envlock(m_env_mutex);
2410 //TODO check if one big lock could be faster then multiple small ones
2412 std::vector<PrioritySortedBlockTransfer> queue;
2414 u32 total_sending = 0, unique_clients = 0;
2417 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2419 std::vector<session_t> clients = m_clients.getClientIDs();
2421 ClientInterface::AutoLock clientlock(m_clients);
2422 for (const session_t client_id : clients) {
2423 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2428 total_sending += client->getSendingCount();
2429 const auto old_count = queue.size();
2430 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2431 unique_clients += queue.size() > old_count ? 1 : 0;
2436 // Lowest priority number comes first.
2437 // Lowest is most important.
2438 std::sort(queue.begin(), queue.end());
2440 ClientInterface::AutoLock clientlock(m_clients);
2442 // Maximal total count calculation
2443 // The per-client block sends is halved with the maximal online users
2444 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2445 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2447 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2448 Map &map = m_env->getMap();
2450 SerializedBlockCache cache, *cache_ptr = nullptr;
2451 if (unique_clients > 1) {
2452 // caching is pointless with a single client
2456 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2457 if (total_sending >= max_blocks_to_send)
2460 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2464 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2469 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2470 client->net_proto_version, cache_ptr);
2472 client->SentBlock(block_to_send.pos);
2477 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2479 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2483 ClientInterface::AutoLock clientlock(m_clients);
2484 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2485 if (!client || client->isBlockSent(blockpos))
2487 SendBlockNoLock(peer_id, block, client->serialization_version,
2488 client->net_proto_version);
2493 bool Server::addMediaFile(const std::string &filename,
2494 const std::string &filepath, std::string *filedata_to,
2495 std::string *digest_to)
2497 // If name contains illegal characters, ignore the file
2498 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2499 infostream << "Server: ignoring illegal file name: \""
2500 << filename << "\"" << std::endl;
2503 // If name is not in a supported format, ignore it
2504 const char *supported_ext[] = {
2505 ".png", ".jpg", ".bmp", ".tga",
2507 ".x", ".b3d", ".obj",
2508 // Custom translation file format
2512 if (removeStringEnd(filename, supported_ext).empty()) {
2513 infostream << "Server: ignoring unsupported file extension: \""
2514 << filename << "\"" << std::endl;
2517 // Ok, attempt to load the file and add to cache
2520 std::string filedata;
2521 if (!fs::ReadFile(filepath, filedata)) {
2522 errorstream << "Server::addMediaFile(): Failed to open \""
2523 << filename << "\" for reading" << std::endl;
2527 if (filedata.empty()) {
2528 errorstream << "Server::addMediaFile(): Empty file \""
2529 << filepath << "\"" << std::endl;
2534 sha1.addBytes(filedata.c_str(), filedata.length());
2536 unsigned char *digest = sha1.getDigest();
2537 std::string sha1_base64 = base64_encode(digest, 20);
2538 std::string sha1_hex = hex_encode((char*) digest, 20);
2540 *digest_to = std::string((char*) digest, 20);
2544 m_media[filename] = MediaInfo(filepath, sha1_base64);
2545 verbosestream << "Server: " << sha1_hex << " is " << filename
2549 *filedata_to = std::move(filedata);
2553 void Server::fillMediaCache()
2555 infostream << "Server: Calculating media file checksums" << std::endl;
2557 // Collect all media file paths
2558 std::vector<std::string> paths;
2560 // ordered in descending priority
2561 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2562 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2563 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2564 m_modmgr->getModsMediaPaths(paths);
2566 // Collect media file information from paths into cache
2567 for (const std::string &mediapath : paths) {
2568 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2569 for (const fs::DirListNode &dln : dirlist) {
2570 if (dln.dir) // Ignore dirs (already in paths)
2573 const std::string &filename = dln.name;
2574 if (m_media.find(filename) != m_media.end()) // Do not override
2577 std::string filepath = mediapath;
2578 filepath.append(DIR_DELIM).append(filename);
2579 addMediaFile(filename, filepath);
2583 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2586 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2589 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2592 std::string lang_suffix;
2593 lang_suffix.append(".").append(lang_code).append(".tr");
2594 for (const auto &i : m_media) {
2595 if (i.second.no_announce)
2597 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2604 for (const auto &i : m_media) {
2605 if (i.second.no_announce)
2607 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2609 pkt << i.first << i.second.sha1_digest;
2612 pkt << g_settings->get("remote_media");
2615 verbosestream << "Server: Announcing files to id(" << peer_id
2616 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2619 struct SendableMedia
2625 SendableMedia(const std::string &name, const std::string &path,
2626 std::string &&data):
2627 name(name), path(path), data(std::move(data))
2631 void Server::sendRequestedMedia(session_t peer_id,
2632 const std::vector<std::string> &tosend)
2634 verbosestream<<"Server::sendRequestedMedia(): "
2635 <<"Sending files to client"<<std::endl;
2639 // Put 5kB in one bunch (this is not accurate)
2640 u32 bytes_per_bunch = 5000;
2642 std::vector< std::vector<SendableMedia> > file_bunches;
2643 file_bunches.emplace_back();
2645 u32 file_size_bunch_total = 0;
2647 for (const std::string &name : tosend) {
2648 if (m_media.find(name) == m_media.end()) {
2649 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2650 <<"unknown file \""<<(name)<<"\""<<std::endl;
2654 const auto &m = m_media[name];
2658 if (!fs::ReadFile(m.path, data)) {
2659 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2660 << name << "\"" << std::endl;
2663 file_size_bunch_total += data.size();
2666 file_bunches.back().emplace_back(name, m.path, std::move(data));
2668 // Start next bunch if got enough data
2669 if(file_size_bunch_total >= bytes_per_bunch) {
2670 file_bunches.emplace_back();
2671 file_size_bunch_total = 0;
2676 /* Create and send packets */
2678 u16 num_bunches = file_bunches.size();
2679 for (u16 i = 0; i < num_bunches; i++) {
2682 u16 total number of texture bunches
2683 u16 index of this bunch
2684 u32 number of files in this bunch
2693 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2694 pkt << num_bunches << i << (u32) file_bunches[i].size();
2696 for (const SendableMedia &j : file_bunches[i]) {
2698 pkt.putLongString(j.data);
2701 verbosestream << "Server::sendRequestedMedia(): bunch "
2702 << i << "/" << num_bunches
2703 << " files=" << file_bunches[i].size()
2704 << " size=" << pkt.getSize() << std::endl;
2709 void Server::stepPendingDynMediaCallbacks(float dtime)
2711 MutexAutoLock lock(m_env_mutex);
2713 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2714 it->second.expiry_timer -= dtime;
2715 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2722 const auto &name = it->second.filename;
2723 if (!name.empty()) {
2724 assert(m_media.count(name));
2725 // if no_announce isn't set we're definitely deleting the wrong file!
2726 sanity_check(m_media[name].no_announce);
2728 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2729 m_media.erase(name);
2731 getScriptIface()->freeDynamicMediaCallback(it->first);
2732 it = m_pending_dyn_media.erase(it);
2736 void Server::SendMinimapModes(session_t peer_id,
2737 std::vector<MinimapMode> &modes, size_t wanted_mode)
2739 RemotePlayer *player = m_env->getPlayer(peer_id);
2741 if (player->getPeerId() == PEER_ID_INEXISTENT)
2744 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2745 pkt << (u16)modes.size() << (u16)wanted_mode;
2747 for (auto &mode : modes)
2748 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2753 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2755 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2759 pkt << false; // Remove inventory
2761 pkt << true; // Update inventory
2763 // Serialization & NetworkPacket isn't a love story
2764 std::ostringstream os(std::ios_base::binary);
2765 inventory->serialize(os);
2766 inventory->setModified(false);
2768 const std::string &os_str = os.str();
2769 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2770 pkt.putRawString(os_str);
2773 if (peer_id == PEER_ID_INEXISTENT)
2774 m_clients.sendToAll(&pkt);
2779 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2781 // Lookup player name, to filter detached inventories just after
2782 std::string peer_name;
2783 if (peer_id != PEER_ID_INEXISTENT) {
2784 peer_name = getClient(peer_id, CS_Created)->getName();
2787 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2788 sendDetachedInventory(inv, name, peer_id);
2791 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2798 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2800 infostream << "Server::DiePlayer(): Player "
2801 << playersao->getPlayer()->getName()
2802 << " dies" << std::endl;
2804 playersao->clearParentAttachment();
2806 // Trigger scripted stuff
2807 m_script->on_dieplayer(playersao, reason);
2809 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2812 void Server::RespawnPlayer(session_t peer_id)
2814 PlayerSAO *playersao = getPlayerSAO(peer_id);
2817 infostream << "Server::RespawnPlayer(): Player "
2818 << playersao->getPlayer()->getName()
2819 << " respawns" << std::endl;
2821 const auto *prop = playersao->accessObjectProperties();
2822 playersao->setHP(prop->hp_max,
2823 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2824 playersao->setBreath(prop->breath_max);
2826 bool repositioned = m_script->on_respawnplayer(playersao);
2827 if (!repositioned) {
2828 // setPos will send the new position to client
2829 playersao->setPos(findSpawnPos());
2834 void Server::DenySudoAccess(session_t peer_id)
2836 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2841 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2842 const std::string &custom_reason, bool reconnect)
2844 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2845 m_clients.event(peer_id, CSE_SetDenied);
2846 DisconnectPeer(peer_id);
2849 void Server::DisconnectPeer(session_t peer_id)
2851 m_modchannel_mgr->leaveAllChannels(peer_id);
2852 m_con->DisconnectPeer(peer_id);
2855 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2858 RemoteClient* client = getClient(peer_id, CS_Invalid);
2860 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2862 // Right now, the auth mechs don't change between login and sudo mode.
2863 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2864 client->allowed_sudo_mechs = sudo_auth_mechs;
2866 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2867 << g_settings->getFloat("dedicated_server_step")
2871 m_clients.event(peer_id, CSE_AuthAccept);
2873 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2875 // We only support SRP right now
2876 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2878 resp_pkt << sudo_auth_mechs;
2880 m_clients.event(peer_id, CSE_SudoSuccess);
2884 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2886 std::wstring message;
2889 Clear references to playing sounds
2891 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2892 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2893 ServerPlayingSound &psound = i->second;
2894 psound.clients.erase(peer_id);
2895 if (psound.clients.empty())
2896 m_playing_sounds.erase(i++);
2901 // clear formspec info so the next client can't abuse the current state
2902 m_formspec_state_data.erase(peer_id);
2904 RemotePlayer *player = m_env->getPlayer(peer_id);
2906 /* Run scripts and remove from environment */
2908 PlayerSAO *playersao = player->getPlayerSAO();
2911 playersao->clearChildAttachments();
2912 playersao->clearParentAttachment();
2914 // inform connected clients
2915 const std::string &player_name = player->getName();
2916 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2917 // (u16) 1 + std::string represents a vector serialization representation
2918 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2919 m_clients.sendToAll(¬ice);
2921 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2923 playersao->disconnected();
2930 if (player && reason != CDR_DENY) {
2931 std::ostringstream os(std::ios_base::binary);
2932 std::vector<session_t> clients = m_clients.getClientIDs();
2934 for (const session_t client_id : clients) {
2936 RemotePlayer *player = m_env->getPlayer(client_id);
2940 // Get name of player
2941 os << player->getName() << " ";
2944 std::string name = player->getName();
2945 actionstream << name << " "
2946 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2947 << " List of players: " << os.str() << std::endl;
2949 m_admin_chat->outgoing_queue.push_back(
2950 new ChatEventNick(CET_NICK_REMOVE, name));
2954 MutexAutoLock env_lock(m_env_mutex);
2955 m_clients.DeleteClient(peer_id);
2959 // Send leave chat message to all remaining clients
2960 if (!message.empty()) {
2961 SendChatMessage(PEER_ID_INEXISTENT,
2962 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2966 void Server::UpdateCrafting(RemotePlayer *player)
2968 InventoryList *clist = player->inventory.getList("craft");
2969 if (!clist || clist->getSize() == 0)
2972 if (!clist->checkModified())
2975 // Get a preview for crafting
2977 InventoryLocation loc;
2978 loc.setPlayer(player->getName());
2979 std::vector<ItemStack> output_replacements;
2980 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2981 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2984 InventoryList *plist = player->inventory.getList("craftpreview");
2985 if (plist && plist->getSize() >= 1) {
2986 // Put the new preview in
2987 plist->changeItem(0, preview);
2991 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2993 if (evt->type == CET_NICK_ADD) {
2994 // The terminal informed us of its nick choice
2995 m_admin_nick = ((ChatEventNick *)evt)->nick;
2996 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2997 errorstream << "You haven't set up an account." << std::endl
2998 << "Please log in using the client as '"
2999 << m_admin_nick << "' with a secure password." << std::endl
3000 << "Until then, you can't execute admin tasks via the console," << std::endl
3001 << "and everybody can claim the user account instead of you," << std::endl
3002 << "giving them full control over this server." << std::endl;
3005 assert(evt->type == CET_CHAT);
3006 handleAdminChat((ChatEventChat *)evt);
3010 std::wstring Server::handleChat(const std::string &name,
3011 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
3013 // If something goes wrong, this player is to blame
3014 RollbackScopeActor rollback_scope(m_rollback,
3015 std::string("player:") + name);
3017 if (g_settings->getBool("strip_color_codes"))
3018 wmessage = unescape_enriched(wmessage);
3021 switch (player->canSendChatMessage()) {
3022 case RPLAYER_CHATRESULT_FLOODING: {
3023 std::wstringstream ws;
3024 ws << L"You cannot send more messages. You are limited to "
3025 << g_settings->getFloat("chat_message_limit_per_10sec")
3026 << L" messages per 10 seconds.";
3029 case RPLAYER_CHATRESULT_KICK:
3030 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
3031 "You have been kicked due to message flooding.");
3033 case RPLAYER_CHATRESULT_OK:
3036 FATAL_ERROR("Unhandled chat filtering result found.");
3040 if (m_max_chatmessage_length > 0
3041 && wmessage.length() > m_max_chatmessage_length) {
3042 return L"Your message exceed the maximum chat message limit set on the server. "
3043 L"It was refused. Send a shorter message";
3046 auto message = trim(wide_to_utf8(wmessage));
3047 if (message.empty())
3050 if (message.find_first_of("\n\r") != std::wstring::npos) {
3051 return L"Newlines are not permitted in chat messages";
3054 // Run script hook, exit if script ate the chat message
3055 if (m_script->on_chat_message(name, message))
3060 // Whether to send line to the player that sent the message, or to all players
3061 bool broadcast_line = true;
3063 if (check_shout_priv && !checkPriv(name, "shout")) {
3064 line += L"-!- You don't have permission to shout.";
3065 broadcast_line = false;
3068 Workaround for fixing chat on Android. Lua doesn't handle
3069 the Cyrillic alphabet and some characters on older Android devices
3072 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3074 line += utf8_to_wide(m_script->formatChatMessage(name,
3075 wide_to_utf8(wmessage)));
3080 Tell calling method to send the message to sender
3082 if (!broadcast_line)
3086 Send the message to others
3088 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3090 ChatMessage chatmsg(line);
3092 std::vector<session_t> clients = m_clients.getClientIDs();
3093 for (u16 cid : clients)
3094 SendChatMessage(cid, chatmsg);
3099 void Server::handleAdminChat(const ChatEventChat *evt)
3101 std::string name = evt->nick;
3102 std::wstring wmessage = evt->evt_msg;
3104 std::wstring answer = handleChat(name, wmessage);
3106 // If asked to send answer to sender
3107 if (!answer.empty()) {
3108 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3112 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3114 RemoteClient *client = getClientNoEx(peer_id,state_min);
3116 throw ClientNotFoundException("Client not found");
3120 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3122 return m_clients.getClientNoEx(peer_id, state_min);
3125 std::string Server::getPlayerName(session_t peer_id)
3127 RemotePlayer *player = m_env->getPlayer(peer_id);
3129 return "[id="+itos(peer_id)+"]";
3130 return player->getName();
3133 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3135 RemotePlayer *player = m_env->getPlayer(peer_id);
3138 return player->getPlayerSAO();
3141 std::string Server::getStatusString()
3143 std::ostringstream os(std::ios_base::binary);
3146 os << "version: " << g_version_string;
3148 os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3150 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3152 os << " | max lag: " << std::setprecision(3);
3153 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3155 // Information about clients
3157 os << " | clients: ";
3159 std::vector<session_t> clients = m_clients.getClientIDs();
3160 for (session_t client_id : clients) {
3161 RemotePlayer *player = m_env->getPlayer(client_id);
3163 // Get name of player
3164 const char *name = player ? player->getName() : "<unknown>";
3166 // Add name to information string
3175 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3176 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3178 if (!g_settings->get("motd").empty())
3179 os << std::endl << "# Server: " << g_settings->get("motd");
3184 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3186 std::set<std::string> privs;
3187 m_script->getAuth(name, NULL, &privs);
3191 bool Server::checkPriv(const std::string &name, const std::string &priv)
3193 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3194 return (privs.count(priv) != 0);
3197 void Server::reportPrivsModified(const std::string &name)
3200 std::vector<session_t> clients = m_clients.getClientIDs();
3201 for (const session_t client_id : clients) {
3202 RemotePlayer *player = m_env->getPlayer(client_id);
3203 reportPrivsModified(player->getName());
3206 RemotePlayer *player = m_env->getPlayer(name.c_str());
3209 SendPlayerPrivileges(player->getPeerId());
3210 PlayerSAO *sao = player->getPlayerSAO();
3213 sao->updatePrivileges(
3214 getPlayerEffectivePrivs(name),
3219 void Server::reportInventoryFormspecModified(const std::string &name)
3221 RemotePlayer *player = m_env->getPlayer(name.c_str());
3224 SendPlayerInventoryFormspec(player->getPeerId());
3227 void Server::reportFormspecPrependModified(const std::string &name)
3229 RemotePlayer *player = m_env->getPlayer(name.c_str());
3232 SendPlayerFormspecPrepend(player->getPeerId());
3235 void Server::setIpBanned(const std::string &ip, const std::string &name)
3237 m_banmanager->add(ip, name);
3240 void Server::unsetIpBanned(const std::string &ip_or_name)
3242 m_banmanager->remove(ip_or_name);
3245 std::string Server::getBanDescription(const std::string &ip_or_name)
3247 return m_banmanager->getBanDescription(ip_or_name);
3250 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3252 // m_env will be NULL if the server is initializing
3256 if (m_admin_nick == name && !m_admin_nick.empty()) {
3257 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3260 RemotePlayer *player = m_env->getPlayer(name);
3265 if (player->getPeerId() == PEER_ID_INEXISTENT)
3268 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3271 bool Server::showFormspec(const char *playername, const std::string &formspec,
3272 const std::string &formname)
3274 // m_env will be NULL if the server is initializing
3278 RemotePlayer *player = m_env->getPlayer(playername);
3282 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3286 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3291 u32 id = player->addHud(form);
3293 SendHUDAdd(player->getPeerId(), id, form);
3298 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3302 HudElement* todel = player->removeHud(id);
3309 SendHUDRemove(player->getPeerId(), id);
3313 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3318 SendHUDChange(player->getPeerId(), id, stat, data);
3322 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3327 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3328 if (new_hud_flags == player->hud_flags) // no change
3331 SendHUDSetFlags(player->getPeerId(), flags, mask);
3332 player->hud_flags = new_hud_flags;
3334 PlayerSAO* playersao = player->getPlayerSAO();
3339 m_script->player_event(playersao, "hud_changed");
3343 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3348 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3351 player->setHotbarItemcount(hotbar_itemcount);
3352 std::ostringstream os(std::ios::binary);
3353 writeS32(os, hotbar_itemcount);
3354 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3358 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3363 player->setHotbarImage(name);
3364 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3367 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3372 player->setHotbarSelectedImage(name);
3373 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3376 Address Server::getPeerAddress(session_t peer_id)
3378 // Note that this is only set after Init was received in Server::handleCommand_Init
3379 return getClient(peer_id, CS_Invalid)->getAddress();
3382 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3383 v2s32 animation_frames[4], f32 frame_speed)
3385 sanity_check(player);
3386 player->setLocalAnimations(animation_frames, frame_speed);
3387 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3390 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3392 sanity_check(player);
3393 player->eye_offset_first = first;
3394 player->eye_offset_third = third;
3395 SendEyeOffset(player->getPeerId(), first, third);
3398 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3400 sanity_check(player);
3401 player->setSky(params);
3402 SendSetSky(player->getPeerId(), params);
3405 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3407 sanity_check(player);
3408 player->setSun(params);
3409 SendSetSun(player->getPeerId(), params);
3412 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3414 sanity_check(player);
3415 player->setMoon(params);
3416 SendSetMoon(player->getPeerId(), params);
3419 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3421 sanity_check(player);
3422 player->setStars(params);
3423 SendSetStars(player->getPeerId(), params);
3426 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3428 sanity_check(player);
3429 player->setCloudParams(params);
3430 SendCloudParams(player->getPeerId(), params);
3433 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3436 sanity_check(player);
3437 player->overrideDayNightRatio(do_override, ratio);
3438 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3441 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3443 sanity_check(player);
3444 player->setLighting(lighting);
3445 SendSetLighting(player->getPeerId(), lighting);
3448 void Server::notifyPlayers(const std::wstring &msg)
3450 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3453 void Server::spawnParticle(const std::string &playername,
3454 const ParticleParameters &p)
3456 // m_env will be NULL if the server is initializing
3460 session_t peer_id = PEER_ID_INEXISTENT;
3462 if (!playername.empty()) {
3463 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3466 peer_id = player->getPeerId();
3467 proto_ver = player->protocol_version;
3470 SendSpawnParticle(peer_id, proto_ver, p);
3473 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3474 ServerActiveObject *attached, const std::string &playername)
3476 // m_env will be NULL if the server is initializing
3480 session_t peer_id = PEER_ID_INEXISTENT;
3482 if (!playername.empty()) {
3483 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3486 peer_id = player->getPeerId();
3487 proto_ver = player->protocol_version;
3490 u16 attached_id = attached ? attached->getId() : 0;
3493 if (attached_id == 0)
3494 id = m_env->addParticleSpawner(p.time);
3496 id = m_env->addParticleSpawner(p.time, attached_id);
3498 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3502 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3504 // m_env will be NULL if the server is initializing
3506 throw ServerError("Can't delete particle spawners during initialisation!");
3508 session_t peer_id = PEER_ID_INEXISTENT;
3509 if (!playername.empty()) {
3510 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3513 peer_id = player->getPeerId();
3516 m_env->deleteParticleSpawner(id);
3517 SendDeleteParticleSpawner(peer_id, id);
3520 bool Server::dynamicAddMedia(std::string filepath,
3521 const u32 token, const std::string &to_player, bool ephemeral)
3523 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3524 auto it = m_media.find(filename);
3525 if (it != m_media.end()) {
3526 // Allow the same path to be "added" again in certain conditions
3527 if (ephemeral || it->second.path != filepath) {
3528 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3529 << "\" already exists in media cache" << std::endl;
3534 // Load the file and add it to our media cache
3535 std::string filedata, raw_hash;
3536 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3541 // Create a copy of the file and swap out the path, this removes the
3542 // requirement that mods keep the file accessible at the original path.
3543 filepath = fs::CreateTempFile();
3544 bool ok = ([&] () -> bool {
3545 if (filepath.empty())
3547 std::ofstream os(filepath.c_str(), std::ios::binary);
3555 errorstream << "Server: failed to create a copy of media file "
3556 << "\"" << filename << "\"" << std::endl;
3557 m_media.erase(filename);
3560 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3561 << filepath << std::endl;
3563 m_media[filename].path = filepath;
3564 m_media[filename].no_announce = true;
3565 // stepPendingDynMediaCallbacks will clean this up later.
3566 } else if (!to_player.empty()) {
3567 m_media[filename].no_announce = true;
3570 // Push file to existing clients
3571 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3572 pkt << raw_hash << filename << (bool)ephemeral;
3574 NetworkPacket legacy_pkt = pkt;
3576 // Newer clients get asked to fetch the file (asynchronous)
3578 // Older clients have an awful hack that just throws the data at them
3579 legacy_pkt.putLongString(filedata);
3581 std::unordered_set<session_t> delivered, waiting;
3583 ClientInterface::AutoLock clientlock(m_clients);
3584 for (auto &pair : m_clients.getClientList()) {
3585 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3587 If a client is in the DefinitionsSent state it is too late to
3588 transfer the file via sendMediaAnnouncement() but at the same
3589 time the client cannot accept a media push yet.
3590 Short of artificially delaying the joining process there is no
3591 way for the server to resolve this so we (currently) opt not to.
3593 warningstream << "The media \"" << filename << "\" (dynamic) could "
3594 "not be delivered to " << pair.second->getName()
3595 << " due to a race condition." << std::endl;
3598 if (pair.second->getState() < CS_Active)
3601 const auto proto_ver = pair.second->net_proto_version;
3605 const session_t peer_id = pair.second->peer_id;
3606 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3609 if (proto_ver < 40) {
3610 delivered.emplace(peer_id);
3612 The network layer only guarantees ordered delivery inside a channel.
3613 Since the very next packet could be one that uses the media, we have
3614 to push the media over ALL channels to ensure it is processed before
3615 it is used. In practice this means channels 1 and 0.
3617 m_clients.send(peer_id, 1, &legacy_pkt, true);
3618 m_clients.send(peer_id, 0, &legacy_pkt, true);
3620 waiting.emplace(peer_id);
3621 Send(peer_id, &pkt);
3626 // Run callback for players that already had the file delivered (legacy-only)
3627 for (session_t peer_id : delivered) {
3628 if (auto player = m_env->getPlayer(peer_id))
3629 getScriptIface()->on_dynamic_media_added(token, player->getName());
3632 // Save all others in our pending state
3633 auto &state = m_pending_dyn_media[token];
3634 state.waiting_players = std::move(waiting);
3635 // regardless of success throw away the callback after a while
3636 state.expiry_timer = 60.0f;
3638 state.filename = filename;
3643 // actions: time-reversed list
3644 // Return value: success/failure
3645 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3646 std::list<std::string> *log)
3648 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3649 ServerMap *map = (ServerMap*)(&m_env->getMap());
3651 // Fail if no actions to handle
3652 if (actions.empty()) {
3654 log->push_back("Nothing to do.");
3661 for (const RollbackAction &action : actions) {
3663 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3666 std::ostringstream os;
3667 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3668 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3670 log->push_back(os.str());
3672 std::ostringstream os;
3673 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3674 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3676 log->push_back(os.str());
3680 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3681 <<" failed"<<std::endl;
3683 // Call it done if less than half failed
3684 return num_failed <= num_tried/2;
3687 // IGameDef interface
3689 IItemDefManager *Server::getItemDefManager()
3694 const NodeDefManager *Server::getNodeDefManager()
3699 ICraftDefManager *Server::getCraftDefManager()
3704 u16 Server::allocateUnknownNodeId(const std::string &name)
3706 return m_nodedef->allocateDummy(name);
3709 IWritableItemDefManager *Server::getWritableItemDefManager()
3714 NodeDefManager *Server::getWritableNodeDefManager()
3719 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3724 const std::vector<ModSpec> & Server::getMods() const
3726 return m_modmgr->getMods();
3729 const ModSpec *Server::getModSpec(const std::string &modname) const
3731 return m_modmgr->getModSpec(modname);
3734 std::string Server::getBuiltinLuaPath()
3736 return porting::path_share + DIR_DELIM + "builtin";
3739 v3f Server::findSpawnPos()
3741 ServerMap &map = m_env->getServerMap();
3743 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3744 return nodeposf * BS;
3746 bool is_good = false;
3747 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3748 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3750 // Try to find a good place a few times
3751 for (s32 i = 0; i < 4000 && !is_good; i++) {
3752 s32 range = MYMIN(1 + i, range_max);
3753 // We're going to try to throw the player to this position
3754 v2s16 nodepos2d = v2s16(
3755 -range + myrand_range(0, range*2),
3756 -range + myrand_range(0, range*2));
3757 // Get spawn level at point
3758 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3759 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3760 // signify an unsuitable spawn position, or if outside limits.
3761 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3762 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3765 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3766 // Consecutive empty nodes
3769 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3770 // avoid obstructions in already-generated mapblocks.
3771 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3772 // no obstructions, but mapgen decorations are generated after spawn so
3773 // the player may end up inside one.
3774 for (s32 i = 0; i < 8; i++) {
3775 v3s16 blockpos = getNodeBlockPos(nodepos);
3776 map.emergeBlock(blockpos, true);
3777 content_t c = map.getNode(nodepos).getContent();
3779 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3780 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3781 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3783 if (air_count >= 2) {
3784 // Spawn in lower empty node
3786 nodeposf = intToFloat(nodepos, BS);
3787 // Don't spawn the player outside map boundaries
3788 if (objectpos_over_limit(nodeposf))
3789 // Exit this loop, positions above are probably over limit
3792 // Good position found, cause an exit from main loop
3806 // No suitable spawn point found, return fallback 0,0,0
3807 return v3f(0.0f, 0.0f, 0.0f);
3810 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3812 if (delay == 0.0f) {
3813 // No delay, shutdown immediately
3814 m_shutdown_state.is_requested = true;
3815 // only print to the infostream, a chat message saying
3816 // "Server Shutting Down" is sent when the server destructs.
3817 infostream << "*** Immediate Server shutdown requested." << std::endl;
3818 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3819 // Negative delay, cancel shutdown if requested
3820 m_shutdown_state.reset();
3821 std::wstringstream ws;
3823 ws << L"*** Server shutdown canceled.";
3825 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3826 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3827 // m_shutdown_* are already handled, skip.
3829 } else if (delay > 0.0f) {
3830 // Positive delay, tell the clients when the server will shut down
3831 std::wstringstream ws;
3833 ws << L"*** Server shutting down in "
3834 << duration_to_string(myround(delay)).c_str()
3837 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3838 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3841 m_shutdown_state.trigger(delay, msg, reconnect);
3844 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3847 Try to get an existing player
3849 RemotePlayer *player = m_env->getPlayer(name);
3851 // If player is already connected, cancel
3852 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3853 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3858 If player with the wanted peer_id already exists, cancel.
3860 if (m_env->getPlayer(peer_id)) {
3861 infostream<<"emergePlayer(): Player with wrong name but same"
3862 " peer_id already exists"<<std::endl;
3867 player = new RemotePlayer(name, idef());
3870 bool newplayer = false;
3873 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3875 // Complete init with server parts
3876 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3877 player->protocol_version = proto_version;
3881 m_script->on_newplayer(playersao);
3887 bool Server::registerModStorage(ModMetadata *storage)
3889 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3890 errorstream << "Unable to register same mod storage twice. Storage name: "
3891 << storage->getModName() << std::endl;
3895 m_mod_storages[storage->getModName()] = storage;
3899 void Server::unregisterModStorage(const std::string &name)
3901 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3902 if (it != m_mod_storages.end())
3903 m_mod_storages.erase(name);
3906 void dedicated_server_loop(Server &server, bool &kill)
3908 verbosestream<<"dedicated_server_loop()"<<std::endl;
3910 IntervalLimiter m_profiler_interval;
3912 static thread_local const float steplen =
3913 g_settings->getFloat("dedicated_server_step");
3914 static thread_local const float profiler_print_interval =
3915 g_settings->getFloat("profiler_print_interval");
3918 * The dedicated server loop only does time-keeping (in Server::step) and
3919 * provides a way to main.cpp to kill the server externally (bool &kill).
3923 // This is kind of a hack but can be done like this
3924 // because server.step() is very light
3925 sleep_ms((int)(steplen*1000.0));
3926 server.step(steplen);
3928 if (server.isShutdownRequested() || kill)
3934 if (profiler_print_interval != 0) {
3935 if(m_profiler_interval.step(steplen, profiler_print_interval))
3937 infostream<<"Profiler:"<<std::endl;
3938 g_profiler->print(infostream);
3939 g_profiler->clear();
3944 infostream << "Dedicated server quitting" << std::endl;
3946 if (g_settings->getBool("server_announce"))
3947 ServerList::sendAnnounce(ServerList::AA_DELETE,
3948 server.m_bind_addr.getPort());
3957 bool Server::joinModChannel(const std::string &channel)
3959 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3960 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3963 bool Server::leaveModChannel(const std::string &channel)
3965 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3968 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3970 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3973 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3977 ModChannel* Server::getModChannel(const std::string &channel)
3979 return m_modchannel_mgr->getModChannel(channel);
3982 void Server::broadcastModChannelMessage(const std::string &channel,
3983 const std::string &message, session_t from_peer)
3985 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3989 if (message.size() > STRING_MAX_LEN) {
3990 warningstream << "ModChannel message too long, dropping before sending "
3991 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3992 << channel << ")" << std::endl;
3997 if (from_peer != PEER_ID_SERVER) {
3998 sender = getPlayerName(from_peer);
4001 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
4002 2 + channel.size() + 2 + sender.size() + 2 + message.size());
4003 resp_pkt << channel << sender << message;
4004 for (session_t peer_id : peers) {
4006 if (peer_id == from_peer)
4009 Send(peer_id, &resp_pkt);
4012 if (from_peer != PEER_ID_SERVER) {
4013 m_script->on_modchannel_message(channel, sender, message);
4017 Translations *Server::getTranslationLanguage(const std::string &lang_code)
4019 if (lang_code.empty())
4022 auto it = server_translations.find(lang_code);
4023 if (it != server_translations.end())
4024 return &it->second; // Already loaded
4026 // [] will create an entry
4027 auto *translations = &server_translations[lang_code];
4029 std::string suffix = "." + lang_code + ".tr";
4030 for (const auto &i : m_media) {
4031 if (str_ends_with(i.first, suffix)) {
4033 if (fs::ReadFile(i.second.path, data)) {
4034 translations->loadTranslation(data);
4039 return translations;
4042 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
4044 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4046 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4047 throw BaseException("Cannot read world.mt!");
4049 std::string backend = world_mt.exists("mod_storage_backend") ?
4050 world_mt.get("mod_storage_backend") : "files";
4051 if (backend == "files")
4052 warningstream << "/!\\ You are using the old mod storage files backend. "
4053 << "This backend is deprecated and may be removed in a future release /!\\"
4054 << std::endl << "Switching to SQLite3 is advised, "
4055 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4057 return openModStorageDatabase(backend, world_path, world_mt);
4060 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
4061 const std::string &world_path, const Settings &world_mt)
4063 if (backend == "sqlite3")
4064 return new ModMetadataDatabaseSQLite3(world_path);
4066 if (backend == "files")
4067 return new ModMetadataDatabaseFiles(world_path);
4069 if (backend == "dummy")
4070 return new Database_Dummy();
4072 throw BaseException("Mod storage database backend " + backend + " not supported");
4075 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4077 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4079 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4080 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4081 errorstream << "Cannot read world.mt!" << std::endl;
4085 std::string backend = world_mt.exists("mod_storage_backend") ?
4086 world_mt.get("mod_storage_backend") : "files";
4087 if (backend == migrate_to) {
4088 errorstream << "Cannot migrate: new backend is same"
4089 << " as the old one" << std::endl;
4093 ModMetadataDatabase *srcdb = nullptr;
4094 ModMetadataDatabase *dstdb = nullptr;
4096 bool succeeded = false;
4099 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4100 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4104 std::vector<std::string> mod_list;
4105 srcdb->listMods(&mod_list);
4106 for (const std::string &modname : mod_list) {
4108 srcdb->getModEntries(modname, &meta);
4109 for (const auto &pair : meta) {
4110 dstdb->setModEntry(modname, pair.first, pair.second);
4118 actionstream << "Successfully migrated the metadata of "
4119 << mod_list.size() << " mods" << std::endl;
4120 world_mt.set("mod_storage_backend", migrate_to);
4121 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4122 errorstream << "Failed to update world.mt!" << std::endl;
4124 actionstream << "world.mt updated" << std::endl;
4126 } catch (BaseException &e) {
4127 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4133 if (succeeded && backend == "files") {
4135 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4136 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4137 if (!fs::Rename(storage_path, backup_path))
4138 warningstream << "After migration, " << storage_path
4139 << " could not be renamed to " << backup_path << std::endl;