3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "network/connection.h"
25 #include "network/networkprotocol.h"
26 #include "network/serveropcodes.h"
28 #include "environment.h"
30 #include "threading/mutex_auto_lock.h"
31 #include "constants.h"
37 #include "server/serveractiveobject.h"
41 #include "scripting_server.h"
46 #include "mapgen/mapgen.h"
47 #include "mapgen/mg_biome.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content/mods.h"
51 #include "modchannels.h"
52 #include "serverlist.h"
53 #include "util/string.h"
55 #include "util/serialize.h"
56 #include "util/thread.h"
57 #include "defaultsettings.h"
58 #include "server/mods.h"
59 #include "util/base64.h"
60 #include "util/sha1.h"
62 #include "database/database.h"
63 #include "chatmessage.h"
64 #include "chat_interface.h"
65 #include "remoteplayer.h"
66 #include "server/player_sao.h"
67 #include "server/serverinventorymgr.h"
68 #include "translation.h"
69 #include "database/database-sqlite3.h"
70 #include "database/database-files.h"
71 #include "database/database-dummy.h"
72 #include "gameparams.h"
74 class ClientNotFoundException : public BaseException
77 ClientNotFoundException(const char *s):
82 class ServerThread : public Thread
86 ServerThread(Server *server):
97 void *ServerThread::run()
99 BEGIN_DEBUG_EXCEPTION_HANDLER
102 * The real business of the server happens on the ServerThread.
104 * AsyncRunStep() runs an actual server step as soon as enough time has
105 * passed (dedicated_server_loop keeps track of that).
106 * Receive() blocks at least(!) 30ms waiting for a packet (so this loop
107 * doesn't busy wait) and will process any remaining packets.
111 m_server->AsyncRunStep(true);
112 } catch (con::ConnectionBindFailed &e) {
113 m_server->setAsyncFatalError(e.what());
114 } catch (LuaError &e) {
115 m_server->setAsyncFatalError(e);
118 while (!stopRequested()) {
120 m_server->AsyncRunStep();
124 } catch (con::PeerNotFoundException &e) {
125 infostream<<"Server: PeerNotFoundException"<<std::endl;
126 } catch (ClientNotFoundException &e) {
127 } catch (con::ConnectionBindFailed &e) {
128 m_server->setAsyncFatalError(e.what());
129 } catch (LuaError &e) {
130 m_server->setAsyncFatalError(e);
134 END_DEBUG_EXCEPTION_HANDLER
139 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
141 if(pos_exists) *pos_exists = false;
146 if(pos_exists) *pos_exists = true;
151 ServerActiveObject *sao = env->getActiveObject(object);
154 if(pos_exists) *pos_exists = true;
155 return sao->getBasePosition(); }
160 void Server::ShutdownState::reset()
164 should_reconnect = false;
165 is_requested = false;
168 void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
172 should_reconnect = reconnect;
175 void Server::ShutdownState::tick(float dtime, Server *server)
181 static const float shutdown_msg_times[] =
183 1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
186 // Automated messages
187 if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
188 for (float t : shutdown_msg_times) {
189 // If shutdown timer matches an automessage, shot it
190 if (m_timer > t && m_timer - dtime < t) {
191 std::wstring periodicMsg = getShutdownTimerMessage();
193 infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
194 server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
201 if (m_timer < 0.0f) {
207 std::wstring Server::ShutdownState::getShutdownTimerMessage() const
209 std::wstringstream ws;
210 ws << L"*** Server shutting down in "
211 << duration_to_string(myround(m_timer)).c_str() << ".";
220 const std::string &path_world,
221 const SubgameSpec &gamespec,
222 bool simple_singleplayer_mode,
225 ChatInterface *iface,
226 std::string *on_shutdown_errmsg
228 m_bind_addr(bind_addr),
229 m_path_world(path_world),
230 m_gamespec(gamespec),
231 m_simple_singleplayer_mode(simple_singleplayer_mode),
232 m_dedicated(dedicated),
233 m_async_fatal_error(""),
234 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
237 m_bind_addr.isIPv6(),
239 m_itemdef(createItemDefManager()),
240 m_nodedef(createNodeDefManager()),
241 m_craftdef(createCraftDefManager()),
242 m_thread(new ServerThread(this)),
245 m_on_shutdown_errmsg(on_shutdown_errmsg),
246 m_modchannel_mgr(new ModChannelMgr())
248 if (m_path_world.empty())
249 throw ServerError("Supplied empty world path");
251 if (!gamespec.isValid())
252 throw ServerError("Supplied invalid gamespec");
255 m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
257 m_metrics_backend = std::unique_ptr<MetricsBackend>(new MetricsBackend());
260 m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
261 m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
263 m_timeofday_gauge = m_metrics_backend->addGauge(
264 "minetest_core_timeofday",
265 "Time of day value");
267 m_lag_gauge = m_metrics_backend->addGauge(
268 "minetest_core_latency",
269 "Latency value (in seconds)");
271 m_aom_buffer_counter = m_metrics_backend->addCounter(
272 "minetest_core_aom_generated_count",
273 "Number of active object messages generated");
275 m_packet_recv_counter = m_metrics_backend->addCounter(
276 "minetest_core_server_packet_recv",
277 "Processable packets received");
279 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
280 "minetest_core_server_packet_recv_processed",
281 "Valid received packets processed");
283 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
289 // Send shutdown message
290 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
291 L"*** Server shutting down"));
294 MutexAutoLock envlock(m_env_mutex);
296 infostream << "Server: Saving players" << std::endl;
297 m_env->saveLoadedPlayers();
299 infostream << "Server: Kicking players" << std::endl;
300 std::string kick_msg;
301 bool reconnect = false;
302 if (isShutdownRequested()) {
303 reconnect = m_shutdown_state.should_reconnect;
304 kick_msg = m_shutdown_state.message;
306 if (kick_msg.empty()) {
307 kick_msg = g_settings->get("kick_msg_shutdown");
309 m_env->saveLoadedPlayers(true);
310 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
311 kick_msg, reconnect);
314 actionstream << "Server: Shutting down" << std::endl;
316 // Do this before stopping the server in case mapgen callbacks need to access
317 // server-controlled resources (like ModStorages). Also do them before
318 // shutdown callbacks since they may modify state that is finalized in a
321 m_emerge->stopThreads();
324 MutexAutoLock envlock(m_env_mutex);
326 // Execute script shutdown hooks
327 infostream << "Executing shutdown hooks" << std::endl;
329 m_script->on_shutdown();
330 } catch (ModError &e) {
331 errorstream << "ModError: " << e.what() << std::endl;
332 if (m_on_shutdown_errmsg) {
333 if (m_on_shutdown_errmsg->empty()) {
334 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
336 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
341 infostream << "Server: Saving environment metadata" << std::endl;
351 // Write any changes before deletion.
352 if (m_mod_storage_database)
353 m_mod_storage_database->endSave();
355 // Delete things in the reverse order of creation
359 delete m_mod_storage_database;
365 // Deinitialize scripting
366 infostream << "Server: Deinitializing scripting" << std::endl;
368 delete m_startup_server_map; // if available
369 delete m_game_settings;
371 while (!m_unsent_map_edit_queue.empty()) {
372 delete m_unsent_map_edit_queue.front();
373 m_unsent_map_edit_queue.pop();
379 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
380 if (m_simple_singleplayer_mode)
381 infostream << " in simple singleplayer mode" << std::endl;
383 infostream << std::endl;
384 infostream << "- world: " << m_path_world << std::endl;
385 infostream << "- game: " << m_gamespec.path << std::endl;
387 m_game_settings = Settings::createLayer(SL_GAME);
389 // Create world if it doesn't exist
391 loadGameConfAndInitWorld(m_path_world,
392 fs::GetFilenameFromPath(m_path_world.c_str()),
394 } catch (const BaseException &e) {
395 throw ServerError(std::string("Failed to initialize world: ") + e.what());
398 // Create emerge manager
399 m_emerge = new EmergeManager(this);
401 // Create ban manager
402 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
403 m_banmanager = new BanManager(ban_path);
405 // Create mod storage database and begin a save for later
406 m_mod_storage_database = openModStorageDatabase(m_path_world);
407 m_mod_storage_database->beginSave();
409 m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager(m_path_world));
410 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
411 // complain about mods with unsatisfied dependencies
412 if (!m_modmgr->isConsistent()) {
413 m_modmgr->printUnsatisfiedModsError();
417 MutexAutoLock envlock(m_env_mutex);
419 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
420 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
421 m_startup_server_map = servermap;
423 // Initialize scripting
424 infostream << "Server: Initializing Lua" << std::endl;
426 m_script = new ServerScripting(this);
428 // Must be created before mod loading because we have some inventory creation
429 m_inventory_mgr = std::unique_ptr<ServerInventoryManager>(new ServerInventoryManager());
431 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
433 m_modmgr->loadMods(m_script);
435 // Read Textures and calculate sha1 sums
438 // Apply item aliases in the node definition manager
439 m_nodedef->updateAliases(m_itemdef);
441 // Apply texture overrides from texturepack/override.txt
442 std::vector<std::string> paths;
443 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
444 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
445 for (const std::string &path : paths) {
446 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
447 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
448 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
451 m_nodedef->setNodeRegistrationStatus(true);
453 // Perform pending node name resolutions
454 m_nodedef->runNodeResolveCallbacks();
456 // unmap node names in cross-references
457 m_nodedef->resolveCrossrefs();
459 // init the recipe hashes to speed up crafting
460 m_craftdef->initHashes(this);
462 // Initialize Environment
463 m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
464 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
466 m_inventory_mgr->setEnv(m_env);
467 m_clients.setEnv(m_env);
469 if (!servermap->settings_mgr.makeMapgenParams())
470 FATAL_ERROR("Couldn't create any mapgen type");
472 // Initialize mapgens
473 m_emerge->initMapgens(servermap->getMapgenParams());
475 if (g_settings->getBool("enable_rollback_recording")) {
476 // Create rollback manager
477 m_rollback = new RollbackManager(m_path_world, this);
480 // Give environment reference to scripting api
481 m_script->initializeEnvironment(m_env);
483 // Register us to receive map edit events
484 servermap->addEventReceiver(this);
488 // Those settings can be overwritten in world.mt, they are
489 // intended to be cached after environment loading.
490 m_liquid_transform_every = g_settings->getFloat("liquid_update");
491 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
492 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
493 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
500 infostream << "Starting server on " << m_bind_addr.serializeString()
501 << "..." << std::endl;
503 // Stop thread if already running
506 // Initialize connection
507 m_con->SetTimeoutMs(30);
508 m_con->Serve(m_bind_addr);
513 // ASCII art for the win!
515 << " .__ __ __ " << std::endl
516 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
517 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
518 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
519 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
520 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
521 actionstream << "World at [" << m_path_world << "]" << std::endl;
522 actionstream << "Server for gameid=\"" << m_gamespec.id
523 << "\" listening on ";
524 m_bind_addr.print(&actionstream);
525 actionstream << "." << std::endl;
530 infostream<<"Server: Stopping and waiting threads"<<std::endl;
532 // Stop threads (set run=false first so both start stopping)
536 infostream<<"Server: Threads stopped"<<std::endl;
539 void Server::step(float dtime)
545 MutexAutoLock lock(m_step_dtime_mutex);
546 m_step_dtime += dtime;
548 // Throw if fatal error occurred in thread
549 std::string async_err = m_async_fatal_error.get();
550 if (!async_err.empty()) {
551 if (!m_simple_singleplayer_mode) {
552 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
553 g_settings->get("kick_msg_crash"),
554 g_settings->getBool("ask_reconnect_on_crash"));
556 throw ServerError("AsyncErr: " + async_err);
560 void Server::AsyncRunStep(bool initial_step)
565 MutexAutoLock lock1(m_step_dtime_mutex);
566 dtime = m_step_dtime;
570 // Send blocks to clients
574 if((dtime < 0.001) && !initial_step)
577 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
580 MutexAutoLock lock1(m_step_dtime_mutex);
581 m_step_dtime -= dtime;
587 m_uptime_counter->increment(dtime);
592 Update time of day and overall game time
594 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
597 Send to clients at constant intervals
600 m_time_of_day_send_timer -= dtime;
601 if (m_time_of_day_send_timer < 0.0) {
602 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
603 u16 time = m_env->getTimeOfDay();
604 float time_speed = g_settings->getFloat("time_speed");
605 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
607 m_timeofday_gauge->set(time);
611 MutexAutoLock lock(m_env_mutex);
612 // Figure out and report maximum lag to environment
613 float max_lag = m_env->getMaxLagEstimate();
614 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
616 if(dtime > 0.1 && dtime > max_lag * 2.0)
617 infostream<<"Server: Maximum lag peaked to "<<dtime
621 m_env->reportMaxLagEstimate(max_lag);
626 static const float map_timer_and_unload_dtime = 2.92;
627 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
629 MutexAutoLock lock(m_env_mutex);
630 // Run Map's timers and unload unused data
631 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
632 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
633 g_settings->getFloat("server_unload_unused_data_timeout"),
638 Listen to the admin chat, if available
641 if (!m_admin_chat->command_queue.empty()) {
642 MutexAutoLock lock(m_env_mutex);
643 while (!m_admin_chat->command_queue.empty()) {
644 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
645 handleChatInterfaceEvent(evt);
649 m_admin_chat->outgoing_queue.push_back(
650 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
657 /* Transform liquids */
658 m_liquid_transform_timer += dtime;
659 if(m_liquid_transform_timer >= m_liquid_transform_every)
661 m_liquid_transform_timer -= m_liquid_transform_every;
663 MutexAutoLock lock(m_env_mutex);
665 ScopeProfiler sp(g_profiler, "Server: liquid transform");
667 std::map<v3s16, MapBlock*> modified_blocks;
668 m_env->getMap().transformLiquids(modified_blocks, m_env);
671 Set the modified blocks unsent for all the clients
673 if (!modified_blocks.empty()) {
674 SetBlocksNotSent(modified_blocks);
677 m_clients.step(dtime);
679 // increase/decrease lag gauge gradually
680 if (m_lag_gauge->get() > dtime) {
681 m_lag_gauge->decrement(dtime/100);
683 m_lag_gauge->increment(dtime/100);
687 float &counter = m_step_pending_dyn_media_timer;
689 if (counter >= 5.0f) {
690 stepPendingDynMediaCallbacks(counter);
697 // send masterserver announce
699 float &counter = m_masterserver_timer;
700 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
701 g_settings->getBool("server_announce")) {
702 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
703 ServerList::AA_START,
704 m_bind_addr.getPort(),
705 m_clients.getPlayerNames(),
706 m_uptime_counter->get(),
707 m_env->getGameTime(),
710 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
720 Check added and deleted active objects
723 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
724 MutexAutoLock envlock(m_env_mutex);
727 const RemoteClientMap &clients = m_clients.getClientList();
728 ScopeProfiler sp(g_profiler, "Server: update objects within range");
730 m_player_gauge->set(clients.size());
731 for (const auto &client_it : clients) {
732 RemoteClient *client = client_it.second;
734 if (client->getState() < CS_DefinitionsSent)
737 // This can happen if the client times out somehow
738 if (!m_env->getPlayer(client->peer_id))
741 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
745 SendActiveObjectRemoveAdd(client, playersao);
749 // Write changes to the mod storage
750 m_mod_storage_save_timer -= dtime;
751 if (m_mod_storage_save_timer <= 0.0f) {
752 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
753 m_mod_storage_database->endSave();
754 m_mod_storage_database->beginSave();
762 MutexAutoLock envlock(m_env_mutex);
763 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
766 // Value = data sent by object
767 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
769 // Get active object messages from environment
770 ActiveObjectMessage aom(0);
773 if (!m_env->getActiveObjectMessage(&aom))
776 std::vector<ActiveObjectMessage>* message_list = nullptr;
777 auto n = buffered_messages.find(aom.id);
778 if (n == buffered_messages.end()) {
779 message_list = new std::vector<ActiveObjectMessage>;
780 buffered_messages[aom.id] = message_list;
782 message_list = n->second;
784 message_list->push_back(std::move(aom));
788 m_aom_buffer_counter->increment(aom_count);
791 const RemoteClientMap &clients = m_clients.getClientList();
792 // Route data to every client
793 std::string reliable_data, unreliable_data;
794 for (const auto &client_it : clients) {
795 reliable_data.clear();
796 unreliable_data.clear();
797 RemoteClient *client = client_it.second;
798 PlayerSAO *player = getPlayerSAO(client->peer_id);
799 // Go through all objects in message buffer
800 for (const auto &buffered_message : buffered_messages) {
801 // If object does not exist or is not known by client, skip it
802 u16 id = buffered_message.first;
803 ServerActiveObject *sao = m_env->getActiveObject(id);
804 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
807 // Get message list of object
808 std::vector<ActiveObjectMessage>* list = buffered_message.second;
809 // Go through every message
810 for (const ActiveObjectMessage &aom : *list) {
811 // Send position updates to players who do not see the attachment
812 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
813 if (sao->getId() == player->getId())
816 // Do not send position updates for attached players
817 // as long the parent is known to the client
818 ServerActiveObject *parent = sao->getParent();
819 if (parent && client->m_known_objects.find(parent->getId()) !=
820 client->m_known_objects.end())
824 // Add full new data to appropriate buffer
825 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
827 writeU16((u8*) idbuf, aom.id);
830 buffer.append(idbuf, sizeof(idbuf));
831 buffer.append(serializeString16(aom.datastring));
835 reliable_data and unreliable_data are now ready.
838 if (!reliable_data.empty()) {
839 SendActiveObjectMessages(client->peer_id, reliable_data);
842 if (!unreliable_data.empty()) {
843 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
848 // Clear buffered_messages
849 for (auto &buffered_message : buffered_messages) {
850 delete buffered_message.second;
855 Send queued-for-sending map edit events.
858 // We will be accessing the environment
859 MutexAutoLock lock(m_env_mutex);
861 // Don't send too many at a time
864 // Single change sending is disabled if queue size is not small
865 bool disable_single_change_sending = false;
866 if(m_unsent_map_edit_queue.size() >= 4)
867 disable_single_change_sending = true;
869 int event_count = m_unsent_map_edit_queue.size();
871 // We'll log the amount of each
874 std::list<v3s16> node_meta_updates;
876 while (!m_unsent_map_edit_queue.empty()) {
877 MapEditEvent* event = m_unsent_map_edit_queue.front();
878 m_unsent_map_edit_queue.pop();
880 // Players far away from the change are stored here.
881 // Instead of sending the changes, MapBlocks are set not sent
883 std::unordered_set<u16> far_players;
885 switch (event->type) {
888 prof.add("MEET_ADDNODE", 1);
889 sendAddNode(event->p, event->n, &far_players,
890 disable_single_change_sending ? 5 : 30,
891 event->type == MEET_ADDNODE);
893 case MEET_REMOVENODE:
894 prof.add("MEET_REMOVENODE", 1);
895 sendRemoveNode(event->p, &far_players,
896 disable_single_change_sending ? 5 : 30);
898 case MEET_BLOCK_NODE_METADATA_CHANGED: {
899 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
900 if (!event->is_private_change) {
901 // Don't send the change yet. Collect them to eliminate dupes.
902 node_meta_updates.remove(event->p);
903 node_meta_updates.push_back(event->p);
906 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
907 getNodeBlockPos(event->p))) {
908 block->raiseModified(MOD_STATE_WRITE_NEEDED,
909 MOD_REASON_REPORT_META_CHANGE);
914 prof.add("MEET_OTHER", 1);
915 for (const v3s16 &modified_block : event->modified_blocks) {
916 m_clients.markBlockposAsNotSent(modified_block);
920 prof.add("unknown", 1);
921 warningstream << "Server: Unknown MapEditEvent "
922 << ((u32)event->type) << std::endl;
927 Set blocks not sent to far players
929 if (!far_players.empty()) {
930 // Convert list format to that wanted by SetBlocksNotSent
931 std::map<v3s16, MapBlock*> modified_blocks2;
932 for (const v3s16 &modified_block : event->modified_blocks) {
933 modified_blocks2[modified_block] =
934 m_env->getMap().getBlockNoCreateNoEx(modified_block);
937 // Set blocks not sent
938 for (const u16 far_player : far_players) {
939 if (RemoteClient *client = getClient(far_player))
940 client->SetBlocksNotSent(modified_blocks2);
947 if (event_count >= 5) {
948 infostream << "Server: MapEditEvents:" << std::endl;
949 prof.print(infostream);
950 } else if (event_count != 0) {
951 verbosestream << "Server: MapEditEvents:" << std::endl;
952 prof.print(verbosestream);
955 // Send all metadata updates
956 if (node_meta_updates.size())
957 sendMetadataChanged(node_meta_updates);
961 Trigger emerge thread
962 Doing this every 2s is left over from old code, unclear if this is still needed.
965 float &counter = m_emergethread_trigger_timer;
967 if (counter <= 0.0f) {
970 m_emerge->startThreads();
974 // Save map, players and auth stuff
976 float &counter = m_savemap_timer;
978 static thread_local const float save_interval =
979 g_settings->getFloat("server_map_save_interval");
980 if (counter >= save_interval) {
982 MutexAutoLock lock(m_env_mutex);
984 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
987 if (m_banmanager->isModified()) {
988 m_banmanager->save();
991 // Save changed parts of map
992 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
995 m_env->saveLoadedPlayers();
997 // Save environment metadata
1002 m_shutdown_state.tick(dtime, this);
1005 void Server::Receive()
1015 In the first iteration *wait* for a packet, afterwards process
1016 all packets that are immediately available (no waiting).
1019 m_con->Receive(&pkt);
1022 if (!m_con->TryReceive(&pkt))
1026 peer_id = pkt.getPeerId();
1027 m_packet_recv_counter->increment();
1029 m_packet_recv_processed_counter->increment();
1030 } catch (const con::InvalidIncomingDataException &e) {
1031 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1032 << e.what() << std::endl;
1033 } catch (const SerializationError &e) {
1034 infostream << "Server::Receive(): SerializationError: what()="
1035 << e.what() << std::endl;
1036 } catch (const ClientStateError &e) {
1037 errorstream << "ProcessData: peer=" << peer_id << " what()="
1038 << e.what() << std::endl;
1039 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
1040 L"Try reconnecting or updating your client");
1041 } catch (const con::PeerNotFoundException &e) {
1043 } catch (const con::NoIncomingDataException &e) {
1049 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1051 std::string playername;
1052 PlayerSAO *playersao = NULL;
1055 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1057 playername = client->getName();
1058 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1060 } catch (std::exception &e) {
1066 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1068 // If failed, cancel
1069 if (!playersao || !player) {
1070 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1071 actionstream << "Server: Failed to emerge player \"" << playername
1072 << "\" (player allocated to an another client)" << std::endl;
1073 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1074 L"name. If your client closed unexpectedly, try again in "
1077 errorstream << "Server: " << playername << ": Failed to emerge player"
1079 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1085 Send complete position information
1087 SendMovePlayer(peer_id);
1090 SendPlayerPrivileges(peer_id);
1092 // Send inventory formspec
1093 SendPlayerInventoryFormspec(peer_id);
1096 SendInventory(playersao, false);
1099 SendPlayerHP(playersao);
1101 // Send death screen
1102 if (playersao->isDead())
1103 SendDeathscreen(peer_id, false, v3f(0,0,0));
1106 SendPlayerBreath(playersao);
1112 Address addr = getPeerAddress(player->getPeerId());
1113 std::string ip_str = addr.serializeString();
1114 const std::vector<std::string> &names = m_clients.getPlayerNames();
1116 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1118 for (const std::string &name : names) {
1119 actionstream << name << " ";
1122 actionstream << player->getName() <<std::endl;
1127 inline void Server::handleCommand(NetworkPacket *pkt)
1129 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1130 (this->*opHandle.handler)(pkt);
1133 void Server::ProcessData(NetworkPacket *pkt)
1135 // Environment is locked first.
1136 MutexAutoLock envlock(m_env_mutex);
1138 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1139 u32 peer_id = pkt->getPeerId();
1142 Address address = getPeerAddress(peer_id);
1143 std::string addr_s = address.serializeString();
1145 if(m_banmanager->isIpBanned(addr_s)) {
1146 std::string ban_name = m_banmanager->getBanName(addr_s);
1147 infostream << "Server: A banned client tried to connect from "
1148 << addr_s << "; banned name was "
1149 << ban_name << std::endl;
1150 // This actually doesn't seem to transfer to the client
1151 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1152 + utf8_to_wide(ban_name));
1156 catch(con::PeerNotFoundException &e) {
1158 * no peer for this packet found
1159 * most common reason is peer timeout, e.g. peer didn't
1160 * respond for some time, your server was overloaded or
1163 infostream << "Server::ProcessData(): Canceling: peer "
1164 << peer_id << " not found" << std::endl;
1169 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1171 // Command must be handled into ToServerCommandHandler
1172 if (command >= TOSERVER_NUM_MSG_TYPES) {
1173 infostream << "Server: Ignoring unknown command "
1174 << command << std::endl;
1178 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1183 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1185 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1186 errorstream << "Server::ProcessData(): Cancelling: Peer"
1187 " serialization format invalid or not initialized."
1188 " Skipping incoming command=" << command << std::endl;
1192 /* Handle commands related to client startup */
1193 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1198 if (m_clients.getClientState(peer_id) < CS_Active) {
1199 if (command == TOSERVER_PLAYERPOS) return;
1201 errorstream << "Got packet command: " << command << " for peer id "
1202 << peer_id << " but client isn't active yet. Dropping packet "
1208 } catch (SendFailedException &e) {
1209 errorstream << "Server::ProcessData(): SendFailedException: "
1210 << "what=" << e.what()
1212 } catch (PacketError &e) {
1213 actionstream << "Server::ProcessData(): PacketError: "
1214 << "what=" << e.what()
1219 void Server::setTimeOfDay(u32 time)
1221 m_env->setTimeOfDay(time);
1222 m_time_of_day_send_timer = 0;
1225 void Server::onMapEditEvent(const MapEditEvent &event)
1227 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1230 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1233 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1235 std::vector<session_t> clients = m_clients.getClientIDs();
1237 // Set the modified blocks unsent for all the clients
1238 for (const session_t client_id : clients) {
1239 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1240 client->SetBlocksNotSent(block);
1245 void Server::peerAdded(con::Peer *peer)
1247 verbosestream<<"Server::peerAdded(): peer->id="
1248 <<peer->id<<std::endl;
1250 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1253 void Server::deletingPeer(con::Peer *peer, bool timeout)
1255 verbosestream<<"Server::deletingPeer(): peer->id="
1256 <<peer->id<<", timeout="<<timeout<<std::endl;
1258 m_clients.event(peer->id, CSE_Disconnect);
1259 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1262 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1264 *retval = m_con->getPeerStat(peer_id,type);
1265 return *retval != -1;
1268 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1271 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1278 ret.state = client->getState();
1279 ret.addr = client->getAddress();
1280 ret.uptime = client->uptime();
1281 ret.ser_vers = client->serialization_version;
1282 ret.prot_vers = client->net_proto_version;
1284 ret.major = client->getMajor();
1285 ret.minor = client->getMinor();
1286 ret.patch = client->getPatch();
1287 ret.vers_string = client->getFullVer();
1289 ret.lang_code = client->getLangCode();
1296 void Server::handlePeerChanges()
1298 while(!m_peer_change_queue.empty())
1300 con::PeerChange c = m_peer_change_queue.front();
1301 m_peer_change_queue.pop();
1303 verbosestream<<"Server: Handling peer change: "
1304 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1309 case con::PEER_ADDED:
1310 m_clients.CreateClient(c.peer_id);
1313 case con::PEER_REMOVED:
1314 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1318 FATAL_ERROR("Invalid peer change event received!");
1324 void Server::printToConsoleOnly(const std::string &text)
1327 m_admin_chat->outgoing_queue.push_back(
1328 new ChatEventChat("", utf8_to_wide(text)));
1330 std::cout << text << std::endl;
1334 void Server::Send(NetworkPacket *pkt)
1336 Send(pkt->getPeerId(), pkt);
1339 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1341 m_clients.send(peer_id,
1342 clientCommandFactoryTable[pkt->getCommand()].channel,
1344 clientCommandFactoryTable[pkt->getCommand()].reliable);
1347 void Server::SendMovement(session_t peer_id)
1349 std::ostringstream os(std::ios_base::binary);
1351 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1353 pkt << g_settings->getFloat("movement_acceleration_default");
1354 pkt << g_settings->getFloat("movement_acceleration_air");
1355 pkt << g_settings->getFloat("movement_acceleration_fast");
1356 pkt << g_settings->getFloat("movement_speed_walk");
1357 pkt << g_settings->getFloat("movement_speed_crouch");
1358 pkt << g_settings->getFloat("movement_speed_fast");
1359 pkt << g_settings->getFloat("movement_speed_climb");
1360 pkt << g_settings->getFloat("movement_speed_jump");
1361 pkt << g_settings->getFloat("movement_liquid_fluidity");
1362 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1363 pkt << g_settings->getFloat("movement_liquid_sink");
1364 pkt << g_settings->getFloat("movement_gravity");
1369 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1371 m_script->player_event(playersao, "health_changed");
1372 SendPlayerHP(playersao);
1374 // Send to other clients
1375 playersao->sendPunchCommand();
1377 if (playersao->isDead())
1378 HandlePlayerDeath(playersao, reason);
1381 void Server::SendPlayerHP(PlayerSAO *playersao)
1383 SendHP(playersao->getPeerID(), playersao->getHP());
1386 void Server::SendHP(session_t peer_id, u16 hp)
1388 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1393 void Server::SendBreath(session_t peer_id, u16 breath)
1395 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1396 pkt << (u16) breath;
1400 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1401 const std::string &custom_reason, bool reconnect)
1403 assert(reason < SERVER_ACCESSDENIED_MAX);
1405 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1407 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1408 pkt << custom_reason;
1409 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1410 reason == SERVER_ACCESSDENIED_CRASH)
1411 pkt << custom_reason << (u8)reconnect;
1415 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1417 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1422 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1423 v3f camera_point_target)
1425 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1426 pkt << set_camera_point_target << camera_point_target;
1430 void Server::SendItemDef(session_t peer_id,
1431 IItemDefManager *itemdef, u16 protocol_version)
1433 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1437 u32 length of the next item
1438 zlib-compressed serialized ItemDefManager
1440 std::ostringstream tmp_os(std::ios::binary);
1441 itemdef->serialize(tmp_os, protocol_version);
1442 std::ostringstream tmp_os2(std::ios::binary);
1443 compressZlib(tmp_os.str(), tmp_os2);
1444 pkt.putLongString(tmp_os2.str());
1447 verbosestream << "Server: Sending item definitions to id(" << peer_id
1448 << "): size=" << pkt.getSize() << std::endl;
1453 void Server::SendNodeDef(session_t peer_id,
1454 const NodeDefManager *nodedef, u16 protocol_version)
1456 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1460 u32 length of the next item
1461 zlib-compressed serialized NodeDefManager
1463 std::ostringstream tmp_os(std::ios::binary);
1464 nodedef->serialize(tmp_os, protocol_version);
1465 std::ostringstream tmp_os2(std::ios::binary);
1466 compressZlib(tmp_os.str(), tmp_os2);
1468 pkt.putLongString(tmp_os2.str());
1471 verbosestream << "Server: Sending node definitions to id(" << peer_id
1472 << "): size=" << pkt.getSize() << std::endl;
1478 Non-static send methods
1481 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1483 RemotePlayer *player = sao->getPlayer();
1485 // Do not send new format to old clients
1486 incremental &= player->protocol_version >= 38;
1488 UpdateCrafting(player);
1494 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1496 std::ostringstream os(std::ios::binary);
1497 sao->getInventory()->serialize(os, incremental);
1498 sao->getInventory()->setModified(false);
1499 player->setModified(true);
1501 const std::string &s = os.str();
1502 pkt.putRawString(s.c_str(), s.size());
1506 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1508 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1510 u8 type = message.type;
1511 pkt << version << type << message.sender << message.message
1512 << static_cast<u64>(message.timestamp);
1514 if (peer_id != PEER_ID_INEXISTENT) {
1515 RemotePlayer *player = m_env->getPlayer(peer_id);
1521 m_clients.sendToAll(&pkt);
1525 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1526 const std::string &formname)
1528 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1529 if (formspec.empty()){
1530 //the client should close the formspec
1531 //but make sure there wasn't another one open in meantime
1532 const auto it = m_formspec_state_data.find(peer_id);
1533 if (it != m_formspec_state_data.end() && it->second == formname) {
1534 m_formspec_state_data.erase(peer_id);
1536 pkt.putLongString("");
1538 m_formspec_state_data[peer_id] = formname;
1539 pkt.putLongString(formspec);
1546 // Spawns a particle on peer with peer_id
1547 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1548 const ParticleParameters &p)
1550 static thread_local const float radius =
1551 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1553 if (peer_id == PEER_ID_INEXISTENT) {
1554 std::vector<session_t> clients = m_clients.getClientIDs();
1555 const v3f pos = p.pos * BS;
1556 const float radius_sq = radius * radius;
1558 for (const session_t client_id : clients) {
1559 RemotePlayer *player = m_env->getPlayer(client_id);
1563 PlayerSAO *sao = player->getPlayerSAO();
1567 // Do not send to distant clients
1568 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1571 SendSpawnParticle(client_id, player->protocol_version, p);
1575 assert(protocol_version != 0);
1577 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1580 // NetworkPacket and iostreams are incompatible...
1581 std::ostringstream oss(std::ios_base::binary);
1582 p.serialize(oss, protocol_version);
1583 pkt.putRawString(oss.str());
1589 // Adds a ParticleSpawner on peer with peer_id
1590 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1591 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1593 static thread_local const float radius =
1594 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1596 if (peer_id == PEER_ID_INEXISTENT) {
1597 std::vector<session_t> clients = m_clients.getClientIDs();
1598 const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS;
1599 const float radius_sq = radius * radius;
1600 /* Don't send short-lived spawners to distant players.
1601 * This could be replaced with proper tracking at some point. */
1602 const bool distance_check = !attached_id && p.time <= 1.0f;
1604 for (const session_t client_id : clients) {
1605 RemotePlayer *player = m_env->getPlayer(client_id);
1609 if (distance_check) {
1610 PlayerSAO *sao = player->getPlayerSAO();
1613 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1617 SendAddParticleSpawner(client_id, player->protocol_version,
1618 p, attached_id, id);
1622 assert(protocol_version != 0);
1624 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1626 pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel
1627 << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime
1628 << p.minsize << p.maxsize << p.collisiondetection;
1630 pkt.putLongString(p.texture);
1632 pkt << id << p.vertical << p.collision_removal << attached_id;
1634 std::ostringstream os(std::ios_base::binary);
1635 p.animation.serialize(os, protocol_version);
1636 pkt.putRawString(os.str());
1638 pkt << p.glow << p.object_collision;
1639 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1644 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1646 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1650 if (peer_id != PEER_ID_INEXISTENT)
1653 m_clients.sendToAll(&pkt);
1657 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1659 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1661 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1662 << form->text << form->number << form->item << form->dir
1663 << form->align << form->offset << form->world_pos << form->size
1664 << form->z_index << form->text2 << form->style;
1669 void Server::SendHUDRemove(session_t peer_id, u32 id)
1671 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1676 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1678 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1679 pkt << id << (u8) stat;
1683 case HUD_STAT_SCALE:
1684 case HUD_STAT_ALIGN:
1685 case HUD_STAT_OFFSET:
1686 pkt << *(v2f *) value;
1690 case HUD_STAT_TEXT2:
1691 pkt << *(std::string *) value;
1693 case HUD_STAT_WORLD_POS:
1694 pkt << *(v3f *) value;
1697 pkt << *(v2s32 *) value;
1699 default: // all other types
1700 pkt << *(u32 *) value;
1707 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1709 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1711 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1713 pkt << flags << mask;
1718 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1720 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1721 pkt << param << value;
1725 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1727 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1729 // Handle prior clients here
1730 if (m_clients.getProtocolVersion(peer_id) < 39) {
1731 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1733 for (const std::string& texture : params.textures)
1736 pkt << params.clouds;
1737 } else { // Handle current clients and future clients
1738 pkt << params.bgcolor << params.type
1739 << params.clouds << params.fog_sun_tint
1740 << params.fog_moon_tint << params.fog_tint_type;
1742 if (params.type == "skybox") {
1743 pkt << (u16) params.textures.size();
1744 for (const std::string &texture : params.textures)
1746 } else if (params.type == "regular") {
1747 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1748 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1749 << params.sky_color.night_sky << params.sky_color.night_horizon
1750 << params.sky_color.indoors;
1757 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1759 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1760 pkt << params.visible << params.texture
1761 << params.tonemap << params.sunrise
1762 << params.sunrise_visible << params.scale;
1766 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1768 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1770 pkt << params.visible << params.texture
1771 << params.tonemap << params.scale;
1775 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1777 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1779 pkt << params.visible << params.count
1780 << params.starcolor << params.scale;
1785 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1787 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1788 pkt << params.density << params.color_bright << params.color_ambient
1789 << params.height << params.thickness << params.speed;
1793 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1796 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1799 pkt << do_override << (u16) (ratio * 65535);
1804 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1806 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1807 pkt << time << time_speed;
1809 if (peer_id == PEER_ID_INEXISTENT) {
1810 m_clients.sendToAll(&pkt);
1817 void Server::SendPlayerBreath(PlayerSAO *sao)
1821 m_script->player_event(sao, "breath_changed");
1822 SendBreath(sao->getPeerID(), sao->getBreath());
1825 void Server::SendMovePlayer(session_t peer_id)
1827 RemotePlayer *player = m_env->getPlayer(peer_id);
1829 PlayerSAO *sao = player->getPlayerSAO();
1832 // Send attachment updates instantly to the client prior updating position
1833 sao->sendOutdatedData();
1835 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1836 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1839 v3f pos = sao->getBasePosition();
1840 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1841 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1842 << " pitch=" << sao->getLookPitch()
1843 << " yaw=" << sao->getRotation().Y
1850 void Server::SendPlayerFov(session_t peer_id)
1852 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1854 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1855 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1860 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1861 f32 animation_speed)
1863 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1866 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1867 << animation_frames[3] << animation_speed;
1872 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1874 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1875 pkt << first << third;
1879 void Server::SendPlayerPrivileges(session_t peer_id)
1881 RemotePlayer *player = m_env->getPlayer(peer_id);
1883 if(player->getPeerId() == PEER_ID_INEXISTENT)
1886 std::set<std::string> privs;
1887 m_script->getAuth(player->getName(), NULL, &privs);
1889 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1890 pkt << (u16) privs.size();
1892 for (const std::string &priv : privs) {
1899 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1901 RemotePlayer *player = m_env->getPlayer(peer_id);
1903 if (player->getPeerId() == PEER_ID_INEXISTENT)
1906 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1907 pkt.putLongString(player->inventory_formspec);
1912 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1914 RemotePlayer *player = m_env->getPlayer(peer_id);
1916 if (player->getPeerId() == PEER_ID_INEXISTENT)
1919 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1920 pkt << player->formspec_prepend;
1924 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1926 // Radius inside which objects are active
1927 static thread_local const s16 radius =
1928 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1930 // Radius inside which players are active
1931 static thread_local const bool is_transfer_limited =
1932 g_settings->exists("unlimited_player_transfer_distance") &&
1933 !g_settings->getBool("unlimited_player_transfer_distance");
1935 static thread_local const s16 player_transfer_dist =
1936 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
1938 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
1939 radius : player_transfer_dist;
1941 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
1945 std::queue<u16> removed_objects, added_objects;
1946 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
1947 client->m_known_objects, removed_objects);
1948 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
1949 client->m_known_objects, added_objects);
1951 int removed_count = removed_objects.size();
1952 int added_count = added_objects.size();
1954 if (removed_objects.empty() && added_objects.empty())
1960 // Handle removed objects
1961 writeU16((u8*)buf, removed_objects.size());
1962 data.append(buf, 2);
1963 while (!removed_objects.empty()) {
1965 u16 id = removed_objects.front();
1966 ServerActiveObject* obj = m_env->getActiveObject(id);
1968 // Add to data buffer for sending
1969 writeU16((u8*)buf, id);
1970 data.append(buf, 2);
1972 // Remove from known objects
1973 client->m_known_objects.erase(id);
1975 if (obj && obj->m_known_by_count > 0)
1976 obj->m_known_by_count--;
1978 removed_objects.pop();
1981 // Handle added objects
1982 writeU16((u8*)buf, added_objects.size());
1983 data.append(buf, 2);
1984 while (!added_objects.empty()) {
1986 u16 id = added_objects.front();
1987 ServerActiveObject *obj = m_env->getActiveObject(id);
1988 added_objects.pop();
1991 warningstream << FUNCTION_NAME << ": NULL object id="
1992 << (int)id << std::endl;
1997 u8 type = obj->getSendType();
1999 // Add to data buffer for sending
2000 writeU16((u8*)buf, id);
2001 data.append(buf, 2);
2002 writeU8((u8*)buf, type);
2003 data.append(buf, 1);
2005 data.append(serializeString32(
2006 obj->getClientInitializationData(client->net_proto_version)));
2008 // Add to known objects
2009 client->m_known_objects.insert(id);
2011 obj->m_known_by_count++;
2014 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2015 pkt.putRawString(data.c_str(), data.size());
2018 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2019 << removed_count << " removed, " << added_count << " added, "
2020 << "packet size is " << pkt.getSize() << std::endl;
2023 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2026 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2027 datas.size(), peer_id);
2029 pkt.putRawString(datas.c_str(), datas.size());
2031 m_clients.send(pkt.getPeerId(),
2032 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2036 void Server::SendCSMRestrictionFlags(session_t peer_id)
2038 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2039 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2040 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2044 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2046 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2051 inline s32 Server::nextSoundId()
2053 s32 ret = m_next_sound_id;
2054 if (m_next_sound_id == INT32_MAX)
2055 m_next_sound_id = 0; // signed overflow is undefined
2061 s32 Server::playSound(const SimpleSoundSpec &spec,
2062 const ServerSoundParams ¶ms, bool ephemeral)
2064 // Find out initial position of sound
2065 bool pos_exists = false;
2066 v3f pos = params.getPos(m_env, &pos_exists);
2067 // If position is not found while it should be, cancel sound
2068 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2071 // Filter destination clients
2072 std::vector<session_t> dst_clients;
2073 if (!params.to_player.empty()) {
2074 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2076 infostream<<"Server::playSound: Player \""<<params.to_player
2077 <<"\" not found"<<std::endl;
2080 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2081 infostream<<"Server::playSound: Player \""<<params.to_player
2082 <<"\" not connected"<<std::endl;
2085 dst_clients.push_back(player->getPeerId());
2087 std::vector<session_t> clients = m_clients.getClientIDs();
2089 for (const session_t client_id : clients) {
2090 RemotePlayer *player = m_env->getPlayer(client_id);
2093 if (!params.exclude_player.empty() &&
2094 params.exclude_player == player->getName())
2097 PlayerSAO *sao = player->getPlayerSAO();
2102 if(sao->getBasePosition().getDistanceFrom(pos) >
2103 params.max_hear_distance)
2106 dst_clients.push_back(client_id);
2110 if(dst_clients.empty())
2115 ServerPlayingSound *psound = nullptr;
2117 id = -1; // old clients will still use this, so pick a reserved ID
2120 // The sound will exist as a reference in m_playing_sounds
2121 m_playing_sounds[id] = ServerPlayingSound();
2122 psound = &m_playing_sounds[id];
2123 psound->params = params;
2124 psound->spec = spec;
2127 float gain = params.gain * spec.gain;
2128 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2129 pkt << id << spec.name << gain
2130 << (u8) params.type << pos << params.object
2131 << params.loop << params.fade << params.pitch
2134 bool as_reliable = !ephemeral;
2136 for (const u16 dst_client : dst_clients) {
2138 psound->clients.insert(dst_client);
2139 m_clients.send(dst_client, 0, &pkt, as_reliable);
2143 void Server::stopSound(s32 handle)
2145 // Get sound reference
2146 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2147 m_playing_sounds.find(handle);
2148 if (i == m_playing_sounds.end())
2150 ServerPlayingSound &psound = i->second;
2152 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2155 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2156 si != psound.clients.end(); ++si) {
2158 m_clients.send(*si, 0, &pkt, true);
2160 // Remove sound reference
2161 m_playing_sounds.erase(i);
2164 void Server::fadeSound(s32 handle, float step, float gain)
2166 // Get sound reference
2167 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2168 m_playing_sounds.find(handle);
2169 if (i == m_playing_sounds.end())
2172 ServerPlayingSound &psound = i->second;
2173 psound.params.gain = gain;
2175 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2176 pkt << handle << step << gain;
2178 // Backwards compability
2179 bool play_sound = gain > 0;
2180 ServerPlayingSound compat_psound = psound;
2181 compat_psound.clients.clear();
2183 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2184 compat_pkt << handle;
2186 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2187 it != psound.clients.end();) {
2188 if (m_clients.getProtocolVersion(*it) >= 32) {
2190 m_clients.send(*it, 0, &pkt, true);
2193 compat_psound.clients.insert(*it);
2195 m_clients.send(*it, 0, &compat_pkt, true);
2196 psound.clients.erase(it++);
2200 // Remove sound reference
2201 if (!play_sound || psound.clients.empty())
2202 m_playing_sounds.erase(i);
2204 if (play_sound && !compat_psound.clients.empty()) {
2205 // Play new sound volume on older clients
2206 playSound(compat_psound.spec, compat_psound.params);
2210 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2213 float maxd = far_d_nodes * BS;
2214 v3f p_f = intToFloat(p, BS);
2215 v3s16 block_pos = getNodeBlockPos(p);
2217 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2220 std::vector<session_t> clients = m_clients.getClientIDs();
2223 for (session_t client_id : clients) {
2224 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2228 RemotePlayer *player = m_env->getPlayer(client_id);
2229 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2231 // If player is far away, only set modified blocks not sent
2232 if (!client->isBlockSent(block_pos) || (sao &&
2233 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2235 far_players->emplace(client_id);
2237 client->SetBlockNotSent(block_pos);
2242 m_clients.send(client_id, 0, &pkt, true);
2248 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2249 float far_d_nodes, bool remove_metadata)
2251 float maxd = far_d_nodes * BS;
2252 v3f p_f = intToFloat(p, BS);
2253 v3s16 block_pos = getNodeBlockPos(p);
2255 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2256 pkt << p << n.param0 << n.param1 << n.param2
2257 << (u8) (remove_metadata ? 0 : 1);
2259 std::vector<session_t> clients = m_clients.getClientIDs();
2262 for (session_t client_id : clients) {
2263 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2267 RemotePlayer *player = m_env->getPlayer(client_id);
2268 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2270 // If player is far away, only set modified blocks not sent
2271 if (!client->isBlockSent(block_pos) || (sao &&
2272 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2274 far_players->emplace(client_id);
2276 client->SetBlockNotSent(block_pos);
2281 m_clients.send(client_id, 0, &pkt, true);
2287 void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes)
2289 float maxd = far_d_nodes * BS;
2290 NodeMetadataList meta_updates_list(false);
2291 std::vector<session_t> clients = m_clients.getClientIDs();
2295 for (session_t i : clients) {
2296 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2300 ServerActiveObject *player = m_env->getActiveObject(i);
2301 v3f player_pos = player ? player->getBasePosition() : v3f();
2303 for (const v3s16 &pos : meta_updates) {
2304 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2309 v3s16 block_pos = getNodeBlockPos(pos);
2310 if (!client->isBlockSent(block_pos) || (player &&
2311 player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) {
2312 client->SetBlockNotSent(block_pos);
2316 // Add the change to send list
2317 meta_updates_list.set(pos, meta);
2319 if (meta_updates_list.size() == 0)
2322 // Send the meta changes
2323 std::ostringstream os(std::ios::binary);
2324 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2325 std::ostringstream oss(std::ios::binary);
2326 compressZlib(os.str(), oss);
2328 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0);
2329 pkt.putLongString(oss.str());
2330 m_clients.send(i, 0, &pkt, true);
2332 meta_updates_list.clear();
2338 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2339 u16 net_proto_version)
2342 Create a packet with the block in the right format
2344 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2345 std::ostringstream os(std::ios_base::binary);
2346 block->serialize(os, ver, false, net_compression_level);
2347 block->serializeNetworkSpecific(os);
2348 std::string s = os.str();
2350 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + s.size(), peer_id);
2352 pkt << block->getPos();
2353 pkt.putRawString(s.c_str(), s.size());
2357 void Server::SendBlocks(float dtime)
2359 MutexAutoLock envlock(m_env_mutex);
2360 //TODO check if one big lock could be faster then multiple small ones
2362 std::vector<PrioritySortedBlockTransfer> queue;
2364 u32 total_sending = 0;
2367 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2369 std::vector<session_t> clients = m_clients.getClientIDs();
2372 for (const session_t client_id : clients) {
2373 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2378 total_sending += client->getSendingCount();
2379 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2385 // Lowest priority number comes first.
2386 // Lowest is most important.
2387 std::sort(queue.begin(), queue.end());
2391 // Maximal total count calculation
2392 // The per-client block sends is halved with the maximal online users
2393 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2394 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2396 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2397 Map &map = m_env->getMap();
2399 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2400 if (total_sending >= max_blocks_to_send)
2403 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2407 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2412 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2413 client->net_proto_version);
2415 client->SentBlock(block_to_send.pos);
2421 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2423 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2428 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2429 if (!client || client->isBlockSent(blockpos)) {
2433 SendBlockNoLock(peer_id, block, client->serialization_version,
2434 client->net_proto_version);
2440 bool Server::addMediaFile(const std::string &filename,
2441 const std::string &filepath, std::string *filedata_to,
2442 std::string *digest_to)
2444 // If name contains illegal characters, ignore the file
2445 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2446 infostream << "Server: ignoring illegal file name: \""
2447 << filename << "\"" << std::endl;
2450 // If name is not in a supported format, ignore it
2451 const char *supported_ext[] = {
2452 ".png", ".jpg", ".bmp", ".tga",
2454 ".x", ".b3d", ".obj",
2455 // Custom translation file format
2459 if (removeStringEnd(filename, supported_ext).empty()) {
2460 infostream << "Server: ignoring unsupported file extension: \""
2461 << filename << "\"" << std::endl;
2464 // Ok, attempt to load the file and add to cache
2467 std::string filedata;
2468 if (!fs::ReadFile(filepath, filedata)) {
2469 errorstream << "Server::addMediaFile(): Failed to open \""
2470 << filename << "\" for reading" << std::endl;
2474 if (filedata.empty()) {
2475 errorstream << "Server::addMediaFile(): Empty file \""
2476 << filepath << "\"" << std::endl;
2481 sha1.addBytes(filedata.c_str(), filedata.length());
2483 unsigned char *digest = sha1.getDigest();
2484 std::string sha1_base64 = base64_encode(digest, 20);
2485 std::string sha1_hex = hex_encode((char*) digest, 20);
2487 *digest_to = std::string((char*) digest, 20);
2491 m_media[filename] = MediaInfo(filepath, sha1_base64);
2492 verbosestream << "Server: " << sha1_hex << " is " << filename
2496 *filedata_to = std::move(filedata);
2500 void Server::fillMediaCache()
2502 infostream << "Server: Calculating media file checksums" << std::endl;
2504 // Collect all media file paths
2505 std::vector<std::string> paths;
2507 // ordered in descending priority
2508 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2509 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2510 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2511 m_modmgr->getModsMediaPaths(paths);
2513 // Collect media file information from paths into cache
2514 for (const std::string &mediapath : paths) {
2515 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2516 for (const fs::DirListNode &dln : dirlist) {
2517 if (dln.dir) // Ignore dirs (already in paths)
2520 const std::string &filename = dln.name;
2521 if (m_media.find(filename) != m_media.end()) // Do not override
2524 std::string filepath = mediapath;
2525 filepath.append(DIR_DELIM).append(filename);
2526 addMediaFile(filename, filepath);
2530 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2533 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2536 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2539 std::string lang_suffix;
2540 lang_suffix.append(".").append(lang_code).append(".tr");
2541 for (const auto &i : m_media) {
2542 if (i.second.no_announce)
2544 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2551 for (const auto &i : m_media) {
2552 if (i.second.no_announce)
2554 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2556 pkt << i.first << i.second.sha1_digest;
2559 pkt << g_settings->get("remote_media");
2562 verbosestream << "Server: Announcing files to id(" << peer_id
2563 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2566 struct SendableMedia
2572 SendableMedia(const std::string &name, const std::string &path,
2573 std::string &&data):
2574 name(name), path(path), data(std::move(data))
2578 void Server::sendRequestedMedia(session_t peer_id,
2579 const std::vector<std::string> &tosend)
2581 verbosestream<<"Server::sendRequestedMedia(): "
2582 <<"Sending files to client"<<std::endl;
2586 // Put 5kB in one bunch (this is not accurate)
2587 u32 bytes_per_bunch = 5000;
2589 std::vector< std::vector<SendableMedia> > file_bunches;
2590 file_bunches.emplace_back();
2592 u32 file_size_bunch_total = 0;
2594 for (const std::string &name : tosend) {
2595 if (m_media.find(name) == m_media.end()) {
2596 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2597 <<"unknown file \""<<(name)<<"\""<<std::endl;
2601 const auto &m = m_media[name];
2605 if (!fs::ReadFile(m.path, data)) {
2606 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2607 << name << "\"" << std::endl;
2610 file_size_bunch_total += data.size();
2613 file_bunches.back().emplace_back(name, m.path, std::move(data));
2615 // Start next bunch if got enough data
2616 if(file_size_bunch_total >= bytes_per_bunch) {
2617 file_bunches.emplace_back();
2618 file_size_bunch_total = 0;
2623 /* Create and send packets */
2625 u16 num_bunches = file_bunches.size();
2626 for (u16 i = 0; i < num_bunches; i++) {
2629 u16 total number of texture bunches
2630 u16 index of this bunch
2631 u32 number of files in this bunch
2640 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2641 pkt << num_bunches << i << (u32) file_bunches[i].size();
2643 for (const SendableMedia &j : file_bunches[i]) {
2645 pkt.putLongString(j.data);
2648 verbosestream << "Server::sendRequestedMedia(): bunch "
2649 << i << "/" << num_bunches
2650 << " files=" << file_bunches[i].size()
2651 << " size=" << pkt.getSize() << std::endl;
2656 void Server::stepPendingDynMediaCallbacks(float dtime)
2658 MutexAutoLock lock(m_env_mutex);
2660 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2661 it->second.expiry_timer -= dtime;
2662 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2669 const auto &name = it->second.filename;
2670 if (!name.empty()) {
2671 assert(m_media.count(name));
2672 // if no_announce isn't set we're definitely deleting the wrong file!
2673 sanity_check(m_media[name].no_announce);
2675 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2676 m_media.erase(name);
2678 getScriptIface()->freeDynamicMediaCallback(it->first);
2679 it = m_pending_dyn_media.erase(it);
2683 void Server::SendMinimapModes(session_t peer_id,
2684 std::vector<MinimapMode> &modes, size_t wanted_mode)
2686 RemotePlayer *player = m_env->getPlayer(peer_id);
2688 if (player->getPeerId() == PEER_ID_INEXISTENT)
2691 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2692 pkt << (u16)modes.size() << (u16)wanted_mode;
2694 for (auto &mode : modes)
2695 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2700 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2702 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2706 pkt << false; // Remove inventory
2708 pkt << true; // Update inventory
2710 // Serialization & NetworkPacket isn't a love story
2711 std::ostringstream os(std::ios_base::binary);
2712 inventory->serialize(os);
2713 inventory->setModified(false);
2715 const std::string &os_str = os.str();
2716 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2717 pkt.putRawString(os_str);
2720 if (peer_id == PEER_ID_INEXISTENT)
2721 m_clients.sendToAll(&pkt);
2726 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2728 // Lookup player name, to filter detached inventories just after
2729 std::string peer_name;
2730 if (peer_id != PEER_ID_INEXISTENT) {
2731 peer_name = getClient(peer_id, CS_Created)->getName();
2734 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2735 sendDetachedInventory(inv, name, peer_id);
2738 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2745 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2747 infostream << "Server::DiePlayer(): Player "
2748 << playersao->getPlayer()->getName()
2749 << " dies" << std::endl;
2751 playersao->clearParentAttachment();
2753 // Trigger scripted stuff
2754 m_script->on_dieplayer(playersao, reason);
2756 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2759 void Server::RespawnPlayer(session_t peer_id)
2761 PlayerSAO *playersao = getPlayerSAO(peer_id);
2764 infostream << "Server::RespawnPlayer(): Player "
2765 << playersao->getPlayer()->getName()
2766 << " respawns" << std::endl;
2768 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2769 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2770 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2772 bool repositioned = m_script->on_respawnplayer(playersao);
2773 if (!repositioned) {
2774 // setPos will send the new position to client
2775 playersao->setPos(findSpawnPos());
2780 void Server::DenySudoAccess(session_t peer_id)
2782 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2787 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2788 const std::string &str_reason, bool reconnect)
2790 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2792 m_clients.event(peer_id, CSE_SetDenied);
2793 DisconnectPeer(peer_id);
2797 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2798 const std::string &custom_reason)
2800 SendAccessDenied(peer_id, reason, custom_reason);
2801 m_clients.event(peer_id, CSE_SetDenied);
2802 DisconnectPeer(peer_id);
2805 // 13/03/15: remove this function when protocol version 25 will become
2806 // the minimum version for MT users, maybe in 1 year
2807 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2809 SendAccessDenied_Legacy(peer_id, reason);
2810 m_clients.event(peer_id, CSE_SetDenied);
2811 DisconnectPeer(peer_id);
2814 void Server::DisconnectPeer(session_t peer_id)
2816 m_modchannel_mgr->leaveAllChannels(peer_id);
2817 m_con->DisconnectPeer(peer_id);
2820 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2823 RemoteClient* client = getClient(peer_id, CS_Invalid);
2825 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2827 // Right now, the auth mechs don't change between login and sudo mode.
2828 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2829 client->allowed_sudo_mechs = sudo_auth_mechs;
2831 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2832 << g_settings->getFloat("dedicated_server_step")
2836 m_clients.event(peer_id, CSE_AuthAccept);
2838 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2840 // We only support SRP right now
2841 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2843 resp_pkt << sudo_auth_mechs;
2845 m_clients.event(peer_id, CSE_SudoSuccess);
2849 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2851 std::wstring message;
2854 Clear references to playing sounds
2856 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2857 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2858 ServerPlayingSound &psound = i->second;
2859 psound.clients.erase(peer_id);
2860 if (psound.clients.empty())
2861 m_playing_sounds.erase(i++);
2866 // clear formspec info so the next client can't abuse the current state
2867 m_formspec_state_data.erase(peer_id);
2869 RemotePlayer *player = m_env->getPlayer(peer_id);
2871 /* Run scripts and remove from environment */
2873 PlayerSAO *playersao = player->getPlayerSAO();
2876 playersao->clearChildAttachments();
2877 playersao->clearParentAttachment();
2879 // inform connected clients
2880 const std::string &player_name = player->getName();
2881 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2882 // (u16) 1 + std::string represents a vector serialization representation
2883 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2884 m_clients.sendToAll(¬ice);
2886 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2888 playersao->disconnected();
2895 if (player && reason != CDR_DENY) {
2896 std::ostringstream os(std::ios_base::binary);
2897 std::vector<session_t> clients = m_clients.getClientIDs();
2899 for (const session_t client_id : clients) {
2901 RemotePlayer *player = m_env->getPlayer(client_id);
2905 // Get name of player
2906 os << player->getName() << " ";
2909 std::string name = player->getName();
2910 actionstream << name << " "
2911 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2912 << " List of players: " << os.str() << std::endl;
2914 m_admin_chat->outgoing_queue.push_back(
2915 new ChatEventNick(CET_NICK_REMOVE, name));
2919 MutexAutoLock env_lock(m_env_mutex);
2920 m_clients.DeleteClient(peer_id);
2924 // Send leave chat message to all remaining clients
2925 if (!message.empty()) {
2926 SendChatMessage(PEER_ID_INEXISTENT,
2927 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2931 void Server::UpdateCrafting(RemotePlayer *player)
2933 InventoryList *clist = player->inventory.getList("craft");
2934 if (!clist || clist->getSize() == 0)
2937 if (!clist->checkModified())
2940 // Get a preview for crafting
2942 InventoryLocation loc;
2943 loc.setPlayer(player->getName());
2944 std::vector<ItemStack> output_replacements;
2945 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2946 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2949 InventoryList *plist = player->inventory.getList("craftpreview");
2950 if (plist && plist->getSize() >= 1) {
2951 // Put the new preview in
2952 plist->changeItem(0, preview);
2956 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2958 if (evt->type == CET_NICK_ADD) {
2959 // The terminal informed us of its nick choice
2960 m_admin_nick = ((ChatEventNick *)evt)->nick;
2961 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2962 errorstream << "You haven't set up an account." << std::endl
2963 << "Please log in using the client as '"
2964 << m_admin_nick << "' with a secure password." << std::endl
2965 << "Until then, you can't execute admin tasks via the console," << std::endl
2966 << "and everybody can claim the user account instead of you," << std::endl
2967 << "giving them full control over this server." << std::endl;
2970 assert(evt->type == CET_CHAT);
2971 handleAdminChat((ChatEventChat *)evt);
2975 std::wstring Server::handleChat(const std::string &name,
2976 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2978 // If something goes wrong, this player is to blame
2979 RollbackScopeActor rollback_scope(m_rollback,
2980 std::string("player:") + name);
2982 if (g_settings->getBool("strip_color_codes"))
2983 wmessage = unescape_enriched(wmessage);
2986 switch (player->canSendChatMessage()) {
2987 case RPLAYER_CHATRESULT_FLOODING: {
2988 std::wstringstream ws;
2989 ws << L"You cannot send more messages. You are limited to "
2990 << g_settings->getFloat("chat_message_limit_per_10sec")
2991 << L" messages per 10 seconds.";
2994 case RPLAYER_CHATRESULT_KICK:
2995 DenyAccess_Legacy(player->getPeerId(),
2996 L"You have been kicked due to message flooding.");
2998 case RPLAYER_CHATRESULT_OK:
3001 FATAL_ERROR("Unhandled chat filtering result found.");
3005 if (m_max_chatmessage_length > 0
3006 && wmessage.length() > m_max_chatmessage_length) {
3007 return L"Your message exceed the maximum chat message limit set on the server. "
3008 L"It was refused. Send a shorter message";
3011 auto message = trim(wide_to_utf8(wmessage));
3012 if (message.empty())
3015 if (message.find_first_of("\n\r") != std::wstring::npos) {
3016 return L"Newlines are not permitted in chat messages";
3019 // Run script hook, exit if script ate the chat message
3020 if (m_script->on_chat_message(name, message))
3025 // Whether to send line to the player that sent the message, or to all players
3026 bool broadcast_line = true;
3028 if (check_shout_priv && !checkPriv(name, "shout")) {
3029 line += L"-!- You don't have permission to shout.";
3030 broadcast_line = false;
3033 Workaround for fixing chat on Android. Lua doesn't handle
3034 the Cyrillic alphabet and some characters on older Android devices
3037 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3039 line += utf8_to_wide(m_script->formatChatMessage(name,
3040 wide_to_utf8(wmessage)));
3045 Tell calling method to send the message to sender
3047 if (!broadcast_line)
3051 Send the message to others
3053 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3055 ChatMessage chatmsg(line);
3057 std::vector<session_t> clients = m_clients.getClientIDs();
3058 for (u16 cid : clients)
3059 SendChatMessage(cid, chatmsg);
3064 void Server::handleAdminChat(const ChatEventChat *evt)
3066 std::string name = evt->nick;
3067 std::wstring wmessage = evt->evt_msg;
3069 std::wstring answer = handleChat(name, wmessage);
3071 // If asked to send answer to sender
3072 if (!answer.empty()) {
3073 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3077 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3079 RemoteClient *client = getClientNoEx(peer_id,state_min);
3081 throw ClientNotFoundException("Client not found");
3085 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3087 return m_clients.getClientNoEx(peer_id, state_min);
3090 std::string Server::getPlayerName(session_t peer_id)
3092 RemotePlayer *player = m_env->getPlayer(peer_id);
3094 return "[id="+itos(peer_id)+"]";
3095 return player->getName();
3098 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3100 RemotePlayer *player = m_env->getPlayer(peer_id);
3103 return player->getPlayerSAO();
3106 std::string Server::getStatusString()
3108 std::ostringstream os(std::ios_base::binary);
3111 os << "version: " << g_version_string;
3113 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3115 os << " | max lag: " << std::setprecision(3);
3116 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3118 // Information about clients
3120 os << " | clients: ";
3122 std::vector<session_t> clients = m_clients.getClientIDs();
3123 for (session_t client_id : clients) {
3124 RemotePlayer *player = m_env->getPlayer(client_id);
3126 // Get name of player
3127 const char *name = player ? player->getName() : "<unknown>";
3129 // Add name to information string
3138 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3139 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3141 if (!g_settings->get("motd").empty())
3142 os << std::endl << "# Server: " << g_settings->get("motd");
3147 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3149 std::set<std::string> privs;
3150 m_script->getAuth(name, NULL, &privs);
3154 bool Server::checkPriv(const std::string &name, const std::string &priv)
3156 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3157 return (privs.count(priv) != 0);
3160 void Server::reportPrivsModified(const std::string &name)
3163 std::vector<session_t> clients = m_clients.getClientIDs();
3164 for (const session_t client_id : clients) {
3165 RemotePlayer *player = m_env->getPlayer(client_id);
3166 reportPrivsModified(player->getName());
3169 RemotePlayer *player = m_env->getPlayer(name.c_str());
3172 SendPlayerPrivileges(player->getPeerId());
3173 PlayerSAO *sao = player->getPlayerSAO();
3176 sao->updatePrivileges(
3177 getPlayerEffectivePrivs(name),
3182 void Server::reportInventoryFormspecModified(const std::string &name)
3184 RemotePlayer *player = m_env->getPlayer(name.c_str());
3187 SendPlayerInventoryFormspec(player->getPeerId());
3190 void Server::reportFormspecPrependModified(const std::string &name)
3192 RemotePlayer *player = m_env->getPlayer(name.c_str());
3195 SendPlayerFormspecPrepend(player->getPeerId());
3198 void Server::setIpBanned(const std::string &ip, const std::string &name)
3200 m_banmanager->add(ip, name);
3203 void Server::unsetIpBanned(const std::string &ip_or_name)
3205 m_banmanager->remove(ip_or_name);
3208 std::string Server::getBanDescription(const std::string &ip_or_name)
3210 return m_banmanager->getBanDescription(ip_or_name);
3213 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3215 // m_env will be NULL if the server is initializing
3219 if (m_admin_nick == name && !m_admin_nick.empty()) {
3220 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3223 RemotePlayer *player = m_env->getPlayer(name);
3228 if (player->getPeerId() == PEER_ID_INEXISTENT)
3231 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3234 bool Server::showFormspec(const char *playername, const std::string &formspec,
3235 const std::string &formname)
3237 // m_env will be NULL if the server is initializing
3241 RemotePlayer *player = m_env->getPlayer(playername);
3245 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3249 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3254 u32 id = player->addHud(form);
3256 SendHUDAdd(player->getPeerId(), id, form);
3261 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3265 HudElement* todel = player->removeHud(id);
3272 SendHUDRemove(player->getPeerId(), id);
3276 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3281 SendHUDChange(player->getPeerId(), id, stat, data);
3285 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3290 SendHUDSetFlags(player->getPeerId(), flags, mask);
3291 player->hud_flags &= ~mask;
3292 player->hud_flags |= flags;
3294 PlayerSAO* playersao = player->getPlayerSAO();
3299 m_script->player_event(playersao, "hud_changed");
3303 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3308 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3311 player->setHotbarItemcount(hotbar_itemcount);
3312 std::ostringstream os(std::ios::binary);
3313 writeS32(os, hotbar_itemcount);
3314 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3318 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3323 player->setHotbarImage(name);
3324 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3327 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3332 player->setHotbarSelectedImage(name);
3333 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3336 Address Server::getPeerAddress(session_t peer_id)
3338 // Note that this is only set after Init was received in Server::handleCommand_Init
3339 return getClient(peer_id, CS_Invalid)->getAddress();
3342 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3343 v2s32 animation_frames[4], f32 frame_speed)
3345 sanity_check(player);
3346 player->setLocalAnimations(animation_frames, frame_speed);
3347 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3350 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3352 sanity_check(player);
3353 player->eye_offset_first = first;
3354 player->eye_offset_third = third;
3355 SendEyeOffset(player->getPeerId(), first, third);
3358 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3360 sanity_check(player);
3361 player->setSky(params);
3362 SendSetSky(player->getPeerId(), params);
3365 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3367 sanity_check(player);
3368 player->setSun(params);
3369 SendSetSun(player->getPeerId(), params);
3372 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3374 sanity_check(player);
3375 player->setMoon(params);
3376 SendSetMoon(player->getPeerId(), params);
3379 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3381 sanity_check(player);
3382 player->setStars(params);
3383 SendSetStars(player->getPeerId(), params);
3386 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3388 sanity_check(player);
3389 player->setCloudParams(params);
3390 SendCloudParams(player->getPeerId(), params);
3393 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3396 sanity_check(player);
3397 player->overrideDayNightRatio(do_override, ratio);
3398 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3401 void Server::notifyPlayers(const std::wstring &msg)
3403 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3406 void Server::spawnParticle(const std::string &playername,
3407 const ParticleParameters &p)
3409 // m_env will be NULL if the server is initializing
3413 session_t peer_id = PEER_ID_INEXISTENT;
3415 if (!playername.empty()) {
3416 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3419 peer_id = player->getPeerId();
3420 proto_ver = player->protocol_version;
3423 SendSpawnParticle(peer_id, proto_ver, p);
3426 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3427 ServerActiveObject *attached, const std::string &playername)
3429 // m_env will be NULL if the server is initializing
3433 session_t peer_id = PEER_ID_INEXISTENT;
3435 if (!playername.empty()) {
3436 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3439 peer_id = player->getPeerId();
3440 proto_ver = player->protocol_version;
3443 u16 attached_id = attached ? attached->getId() : 0;
3446 if (attached_id == 0)
3447 id = m_env->addParticleSpawner(p.time);
3449 id = m_env->addParticleSpawner(p.time, attached_id);
3451 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3455 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3457 // m_env will be NULL if the server is initializing
3459 throw ServerError("Can't delete particle spawners during initialisation!");
3461 session_t peer_id = PEER_ID_INEXISTENT;
3462 if (!playername.empty()) {
3463 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3466 peer_id = player->getPeerId();
3469 m_env->deleteParticleSpawner(id);
3470 SendDeleteParticleSpawner(peer_id, id);
3473 bool Server::dynamicAddMedia(std::string filepath,
3474 const u32 token, const std::string &to_player, bool ephemeral)
3476 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3477 auto it = m_media.find(filename);
3478 if (it != m_media.end()) {
3479 // Allow the same path to be "added" again in certain conditions
3480 if (ephemeral || it->second.path != filepath) {
3481 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3482 << "\" already exists in media cache" << std::endl;
3487 // Load the file and add it to our media cache
3488 std::string filedata, raw_hash;
3489 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3494 // Create a copy of the file and swap out the path, this removes the
3495 // requirement that mods keep the file accessible at the original path.
3496 filepath = fs::CreateTempFile();
3497 bool ok = ([&] () -> bool {
3498 if (filepath.empty())
3500 std::ofstream os(filepath.c_str(), std::ios::binary);
3508 errorstream << "Server: failed to create a copy of media file "
3509 << "\"" << filename << "\"" << std::endl;
3510 m_media.erase(filename);
3513 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3514 << filepath << std::endl;
3516 m_media[filename].path = filepath;
3517 m_media[filename].no_announce = true;
3518 // stepPendingDynMediaCallbacks will clean this up later.
3519 } else if (!to_player.empty()) {
3520 m_media[filename].no_announce = true;
3523 // Push file to existing clients
3524 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3525 pkt << raw_hash << filename << (bool)ephemeral;
3527 NetworkPacket legacy_pkt = pkt;
3529 // Newer clients get asked to fetch the file (asynchronous)
3531 // Older clients have an awful hack that just throws the data at them
3532 legacy_pkt.putLongString(filedata);
3534 std::unordered_set<session_t> delivered, waiting;
3536 for (auto &pair : m_clients.getClientList()) {
3537 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3539 If a client is in the DefinitionsSent state it is too late to
3540 transfer the file via sendMediaAnnouncement() but at the same
3541 time the client cannot accept a media push yet.
3542 Short of artificially delaying the joining process there is no
3543 way for the server to resolve this so we (currently) opt not to.
3545 warningstream << "The media \"" << filename << "\" (dynamic) could "
3546 "not be delivered to " << pair.second->getName()
3547 << " due to a race condition." << std::endl;
3550 if (pair.second->getState() < CS_Active)
3553 const auto proto_ver = pair.second->net_proto_version;
3557 const session_t peer_id = pair.second->peer_id;
3558 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3561 if (proto_ver < 40) {
3562 delivered.emplace(peer_id);
3564 The network layer only guarantees ordered delivery inside a channel.
3565 Since the very next packet could be one that uses the media, we have
3566 to push the media over ALL channels to ensure it is processed before
3567 it is used. In practice this means channels 1 and 0.
3569 m_clients.send(peer_id, 1, &legacy_pkt, true);
3570 m_clients.send(peer_id, 0, &legacy_pkt, true);
3572 waiting.emplace(peer_id);
3573 Send(peer_id, &pkt);
3578 // Run callback for players that already had the file delivered (legacy-only)
3579 for (session_t peer_id : delivered) {
3580 if (auto player = m_env->getPlayer(peer_id))
3581 getScriptIface()->on_dynamic_media_added(token, player->getName());
3584 // Save all others in our pending state
3585 auto &state = m_pending_dyn_media[token];
3586 state.waiting_players = std::move(waiting);
3587 // regardless of success throw away the callback after a while
3588 state.expiry_timer = 60.0f;
3590 state.filename = filename;
3595 // actions: time-reversed list
3596 // Return value: success/failure
3597 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3598 std::list<std::string> *log)
3600 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3601 ServerMap *map = (ServerMap*)(&m_env->getMap());
3603 // Fail if no actions to handle
3604 if (actions.empty()) {
3606 log->push_back("Nothing to do.");
3613 for (const RollbackAction &action : actions) {
3615 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3618 std::ostringstream os;
3619 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3620 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3622 log->push_back(os.str());
3624 std::ostringstream os;
3625 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3626 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3628 log->push_back(os.str());
3632 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3633 <<" failed"<<std::endl;
3635 // Call it done if less than half failed
3636 return num_failed <= num_tried/2;
3639 // IGameDef interface
3641 IItemDefManager *Server::getItemDefManager()
3646 const NodeDefManager *Server::getNodeDefManager()
3651 ICraftDefManager *Server::getCraftDefManager()
3656 u16 Server::allocateUnknownNodeId(const std::string &name)
3658 return m_nodedef->allocateDummy(name);
3661 IWritableItemDefManager *Server::getWritableItemDefManager()
3666 NodeDefManager *Server::getWritableNodeDefManager()
3671 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3676 const std::vector<ModSpec> & Server::getMods() const
3678 return m_modmgr->getMods();
3681 const ModSpec *Server::getModSpec(const std::string &modname) const
3683 return m_modmgr->getModSpec(modname);
3686 void Server::getModNames(std::vector<std::string> &modlist)
3688 m_modmgr->getModNames(modlist);
3691 std::string Server::getBuiltinLuaPath()
3693 return porting::path_share + DIR_DELIM + "builtin";
3696 v3f Server::findSpawnPos()
3698 ServerMap &map = m_env->getServerMap();
3700 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3701 return nodeposf * BS;
3703 bool is_good = false;
3704 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3705 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3707 // Try to find a good place a few times
3708 for (s32 i = 0; i < 4000 && !is_good; i++) {
3709 s32 range = MYMIN(1 + i, range_max);
3710 // We're going to try to throw the player to this position
3711 v2s16 nodepos2d = v2s16(
3712 -range + (myrand() % (range * 2)),
3713 -range + (myrand() % (range * 2)));
3714 // Get spawn level at point
3715 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3716 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3717 // signify an unsuitable spawn position, or if outside limits.
3718 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3719 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3722 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3723 // Consecutive empty nodes
3726 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3727 // avoid obstructions in already-generated mapblocks.
3728 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3729 // no obstructions, but mapgen decorations are generated after spawn so
3730 // the player may end up inside one.
3731 for (s32 i = 0; i < 8; i++) {
3732 v3s16 blockpos = getNodeBlockPos(nodepos);
3733 map.emergeBlock(blockpos, true);
3734 content_t c = map.getNode(nodepos).getContent();
3736 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3737 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3738 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3740 if (air_count >= 2) {
3741 // Spawn in lower empty node
3743 nodeposf = intToFloat(nodepos, BS);
3744 // Don't spawn the player outside map boundaries
3745 if (objectpos_over_limit(nodeposf))
3746 // Exit this loop, positions above are probably over limit
3749 // Good position found, cause an exit from main loop
3763 // No suitable spawn point found, return fallback 0,0,0
3764 return v3f(0.0f, 0.0f, 0.0f);
3767 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3769 if (delay == 0.0f) {
3770 // No delay, shutdown immediately
3771 m_shutdown_state.is_requested = true;
3772 // only print to the infostream, a chat message saying
3773 // "Server Shutting Down" is sent when the server destructs.
3774 infostream << "*** Immediate Server shutdown requested." << std::endl;
3775 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3776 // Negative delay, cancel shutdown if requested
3777 m_shutdown_state.reset();
3778 std::wstringstream ws;
3780 ws << L"*** Server shutdown canceled.";
3782 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3783 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3784 // m_shutdown_* are already handled, skip.
3786 } else if (delay > 0.0f) {
3787 // Positive delay, tell the clients when the server will shut down
3788 std::wstringstream ws;
3790 ws << L"*** Server shutting down in "
3791 << duration_to_string(myround(delay)).c_str()
3794 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3795 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3798 m_shutdown_state.trigger(delay, msg, reconnect);
3801 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3804 Try to get an existing player
3806 RemotePlayer *player = m_env->getPlayer(name);
3808 // If player is already connected, cancel
3809 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3810 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3815 If player with the wanted peer_id already exists, cancel.
3817 if (m_env->getPlayer(peer_id)) {
3818 infostream<<"emergePlayer(): Player with wrong name but same"
3819 " peer_id already exists"<<std::endl;
3824 player = new RemotePlayer(name, idef());
3827 bool newplayer = false;
3830 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3832 // Complete init with server parts
3833 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3834 player->protocol_version = proto_version;
3838 m_script->on_newplayer(playersao);
3844 bool Server::registerModStorage(ModMetadata *storage)
3846 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3847 errorstream << "Unable to register same mod storage twice. Storage name: "
3848 << storage->getModName() << std::endl;
3852 m_mod_storages[storage->getModName()] = storage;
3856 void Server::unregisterModStorage(const std::string &name)
3858 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3859 if (it != m_mod_storages.end())
3860 m_mod_storages.erase(name);
3863 void dedicated_server_loop(Server &server, bool &kill)
3865 verbosestream<<"dedicated_server_loop()"<<std::endl;
3867 IntervalLimiter m_profiler_interval;
3869 static thread_local const float steplen =
3870 g_settings->getFloat("dedicated_server_step");
3871 static thread_local const float profiler_print_interval =
3872 g_settings->getFloat("profiler_print_interval");
3875 * The dedicated server loop only does time-keeping (in Server::step) and
3876 * provides a way to main.cpp to kill the server externally (bool &kill).
3880 // This is kind of a hack but can be done like this
3881 // because server.step() is very light
3882 sleep_ms((int)(steplen*1000.0));
3883 server.step(steplen);
3885 if (server.isShutdownRequested() || kill)
3891 if (profiler_print_interval != 0) {
3892 if(m_profiler_interval.step(steplen, profiler_print_interval))
3894 infostream<<"Profiler:"<<std::endl;
3895 g_profiler->print(infostream);
3896 g_profiler->clear();
3901 infostream << "Dedicated server quitting" << std::endl;
3903 if (g_settings->getBool("server_announce"))
3904 ServerList::sendAnnounce(ServerList::AA_DELETE,
3905 server.m_bind_addr.getPort());
3914 bool Server::joinModChannel(const std::string &channel)
3916 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3917 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3920 bool Server::leaveModChannel(const std::string &channel)
3922 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3925 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3927 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3930 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3934 ModChannel* Server::getModChannel(const std::string &channel)
3936 return m_modchannel_mgr->getModChannel(channel);
3939 void Server::broadcastModChannelMessage(const std::string &channel,
3940 const std::string &message, session_t from_peer)
3942 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3946 if (message.size() > STRING_MAX_LEN) {
3947 warningstream << "ModChannel message too long, dropping before sending "
3948 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3949 << channel << ")" << std::endl;
3954 if (from_peer != PEER_ID_SERVER) {
3955 sender = getPlayerName(from_peer);
3958 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3959 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3960 resp_pkt << channel << sender << message;
3961 for (session_t peer_id : peers) {
3963 if (peer_id == from_peer)
3966 Send(peer_id, &resp_pkt);
3969 if (from_peer != PEER_ID_SERVER) {
3970 m_script->on_modchannel_message(channel, sender, message);
3974 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3976 if (lang_code.empty())
3979 auto it = server_translations.find(lang_code);
3980 if (it != server_translations.end())
3981 return &it->second; // Already loaded
3983 // [] will create an entry
3984 auto *translations = &server_translations[lang_code];
3986 std::string suffix = "." + lang_code + ".tr";
3987 for (const auto &i : m_media) {
3988 if (str_ends_with(i.first, suffix)) {
3990 if (fs::ReadFile(i.second.path, data)) {
3991 translations->loadTranslation(data);
3996 return translations;
3999 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
4001 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4003 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4004 throw BaseException("Cannot read world.mt!");
4006 std::string backend = world_mt.exists("mod_storage_backend") ?
4007 world_mt.get("mod_storage_backend") : "files";
4008 if (backend == "files")
4009 warningstream << "/!\\ You are using the old mod storage files backend. "
4010 << "This backend is deprecated and may be removed in a future release /!\\"
4011 << std::endl << "Switching to SQLite3 is advised, "
4012 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4014 return openModStorageDatabase(backend, world_path, world_mt);
4017 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
4018 const std::string &world_path, const Settings &world_mt)
4020 if (backend == "sqlite3")
4021 return new ModMetadataDatabaseSQLite3(world_path);
4023 if (backend == "files")
4024 return new ModMetadataDatabaseFiles(world_path);
4026 if (backend == "dummy")
4027 return new Database_Dummy();
4029 throw BaseException("Mod storage database backend " + backend + " not supported");
4032 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4034 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4036 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4037 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4038 errorstream << "Cannot read world.mt!" << std::endl;
4042 std::string backend = world_mt.exists("mod_storage_backend") ?
4043 world_mt.get("mod_storage_backend") : "files";
4044 if (backend == migrate_to) {
4045 errorstream << "Cannot migrate: new backend is same"
4046 << " as the old one" << std::endl;
4050 ModMetadataDatabase *srcdb = nullptr;
4051 ModMetadataDatabase *dstdb = nullptr;
4053 bool succeeded = false;
4056 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4057 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4061 std::vector<std::string> mod_list;
4062 srcdb->listMods(&mod_list);
4063 for (const std::string &modname : mod_list) {
4065 srcdb->getModEntries(modname, &meta);
4066 for (const auto &pair : meta) {
4067 dstdb->setModEntry(modname, pair.first, pair.second);
4075 actionstream << "Successfully migrated the metadata of "
4076 << mod_list.size() << " mods" << std::endl;
4077 world_mt.set("mod_storage_backend", migrate_to);
4078 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4079 errorstream << "Failed to update world.mt!" << std::endl;
4081 actionstream << "world.mt updated" << std::endl;
4083 } catch (BaseException &e) {
4084 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4090 if (succeeded && backend == "files") {
4092 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4093 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4094 if (!fs::Rename(storage_path, backup_path))
4095 warningstream << "After migration, " << storage_path
4096 << " could not be renamed to " << backup_path << std::endl;