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"
71 #include "database/database-postgresql.h"
73 #include "database/database-files.h"
74 #include "database/database-dummy.h"
75 #include "gameparams.h"
77 class ClientNotFoundException : public BaseException
80 ClientNotFoundException(const char *s):
85 class ServerThread : public Thread
89 ServerThread(Server *server):
100 void *ServerThread::run()
102 BEGIN_DEBUG_EXCEPTION_HANDLER
105 * The real business of the server happens on the ServerThread.
107 * AsyncRunStep() runs an actual server step as soon as enough time has
108 * passed (dedicated_server_loop keeps track of that).
109 * Receive() blocks at least(!) 30ms waiting for a packet (so this loop
110 * doesn't busy wait) and will process any remaining packets.
114 m_server->AsyncRunStep(true);
115 } catch (con::ConnectionBindFailed &e) {
116 m_server->setAsyncFatalError(e.what());
117 } catch (LuaError &e) {
118 m_server->setAsyncFatalError(e);
121 while (!stopRequested()) {
123 m_server->AsyncRunStep();
127 } catch (con::PeerNotFoundException &e) {
128 infostream<<"Server: PeerNotFoundException"<<std::endl;
129 } catch (ClientNotFoundException &e) {
130 } catch (con::ConnectionBindFailed &e) {
131 m_server->setAsyncFatalError(e.what());
132 } catch (LuaError &e) {
133 m_server->setAsyncFatalError(e);
137 END_DEBUG_EXCEPTION_HANDLER
142 v3f ServerPlayingSound::getPos(ServerEnvironment *env, bool *pos_exists) const
148 case SoundLocation::Local:
150 case SoundLocation::Position:
154 case SoundLocation::Object:
158 ServerActiveObject *sao = env->getActiveObject(object);
163 return sao->getBasePosition();
170 void Server::ShutdownState::reset()
174 should_reconnect = false;
175 is_requested = false;
178 void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
182 should_reconnect = reconnect;
185 void Server::ShutdownState::tick(float dtime, Server *server)
191 static const float shutdown_msg_times[] =
193 1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
196 // Automated messages
197 if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
198 for (float t : shutdown_msg_times) {
199 // If shutdown timer matches an automessage, shot it
200 if (m_timer > t && m_timer - dtime < t) {
201 std::wstring periodicMsg = getShutdownTimerMessage();
203 infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
204 server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
211 if (m_timer < 0.0f) {
217 std::wstring Server::ShutdownState::getShutdownTimerMessage() const
219 std::wstringstream ws;
220 ws << L"*** Server shutting down in "
221 << duration_to_string(myround(m_timer)).c_str() << ".";
230 const std::string &path_world,
231 const SubgameSpec &gamespec,
232 bool simple_singleplayer_mode,
235 ChatInterface *iface,
236 std::string *on_shutdown_errmsg
238 m_bind_addr(bind_addr),
239 m_path_world(path_world),
240 m_gamespec(gamespec),
241 m_simple_singleplayer_mode(simple_singleplayer_mode),
242 m_dedicated(dedicated),
243 m_async_fatal_error(""),
244 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
247 m_bind_addr.isIPv6(),
249 m_itemdef(createItemDefManager()),
250 m_nodedef(createNodeDefManager()),
251 m_craftdef(createCraftDefManager()),
252 m_thread(new ServerThread(this)),
255 m_on_shutdown_errmsg(on_shutdown_errmsg),
256 m_modchannel_mgr(new ModChannelMgr())
258 if (m_path_world.empty())
259 throw ServerError("Supplied empty world path");
261 if (!gamespec.isValid())
262 throw ServerError("Supplied invalid gamespec");
265 m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
267 m_metrics_backend = std::make_unique<MetricsBackend>();
270 m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
271 m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
273 m_timeofday_gauge = m_metrics_backend->addGauge(
274 "minetest_core_timeofday",
275 "Time of day value");
277 m_lag_gauge = m_metrics_backend->addGauge(
278 "minetest_core_latency",
279 "Latency value (in seconds)");
282 const std::string aom_types[] = {"reliable", "unreliable"};
283 for (u32 i = 0; i < ARRLEN(aom_types); i++) {
284 std::string help_str("Number of active object messages generated (");
285 help_str.append(aom_types[i]).append(")");
286 m_aom_buffer_counter[i] = m_metrics_backend->addCounter(
287 "minetest_core_aom_generated_count", help_str,
288 {{"type", aom_types[i]}});
291 m_packet_recv_counter = m_metrics_backend->addCounter(
292 "minetest_core_server_packet_recv",
293 "Processable packets received");
295 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
296 "minetest_core_server_packet_recv_processed",
297 "Valid received packets processed");
299 m_map_edit_event_counter = m_metrics_backend->addCounter(
300 "minetest_core_map_edit_events",
301 "Number of map edit events");
303 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
309 // Send shutdown message
310 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
311 L"*** Server shutting down"));
314 MutexAutoLock envlock(m_env_mutex);
316 infostream << "Server: Saving players" << std::endl;
317 m_env->saveLoadedPlayers();
319 infostream << "Server: Kicking players" << std::endl;
320 std::string kick_msg;
321 bool reconnect = false;
322 if (isShutdownRequested()) {
323 reconnect = m_shutdown_state.should_reconnect;
324 kick_msg = m_shutdown_state.message;
326 if (kick_msg.empty()) {
327 kick_msg = g_settings->get("kick_msg_shutdown");
329 m_env->saveLoadedPlayers(true);
330 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
331 kick_msg, reconnect);
334 actionstream << "Server: Shutting down" << std::endl;
336 // Do this before stopping the server in case mapgen callbacks need to access
337 // server-controlled resources (like ModStorages). Also do them before
338 // shutdown callbacks since they may modify state that is finalized in a
341 m_emerge->stopThreads();
344 MutexAutoLock envlock(m_env_mutex);
346 // Execute script shutdown hooks
347 infostream << "Executing shutdown hooks" << std::endl;
349 m_script->on_shutdown();
350 } catch (ModError &e) {
351 errorstream << "ModError: " << e.what() << std::endl;
352 if (m_on_shutdown_errmsg) {
353 if (m_on_shutdown_errmsg->empty()) {
354 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
356 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
361 infostream << "Server: Saving environment metadata" << std::endl;
371 // Write any changes before deletion.
372 if (m_mod_storage_database)
373 m_mod_storage_database->endSave();
375 // Delete things in the reverse order of creation
379 delete m_mod_storage_database;
385 // Deinitialize scripting
386 infostream << "Server: Deinitializing scripting" << std::endl;
388 delete m_startup_server_map; // if available
389 delete m_game_settings;
391 while (!m_unsent_map_edit_queue.empty()) {
392 delete m_unsent_map_edit_queue.front();
393 m_unsent_map_edit_queue.pop();
399 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
400 if (m_simple_singleplayer_mode)
401 infostream << " in simple singleplayer mode" << std::endl;
403 infostream << std::endl;
404 infostream << "- world: " << m_path_world << std::endl;
405 infostream << "- game: " << m_gamespec.path << std::endl;
407 m_game_settings = Settings::createLayer(SL_GAME);
409 // Create world if it doesn't exist
411 loadGameConfAndInitWorld(m_path_world,
412 fs::GetFilenameFromPath(m_path_world.c_str()),
414 } catch (const BaseException &e) {
415 throw ServerError(std::string("Failed to initialize world: ") + e.what());
418 // Create emerge manager
419 m_emerge = new EmergeManager(this, m_metrics_backend.get());
421 // Create ban manager
422 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
423 m_banmanager = new BanManager(ban_path);
425 // Create mod storage database and begin a save for later
426 m_mod_storage_database = openModStorageDatabase(m_path_world);
427 m_mod_storage_database->beginSave();
429 m_modmgr = std::make_unique<ServerModManager>(m_path_world);
431 // complain about mods with unsatisfied dependencies
432 if (!m_modmgr->isConsistent()) {
433 std::string error = m_modmgr->getUnsatisfiedModsError();
434 throw ServerError(error);
438 MutexAutoLock envlock(m_env_mutex);
440 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
441 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
442 m_startup_server_map = servermap;
444 // Initialize scripting
445 infostream << "Server: Initializing Lua" << std::endl;
447 m_script = new ServerScripting(this);
449 // Must be created before mod loading because we have some inventory creation
450 m_inventory_mgr = std::make_unique<ServerInventoryManager>();
452 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
453 m_script->checkSetByBuiltin();
455 m_gamespec.checkAndLog();
456 m_modmgr->loadMods(m_script);
458 // Read Textures and calculate sha1 sums
461 // Apply item aliases in the node definition manager
462 m_nodedef->updateAliases(m_itemdef);
464 // Apply texture overrides from texturepack/override.txt
465 std::vector<std::string> paths;
466 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
467 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
468 for (const std::string &path : paths) {
469 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
470 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
471 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
474 m_nodedef->setNodeRegistrationStatus(true);
476 // Perform pending node name resolutions
477 m_nodedef->runNodeResolveCallbacks();
479 // unmap node names in cross-references
480 m_nodedef->resolveCrossrefs();
482 // init the recipe hashes to speed up crafting
483 m_craftdef->initHashes(this);
485 // Initialize Environment
486 m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
487 m_env = new ServerEnvironment(servermap, m_script, this,
488 m_path_world, m_metrics_backend.get());
491 m_inventory_mgr->setEnv(m_env);
492 m_clients.setEnv(m_env);
494 if (!servermap->settings_mgr.makeMapgenParams())
495 FATAL_ERROR("Couldn't create any mapgen type");
497 // Initialize mapgens
498 m_emerge->initMapgens(servermap->getMapgenParams());
500 if (g_settings->getBool("enable_rollback_recording")) {
501 // Create rollback manager
502 m_rollback = new RollbackManager(m_path_world, this);
505 // Give environment reference to scripting api
506 m_script->initializeEnvironment(m_env);
508 // Do this after regular script init is done
509 m_script->initAsync();
511 // Register us to receive map edit events
512 servermap->addEventReceiver(this);
516 // Those settings can be overwritten in world.mt, they are
517 // intended to be cached after environment loading.
518 m_liquid_transform_every = g_settings->getFloat("liquid_update");
519 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
520 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
521 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
528 infostream << "Starting server on " << m_bind_addr.serializeString()
529 << "..." << std::endl;
531 // Stop thread if already running
534 // Initialize connection
535 m_con->SetTimeoutMs(30);
536 m_con->Serve(m_bind_addr);
541 // ASCII art for the win!
543 << " __. __. __. " << std::endl
544 << " _____ |__| ____ _____ / |_ _____ _____ / |_ " << std::endl
545 << " / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\" << std::endl
546 << "| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | " << std::endl
547 << "|__|_| / |___| /\\______> | \\______>_____/| | " << std::endl
548 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
549 actionstream << "World at [" << m_path_world << "]" << std::endl;
550 actionstream << "Server for gameid=\"" << m_gamespec.id
551 << "\" listening on ";
552 m_bind_addr.print(actionstream);
553 actionstream << "." << std::endl;
558 infostream<<"Server: Stopping and waiting threads"<<std::endl;
560 // Stop threads (set run=false first so both start stopping)
564 infostream<<"Server: Threads stopped"<<std::endl;
567 void Server::step(float dtime)
573 MutexAutoLock lock(m_step_dtime_mutex);
574 m_step_dtime += dtime;
576 // Throw if fatal error occurred in thread
577 std::string async_err = m_async_fatal_error.get();
578 if (!async_err.empty()) {
579 if (!m_simple_singleplayer_mode) {
580 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
581 g_settings->get("kick_msg_crash"),
582 g_settings->getBool("ask_reconnect_on_crash"));
584 throw ServerError("AsyncErr: " + async_err);
588 void Server::AsyncRunStep(bool initial_step)
593 MutexAutoLock lock1(m_step_dtime_mutex);
594 dtime = m_step_dtime;
598 // Send blocks to clients
602 if((dtime < 0.001) && !initial_step)
605 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
608 MutexAutoLock lock1(m_step_dtime_mutex);
609 m_step_dtime -= dtime;
615 m_uptime_counter->increment(dtime);
620 Update time of day and overall game time
622 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
625 Send to clients at constant intervals
628 m_time_of_day_send_timer -= dtime;
629 if (m_time_of_day_send_timer < 0.0) {
630 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
631 u16 time = m_env->getTimeOfDay();
632 float time_speed = g_settings->getFloat("time_speed");
633 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
635 m_timeofday_gauge->set(time);
639 MutexAutoLock lock(m_env_mutex);
640 // Figure out and report maximum lag to environment
641 float max_lag = m_env->getMaxLagEstimate();
642 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
644 if(dtime > 0.1 && dtime > max_lag * 2.0)
645 infostream<<"Server: Maximum lag peaked to "<<dtime
649 m_env->reportMaxLagEstimate(max_lag);
655 static const float map_timer_and_unload_dtime = 2.92;
656 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
658 MutexAutoLock lock(m_env_mutex);
659 // Run Map's timers and unload unused data
660 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
661 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
662 std::max(g_settings->getFloat("server_unload_unused_data_timeout"), 0.0f),
667 Listen to the admin chat, if available
670 if (!m_admin_chat->command_queue.empty()) {
671 MutexAutoLock lock(m_env_mutex);
672 while (!m_admin_chat->command_queue.empty()) {
673 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
674 handleChatInterfaceEvent(evt);
678 m_admin_chat->outgoing_queue.push_back(
679 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
686 /* Transform liquids */
687 m_liquid_transform_timer += dtime;
688 if(m_liquid_transform_timer >= m_liquid_transform_every)
690 m_liquid_transform_timer -= m_liquid_transform_every;
692 MutexAutoLock lock(m_env_mutex);
694 ScopeProfiler sp(g_profiler, "Server: liquid transform");
696 std::map<v3s16, MapBlock*> modified_blocks;
697 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
699 if (!modified_blocks.empty()) {
701 event.type = MEET_OTHER;
702 event.setModifiedBlocks(modified_blocks);
703 m_env->getMap().dispatchEvent(event);
706 m_clients.step(dtime);
708 // increase/decrease lag gauge gradually
709 if (m_lag_gauge->get() > dtime) {
710 m_lag_gauge->decrement(dtime/100);
712 m_lag_gauge->increment(dtime/100);
716 float &counter = m_step_pending_dyn_media_timer;
718 if (counter >= 5.0f) {
719 stepPendingDynMediaCallbacks(counter);
726 // send masterserver announce
728 float &counter = m_masterserver_timer;
729 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
730 g_settings->getBool("server_announce")) {
731 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
732 ServerList::AA_START,
733 m_bind_addr.getPort(),
734 m_clients.getPlayerNames(),
735 m_uptime_counter->get(),
736 m_env->getGameTime(),
739 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
749 Check added and deleted active objects
752 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
753 MutexAutoLock envlock(m_env_mutex);
756 ClientInterface::AutoLock clientlock(m_clients);
757 const RemoteClientMap &clients = m_clients.getClientList();
758 ScopeProfiler sp(g_profiler, "Server: update objects within range");
760 m_player_gauge->set(clients.size());
761 for (const auto &client_it : clients) {
762 RemoteClient *client = client_it.second;
764 if (client->getState() < CS_DefinitionsSent)
767 // This can happen if the client times out somehow
768 if (!m_env->getPlayer(client->peer_id))
771 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
775 SendActiveObjectRemoveAdd(client, playersao);
779 // Write changes to the mod storage
780 m_mod_storage_save_timer -= dtime;
781 if (m_mod_storage_save_timer <= 0.0f) {
782 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
783 m_mod_storage_database->endSave();
784 m_mod_storage_database->beginSave();
792 MutexAutoLock envlock(m_env_mutex);
793 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
796 // Value = data sent by object
797 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
799 // Get active object messages from environment
800 ActiveObjectMessage aom(0);
801 u32 count_reliable = 0, count_unreliable = 0;
803 if (!m_env->getActiveObjectMessage(&aom))
810 std::vector<ActiveObjectMessage>* message_list = nullptr;
811 auto n = buffered_messages.find(aom.id);
812 if (n == buffered_messages.end()) {
813 message_list = new std::vector<ActiveObjectMessage>;
814 buffered_messages[aom.id] = message_list;
816 message_list = n->second;
818 message_list->push_back(std::move(aom));
821 m_aom_buffer_counter[0]->increment(count_reliable);
822 m_aom_buffer_counter[1]->increment(count_unreliable);
825 ClientInterface::AutoLock clientlock(m_clients);
826 const RemoteClientMap &clients = m_clients.getClientList();
827 // Route data to every client
828 std::string reliable_data, unreliable_data;
829 for (const auto &client_it : clients) {
830 reliable_data.clear();
831 unreliable_data.clear();
832 RemoteClient *client = client_it.second;
833 PlayerSAO *player = getPlayerSAO(client->peer_id);
834 // Go through all objects in message buffer
835 for (const auto &buffered_message : buffered_messages) {
836 // If object does not exist or is not known by client, skip it
837 u16 id = buffered_message.first;
838 ServerActiveObject *sao = m_env->getActiveObject(id);
839 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
842 // Get message list of object
843 std::vector<ActiveObjectMessage>* list = buffered_message.second;
844 // Go through every message
845 for (const ActiveObjectMessage &aom : *list) {
846 // Send position updates to players who do not see the attachment
847 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
848 if (sao->getId() == player->getId())
851 // Do not send position updates for attached players
852 // as long the parent is known to the client
853 ServerActiveObject *parent = sao->getParent();
854 if (parent && client->m_known_objects.find(parent->getId()) !=
855 client->m_known_objects.end())
859 // Add full new data to appropriate buffer
860 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
862 writeU16((u8*) idbuf, aom.id);
865 buffer.append(idbuf, sizeof(idbuf));
866 buffer.append(serializeString16(aom.datastring));
870 reliable_data and unreliable_data are now ready.
873 if (!reliable_data.empty()) {
874 SendActiveObjectMessages(client->peer_id, reliable_data);
877 if (!unreliable_data.empty()) {
878 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
883 // Clear buffered_messages
884 for (auto &buffered_message : buffered_messages) {
885 delete buffered_message.second;
890 Send queued-for-sending map edit events.
893 // We will be accessing the environment
894 MutexAutoLock lock(m_env_mutex);
896 // Single change sending is disabled if queue size is big
897 bool disable_single_change_sending = false;
898 if(m_unsent_map_edit_queue.size() >= 4)
899 disable_single_change_sending = true;
901 const auto event_count = m_unsent_map_edit_queue.size();
902 m_map_edit_event_counter->increment(event_count);
904 // We'll log the amount of each
907 std::unordered_set<v3s16> node_meta_updates;
909 while (!m_unsent_map_edit_queue.empty()) {
910 MapEditEvent* event = m_unsent_map_edit_queue.front();
911 m_unsent_map_edit_queue.pop();
913 // Players far away from the change are stored here.
914 // Instead of sending the changes, MapBlocks are set not sent
916 std::unordered_set<u16> far_players;
918 switch (event->type) {
921 prof.add("MEET_ADDNODE", 1);
922 sendAddNode(event->p, event->n, &far_players,
923 disable_single_change_sending ? 5 : 30,
924 event->type == MEET_ADDNODE);
926 case MEET_REMOVENODE:
927 prof.add("MEET_REMOVENODE", 1);
928 sendRemoveNode(event->p, &far_players,
929 disable_single_change_sending ? 5 : 30);
931 case MEET_BLOCK_NODE_METADATA_CHANGED: {
932 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
933 if (!event->is_private_change) {
934 node_meta_updates.emplace(event->p);
937 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
938 getNodeBlockPos(event->p))) {
939 block->raiseModified(MOD_STATE_WRITE_NEEDED,
940 MOD_REASON_REPORT_META_CHANGE);
945 prof.add("MEET_OTHER", 1);
946 for (const v3s16 &modified_block : event->modified_blocks) {
947 m_clients.markBlockposAsNotSent(modified_block);
951 prof.add("unknown", 1);
952 warningstream << "Server: Unknown MapEditEvent "
953 << ((u32)event->type) << std::endl;
958 Set blocks not sent to far players
960 if (!far_players.empty()) {
961 // Convert list format to that wanted by SetBlocksNotSent
962 std::map<v3s16, MapBlock*> modified_blocks2;
963 for (const v3s16 &modified_block : event->modified_blocks) {
964 modified_blocks2[modified_block] =
965 m_env->getMap().getBlockNoCreateNoEx(modified_block);
968 // Set blocks not sent
969 for (const u16 far_player : far_players) {
970 if (RemoteClient *client = getClient(far_player))
971 client->SetBlocksNotSent(modified_blocks2);
978 if (event_count >= 5) {
979 infostream << "Server: MapEditEvents:" << std::endl;
980 prof.print(infostream);
981 } else if (event_count != 0) {
982 verbosestream << "Server: MapEditEvents:" << std::endl;
983 prof.print(verbosestream);
986 // Send all metadata updates
987 if (!node_meta_updates.empty())
988 sendMetadataChanged(node_meta_updates);
992 Trigger emerge thread
993 Doing this every 2s is left over from old code, unclear if this is still needed.
996 float &counter = m_emergethread_trigger_timer;
998 if (counter <= 0.0f) {
1001 m_emerge->startThreads();
1005 // Save map, players and auth stuff
1007 float &counter = m_savemap_timer;
1009 static thread_local const float save_interval =
1010 g_settings->getFloat("server_map_save_interval");
1011 if (counter >= save_interval) {
1013 MutexAutoLock lock(m_env_mutex);
1015 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
1018 if (m_banmanager->isModified()) {
1019 m_banmanager->save();
1022 // Save changed parts of map
1023 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1026 m_env->saveLoadedPlayers();
1028 // Save environment metadata
1033 m_shutdown_state.tick(dtime, this);
1036 void Server::Receive()
1046 In the first iteration *wait* for a packet, afterwards process
1047 all packets that are immediately available (no waiting).
1050 m_con->Receive(&pkt);
1053 if (!m_con->TryReceive(&pkt))
1057 peer_id = pkt.getPeerId();
1058 m_packet_recv_counter->increment();
1060 m_packet_recv_processed_counter->increment();
1061 } catch (const con::InvalidIncomingDataException &e) {
1062 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1063 << e.what() << std::endl;
1064 } catch (const SerializationError &e) {
1065 infostream << "Server::Receive(): SerializationError: what()="
1066 << e.what() << std::endl;
1067 } catch (const ClientStateError &e) {
1068 errorstream << "ProcessData: peer=" << peer_id << " what()="
1069 << e.what() << std::endl;
1070 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1071 } catch (const con::PeerNotFoundException &e) {
1073 } catch (const con::NoIncomingDataException &e) {
1079 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1081 std::string playername;
1082 PlayerSAO *playersao = NULL;
1084 ClientInterface::AutoLock clientlock(m_clients);
1085 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1087 playername = client->getName();
1088 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1092 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1094 // If failed, cancel
1095 if (!playersao || !player) {
1096 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1097 actionstream << "Server: Failed to emerge player \"" << playername
1098 << "\" (player allocated to another client)" << std::endl;
1099 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1101 errorstream << "Server: " << playername << ": Failed to emerge player"
1103 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1109 Send complete position information
1111 SendMovePlayer(peer_id);
1114 SendPlayerPrivileges(peer_id);
1116 // Send inventory formspec
1117 SendPlayerInventoryFormspec(peer_id);
1120 SendInventory(playersao, false);
1123 SendPlayerHP(playersao, false);
1125 // Send death screen
1126 if (playersao->isDead())
1127 SendDeathscreen(peer_id, false, v3f(0,0,0));
1130 SendPlayerBreath(playersao);
1133 Update player list and print action
1136 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1137 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1138 m_clients.sendToAll(¬ice_pkt);
1141 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1142 const auto &names = m_clients.getPlayerNames();
1144 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1145 for (const std::string &name : names)
1146 actionstream << name << " ";
1147 actionstream << player->getName() << std::endl;
1152 inline void Server::handleCommand(NetworkPacket *pkt)
1154 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1155 (this->*opHandle.handler)(pkt);
1158 void Server::ProcessData(NetworkPacket *pkt)
1160 // Environment is locked first.
1161 MutexAutoLock envlock(m_env_mutex);
1163 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1164 u32 peer_id = pkt->getPeerId();
1167 Address address = getPeerAddress(peer_id);
1168 std::string addr_s = address.serializeString();
1170 // FIXME: Isn't it a bit excessive to check this for every packet?
1171 if (m_banmanager->isIpBanned(addr_s)) {
1172 std::string ban_name = m_banmanager->getBanName(addr_s);
1173 infostream << "Server: A banned client tried to connect from "
1174 << addr_s << "; banned name was " << ban_name << std::endl;
1175 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1176 "Your IP is banned. Banned name was " + ban_name);
1179 } catch (con::PeerNotFoundException &e) {
1181 * no peer for this packet found
1182 * most common reason is peer timeout, e.g. peer didn't
1183 * respond for some time, your server was overloaded or
1186 infostream << "Server::ProcessData(): Canceling: peer "
1187 << peer_id << " not found" << std::endl;
1192 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1194 // Command must be handled into ToServerCommandHandler
1195 if (command >= TOSERVER_NUM_MSG_TYPES) {
1196 infostream << "Server: Ignoring unknown command "
1197 << command << std::endl;
1201 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1206 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1208 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1209 errorstream << "Server::ProcessData(): Cancelling: Peer"
1210 " serialization format invalid or not initialized."
1211 " Skipping incoming command=" << command << std::endl;
1215 /* Handle commands related to client startup */
1216 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1221 if (m_clients.getClientState(peer_id) < CS_Active) {
1222 if (command == TOSERVER_PLAYERPOS) return;
1224 errorstream << "Got packet command: " << command << " for peer id "
1225 << peer_id << " but client isn't active yet. Dropping packet "
1231 } catch (SendFailedException &e) {
1232 errorstream << "Server::ProcessData(): SendFailedException: "
1233 << "what=" << e.what()
1235 } catch (PacketError &e) {
1236 actionstream << "Server::ProcessData(): PacketError: "
1237 << "what=" << e.what()
1242 void Server::setTimeOfDay(u32 time)
1244 m_env->setTimeOfDay(time);
1245 m_time_of_day_send_timer = 0;
1248 void Server::onMapEditEvent(const MapEditEvent &event)
1250 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1253 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1256 void Server::peerAdded(con::Peer *peer)
1258 verbosestream<<"Server::peerAdded(): peer->id="
1259 <<peer->id<<std::endl;
1261 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1264 void Server::deletingPeer(con::Peer *peer, bool timeout)
1266 verbosestream<<"Server::deletingPeer(): peer->id="
1267 <<peer->id<<", timeout="<<timeout<<std::endl;
1269 m_clients.event(peer->id, CSE_Disconnect);
1270 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1273 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1275 *retval = m_con->getPeerStat(peer_id,type);
1276 return *retval != -1;
1279 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1281 ClientInterface::AutoLock clientlock(m_clients);
1282 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1287 ret.state = client->getState();
1288 ret.addr = client->getAddress();
1289 ret.uptime = client->uptime();
1290 ret.ser_vers = client->serialization_version;
1291 ret.prot_vers = client->net_proto_version;
1293 ret.major = client->getMajor();
1294 ret.minor = client->getMinor();
1295 ret.patch = client->getPatch();
1296 ret.vers_string = client->getFullVer();
1298 ret.lang_code = client->getLangCode();
1303 void Server::handlePeerChanges()
1305 while(!m_peer_change_queue.empty())
1307 con::PeerChange c = m_peer_change_queue.front();
1308 m_peer_change_queue.pop();
1310 verbosestream<<"Server: Handling peer change: "
1311 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1316 case con::PEER_ADDED:
1317 m_clients.CreateClient(c.peer_id);
1320 case con::PEER_REMOVED:
1321 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1325 FATAL_ERROR("Invalid peer change event received!");
1331 void Server::printToConsoleOnly(const std::string &text)
1334 m_admin_chat->outgoing_queue.push_back(
1335 new ChatEventChat("", utf8_to_wide(text)));
1337 std::cout << text << std::endl;
1341 void Server::Send(NetworkPacket *pkt)
1343 Send(pkt->getPeerId(), pkt);
1346 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1348 m_clients.send(peer_id,
1349 clientCommandFactoryTable[pkt->getCommand()].channel,
1351 clientCommandFactoryTable[pkt->getCommand()].reliable);
1354 void Server::SendMovement(session_t peer_id)
1356 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1358 pkt << g_settings->getFloat("movement_acceleration_default");
1359 pkt << g_settings->getFloat("movement_acceleration_air");
1360 pkt << g_settings->getFloat("movement_acceleration_fast");
1361 pkt << g_settings->getFloat("movement_speed_walk");
1362 pkt << g_settings->getFloat("movement_speed_crouch");
1363 pkt << g_settings->getFloat("movement_speed_fast");
1364 pkt << g_settings->getFloat("movement_speed_climb");
1365 pkt << g_settings->getFloat("movement_speed_jump");
1366 pkt << g_settings->getFloat("movement_liquid_fluidity");
1367 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1368 pkt << g_settings->getFloat("movement_liquid_sink");
1369 pkt << g_settings->getFloat("movement_gravity");
1374 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1376 m_script->player_event(playersao, "health_changed");
1377 SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX);
1379 // Send to other clients
1380 playersao->sendPunchCommand();
1382 if (playersao->isDead())
1383 HandlePlayerDeath(playersao, reason);
1386 void Server::SendPlayerHP(PlayerSAO *playersao, bool effect)
1388 SendHP(playersao->getPeerID(), playersao->getHP(), effect);
1391 void Server::SendHP(session_t peer_id, u16 hp, bool effect)
1393 NetworkPacket pkt(TOCLIENT_HP, 3, peer_id);
1394 pkt << hp << effect;
1398 void Server::SendBreath(session_t peer_id, u16 breath)
1400 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1401 pkt << (u16) breath;
1405 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1406 const std::string &custom_reason, bool reconnect)
1408 assert(reason < SERVER_ACCESSDENIED_MAX);
1410 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1412 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1413 pkt << custom_reason;
1414 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1415 reason == SERVER_ACCESSDENIED_CRASH)
1416 pkt << custom_reason << (u8)reconnect;
1420 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1421 v3f camera_point_target)
1423 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1424 pkt << set_camera_point_target << camera_point_target;
1428 void Server::SendItemDef(session_t peer_id,
1429 IItemDefManager *itemdef, u16 protocol_version)
1431 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1435 u32 length of the next item
1436 zlib-compressed serialized ItemDefManager
1438 std::ostringstream tmp_os(std::ios::binary);
1439 itemdef->serialize(tmp_os, protocol_version);
1440 std::ostringstream tmp_os2(std::ios::binary);
1441 compressZlib(tmp_os.str(), tmp_os2);
1442 pkt.putLongString(tmp_os2.str());
1445 verbosestream << "Server: Sending item definitions to id(" << peer_id
1446 << "): size=" << pkt.getSize() << std::endl;
1451 void Server::SendNodeDef(session_t peer_id,
1452 const NodeDefManager *nodedef, u16 protocol_version)
1454 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1458 u32 length of the next item
1459 zlib-compressed serialized NodeDefManager
1461 std::ostringstream tmp_os(std::ios::binary);
1462 nodedef->serialize(tmp_os, protocol_version);
1463 std::ostringstream tmp_os2(std::ios::binary);
1464 compressZlib(tmp_os.str(), tmp_os2);
1466 pkt.putLongString(tmp_os2.str());
1469 verbosestream << "Server: Sending node definitions to id(" << peer_id
1470 << "): size=" << pkt.getSize() << std::endl;
1476 Non-static send methods
1479 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1481 RemotePlayer *player = sao->getPlayer();
1483 // Do not send new format to old clients
1484 incremental &= player->protocol_version >= 38;
1486 UpdateCrafting(player);
1492 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1494 std::ostringstream os(std::ios::binary);
1495 sao->getInventory()->serialize(os, incremental);
1496 sao->getInventory()->setModified(false);
1497 player->setModified(true);
1499 const std::string &s = os.str();
1500 pkt.putRawString(s.c_str(), s.size());
1504 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1506 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1508 u8 type = message.type;
1509 pkt << version << type << message.sender << message.message
1510 << static_cast<u64>(message.timestamp);
1512 if (peer_id != PEER_ID_INEXISTENT) {
1513 RemotePlayer *player = m_env->getPlayer(peer_id);
1519 m_clients.sendToAll(&pkt);
1523 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1524 const std::string &formname)
1526 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1527 if (formspec.empty()){
1528 //the client should close the formspec
1529 //but make sure there wasn't another one open in meantime
1530 const auto it = m_formspec_state_data.find(peer_id);
1531 if (it != m_formspec_state_data.end() && it->second == formname) {
1532 m_formspec_state_data.erase(peer_id);
1534 pkt.putLongString("");
1536 m_formspec_state_data[peer_id] = formname;
1537 pkt.putLongString(formspec);
1544 // Spawns a particle on peer with peer_id
1545 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1546 const ParticleParameters &p)
1548 static thread_local const float radius =
1549 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1551 if (peer_id == PEER_ID_INEXISTENT) {
1552 std::vector<session_t> clients = m_clients.getClientIDs();
1553 const v3f pos = p.pos * BS;
1554 const float radius_sq = radius * radius;
1556 for (const session_t client_id : clients) {
1557 RemotePlayer *player = m_env->getPlayer(client_id);
1561 PlayerSAO *sao = player->getPlayerSAO();
1565 // Do not send to distant clients
1566 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1569 SendSpawnParticle(client_id, player->protocol_version, p);
1573 assert(protocol_version != 0);
1575 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1578 // NetworkPacket and iostreams are incompatible...
1579 std::ostringstream oss(std::ios_base::binary);
1580 p.serialize(oss, protocol_version);
1581 pkt.putRawString(oss.str());
1587 // Adds a ParticleSpawner on peer with peer_id
1588 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1589 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1591 static thread_local const float radius =
1592 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1594 if (peer_id == PEER_ID_INEXISTENT) {
1595 std::vector<session_t> clients = m_clients.getClientIDs();
1597 p.pos.start.min.val +
1598 p.pos.start.max.val +
1602 const float radius_sq = radius * radius;
1603 /* Don't send short-lived spawners to distant players.
1604 * This could be replaced with proper tracking at some point. */
1605 const bool distance_check = !attached_id && p.time <= 1.0f;
1607 for (const session_t client_id : clients) {
1608 RemotePlayer *player = m_env->getPlayer(client_id);
1612 if (distance_check) {
1613 PlayerSAO *sao = player->getPlayerSAO();
1616 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1620 SendAddParticleSpawner(client_id, player->protocol_version,
1621 p, attached_id, id);
1625 assert(protocol_version != 0);
1627 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1629 pkt << p.amount << p.time;
1630 { // serialize legacy fields
1631 std::ostringstream os(std::ios_base::binary);
1632 p.pos.start.legacySerialize(os);
1633 p.vel.start.legacySerialize(os);
1634 p.acc.start.legacySerialize(os);
1635 p.exptime.start.legacySerialize(os);
1636 p.size.start.legacySerialize(os);
1637 pkt.putRawString(os.str());
1639 pkt << p.collisiondetection;
1641 pkt.putLongString(p.texture.string);
1643 pkt << id << p.vertical << p.collision_removal << attached_id;
1645 std::ostringstream os(std::ios_base::binary);
1646 p.animation.serialize(os, protocol_version);
1647 pkt.putRawString(os.str());
1649 pkt << p.glow << p.object_collision;
1650 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1652 { // serialize new fields
1653 // initial bias for older properties
1654 pkt << p.pos.start.bias
1657 << p.exptime.start.bias
1658 << p.size.start.bias;
1660 std::ostringstream os(std::ios_base::binary);
1662 // final tween frames of older properties
1663 p.pos.end.serialize(os);
1664 p.vel.end.serialize(os);
1665 p.acc.end.serialize(os);
1666 p.exptime.end.serialize(os);
1667 p.size.end.serialize(os);
1669 // properties for legacy texture field
1670 p.texture.serialize(os, protocol_version, true);
1673 p.drag.serialize(os);
1674 p.jitter.serialize(os);
1675 p.bounce.serialize(os);
1676 ParticleParamTypes::serializeParameterValue(os, p.attractor_kind);
1677 if (p.attractor_kind != ParticleParamTypes::AttractorKind::none) {
1678 p.attract.serialize(os);
1679 p.attractor_origin.serialize(os);
1680 writeU16(os, p.attractor_attachment); /* object ID */
1681 writeU8(os, p.attractor_kill);
1682 if (p.attractor_kind != ParticleParamTypes::AttractorKind::point) {
1683 p.attractor_direction.serialize(os);
1684 writeU16(os, p.attractor_direction_attachment);
1687 p.radius.serialize(os);
1689 ParticleParamTypes::serializeParameterValue(os, (u16)p.texpool.size());
1690 for (const auto& tex : p.texpool) {
1691 tex.serialize(os, protocol_version);
1694 pkt.putRawString(os.str());
1700 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1702 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1706 if (peer_id != PEER_ID_INEXISTENT)
1709 m_clients.sendToAll(&pkt);
1713 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1715 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1717 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1718 << form->text << form->number << form->item << form->dir
1719 << form->align << form->offset << form->world_pos << form->size
1720 << form->z_index << form->text2 << form->style;
1725 void Server::SendHUDRemove(session_t peer_id, u32 id)
1727 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1732 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1734 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1735 pkt << id << (u8) stat;
1739 case HUD_STAT_SCALE:
1740 case HUD_STAT_ALIGN:
1741 case HUD_STAT_OFFSET:
1742 pkt << *(v2f *) value;
1746 case HUD_STAT_TEXT2:
1747 pkt << *(std::string *) value;
1749 case HUD_STAT_WORLD_POS:
1750 pkt << *(v3f *) value;
1753 pkt << *(v2s32 *) value;
1755 default: // all other types
1756 pkt << *(u32 *) value;
1763 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1765 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1767 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1769 pkt << flags << mask;
1774 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1776 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1777 pkt << param << value;
1781 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1783 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1785 // Handle prior clients here
1786 if (m_clients.getProtocolVersion(peer_id) < 39) {
1787 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1789 for (const std::string& texture : params.textures)
1792 pkt << params.clouds;
1793 } else { // Handle current clients and future clients
1794 pkt << params.bgcolor << params.type
1795 << params.clouds << params.fog_sun_tint
1796 << params.fog_moon_tint << params.fog_tint_type;
1798 if (params.type == "skybox") {
1799 pkt << (u16) params.textures.size();
1800 for (const std::string &texture : params.textures)
1802 } else if (params.type == "regular") {
1803 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1804 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1805 << params.sky_color.night_sky << params.sky_color.night_horizon
1806 << params.sky_color.indoors;
1813 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1815 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1816 pkt << params.visible << params.texture
1817 << params.tonemap << params.sunrise
1818 << params.sunrise_visible << params.scale;
1822 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1824 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1826 pkt << params.visible << params.texture
1827 << params.tonemap << params.scale;
1831 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1833 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1835 pkt << params.visible << params.count
1836 << params.starcolor << params.scale
1837 << params.day_opacity;
1842 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1844 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1845 pkt << params.density << params.color_bright << params.color_ambient
1846 << params.height << params.thickness << params.speed;
1850 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1853 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1856 pkt << do_override << (u16) (ratio * 65535);
1861 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1863 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1866 pkt << lighting.shadow_intensity;
1867 pkt << lighting.saturation;
1869 pkt << lighting.exposure.luminance_min
1870 << lighting.exposure.luminance_max
1871 << lighting.exposure.exposure_correction
1872 << lighting.exposure.speed_dark_bright
1873 << lighting.exposure.speed_bright_dark
1874 << lighting.exposure.center_weight_power;
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 v3f p_f = intToFloat(p, BS);
2255 v3s16 block_pos = getNodeBlockPos(p);
2257 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2260 sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2263 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2264 float far_d_nodes, bool remove_metadata)
2266 v3f p_f = intToFloat(p, BS);
2267 v3s16 block_pos = getNodeBlockPos(p);
2269 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2270 pkt << p << n.param0 << n.param1 << n.param2
2271 << (u8) (remove_metadata ? 0 : 1);
2272 sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2275 void Server::sendNodeChangePkt(NetworkPacket &pkt, v3s16 block_pos,
2276 v3f p, float far_d_nodes, std::unordered_set<u16> *far_players)
2278 float maxd = far_d_nodes * BS;
2279 std::vector<session_t> clients = m_clients.getClientIDs();
2280 ClientInterface::AutoLock clientlock(m_clients);
2282 for (session_t client_id : clients) {
2283 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2287 RemotePlayer *player = m_env->getPlayer(client_id);
2288 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2290 // If player is far away, only set modified blocks not sent
2291 if (!client->isBlockSent(block_pos) || (sao &&
2292 sao->getBasePosition().getDistanceFrom(p) > maxd)) {
2294 far_players->emplace(client_id);
2296 client->SetBlockNotSent(block_pos);
2301 m_clients.send(client_id, 0, &pkt, true);
2305 void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
2307 NodeMetadataList meta_updates_list(false);
2308 std::ostringstream os(std::ios::binary);
2310 std::vector<session_t> clients = m_clients.getClientIDs();
2311 ClientInterface::AutoLock clientlock(m_clients);
2313 for (session_t i : clients) {
2314 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2318 ServerActiveObject *player = getPlayerSAO(i);
2321 player_pos = floatToInt(player->getBasePosition(), BS);
2323 for (const v3s16 pos : positions) {
2324 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2329 v3s16 block_pos = getNodeBlockPos(pos);
2330 if (!client->isBlockSent(block_pos) ||
2331 player_pos.getDistanceFrom(pos) > far_d_nodes) {
2332 client->SetBlockNotSent(block_pos);
2336 // Add the change to send list
2337 meta_updates_list.set(pos, meta);
2339 if (meta_updates_list.size() == 0)
2342 // Send the meta changes
2344 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2345 std::string raw = os.str();
2347 compressZlib(raw, os);
2349 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
2350 pkt.putLongString(os.str());
2353 meta_updates_list.clear();
2357 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2358 u16 net_proto_version, SerializedBlockCache *cache)
2360 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2361 std::string s, *sptr = nullptr;
2364 auto it = cache->find({block->getPos(), ver});
2365 if (it != cache->end())
2369 // Serialize the block in the right format
2371 std::ostringstream os(std::ios_base::binary);
2372 block->serialize(os, ver, false, net_compression_level);
2373 block->serializeNetworkSpecific(os);
2378 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2379 pkt << block->getPos();
2380 pkt.putRawString(*sptr);
2383 // Store away in cache
2384 if (cache && sptr == &s)
2385 (*cache)[{block->getPos(), ver}] = std::move(s);
2388 void Server::SendBlocks(float dtime)
2390 MutexAutoLock envlock(m_env_mutex);
2391 //TODO check if one big lock could be faster then multiple small ones
2393 std::vector<PrioritySortedBlockTransfer> queue;
2395 u32 total_sending = 0, unique_clients = 0;
2398 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2400 std::vector<session_t> clients = m_clients.getClientIDs();
2402 ClientInterface::AutoLock clientlock(m_clients);
2403 for (const session_t client_id : clients) {
2404 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2409 total_sending += client->getSendingCount();
2410 const auto old_count = queue.size();
2411 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2412 unique_clients += queue.size() > old_count ? 1 : 0;
2417 // Lowest priority number comes first.
2418 // Lowest is most important.
2419 std::sort(queue.begin(), queue.end());
2421 ClientInterface::AutoLock clientlock(m_clients);
2423 // Maximal total count calculation
2424 // The per-client block sends is halved with the maximal online users
2425 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2426 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2428 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2429 Map &map = m_env->getMap();
2431 SerializedBlockCache cache, *cache_ptr = nullptr;
2432 if (unique_clients > 1) {
2433 // caching is pointless with a single client
2437 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2438 if (total_sending >= max_blocks_to_send)
2441 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2445 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2450 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2451 client->net_proto_version, cache_ptr);
2453 client->SentBlock(block_to_send.pos);
2458 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2460 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2464 ClientInterface::AutoLock clientlock(m_clients);
2465 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2466 if (!client || client->isBlockSent(blockpos))
2468 SendBlockNoLock(peer_id, block, client->serialization_version,
2469 client->net_proto_version);
2474 bool Server::addMediaFile(const std::string &filename,
2475 const std::string &filepath, std::string *filedata_to,
2476 std::string *digest_to)
2478 // If name contains illegal characters, ignore the file
2479 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2480 infostream << "Server: ignoring illegal file name: \""
2481 << filename << "\"" << std::endl;
2484 // If name is not in a supported format, ignore it
2485 const char *supported_ext[] = {
2486 ".png", ".jpg", ".bmp", ".tga",
2488 ".x", ".b3d", ".obj",
2489 // Custom translation file format
2493 if (removeStringEnd(filename, supported_ext).empty()) {
2494 infostream << "Server: ignoring unsupported file extension: \""
2495 << filename << "\"" << std::endl;
2498 // Ok, attempt to load the file and add to cache
2501 std::string filedata;
2502 if (!fs::ReadFile(filepath, filedata)) {
2503 errorstream << "Server::addMediaFile(): Failed to open \""
2504 << filename << "\" for reading" << std::endl;
2508 if (filedata.empty()) {
2509 errorstream << "Server::addMediaFile(): Empty file \""
2510 << filepath << "\"" << std::endl;
2515 sha1.addBytes(filedata.c_str(), filedata.length());
2517 unsigned char *digest = sha1.getDigest();
2518 std::string sha1_base64 = base64_encode(digest, 20);
2519 std::string sha1_hex = hex_encode((char*) digest, 20);
2521 *digest_to = std::string((char*) digest, 20);
2525 m_media[filename] = MediaInfo(filepath, sha1_base64);
2526 verbosestream << "Server: " << sha1_hex << " is " << filename
2530 *filedata_to = std::move(filedata);
2534 void Server::fillMediaCache()
2536 infostream << "Server: Calculating media file checksums" << std::endl;
2538 // Collect all media file paths
2539 std::vector<std::string> paths;
2541 // ordered in descending priority
2542 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2543 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2544 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2545 m_modmgr->getModsMediaPaths(paths);
2547 // Collect media file information from paths into cache
2548 for (const std::string &mediapath : paths) {
2549 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2550 for (const fs::DirListNode &dln : dirlist) {
2551 if (dln.dir) // Ignore dirs (already in paths)
2554 const std::string &filename = dln.name;
2555 if (m_media.find(filename) != m_media.end()) // Do not override
2558 std::string filepath = mediapath;
2559 filepath.append(DIR_DELIM).append(filename);
2560 addMediaFile(filename, filepath);
2564 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2567 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2570 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2573 std::string lang_suffix;
2574 lang_suffix.append(".").append(lang_code).append(".tr");
2575 for (const auto &i : m_media) {
2576 if (i.second.no_announce)
2578 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2585 for (const auto &i : m_media) {
2586 if (i.second.no_announce)
2588 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2590 pkt << i.first << i.second.sha1_digest;
2593 pkt << g_settings->get("remote_media");
2596 verbosestream << "Server: Announcing files to id(" << peer_id
2597 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2600 struct SendableMedia
2606 SendableMedia(const std::string &name, const std::string &path,
2607 std::string &&data):
2608 name(name), path(path), data(std::move(data))
2612 void Server::sendRequestedMedia(session_t peer_id,
2613 const std::vector<std::string> &tosend)
2615 verbosestream<<"Server::sendRequestedMedia(): "
2616 <<"Sending files to client"<<std::endl;
2620 // Put 5kB in one bunch (this is not accurate)
2621 u32 bytes_per_bunch = 5000;
2623 std::vector< std::vector<SendableMedia> > file_bunches;
2624 file_bunches.emplace_back();
2626 u32 file_size_bunch_total = 0;
2628 for (const std::string &name : tosend) {
2629 if (m_media.find(name) == m_media.end()) {
2630 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2631 <<"unknown file \""<<(name)<<"\""<<std::endl;
2635 const auto &m = m_media[name];
2639 if (!fs::ReadFile(m.path, data)) {
2640 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2641 << name << "\"" << std::endl;
2644 file_size_bunch_total += data.size();
2647 file_bunches.back().emplace_back(name, m.path, std::move(data));
2649 // Start next bunch if got enough data
2650 if(file_size_bunch_total >= bytes_per_bunch) {
2651 file_bunches.emplace_back();
2652 file_size_bunch_total = 0;
2657 /* Create and send packets */
2659 u16 num_bunches = file_bunches.size();
2660 for (u16 i = 0; i < num_bunches; i++) {
2663 u16 total number of texture bunches
2664 u16 index of this bunch
2665 u32 number of files in this bunch
2674 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2675 pkt << num_bunches << i << (u32) file_bunches[i].size();
2677 for (const SendableMedia &j : file_bunches[i]) {
2679 pkt.putLongString(j.data);
2682 verbosestream << "Server::sendRequestedMedia(): bunch "
2683 << i << "/" << num_bunches
2684 << " files=" << file_bunches[i].size()
2685 << " size=" << pkt.getSize() << std::endl;
2690 void Server::stepPendingDynMediaCallbacks(float dtime)
2692 MutexAutoLock lock(m_env_mutex);
2694 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2695 it->second.expiry_timer -= dtime;
2696 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2703 const auto &name = it->second.filename;
2704 if (!name.empty()) {
2705 assert(m_media.count(name));
2706 // if no_announce isn't set we're definitely deleting the wrong file!
2707 sanity_check(m_media[name].no_announce);
2709 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2710 m_media.erase(name);
2712 getScriptIface()->freeDynamicMediaCallback(it->first);
2713 it = m_pending_dyn_media.erase(it);
2717 void Server::SendMinimapModes(session_t peer_id,
2718 std::vector<MinimapMode> &modes, size_t wanted_mode)
2720 RemotePlayer *player = m_env->getPlayer(peer_id);
2722 if (player->getPeerId() == PEER_ID_INEXISTENT)
2725 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2726 pkt << (u16)modes.size() << (u16)wanted_mode;
2728 for (auto &mode : modes)
2729 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2734 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2736 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2740 pkt << false; // Remove inventory
2742 pkt << true; // Update inventory
2744 // Serialization & NetworkPacket isn't a love story
2745 std::ostringstream os(std::ios_base::binary);
2746 inventory->serialize(os);
2747 inventory->setModified(false);
2749 const std::string &os_str = os.str();
2750 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2751 pkt.putRawString(os_str);
2754 if (peer_id == PEER_ID_INEXISTENT)
2755 m_clients.sendToAll(&pkt);
2760 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2762 // Lookup player name, to filter detached inventories just after
2763 std::string peer_name;
2764 if (peer_id != PEER_ID_INEXISTENT) {
2765 peer_name = getClient(peer_id, CS_Created)->getName();
2768 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2769 sendDetachedInventory(inv, name, peer_id);
2772 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2779 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2781 infostream << "Server::DiePlayer(): Player "
2782 << playersao->getPlayer()->getName()
2783 << " dies" << std::endl;
2785 playersao->clearParentAttachment();
2787 // Trigger scripted stuff
2788 m_script->on_dieplayer(playersao, reason);
2790 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2793 void Server::RespawnPlayer(session_t peer_id)
2795 PlayerSAO *playersao = getPlayerSAO(peer_id);
2798 infostream << "Server::RespawnPlayer(): Player "
2799 << playersao->getPlayer()->getName()
2800 << " respawns" << std::endl;
2802 const auto *prop = playersao->accessObjectProperties();
2803 playersao->setHP(prop->hp_max,
2804 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2805 playersao->setBreath(prop->breath_max);
2807 bool repositioned = m_script->on_respawnplayer(playersao);
2808 if (!repositioned) {
2809 // setPos will send the new position to client
2810 playersao->setPos(findSpawnPos());
2815 void Server::DenySudoAccess(session_t peer_id)
2817 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2822 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2823 const std::string &custom_reason, bool reconnect)
2825 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2826 m_clients.event(peer_id, CSE_SetDenied);
2827 DisconnectPeer(peer_id);
2830 void Server::DisconnectPeer(session_t peer_id)
2832 m_modchannel_mgr->leaveAllChannels(peer_id);
2833 m_con->DisconnectPeer(peer_id);
2836 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2839 RemoteClient* client = getClient(peer_id, CS_Invalid);
2841 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2843 // Right now, the auth mechs don't change between login and sudo mode.
2844 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2845 client->allowed_sudo_mechs = sudo_auth_mechs;
2847 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2848 << g_settings->getFloat("dedicated_server_step")
2852 m_clients.event(peer_id, CSE_AuthAccept);
2854 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2856 // We only support SRP right now
2857 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2859 resp_pkt << sudo_auth_mechs;
2861 m_clients.event(peer_id, CSE_SudoSuccess);
2865 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2867 std::wstring message;
2870 Clear references to playing sounds
2872 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2873 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2874 ServerPlayingSound &psound = i->second;
2875 psound.clients.erase(peer_id);
2876 if (psound.clients.empty())
2877 m_playing_sounds.erase(i++);
2882 // clear formspec info so the next client can't abuse the current state
2883 m_formspec_state_data.erase(peer_id);
2885 RemotePlayer *player = m_env->getPlayer(peer_id);
2887 /* Run scripts and remove from environment */
2889 PlayerSAO *playersao = player->getPlayerSAO();
2892 playersao->clearChildAttachments();
2893 playersao->clearParentAttachment();
2895 // inform connected clients
2896 const std::string &player_name = player->getName();
2897 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2898 // (u16) 1 + std::string represents a vector serialization representation
2899 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2900 m_clients.sendToAll(¬ice);
2902 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2904 playersao->disconnected();
2911 if (player && reason != CDR_DENY) {
2912 std::ostringstream os(std::ios_base::binary);
2913 std::vector<session_t> clients = m_clients.getClientIDs();
2915 for (const session_t client_id : clients) {
2917 RemotePlayer *player = m_env->getPlayer(client_id);
2921 // Get name of player
2922 os << player->getName() << " ";
2925 std::string name = player->getName();
2926 actionstream << name << " "
2927 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2928 << " List of players: " << os.str() << std::endl;
2930 m_admin_chat->outgoing_queue.push_back(
2931 new ChatEventNick(CET_NICK_REMOVE, name));
2935 MutexAutoLock env_lock(m_env_mutex);
2936 m_clients.DeleteClient(peer_id);
2940 // Send leave chat message to all remaining clients
2941 if (!message.empty()) {
2942 SendChatMessage(PEER_ID_INEXISTENT,
2943 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2947 void Server::UpdateCrafting(RemotePlayer *player)
2949 InventoryList *clist = player->inventory.getList("craft");
2950 if (!clist || clist->getSize() == 0)
2953 if (!clist->checkModified())
2956 // Get a preview for crafting
2958 InventoryLocation loc;
2959 loc.setPlayer(player->getName());
2960 std::vector<ItemStack> output_replacements;
2961 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2962 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2965 InventoryList *plist = player->inventory.getList("craftpreview");
2966 if (plist && plist->getSize() >= 1) {
2967 // Put the new preview in
2968 plist->changeItem(0, preview);
2972 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2974 if (evt->type == CET_NICK_ADD) {
2975 // The terminal informed us of its nick choice
2976 m_admin_nick = ((ChatEventNick *)evt)->nick;
2977 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2978 errorstream << "You haven't set up an account." << std::endl
2979 << "Please log in using the client as '"
2980 << m_admin_nick << "' with a secure password." << std::endl
2981 << "Until then, you can't execute admin tasks via the console," << std::endl
2982 << "and everybody can claim the user account instead of you," << std::endl
2983 << "giving them full control over this server." << std::endl;
2986 assert(evt->type == CET_CHAT);
2987 handleAdminChat((ChatEventChat *)evt);
2991 std::wstring Server::handleChat(const std::string &name,
2992 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2994 // If something goes wrong, this player is to blame
2995 RollbackScopeActor rollback_scope(m_rollback,
2996 std::string("player:") + name);
2998 if (g_settings->getBool("strip_color_codes"))
2999 wmessage = unescape_enriched(wmessage);
3002 switch (player->canSendChatMessage()) {
3003 case RPLAYER_CHATRESULT_FLOODING: {
3004 std::wstringstream ws;
3005 ws << L"You cannot send more messages. You are limited to "
3006 << g_settings->getFloat("chat_message_limit_per_10sec")
3007 << L" messages per 10 seconds.";
3010 case RPLAYER_CHATRESULT_KICK:
3011 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
3012 "You have been kicked due to message flooding.");
3014 case RPLAYER_CHATRESULT_OK:
3017 FATAL_ERROR("Unhandled chat filtering result found.");
3021 if (m_max_chatmessage_length > 0
3022 && wmessage.length() > m_max_chatmessage_length) {
3023 return L"Your message exceed the maximum chat message limit set on the server. "
3024 L"It was refused. Send a shorter message";
3027 auto message = trim(wide_to_utf8(wmessage));
3028 if (message.empty())
3031 if (message.find_first_of("\n\r") != std::wstring::npos) {
3032 return L"Newlines are not permitted in chat messages";
3035 // Run script hook, exit if script ate the chat message
3036 if (m_script->on_chat_message(name, message))
3041 // Whether to send line to the player that sent the message, or to all players
3042 bool broadcast_line = true;
3044 if (check_shout_priv && !checkPriv(name, "shout")) {
3045 line += L"-!- You don't have permission to shout.";
3046 broadcast_line = false;
3049 Workaround for fixing chat on Android. Lua doesn't handle
3050 the Cyrillic alphabet and some characters on older Android devices
3053 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3055 line += utf8_to_wide(m_script->formatChatMessage(name,
3056 wide_to_utf8(wmessage)));
3061 Tell calling method to send the message to sender
3063 if (!broadcast_line)
3067 Send the message to others
3069 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3071 ChatMessage chatmsg(line);
3073 std::vector<session_t> clients = m_clients.getClientIDs();
3074 for (u16 cid : clients)
3075 SendChatMessage(cid, chatmsg);
3080 void Server::handleAdminChat(const ChatEventChat *evt)
3082 std::string name = evt->nick;
3083 std::wstring wmessage = evt->evt_msg;
3085 std::wstring answer = handleChat(name, wmessage);
3087 // If asked to send answer to sender
3088 if (!answer.empty()) {
3089 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3093 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3095 RemoteClient *client = getClientNoEx(peer_id,state_min);
3097 throw ClientNotFoundException("Client not found");
3101 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3103 return m_clients.getClientNoEx(peer_id, state_min);
3106 std::string Server::getPlayerName(session_t peer_id)
3108 RemotePlayer *player = m_env->getPlayer(peer_id);
3110 return "[id="+itos(peer_id)+"]";
3111 return player->getName();
3114 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3116 RemotePlayer *player = m_env->getPlayer(peer_id);
3119 return player->getPlayerSAO();
3122 std::string Server::getStatusString()
3124 std::ostringstream os(std::ios_base::binary);
3127 os << "version: " << g_version_string;
3129 os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3131 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3133 os << " | max lag: " << std::setprecision(3);
3134 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3136 // Information about clients
3138 os << " | clients: ";
3140 std::vector<session_t> clients = m_clients.getClientIDs();
3141 for (session_t client_id : clients) {
3142 RemotePlayer *player = m_env->getPlayer(client_id);
3144 // Get name of player
3145 const char *name = player ? player->getName() : "<unknown>";
3147 // Add name to information string
3156 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3157 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3159 if (!g_settings->get("motd").empty())
3160 os << std::endl << "# Server: " << g_settings->get("motd");
3165 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3167 std::set<std::string> privs;
3168 m_script->getAuth(name, NULL, &privs);
3172 bool Server::checkPriv(const std::string &name, const std::string &priv)
3174 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3175 return (privs.count(priv) != 0);
3178 void Server::reportPrivsModified(const std::string &name)
3181 std::vector<session_t> clients = m_clients.getClientIDs();
3182 for (const session_t client_id : clients) {
3183 RemotePlayer *player = m_env->getPlayer(client_id);
3184 reportPrivsModified(player->getName());
3187 RemotePlayer *player = m_env->getPlayer(name.c_str());
3190 SendPlayerPrivileges(player->getPeerId());
3191 PlayerSAO *sao = player->getPlayerSAO();
3194 sao->updatePrivileges(
3195 getPlayerEffectivePrivs(name),
3200 void Server::reportInventoryFormspecModified(const std::string &name)
3202 RemotePlayer *player = m_env->getPlayer(name.c_str());
3205 SendPlayerInventoryFormspec(player->getPeerId());
3208 void Server::reportFormspecPrependModified(const std::string &name)
3210 RemotePlayer *player = m_env->getPlayer(name.c_str());
3213 SendPlayerFormspecPrepend(player->getPeerId());
3216 void Server::setIpBanned(const std::string &ip, const std::string &name)
3218 m_banmanager->add(ip, name);
3221 void Server::unsetIpBanned(const std::string &ip_or_name)
3223 m_banmanager->remove(ip_or_name);
3226 std::string Server::getBanDescription(const std::string &ip_or_name)
3228 return m_banmanager->getBanDescription(ip_or_name);
3231 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3233 // m_env will be NULL if the server is initializing
3237 if (m_admin_nick == name && !m_admin_nick.empty()) {
3238 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3241 RemotePlayer *player = m_env->getPlayer(name);
3246 if (player->getPeerId() == PEER_ID_INEXISTENT)
3249 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3252 bool Server::showFormspec(const char *playername, const std::string &formspec,
3253 const std::string &formname)
3255 // m_env will be NULL if the server is initializing
3259 RemotePlayer *player = m_env->getPlayer(playername);
3263 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3267 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3272 u32 id = player->addHud(form);
3274 SendHUDAdd(player->getPeerId(), id, form);
3279 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3283 HudElement* todel = player->removeHud(id);
3290 SendHUDRemove(player->getPeerId(), id);
3294 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3299 SendHUDChange(player->getPeerId(), id, stat, data);
3303 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3308 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3309 if (new_hud_flags == player->hud_flags) // no change
3312 SendHUDSetFlags(player->getPeerId(), flags, mask);
3313 player->hud_flags = new_hud_flags;
3315 PlayerSAO* playersao = player->getPlayerSAO();
3320 m_script->player_event(playersao, "hud_changed");
3324 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3329 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3332 player->setHotbarItemcount(hotbar_itemcount);
3333 std::ostringstream os(std::ios::binary);
3334 writeS32(os, hotbar_itemcount);
3335 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3339 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3344 player->setHotbarImage(name);
3345 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3348 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3353 player->setHotbarSelectedImage(name);
3354 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3357 Address Server::getPeerAddress(session_t peer_id)
3359 // Note that this is only set after Init was received in Server::handleCommand_Init
3360 return getClient(peer_id, CS_Invalid)->getAddress();
3363 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3364 v2s32 animation_frames[4], f32 frame_speed)
3366 sanity_check(player);
3367 player->setLocalAnimations(animation_frames, frame_speed);
3368 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3371 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3373 sanity_check(player);
3374 player->eye_offset_first = first;
3375 player->eye_offset_third = third;
3376 SendEyeOffset(player->getPeerId(), first, third);
3379 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3381 sanity_check(player);
3382 player->setSky(params);
3383 SendSetSky(player->getPeerId(), params);
3386 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3388 sanity_check(player);
3389 player->setSun(params);
3390 SendSetSun(player->getPeerId(), params);
3393 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3395 sanity_check(player);
3396 player->setMoon(params);
3397 SendSetMoon(player->getPeerId(), params);
3400 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3402 sanity_check(player);
3403 player->setStars(params);
3404 SendSetStars(player->getPeerId(), params);
3407 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3409 sanity_check(player);
3410 player->setCloudParams(params);
3411 SendCloudParams(player->getPeerId(), params);
3414 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3417 sanity_check(player);
3418 player->overrideDayNightRatio(do_override, ratio);
3419 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3422 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3424 sanity_check(player);
3425 player->setLighting(lighting);
3426 SendSetLighting(player->getPeerId(), lighting);
3429 void Server::notifyPlayers(const std::wstring &msg)
3431 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3434 void Server::spawnParticle(const std::string &playername,
3435 const ParticleParameters &p)
3437 // m_env will be NULL if the server is initializing
3441 session_t peer_id = PEER_ID_INEXISTENT;
3443 if (!playername.empty()) {
3444 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3447 peer_id = player->getPeerId();
3448 proto_ver = player->protocol_version;
3451 SendSpawnParticle(peer_id, proto_ver, p);
3454 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3455 ServerActiveObject *attached, const std::string &playername)
3457 // m_env will be NULL if the server is initializing
3461 session_t peer_id = PEER_ID_INEXISTENT;
3463 if (!playername.empty()) {
3464 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3467 peer_id = player->getPeerId();
3468 proto_ver = player->protocol_version;
3471 u16 attached_id = attached ? attached->getId() : 0;
3474 if (attached_id == 0)
3475 id = m_env->addParticleSpawner(p.time);
3477 id = m_env->addParticleSpawner(p.time, attached_id);
3479 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3483 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3485 // m_env will be NULL if the server is initializing
3487 throw ServerError("Can't delete particle spawners during initialisation!");
3489 session_t peer_id = PEER_ID_INEXISTENT;
3490 if (!playername.empty()) {
3491 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3494 peer_id = player->getPeerId();
3497 m_env->deleteParticleSpawner(id);
3498 SendDeleteParticleSpawner(peer_id, id);
3501 bool Server::dynamicAddMedia(std::string filepath,
3502 const u32 token, const std::string &to_player, bool ephemeral)
3504 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3505 auto it = m_media.find(filename);
3506 if (it != m_media.end()) {
3507 // Allow the same path to be "added" again in certain conditions
3508 if (ephemeral || it->second.path != filepath) {
3509 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3510 << "\" already exists in media cache" << std::endl;
3515 // Load the file and add it to our media cache
3516 std::string filedata, raw_hash;
3517 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3522 // Create a copy of the file and swap out the path, this removes the
3523 // requirement that mods keep the file accessible at the original path.
3524 filepath = fs::CreateTempFile();
3525 bool ok = ([&] () -> bool {
3526 if (filepath.empty())
3528 std::ofstream os(filepath.c_str(), std::ios::binary);
3536 errorstream << "Server: failed to create a copy of media file "
3537 << "\"" << filename << "\"" << std::endl;
3538 m_media.erase(filename);
3541 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3542 << filepath << std::endl;
3544 m_media[filename].path = filepath;
3545 m_media[filename].no_announce = true;
3546 // stepPendingDynMediaCallbacks will clean this up later.
3547 } else if (!to_player.empty()) {
3548 m_media[filename].no_announce = true;
3551 // Push file to existing clients
3552 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3553 pkt << raw_hash << filename << (bool)ephemeral;
3555 NetworkPacket legacy_pkt = pkt;
3557 // Newer clients get asked to fetch the file (asynchronous)
3559 // Older clients have an awful hack that just throws the data at them
3560 legacy_pkt.putLongString(filedata);
3562 std::unordered_set<session_t> delivered, waiting;
3564 ClientInterface::AutoLock clientlock(m_clients);
3565 for (auto &pair : m_clients.getClientList()) {
3566 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3568 If a client is in the DefinitionsSent state it is too late to
3569 transfer the file via sendMediaAnnouncement() but at the same
3570 time the client cannot accept a media push yet.
3571 Short of artificially delaying the joining process there is no
3572 way for the server to resolve this so we (currently) opt not to.
3574 warningstream << "The media \"" << filename << "\" (dynamic) could "
3575 "not be delivered to " << pair.second->getName()
3576 << " due to a race condition." << std::endl;
3579 if (pair.second->getState() < CS_Active)
3582 const auto proto_ver = pair.second->net_proto_version;
3586 const session_t peer_id = pair.second->peer_id;
3587 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3590 if (proto_ver < 40) {
3591 delivered.emplace(peer_id);
3593 The network layer only guarantees ordered delivery inside a channel.
3594 Since the very next packet could be one that uses the media, we have
3595 to push the media over ALL channels to ensure it is processed before
3596 it is used. In practice this means channels 1 and 0.
3598 m_clients.send(peer_id, 1, &legacy_pkt, true);
3599 m_clients.send(peer_id, 0, &legacy_pkt, true);
3601 waiting.emplace(peer_id);
3602 Send(peer_id, &pkt);
3607 // Run callback for players that already had the file delivered (legacy-only)
3608 for (session_t peer_id : delivered) {
3609 if (auto player = m_env->getPlayer(peer_id))
3610 getScriptIface()->on_dynamic_media_added(token, player->getName());
3613 // Save all others in our pending state
3614 auto &state = m_pending_dyn_media[token];
3615 state.waiting_players = std::move(waiting);
3616 // regardless of success throw away the callback after a while
3617 state.expiry_timer = 60.0f;
3619 state.filename = filename;
3624 // actions: time-reversed list
3625 // Return value: success/failure
3626 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3627 std::list<std::string> *log)
3629 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3630 ServerMap *map = (ServerMap*)(&m_env->getMap());
3632 // Fail if no actions to handle
3633 if (actions.empty()) {
3635 log->push_back("Nothing to do.");
3642 for (const RollbackAction &action : actions) {
3644 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3647 std::ostringstream os;
3648 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3649 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3651 log->push_back(os.str());
3653 std::ostringstream os;
3654 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3655 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3657 log->push_back(os.str());
3661 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3662 <<" failed"<<std::endl;
3664 // Call it done if less than half failed
3665 return num_failed <= num_tried/2;
3668 // IGameDef interface
3670 IItemDefManager *Server::getItemDefManager()
3675 const NodeDefManager *Server::getNodeDefManager()
3680 ICraftDefManager *Server::getCraftDefManager()
3685 u16 Server::allocateUnknownNodeId(const std::string &name)
3687 return m_nodedef->allocateDummy(name);
3690 IWritableItemDefManager *Server::getWritableItemDefManager()
3695 NodeDefManager *Server::getWritableNodeDefManager()
3700 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3705 const std::vector<ModSpec> & Server::getMods() const
3707 return m_modmgr->getMods();
3710 const ModSpec *Server::getModSpec(const std::string &modname) const
3712 return m_modmgr->getModSpec(modname);
3715 std::string Server::getBuiltinLuaPath()
3717 return porting::path_share + DIR_DELIM + "builtin";
3720 v3f Server::findSpawnPos()
3722 ServerMap &map = m_env->getServerMap();
3724 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3725 return nodeposf * BS;
3727 bool is_good = false;
3728 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3729 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3731 // Try to find a good place a few times
3732 for (s32 i = 0; i < 4000 && !is_good; i++) {
3733 s32 range = MYMIN(1 + i, range_max);
3734 // We're going to try to throw the player to this position
3735 v2s16 nodepos2d = v2s16(
3736 -range + myrand_range(0, range*2),
3737 -range + myrand_range(0, range*2));
3738 // Get spawn level at point
3739 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3740 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3741 // signify an unsuitable spawn position, or if outside limits.
3742 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3743 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3746 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3747 // Consecutive empty nodes
3750 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3751 // avoid obstructions in already-generated mapblocks.
3752 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3753 // no obstructions, but mapgen decorations are generated after spawn so
3754 // the player may end up inside one.
3755 for (s32 i = 0; i < 8; i++) {
3756 v3s16 blockpos = getNodeBlockPos(nodepos);
3757 map.emergeBlock(blockpos, true);
3758 content_t c = map.getNode(nodepos).getContent();
3760 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3761 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3762 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3764 if (air_count >= 2) {
3765 // Spawn in lower empty node
3767 nodeposf = intToFloat(nodepos, BS);
3768 // Don't spawn the player outside map boundaries
3769 if (objectpos_over_limit(nodeposf))
3770 // Exit this loop, positions above are probably over limit
3773 // Good position found, cause an exit from main loop
3787 // No suitable spawn point found, return fallback 0,0,0
3788 return v3f(0.0f, 0.0f, 0.0f);
3791 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3793 if (delay == 0.0f) {
3794 // No delay, shutdown immediately
3795 m_shutdown_state.is_requested = true;
3796 // only print to the infostream, a chat message saying
3797 // "Server Shutting Down" is sent when the server destructs.
3798 infostream << "*** Immediate Server shutdown requested." << std::endl;
3799 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3800 // Negative delay, cancel shutdown if requested
3801 m_shutdown_state.reset();
3802 std::wstringstream ws;
3804 ws << L"*** Server shutdown canceled.";
3806 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3807 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3808 // m_shutdown_* are already handled, skip.
3810 } else if (delay > 0.0f) {
3811 // Positive delay, tell the clients when the server will shut down
3812 std::wstringstream ws;
3814 ws << L"*** Server shutting down in "
3815 << duration_to_string(myround(delay)).c_str()
3818 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3819 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3822 m_shutdown_state.trigger(delay, msg, reconnect);
3825 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3828 Try to get an existing player
3830 RemotePlayer *player = m_env->getPlayer(name);
3832 // If player is already connected, cancel
3833 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3834 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3839 If player with the wanted peer_id already exists, cancel.
3841 if (m_env->getPlayer(peer_id)) {
3842 infostream<<"emergePlayer(): Player with wrong name but same"
3843 " peer_id already exists"<<std::endl;
3848 player = new RemotePlayer(name, idef());
3851 bool newplayer = false;
3854 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3856 // Complete init with server parts
3857 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3858 player->protocol_version = proto_version;
3862 m_script->on_newplayer(playersao);
3868 void dedicated_server_loop(Server &server, bool &kill)
3870 verbosestream<<"dedicated_server_loop()"<<std::endl;
3872 IntervalLimiter m_profiler_interval;
3874 static thread_local const float steplen =
3875 g_settings->getFloat("dedicated_server_step");
3876 static thread_local const float profiler_print_interval =
3877 g_settings->getFloat("profiler_print_interval");
3880 * The dedicated server loop only does time-keeping (in Server::step) and
3881 * provides a way to main.cpp to kill the server externally (bool &kill).
3885 // This is kind of a hack but can be done like this
3886 // because server.step() is very light
3887 sleep_ms((int)(steplen*1000.0));
3888 server.step(steplen);
3890 if (server.isShutdownRequested() || kill)
3896 if (profiler_print_interval != 0) {
3897 if(m_profiler_interval.step(steplen, profiler_print_interval))
3899 infostream<<"Profiler:"<<std::endl;
3900 g_profiler->print(infostream);
3901 g_profiler->clear();
3906 infostream << "Dedicated server quitting" << std::endl;
3908 if (g_settings->getBool("server_announce"))
3909 ServerList::sendAnnounce(ServerList::AA_DELETE,
3910 server.m_bind_addr.getPort());
3919 bool Server::joinModChannel(const std::string &channel)
3921 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3922 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3925 bool Server::leaveModChannel(const std::string &channel)
3927 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3930 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3932 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3935 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3939 ModChannel* Server::getModChannel(const std::string &channel)
3941 return m_modchannel_mgr->getModChannel(channel);
3944 void Server::broadcastModChannelMessage(const std::string &channel,
3945 const std::string &message, session_t from_peer)
3947 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3951 if (message.size() > STRING_MAX_LEN) {
3952 warningstream << "ModChannel message too long, dropping before sending "
3953 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3954 << channel << ")" << std::endl;
3959 if (from_peer != PEER_ID_SERVER) {
3960 sender = getPlayerName(from_peer);
3963 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3964 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3965 resp_pkt << channel << sender << message;
3966 for (session_t peer_id : peers) {
3968 if (peer_id == from_peer)
3971 Send(peer_id, &resp_pkt);
3974 if (from_peer != PEER_ID_SERVER) {
3975 m_script->on_modchannel_message(channel, sender, message);
3979 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3981 if (lang_code.empty())
3984 auto it = server_translations.find(lang_code);
3985 if (it != server_translations.end())
3986 return &it->second; // Already loaded
3988 // [] will create an entry
3989 auto *translations = &server_translations[lang_code];
3991 std::string suffix = "." + lang_code + ".tr";
3992 for (const auto &i : m_media) {
3993 if (str_ends_with(i.first, suffix)) {
3995 if (fs::ReadFile(i.second.path, data)) {
3996 translations->loadTranslation(data);
4001 return translations;
4004 ModStorageDatabase *Server::openModStorageDatabase(const std::string &world_path)
4006 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4008 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4009 throw BaseException("Cannot read world.mt!");
4011 std::string backend = world_mt.exists("mod_storage_backend") ?
4012 world_mt.get("mod_storage_backend") : "files";
4013 if (backend == "files")
4014 warningstream << "/!\\ You are using the old mod storage files backend. "
4015 << "This backend is deprecated and may be removed in a future release /!\\"
4016 << std::endl << "Switching to SQLite3 is advised, "
4017 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4019 return openModStorageDatabase(backend, world_path, world_mt);
4022 ModStorageDatabase *Server::openModStorageDatabase(const std::string &backend,
4023 const std::string &world_path, const Settings &world_mt)
4025 if (backend == "sqlite3")
4026 return new ModStorageDatabaseSQLite3(world_path);
4029 if (backend == "postgresql") {
4030 std::string connect_string;
4031 world_mt.getNoEx("pgsql_mod_storage_connection", connect_string);
4032 return new ModStorageDatabasePostgreSQL(connect_string);
4034 #endif // USE_POSTGRESQL
4036 if (backend == "files")
4037 return new ModStorageDatabaseFiles(world_path);
4039 if (backend == "dummy")
4040 return new Database_Dummy();
4042 throw BaseException("Mod storage database backend " + backend + " not supported");
4045 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4047 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4049 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4050 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4051 errorstream << "Cannot read world.mt!" << std::endl;
4055 std::string backend = world_mt.exists("mod_storage_backend") ?
4056 world_mt.get("mod_storage_backend") : "files";
4057 if (backend == migrate_to) {
4058 errorstream << "Cannot migrate: new backend is same"
4059 << " as the old one" << std::endl;
4063 ModStorageDatabase *srcdb = nullptr;
4064 ModStorageDatabase *dstdb = nullptr;
4066 bool succeeded = false;
4069 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4070 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4074 std::vector<std::string> mod_list;
4075 srcdb->listMods(&mod_list);
4076 for (const std::string &modname : mod_list) {
4078 srcdb->getModEntries(modname, &meta);
4079 for (const auto &pair : meta) {
4080 dstdb->setModEntry(modname, pair.first, pair.second);
4088 actionstream << "Successfully migrated the metadata of "
4089 << mod_list.size() << " mods" << std::endl;
4090 world_mt.set("mod_storage_backend", migrate_to);
4091 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4092 errorstream << "Failed to update world.mt!" << std::endl;
4094 actionstream << "world.mt updated" << std::endl;
4096 } catch (BaseException &e) {
4097 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4103 if (succeeded && backend == "files") {
4105 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4106 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4107 if (!fs::Rename(storage_path, backup_path))
4108 warningstream << "After migration, " << storage_path
4109 << " could not be renamed to " << backup_path << std::endl;