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);
1098 // Send HP or death screen
1099 if (playersao->isDead())
1100 SendDeathscreen(peer_id, false, v3f(0,0,0));
1102 SendPlayerHP(peer_id);
1105 SendPlayerBreath(playersao);
1111 Address addr = getPeerAddress(player->getPeerId());
1112 std::string ip_str = addr.serializeString();
1113 const std::vector<std::string> &names = m_clients.getPlayerNames();
1115 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1117 for (const std::string &name : names) {
1118 actionstream << name << " ";
1121 actionstream << player->getName() <<std::endl;
1126 inline void Server::handleCommand(NetworkPacket *pkt)
1128 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1129 (this->*opHandle.handler)(pkt);
1132 void Server::ProcessData(NetworkPacket *pkt)
1134 // Environment is locked first.
1135 MutexAutoLock envlock(m_env_mutex);
1137 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1138 u32 peer_id = pkt->getPeerId();
1141 Address address = getPeerAddress(peer_id);
1142 std::string addr_s = address.serializeString();
1144 if(m_banmanager->isIpBanned(addr_s)) {
1145 std::string ban_name = m_banmanager->getBanName(addr_s);
1146 infostream << "Server: A banned client tried to connect from "
1147 << addr_s << "; banned name was "
1148 << ban_name << std::endl;
1149 // This actually doesn't seem to transfer to the client
1150 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1151 + utf8_to_wide(ban_name));
1155 catch(con::PeerNotFoundException &e) {
1157 * no peer for this packet found
1158 * most common reason is peer timeout, e.g. peer didn't
1159 * respond for some time, your server was overloaded or
1162 infostream << "Server::ProcessData(): Canceling: peer "
1163 << peer_id << " not found" << std::endl;
1168 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1170 // Command must be handled into ToServerCommandHandler
1171 if (command >= TOSERVER_NUM_MSG_TYPES) {
1172 infostream << "Server: Ignoring unknown command "
1173 << command << std::endl;
1177 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1182 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1184 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1185 errorstream << "Server::ProcessData(): Cancelling: Peer"
1186 " serialization format invalid or not initialized."
1187 " Skipping incoming command=" << command << std::endl;
1191 /* Handle commands related to client startup */
1192 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1197 if (m_clients.getClientState(peer_id) < CS_Active) {
1198 if (command == TOSERVER_PLAYERPOS) return;
1200 errorstream << "Got packet command: " << command << " for peer id "
1201 << peer_id << " but client isn't active yet. Dropping packet "
1207 } catch (SendFailedException &e) {
1208 errorstream << "Server::ProcessData(): SendFailedException: "
1209 << "what=" << e.what()
1211 } catch (PacketError &e) {
1212 actionstream << "Server::ProcessData(): PacketError: "
1213 << "what=" << e.what()
1218 void Server::setTimeOfDay(u32 time)
1220 m_env->setTimeOfDay(time);
1221 m_time_of_day_send_timer = 0;
1224 void Server::onMapEditEvent(const MapEditEvent &event)
1226 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1229 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1232 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1234 std::vector<session_t> clients = m_clients.getClientIDs();
1236 // Set the modified blocks unsent for all the clients
1237 for (const session_t client_id : clients) {
1238 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1239 client->SetBlocksNotSent(block);
1244 void Server::peerAdded(con::Peer *peer)
1246 verbosestream<<"Server::peerAdded(): peer->id="
1247 <<peer->id<<std::endl;
1249 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1252 void Server::deletingPeer(con::Peer *peer, bool timeout)
1254 verbosestream<<"Server::deletingPeer(): peer->id="
1255 <<peer->id<<", timeout="<<timeout<<std::endl;
1257 m_clients.event(peer->id, CSE_Disconnect);
1258 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1261 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1263 *retval = m_con->getPeerStat(peer_id,type);
1264 return *retval != -1;
1267 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1270 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1277 ret.state = client->getState();
1278 ret.addr = client->getAddress();
1279 ret.uptime = client->uptime();
1280 ret.ser_vers = client->serialization_version;
1281 ret.prot_vers = client->net_proto_version;
1283 ret.major = client->getMajor();
1284 ret.minor = client->getMinor();
1285 ret.patch = client->getPatch();
1286 ret.vers_string = client->getFullVer();
1288 ret.lang_code = client->getLangCode();
1295 void Server::handlePeerChanges()
1297 while(!m_peer_change_queue.empty())
1299 con::PeerChange c = m_peer_change_queue.front();
1300 m_peer_change_queue.pop();
1302 verbosestream<<"Server: Handling peer change: "
1303 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1308 case con::PEER_ADDED:
1309 m_clients.CreateClient(c.peer_id);
1312 case con::PEER_REMOVED:
1313 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1317 FATAL_ERROR("Invalid peer change event received!");
1323 void Server::printToConsoleOnly(const std::string &text)
1326 m_admin_chat->outgoing_queue.push_back(
1327 new ChatEventChat("", utf8_to_wide(text)));
1329 std::cout << text << std::endl;
1333 void Server::Send(NetworkPacket *pkt)
1335 Send(pkt->getPeerId(), pkt);
1338 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1340 m_clients.send(peer_id,
1341 clientCommandFactoryTable[pkt->getCommand()].channel,
1343 clientCommandFactoryTable[pkt->getCommand()].reliable);
1346 void Server::SendMovement(session_t peer_id)
1348 std::ostringstream os(std::ios_base::binary);
1350 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1352 pkt << g_settings->getFloat("movement_acceleration_default");
1353 pkt << g_settings->getFloat("movement_acceleration_air");
1354 pkt << g_settings->getFloat("movement_acceleration_fast");
1355 pkt << g_settings->getFloat("movement_speed_walk");
1356 pkt << g_settings->getFloat("movement_speed_crouch");
1357 pkt << g_settings->getFloat("movement_speed_fast");
1358 pkt << g_settings->getFloat("movement_speed_climb");
1359 pkt << g_settings->getFloat("movement_speed_jump");
1360 pkt << g_settings->getFloat("movement_liquid_fluidity");
1361 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1362 pkt << g_settings->getFloat("movement_liquid_sink");
1363 pkt << g_settings->getFloat("movement_gravity");
1368 void Server::SendPlayerHPOrDie(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1370 if (playersao->isImmortal())
1373 session_t peer_id = playersao->getPeerID();
1374 bool is_alive = !playersao->isDead();
1377 SendPlayerHP(peer_id);
1379 DiePlayer(peer_id, reason);
1382 void Server::SendHP(session_t peer_id, u16 hp)
1384 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1389 void Server::SendBreath(session_t peer_id, u16 breath)
1391 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1392 pkt << (u16) breath;
1396 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1397 const std::string &custom_reason, bool reconnect)
1399 assert(reason < SERVER_ACCESSDENIED_MAX);
1401 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1403 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1404 pkt << custom_reason;
1405 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1406 reason == SERVER_ACCESSDENIED_CRASH)
1407 pkt << custom_reason << (u8)reconnect;
1411 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1413 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1418 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1419 v3f camera_point_target)
1421 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1422 pkt << set_camera_point_target << camera_point_target;
1426 void Server::SendItemDef(session_t peer_id,
1427 IItemDefManager *itemdef, u16 protocol_version)
1429 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1433 u32 length of the next item
1434 zlib-compressed serialized ItemDefManager
1436 std::ostringstream tmp_os(std::ios::binary);
1437 itemdef->serialize(tmp_os, protocol_version);
1438 std::ostringstream tmp_os2(std::ios::binary);
1439 compressZlib(tmp_os.str(), tmp_os2);
1440 pkt.putLongString(tmp_os2.str());
1443 verbosestream << "Server: Sending item definitions to id(" << peer_id
1444 << "): size=" << pkt.getSize() << std::endl;
1449 void Server::SendNodeDef(session_t peer_id,
1450 const NodeDefManager *nodedef, u16 protocol_version)
1452 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1456 u32 length of the next item
1457 zlib-compressed serialized NodeDefManager
1459 std::ostringstream tmp_os(std::ios::binary);
1460 nodedef->serialize(tmp_os, protocol_version);
1461 std::ostringstream tmp_os2(std::ios::binary);
1462 compressZlib(tmp_os.str(), tmp_os2);
1464 pkt.putLongString(tmp_os2.str());
1467 verbosestream << "Server: Sending node definitions to id(" << peer_id
1468 << "): size=" << pkt.getSize() << std::endl;
1474 Non-static send methods
1477 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1479 RemotePlayer *player = sao->getPlayer();
1481 // Do not send new format to old clients
1482 incremental &= player->protocol_version >= 38;
1484 UpdateCrafting(player);
1490 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1492 std::ostringstream os(std::ios::binary);
1493 sao->getInventory()->serialize(os, incremental);
1494 sao->getInventory()->setModified(false);
1495 player->setModified(true);
1497 const std::string &s = os.str();
1498 pkt.putRawString(s.c_str(), s.size());
1502 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1504 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1506 u8 type = message.type;
1507 pkt << version << type << message.sender << message.message
1508 << static_cast<u64>(message.timestamp);
1510 if (peer_id != PEER_ID_INEXISTENT) {
1511 RemotePlayer *player = m_env->getPlayer(peer_id);
1517 m_clients.sendToAll(&pkt);
1521 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1522 const std::string &formname)
1524 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1525 if (formspec.empty()){
1526 //the client should close the formspec
1527 //but make sure there wasn't another one open in meantime
1528 const auto it = m_formspec_state_data.find(peer_id);
1529 if (it != m_formspec_state_data.end() && it->second == formname) {
1530 m_formspec_state_data.erase(peer_id);
1532 pkt.putLongString("");
1534 m_formspec_state_data[peer_id] = formname;
1535 pkt.putLongString(formspec);
1542 // Spawns a particle on peer with peer_id
1543 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1544 const ParticleParameters &p)
1546 static thread_local const float radius =
1547 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1549 if (peer_id == PEER_ID_INEXISTENT) {
1550 std::vector<session_t> clients = m_clients.getClientIDs();
1551 const v3f pos = p.pos * BS;
1552 const float radius_sq = radius * radius;
1554 for (const session_t client_id : clients) {
1555 RemotePlayer *player = m_env->getPlayer(client_id);
1559 PlayerSAO *sao = player->getPlayerSAO();
1563 // Do not send to distant clients
1564 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1567 SendSpawnParticle(client_id, player->protocol_version, p);
1571 assert(protocol_version != 0);
1573 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1576 // NetworkPacket and iostreams are incompatible...
1577 std::ostringstream oss(std::ios_base::binary);
1578 p.serialize(oss, protocol_version);
1579 pkt.putRawString(oss.str());
1585 // Adds a ParticleSpawner on peer with peer_id
1586 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1587 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1589 static thread_local const float radius =
1590 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1592 if (peer_id == PEER_ID_INEXISTENT) {
1593 std::vector<session_t> clients = m_clients.getClientIDs();
1594 const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS;
1595 const float radius_sq = radius * radius;
1596 /* Don't send short-lived spawners to distant players.
1597 * This could be replaced with proper tracking at some point. */
1598 const bool distance_check = !attached_id && p.time <= 1.0f;
1600 for (const session_t client_id : clients) {
1601 RemotePlayer *player = m_env->getPlayer(client_id);
1605 if (distance_check) {
1606 PlayerSAO *sao = player->getPlayerSAO();
1609 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1613 SendAddParticleSpawner(client_id, player->protocol_version,
1614 p, attached_id, id);
1618 assert(protocol_version != 0);
1620 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1622 pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel
1623 << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime
1624 << p.minsize << p.maxsize << p.collisiondetection;
1626 pkt.putLongString(p.texture);
1628 pkt << id << p.vertical << p.collision_removal << attached_id;
1630 std::ostringstream os(std::ios_base::binary);
1631 p.animation.serialize(os, protocol_version);
1632 pkt.putRawString(os.str());
1634 pkt << p.glow << p.object_collision;
1635 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1640 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1642 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1646 if (peer_id != PEER_ID_INEXISTENT)
1649 m_clients.sendToAll(&pkt);
1653 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1655 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1657 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1658 << form->text << form->number << form->item << form->dir
1659 << form->align << form->offset << form->world_pos << form->size
1660 << form->z_index << form->text2 << form->style;
1665 void Server::SendHUDRemove(session_t peer_id, u32 id)
1667 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1672 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1674 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1675 pkt << id << (u8) stat;
1679 case HUD_STAT_SCALE:
1680 case HUD_STAT_ALIGN:
1681 case HUD_STAT_OFFSET:
1682 pkt << *(v2f *) value;
1686 case HUD_STAT_TEXT2:
1687 pkt << *(std::string *) value;
1689 case HUD_STAT_WORLD_POS:
1690 pkt << *(v3f *) value;
1693 pkt << *(v2s32 *) value;
1695 default: // all other types
1696 pkt << *(u32 *) value;
1703 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1705 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1707 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1709 pkt << flags << mask;
1714 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1716 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1717 pkt << param << value;
1721 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1723 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1725 // Handle prior clients here
1726 if (m_clients.getProtocolVersion(peer_id) < 39) {
1727 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1729 for (const std::string& texture : params.textures)
1732 pkt << params.clouds;
1733 } else { // Handle current clients and future clients
1734 pkt << params.bgcolor << params.type
1735 << params.clouds << params.fog_sun_tint
1736 << params.fog_moon_tint << params.fog_tint_type;
1738 if (params.type == "skybox") {
1739 pkt << (u16) params.textures.size();
1740 for (const std::string &texture : params.textures)
1742 } else if (params.type == "regular") {
1743 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1744 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1745 << params.sky_color.night_sky << params.sky_color.night_horizon
1746 << params.sky_color.indoors;
1753 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1755 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1756 pkt << params.visible << params.texture
1757 << params.tonemap << params.sunrise
1758 << params.sunrise_visible << params.scale;
1762 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1764 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1766 pkt << params.visible << params.texture
1767 << params.tonemap << params.scale;
1771 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1773 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1775 pkt << params.visible << params.count
1776 << params.starcolor << params.scale;
1781 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1783 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1784 pkt << params.density << params.color_bright << params.color_ambient
1785 << params.height << params.thickness << params.speed;
1789 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1792 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1795 pkt << do_override << (u16) (ratio * 65535);
1800 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1802 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1803 pkt << time << time_speed;
1805 if (peer_id == PEER_ID_INEXISTENT) {
1806 m_clients.sendToAll(&pkt);
1813 void Server::SendPlayerHP(session_t peer_id)
1815 PlayerSAO *playersao = getPlayerSAO(peer_id);
1818 SendHP(peer_id, playersao->getHP());
1819 m_script->player_event(playersao,"health_changed");
1821 // Send to other clients
1822 playersao->sendPunchCommand();
1825 void Server::SendPlayerBreath(PlayerSAO *sao)
1829 m_script->player_event(sao, "breath_changed");
1830 SendBreath(sao->getPeerID(), sao->getBreath());
1833 void Server::SendMovePlayer(session_t peer_id)
1835 RemotePlayer *player = m_env->getPlayer(peer_id);
1837 PlayerSAO *sao = player->getPlayerSAO();
1840 // Send attachment updates instantly to the client prior updating position
1841 sao->sendOutdatedData();
1843 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1844 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1847 v3f pos = sao->getBasePosition();
1848 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1849 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1850 << " pitch=" << sao->getLookPitch()
1851 << " yaw=" << sao->getRotation().Y
1858 void Server::SendPlayerFov(session_t peer_id)
1860 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1862 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1863 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1868 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1869 f32 animation_speed)
1871 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1874 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1875 << animation_frames[3] << animation_speed;
1880 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1882 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1883 pkt << first << third;
1887 void Server::SendPlayerPrivileges(session_t peer_id)
1889 RemotePlayer *player = m_env->getPlayer(peer_id);
1891 if(player->getPeerId() == PEER_ID_INEXISTENT)
1894 std::set<std::string> privs;
1895 m_script->getAuth(player->getName(), NULL, &privs);
1897 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1898 pkt << (u16) privs.size();
1900 for (const std::string &priv : privs) {
1907 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1909 RemotePlayer *player = m_env->getPlayer(peer_id);
1911 if (player->getPeerId() == PEER_ID_INEXISTENT)
1914 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1915 pkt.putLongString(player->inventory_formspec);
1920 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1922 RemotePlayer *player = m_env->getPlayer(peer_id);
1924 if (player->getPeerId() == PEER_ID_INEXISTENT)
1927 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1928 pkt << player->formspec_prepend;
1932 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1934 // Radius inside which objects are active
1935 static thread_local const s16 radius =
1936 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1938 // Radius inside which players are active
1939 static thread_local const bool is_transfer_limited =
1940 g_settings->exists("unlimited_player_transfer_distance") &&
1941 !g_settings->getBool("unlimited_player_transfer_distance");
1943 static thread_local const s16 player_transfer_dist =
1944 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
1946 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
1947 radius : player_transfer_dist;
1949 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
1953 std::queue<u16> removed_objects, added_objects;
1954 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
1955 client->m_known_objects, removed_objects);
1956 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
1957 client->m_known_objects, added_objects);
1959 int removed_count = removed_objects.size();
1960 int added_count = added_objects.size();
1962 if (removed_objects.empty() && added_objects.empty())
1968 // Handle removed objects
1969 writeU16((u8*)buf, removed_objects.size());
1970 data.append(buf, 2);
1971 while (!removed_objects.empty()) {
1973 u16 id = removed_objects.front();
1974 ServerActiveObject* obj = m_env->getActiveObject(id);
1976 // Add to data buffer for sending
1977 writeU16((u8*)buf, id);
1978 data.append(buf, 2);
1980 // Remove from known objects
1981 client->m_known_objects.erase(id);
1983 if (obj && obj->m_known_by_count > 0)
1984 obj->m_known_by_count--;
1986 removed_objects.pop();
1989 // Handle added objects
1990 writeU16((u8*)buf, added_objects.size());
1991 data.append(buf, 2);
1992 while (!added_objects.empty()) {
1994 u16 id = added_objects.front();
1995 ServerActiveObject *obj = m_env->getActiveObject(id);
1996 added_objects.pop();
1999 warningstream << FUNCTION_NAME << ": NULL object id="
2000 << (int)id << std::endl;
2005 u8 type = obj->getSendType();
2007 // Add to data buffer for sending
2008 writeU16((u8*)buf, id);
2009 data.append(buf, 2);
2010 writeU8((u8*)buf, type);
2011 data.append(buf, 1);
2013 data.append(serializeString32(
2014 obj->getClientInitializationData(client->net_proto_version)));
2016 // Add to known objects
2017 client->m_known_objects.insert(id);
2019 obj->m_known_by_count++;
2022 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2023 pkt.putRawString(data.c_str(), data.size());
2026 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2027 << removed_count << " removed, " << added_count << " added, "
2028 << "packet size is " << pkt.getSize() << std::endl;
2031 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2034 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2035 datas.size(), peer_id);
2037 pkt.putRawString(datas.c_str(), datas.size());
2039 m_clients.send(pkt.getPeerId(),
2040 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2044 void Server::SendCSMRestrictionFlags(session_t peer_id)
2046 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2047 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2048 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2052 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2054 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2059 inline s32 Server::nextSoundId()
2061 s32 ret = m_next_sound_id;
2062 if (m_next_sound_id == INT32_MAX)
2063 m_next_sound_id = 0; // signed overflow is undefined
2069 s32 Server::playSound(const SimpleSoundSpec &spec,
2070 const ServerSoundParams ¶ms, bool ephemeral)
2072 // Find out initial position of sound
2073 bool pos_exists = false;
2074 v3f pos = params.getPos(m_env, &pos_exists);
2075 // If position is not found while it should be, cancel sound
2076 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2079 // Filter destination clients
2080 std::vector<session_t> dst_clients;
2081 if (!params.to_player.empty()) {
2082 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2084 infostream<<"Server::playSound: Player \""<<params.to_player
2085 <<"\" not found"<<std::endl;
2088 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2089 infostream<<"Server::playSound: Player \""<<params.to_player
2090 <<"\" not connected"<<std::endl;
2093 dst_clients.push_back(player->getPeerId());
2095 std::vector<session_t> clients = m_clients.getClientIDs();
2097 for (const session_t client_id : clients) {
2098 RemotePlayer *player = m_env->getPlayer(client_id);
2101 if (!params.exclude_player.empty() &&
2102 params.exclude_player == player->getName())
2105 PlayerSAO *sao = player->getPlayerSAO();
2110 if(sao->getBasePosition().getDistanceFrom(pos) >
2111 params.max_hear_distance)
2114 dst_clients.push_back(client_id);
2118 if(dst_clients.empty())
2123 ServerPlayingSound *psound = nullptr;
2125 id = -1; // old clients will still use this, so pick a reserved ID
2128 // The sound will exist as a reference in m_playing_sounds
2129 m_playing_sounds[id] = ServerPlayingSound();
2130 psound = &m_playing_sounds[id];
2131 psound->params = params;
2132 psound->spec = spec;
2135 float gain = params.gain * spec.gain;
2136 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2137 pkt << id << spec.name << gain
2138 << (u8) params.type << pos << params.object
2139 << params.loop << params.fade << params.pitch
2142 bool as_reliable = !ephemeral;
2144 for (const u16 dst_client : dst_clients) {
2146 psound->clients.insert(dst_client);
2147 m_clients.send(dst_client, 0, &pkt, as_reliable);
2151 void Server::stopSound(s32 handle)
2153 // Get sound reference
2154 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2155 m_playing_sounds.find(handle);
2156 if (i == m_playing_sounds.end())
2158 ServerPlayingSound &psound = i->second;
2160 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2163 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2164 si != psound.clients.end(); ++si) {
2166 m_clients.send(*si, 0, &pkt, true);
2168 // Remove sound reference
2169 m_playing_sounds.erase(i);
2172 void Server::fadeSound(s32 handle, float step, float gain)
2174 // Get sound reference
2175 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2176 m_playing_sounds.find(handle);
2177 if (i == m_playing_sounds.end())
2180 ServerPlayingSound &psound = i->second;
2181 psound.params.gain = gain;
2183 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2184 pkt << handle << step << gain;
2186 // Backwards compability
2187 bool play_sound = gain > 0;
2188 ServerPlayingSound compat_psound = psound;
2189 compat_psound.clients.clear();
2191 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2192 compat_pkt << handle;
2194 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2195 it != psound.clients.end();) {
2196 if (m_clients.getProtocolVersion(*it) >= 32) {
2198 m_clients.send(*it, 0, &pkt, true);
2201 compat_psound.clients.insert(*it);
2203 m_clients.send(*it, 0, &compat_pkt, true);
2204 psound.clients.erase(it++);
2208 // Remove sound reference
2209 if (!play_sound || psound.clients.empty())
2210 m_playing_sounds.erase(i);
2212 if (play_sound && !compat_psound.clients.empty()) {
2213 // Play new sound volume on older clients
2214 playSound(compat_psound.spec, compat_psound.params);
2218 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2221 float maxd = far_d_nodes * BS;
2222 v3f p_f = intToFloat(p, BS);
2223 v3s16 block_pos = getNodeBlockPos(p);
2225 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2228 std::vector<session_t> clients = m_clients.getClientIDs();
2231 for (session_t client_id : clients) {
2232 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2236 RemotePlayer *player = m_env->getPlayer(client_id);
2237 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2239 // If player is far away, only set modified blocks not sent
2240 if (!client->isBlockSent(block_pos) || (sao &&
2241 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2243 far_players->emplace(client_id);
2245 client->SetBlockNotSent(block_pos);
2250 m_clients.send(client_id, 0, &pkt, true);
2256 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2257 float far_d_nodes, bool remove_metadata)
2259 float maxd = far_d_nodes * BS;
2260 v3f p_f = intToFloat(p, BS);
2261 v3s16 block_pos = getNodeBlockPos(p);
2263 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2264 pkt << p << n.param0 << n.param1 << n.param2
2265 << (u8) (remove_metadata ? 0 : 1);
2267 std::vector<session_t> clients = m_clients.getClientIDs();
2270 for (session_t client_id : clients) {
2271 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2275 RemotePlayer *player = m_env->getPlayer(client_id);
2276 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2278 // If player is far away, only set modified blocks not sent
2279 if (!client->isBlockSent(block_pos) || (sao &&
2280 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2282 far_players->emplace(client_id);
2284 client->SetBlockNotSent(block_pos);
2289 m_clients.send(client_id, 0, &pkt, true);
2295 void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes)
2297 float maxd = far_d_nodes * BS;
2298 NodeMetadataList meta_updates_list(false);
2299 std::vector<session_t> clients = m_clients.getClientIDs();
2303 for (session_t i : clients) {
2304 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2308 ServerActiveObject *player = m_env->getActiveObject(i);
2309 v3f player_pos = player ? player->getBasePosition() : v3f();
2311 for (const v3s16 &pos : meta_updates) {
2312 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2317 v3s16 block_pos = getNodeBlockPos(pos);
2318 if (!client->isBlockSent(block_pos) || (player &&
2319 player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) {
2320 client->SetBlockNotSent(block_pos);
2324 // Add the change to send list
2325 meta_updates_list.set(pos, meta);
2327 if (meta_updates_list.size() == 0)
2330 // Send the meta changes
2331 std::ostringstream os(std::ios::binary);
2332 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2333 std::ostringstream oss(std::ios::binary);
2334 compressZlib(os.str(), oss);
2336 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0);
2337 pkt.putLongString(oss.str());
2338 m_clients.send(i, 0, &pkt, true);
2340 meta_updates_list.clear();
2346 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2347 u16 net_proto_version)
2350 Create a packet with the block in the right format
2352 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2353 std::ostringstream os(std::ios_base::binary);
2354 block->serialize(os, ver, false, net_compression_level);
2355 block->serializeNetworkSpecific(os);
2356 std::string s = os.str();
2358 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + s.size(), peer_id);
2360 pkt << block->getPos();
2361 pkt.putRawString(s.c_str(), s.size());
2365 void Server::SendBlocks(float dtime)
2367 MutexAutoLock envlock(m_env_mutex);
2368 //TODO check if one big lock could be faster then multiple small ones
2370 std::vector<PrioritySortedBlockTransfer> queue;
2372 u32 total_sending = 0;
2375 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2377 std::vector<session_t> clients = m_clients.getClientIDs();
2380 for (const session_t client_id : clients) {
2381 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2386 total_sending += client->getSendingCount();
2387 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2393 // Lowest priority number comes first.
2394 // Lowest is most important.
2395 std::sort(queue.begin(), queue.end());
2399 // Maximal total count calculation
2400 // The per-client block sends is halved with the maximal online users
2401 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2402 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2404 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2405 Map &map = m_env->getMap();
2407 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2408 if (total_sending >= max_blocks_to_send)
2411 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2415 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2420 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2421 client->net_proto_version);
2423 client->SentBlock(block_to_send.pos);
2429 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2431 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2436 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2437 if (!client || client->isBlockSent(blockpos)) {
2441 SendBlockNoLock(peer_id, block, client->serialization_version,
2442 client->net_proto_version);
2448 bool Server::addMediaFile(const std::string &filename,
2449 const std::string &filepath, std::string *filedata_to,
2450 std::string *digest_to)
2452 // If name contains illegal characters, ignore the file
2453 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2454 infostream << "Server: ignoring illegal file name: \""
2455 << filename << "\"" << std::endl;
2458 // If name is not in a supported format, ignore it
2459 const char *supported_ext[] = {
2460 ".png", ".jpg", ".bmp", ".tga",
2462 ".x", ".b3d", ".obj",
2463 // Custom translation file format
2467 if (removeStringEnd(filename, supported_ext).empty()) {
2468 infostream << "Server: ignoring unsupported file extension: \""
2469 << filename << "\"" << std::endl;
2472 // Ok, attempt to load the file and add to cache
2475 std::string filedata;
2476 if (!fs::ReadFile(filepath, filedata)) {
2477 errorstream << "Server::addMediaFile(): Failed to open \""
2478 << filename << "\" for reading" << std::endl;
2482 if (filedata.empty()) {
2483 errorstream << "Server::addMediaFile(): Empty file \""
2484 << filepath << "\"" << std::endl;
2489 sha1.addBytes(filedata.c_str(), filedata.length());
2491 unsigned char *digest = sha1.getDigest();
2492 std::string sha1_base64 = base64_encode(digest, 20);
2493 std::string sha1_hex = hex_encode((char*) digest, 20);
2495 *digest_to = std::string((char*) digest, 20);
2499 m_media[filename] = MediaInfo(filepath, sha1_base64);
2500 verbosestream << "Server: " << sha1_hex << " is " << filename
2504 *filedata_to = std::move(filedata);
2508 void Server::fillMediaCache()
2510 infostream << "Server: Calculating media file checksums" << std::endl;
2512 // Collect all media file paths
2513 std::vector<std::string> paths;
2515 // ordered in descending priority
2516 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2517 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2518 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2519 m_modmgr->getModsMediaPaths(paths);
2521 // Collect media file information from paths into cache
2522 for (const std::string &mediapath : paths) {
2523 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2524 for (const fs::DirListNode &dln : dirlist) {
2525 if (dln.dir) // Ignore dirs (already in paths)
2528 const std::string &filename = dln.name;
2529 if (m_media.find(filename) != m_media.end()) // Do not override
2532 std::string filepath = mediapath;
2533 filepath.append(DIR_DELIM).append(filename);
2534 addMediaFile(filename, filepath);
2538 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2541 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2544 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2547 std::string lang_suffix;
2548 lang_suffix.append(".").append(lang_code).append(".tr");
2549 for (const auto &i : m_media) {
2550 if (i.second.no_announce)
2552 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2559 for (const auto &i : m_media) {
2560 if (i.second.no_announce)
2562 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2564 pkt << i.first << i.second.sha1_digest;
2567 pkt << g_settings->get("remote_media");
2570 verbosestream << "Server: Announcing files to id(" << peer_id
2571 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2574 struct SendableMedia
2580 SendableMedia(const std::string &name, const std::string &path,
2581 std::string &&data):
2582 name(name), path(path), data(std::move(data))
2586 void Server::sendRequestedMedia(session_t peer_id,
2587 const std::vector<std::string> &tosend)
2589 verbosestream<<"Server::sendRequestedMedia(): "
2590 <<"Sending files to client"<<std::endl;
2594 // Put 5kB in one bunch (this is not accurate)
2595 u32 bytes_per_bunch = 5000;
2597 std::vector< std::vector<SendableMedia> > file_bunches;
2598 file_bunches.emplace_back();
2600 u32 file_size_bunch_total = 0;
2602 for (const std::string &name : tosend) {
2603 if (m_media.find(name) == m_media.end()) {
2604 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2605 <<"unknown file \""<<(name)<<"\""<<std::endl;
2609 const auto &m = m_media[name];
2613 if (!fs::ReadFile(m.path, data)) {
2614 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2615 << name << "\"" << std::endl;
2618 file_size_bunch_total += data.size();
2621 file_bunches.back().emplace_back(name, m.path, std::move(data));
2623 // Start next bunch if got enough data
2624 if(file_size_bunch_total >= bytes_per_bunch) {
2625 file_bunches.emplace_back();
2626 file_size_bunch_total = 0;
2631 /* Create and send packets */
2633 u16 num_bunches = file_bunches.size();
2634 for (u16 i = 0; i < num_bunches; i++) {
2637 u16 total number of texture bunches
2638 u16 index of this bunch
2639 u32 number of files in this bunch
2648 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2649 pkt << num_bunches << i << (u32) file_bunches[i].size();
2651 for (const SendableMedia &j : file_bunches[i]) {
2653 pkt.putLongString(j.data);
2656 verbosestream << "Server::sendRequestedMedia(): bunch "
2657 << i << "/" << num_bunches
2658 << " files=" << file_bunches[i].size()
2659 << " size=" << pkt.getSize() << std::endl;
2664 void Server::stepPendingDynMediaCallbacks(float dtime)
2666 MutexAutoLock lock(m_env_mutex);
2668 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2669 it->second.expiry_timer -= dtime;
2670 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2677 const auto &name = it->second.filename;
2678 if (!name.empty()) {
2679 assert(m_media.count(name));
2680 // if no_announce isn't set we're definitely deleting the wrong file!
2681 sanity_check(m_media[name].no_announce);
2683 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2684 m_media.erase(name);
2686 getScriptIface()->freeDynamicMediaCallback(it->first);
2687 it = m_pending_dyn_media.erase(it);
2691 void Server::SendMinimapModes(session_t peer_id,
2692 std::vector<MinimapMode> &modes, size_t wanted_mode)
2694 RemotePlayer *player = m_env->getPlayer(peer_id);
2696 if (player->getPeerId() == PEER_ID_INEXISTENT)
2699 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2700 pkt << (u16)modes.size() << (u16)wanted_mode;
2702 for (auto &mode : modes)
2703 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2708 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2710 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2714 pkt << false; // Remove inventory
2716 pkt << true; // Update inventory
2718 // Serialization & NetworkPacket isn't a love story
2719 std::ostringstream os(std::ios_base::binary);
2720 inventory->serialize(os);
2721 inventory->setModified(false);
2723 const std::string &os_str = os.str();
2724 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2725 pkt.putRawString(os_str);
2728 if (peer_id == PEER_ID_INEXISTENT)
2729 m_clients.sendToAll(&pkt);
2734 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2736 // Lookup player name, to filter detached inventories just after
2737 std::string peer_name;
2738 if (peer_id != PEER_ID_INEXISTENT) {
2739 peer_name = getClient(peer_id, CS_Created)->getName();
2742 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2743 sendDetachedInventory(inv, name, peer_id);
2746 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2753 void Server::DiePlayer(session_t peer_id, const PlayerHPChangeReason &reason)
2755 PlayerSAO *playersao = getPlayerSAO(peer_id);
2758 infostream << "Server::DiePlayer(): Player "
2759 << playersao->getPlayer()->getName()
2760 << " dies" << std::endl;
2762 playersao->setHP(0, reason);
2763 playersao->clearParentAttachment();
2765 // Trigger scripted stuff
2766 m_script->on_dieplayer(playersao, reason);
2768 SendPlayerHP(peer_id);
2769 SendDeathscreen(peer_id, false, v3f(0,0,0));
2772 void Server::RespawnPlayer(session_t peer_id)
2774 PlayerSAO *playersao = getPlayerSAO(peer_id);
2777 infostream << "Server::RespawnPlayer(): Player "
2778 << playersao->getPlayer()->getName()
2779 << " respawns" << std::endl;
2781 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2782 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2783 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2785 bool repositioned = m_script->on_respawnplayer(playersao);
2786 if (!repositioned) {
2787 // setPos will send the new position to client
2788 playersao->setPos(findSpawnPos());
2791 SendPlayerHP(peer_id);
2795 void Server::DenySudoAccess(session_t peer_id)
2797 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2802 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2803 const std::string &str_reason, bool reconnect)
2805 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2807 m_clients.event(peer_id, CSE_SetDenied);
2808 DisconnectPeer(peer_id);
2812 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2813 const std::string &custom_reason)
2815 SendAccessDenied(peer_id, reason, custom_reason);
2816 m_clients.event(peer_id, CSE_SetDenied);
2817 DisconnectPeer(peer_id);
2820 // 13/03/15: remove this function when protocol version 25 will become
2821 // the minimum version for MT users, maybe in 1 year
2822 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2824 SendAccessDenied_Legacy(peer_id, reason);
2825 m_clients.event(peer_id, CSE_SetDenied);
2826 DisconnectPeer(peer_id);
2829 void Server::DisconnectPeer(session_t peer_id)
2831 m_modchannel_mgr->leaveAllChannels(peer_id);
2832 m_con->DisconnectPeer(peer_id);
2835 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2838 RemoteClient* client = getClient(peer_id, CS_Invalid);
2840 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2842 // Right now, the auth mechs don't change between login and sudo mode.
2843 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2844 client->allowed_sudo_mechs = sudo_auth_mechs;
2846 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2847 << g_settings->getFloat("dedicated_server_step")
2851 m_clients.event(peer_id, CSE_AuthAccept);
2853 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2855 // We only support SRP right now
2856 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2858 resp_pkt << sudo_auth_mechs;
2860 m_clients.event(peer_id, CSE_SudoSuccess);
2864 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2866 std::wstring message;
2869 Clear references to playing sounds
2871 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2872 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2873 ServerPlayingSound &psound = i->second;
2874 psound.clients.erase(peer_id);
2875 if (psound.clients.empty())
2876 m_playing_sounds.erase(i++);
2881 // clear formspec info so the next client can't abuse the current state
2882 m_formspec_state_data.erase(peer_id);
2884 RemotePlayer *player = m_env->getPlayer(peer_id);
2886 /* Run scripts and remove from environment */
2888 PlayerSAO *playersao = player->getPlayerSAO();
2891 playersao->clearChildAttachments();
2892 playersao->clearParentAttachment();
2894 // inform connected clients
2895 const std::string &player_name = player->getName();
2896 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2897 // (u16) 1 + std::string represents a vector serialization representation
2898 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2899 m_clients.sendToAll(¬ice);
2901 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2903 playersao->disconnected();
2910 if (player && reason != CDR_DENY) {
2911 std::ostringstream os(std::ios_base::binary);
2912 std::vector<session_t> clients = m_clients.getClientIDs();
2914 for (const session_t client_id : clients) {
2916 RemotePlayer *player = m_env->getPlayer(client_id);
2920 // Get name of player
2921 os << player->getName() << " ";
2924 std::string name = player->getName();
2925 actionstream << name << " "
2926 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2927 << " List of players: " << os.str() << std::endl;
2929 m_admin_chat->outgoing_queue.push_back(
2930 new ChatEventNick(CET_NICK_REMOVE, name));
2934 MutexAutoLock env_lock(m_env_mutex);
2935 m_clients.DeleteClient(peer_id);
2939 // Send leave chat message to all remaining clients
2940 if (!message.empty()) {
2941 SendChatMessage(PEER_ID_INEXISTENT,
2942 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2946 void Server::UpdateCrafting(RemotePlayer *player)
2948 InventoryList *clist = player->inventory.getList("craft");
2949 if (!clist || clist->getSize() == 0)
2952 if (!clist->checkModified())
2955 // Get a preview for crafting
2957 InventoryLocation loc;
2958 loc.setPlayer(player->getName());
2959 std::vector<ItemStack> output_replacements;
2960 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2961 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2964 InventoryList *plist = player->inventory.getList("craftpreview");
2965 if (plist && plist->getSize() >= 1) {
2966 // Put the new preview in
2967 plist->changeItem(0, preview);
2971 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2973 if (evt->type == CET_NICK_ADD) {
2974 // The terminal informed us of its nick choice
2975 m_admin_nick = ((ChatEventNick *)evt)->nick;
2976 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2977 errorstream << "You haven't set up an account." << std::endl
2978 << "Please log in using the client as '"
2979 << m_admin_nick << "' with a secure password." << std::endl
2980 << "Until then, you can't execute admin tasks via the console," << std::endl
2981 << "and everybody can claim the user account instead of you," << std::endl
2982 << "giving them full control over this server." << std::endl;
2985 assert(evt->type == CET_CHAT);
2986 handleAdminChat((ChatEventChat *)evt);
2990 std::wstring Server::handleChat(const std::string &name,
2991 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2993 // If something goes wrong, this player is to blame
2994 RollbackScopeActor rollback_scope(m_rollback,
2995 std::string("player:") + name);
2997 if (g_settings->getBool("strip_color_codes"))
2998 wmessage = unescape_enriched(wmessage);
3001 switch (player->canSendChatMessage()) {
3002 case RPLAYER_CHATRESULT_FLOODING: {
3003 std::wstringstream ws;
3004 ws << L"You cannot send more messages. You are limited to "
3005 << g_settings->getFloat("chat_message_limit_per_10sec")
3006 << L" messages per 10 seconds.";
3009 case RPLAYER_CHATRESULT_KICK:
3010 DenyAccess_Legacy(player->getPeerId(),
3011 L"You have been kicked due to message flooding.");
3013 case RPLAYER_CHATRESULT_OK:
3016 FATAL_ERROR("Unhandled chat filtering result found.");
3020 if (m_max_chatmessage_length > 0
3021 && wmessage.length() > m_max_chatmessage_length) {
3022 return L"Your message exceed the maximum chat message limit set on the server. "
3023 L"It was refused. Send a shorter message";
3026 auto message = trim(wide_to_utf8(wmessage));
3027 if (message.empty())
3030 if (message.find_first_of("\n\r") != std::wstring::npos) {
3031 return L"Newlines are not permitted in chat messages";
3034 // Run script hook, exit if script ate the chat message
3035 if (m_script->on_chat_message(name, message))
3040 // Whether to send line to the player that sent the message, or to all players
3041 bool broadcast_line = true;
3043 if (check_shout_priv && !checkPriv(name, "shout")) {
3044 line += L"-!- You don't have permission to shout.";
3045 broadcast_line = false;
3048 Workaround for fixing chat on Android. Lua doesn't handle
3049 the Cyrillic alphabet and some characters on older Android devices
3052 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3054 line += utf8_to_wide(m_script->formatChatMessage(name,
3055 wide_to_utf8(wmessage)));
3060 Tell calling method to send the message to sender
3062 if (!broadcast_line)
3066 Send the message to others
3068 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3070 ChatMessage chatmsg(line);
3072 std::vector<session_t> clients = m_clients.getClientIDs();
3073 for (u16 cid : clients)
3074 SendChatMessage(cid, chatmsg);
3079 void Server::handleAdminChat(const ChatEventChat *evt)
3081 std::string name = evt->nick;
3082 std::wstring wmessage = evt->evt_msg;
3084 std::wstring answer = handleChat(name, wmessage);
3086 // If asked to send answer to sender
3087 if (!answer.empty()) {
3088 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3092 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3094 RemoteClient *client = getClientNoEx(peer_id,state_min);
3096 throw ClientNotFoundException("Client not found");
3100 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3102 return m_clients.getClientNoEx(peer_id, state_min);
3105 std::string Server::getPlayerName(session_t peer_id)
3107 RemotePlayer *player = m_env->getPlayer(peer_id);
3109 return "[id="+itos(peer_id)+"]";
3110 return player->getName();
3113 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3115 RemotePlayer *player = m_env->getPlayer(peer_id);
3118 return player->getPlayerSAO();
3121 std::string Server::getStatusString()
3123 std::ostringstream os(std::ios_base::binary);
3126 os << "version: " << g_version_string;
3128 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3130 os << " | max lag: " << std::setprecision(3);
3131 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3133 // Information about clients
3135 os << " | clients: ";
3137 std::vector<session_t> clients = m_clients.getClientIDs();
3138 for (session_t client_id : clients) {
3139 RemotePlayer *player = m_env->getPlayer(client_id);
3141 // Get name of player
3142 const char *name = player ? player->getName() : "<unknown>";
3144 // Add name to information string
3153 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3154 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3156 if (!g_settings->get("motd").empty())
3157 os << std::endl << "# Server: " << g_settings->get("motd");
3162 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3164 std::set<std::string> privs;
3165 m_script->getAuth(name, NULL, &privs);
3169 bool Server::checkPriv(const std::string &name, const std::string &priv)
3171 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3172 return (privs.count(priv) != 0);
3175 void Server::reportPrivsModified(const std::string &name)
3178 std::vector<session_t> clients = m_clients.getClientIDs();
3179 for (const session_t client_id : clients) {
3180 RemotePlayer *player = m_env->getPlayer(client_id);
3181 reportPrivsModified(player->getName());
3184 RemotePlayer *player = m_env->getPlayer(name.c_str());
3187 SendPlayerPrivileges(player->getPeerId());
3188 PlayerSAO *sao = player->getPlayerSAO();
3191 sao->updatePrivileges(
3192 getPlayerEffectivePrivs(name),
3197 void Server::reportInventoryFormspecModified(const std::string &name)
3199 RemotePlayer *player = m_env->getPlayer(name.c_str());
3202 SendPlayerInventoryFormspec(player->getPeerId());
3205 void Server::reportFormspecPrependModified(const std::string &name)
3207 RemotePlayer *player = m_env->getPlayer(name.c_str());
3210 SendPlayerFormspecPrepend(player->getPeerId());
3213 void Server::setIpBanned(const std::string &ip, const std::string &name)
3215 m_banmanager->add(ip, name);
3218 void Server::unsetIpBanned(const std::string &ip_or_name)
3220 m_banmanager->remove(ip_or_name);
3223 std::string Server::getBanDescription(const std::string &ip_or_name)
3225 return m_banmanager->getBanDescription(ip_or_name);
3228 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3230 // m_env will be NULL if the server is initializing
3234 if (m_admin_nick == name && !m_admin_nick.empty()) {
3235 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3238 RemotePlayer *player = m_env->getPlayer(name);
3243 if (player->getPeerId() == PEER_ID_INEXISTENT)
3246 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3249 bool Server::showFormspec(const char *playername, const std::string &formspec,
3250 const std::string &formname)
3252 // m_env will be NULL if the server is initializing
3256 RemotePlayer *player = m_env->getPlayer(playername);
3260 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3264 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3269 u32 id = player->addHud(form);
3271 SendHUDAdd(player->getPeerId(), id, form);
3276 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3280 HudElement* todel = player->removeHud(id);
3287 SendHUDRemove(player->getPeerId(), id);
3291 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3296 SendHUDChange(player->getPeerId(), id, stat, data);
3300 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3305 SendHUDSetFlags(player->getPeerId(), flags, mask);
3306 player->hud_flags &= ~mask;
3307 player->hud_flags |= flags;
3309 PlayerSAO* playersao = player->getPlayerSAO();
3314 m_script->player_event(playersao, "hud_changed");
3318 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3323 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3326 player->setHotbarItemcount(hotbar_itemcount);
3327 std::ostringstream os(std::ios::binary);
3328 writeS32(os, hotbar_itemcount);
3329 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3333 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3338 player->setHotbarImage(name);
3339 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3342 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3347 player->setHotbarSelectedImage(name);
3348 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3351 Address Server::getPeerAddress(session_t peer_id)
3353 // Note that this is only set after Init was received in Server::handleCommand_Init
3354 return getClient(peer_id, CS_Invalid)->getAddress();
3357 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3358 v2s32 animation_frames[4], f32 frame_speed)
3360 sanity_check(player);
3361 player->setLocalAnimations(animation_frames, frame_speed);
3362 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3365 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3367 sanity_check(player);
3368 player->eye_offset_first = first;
3369 player->eye_offset_third = third;
3370 SendEyeOffset(player->getPeerId(), first, third);
3373 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3375 sanity_check(player);
3376 player->setSky(params);
3377 SendSetSky(player->getPeerId(), params);
3380 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3382 sanity_check(player);
3383 player->setSun(params);
3384 SendSetSun(player->getPeerId(), params);
3387 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3389 sanity_check(player);
3390 player->setMoon(params);
3391 SendSetMoon(player->getPeerId(), params);
3394 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3396 sanity_check(player);
3397 player->setStars(params);
3398 SendSetStars(player->getPeerId(), params);
3401 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3403 sanity_check(player);
3404 player->setCloudParams(params);
3405 SendCloudParams(player->getPeerId(), params);
3408 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3411 sanity_check(player);
3412 player->overrideDayNightRatio(do_override, ratio);
3413 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3416 void Server::notifyPlayers(const std::wstring &msg)
3418 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3421 void Server::spawnParticle(const std::string &playername,
3422 const ParticleParameters &p)
3424 // m_env will be NULL if the server is initializing
3428 session_t peer_id = PEER_ID_INEXISTENT;
3430 if (!playername.empty()) {
3431 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3434 peer_id = player->getPeerId();
3435 proto_ver = player->protocol_version;
3438 SendSpawnParticle(peer_id, proto_ver, p);
3441 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3442 ServerActiveObject *attached, const std::string &playername)
3444 // m_env will be NULL if the server is initializing
3448 session_t peer_id = PEER_ID_INEXISTENT;
3450 if (!playername.empty()) {
3451 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3454 peer_id = player->getPeerId();
3455 proto_ver = player->protocol_version;
3458 u16 attached_id = attached ? attached->getId() : 0;
3461 if (attached_id == 0)
3462 id = m_env->addParticleSpawner(p.time);
3464 id = m_env->addParticleSpawner(p.time, attached_id);
3466 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3470 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3472 // m_env will be NULL if the server is initializing
3474 throw ServerError("Can't delete particle spawners during initialisation!");
3476 session_t peer_id = PEER_ID_INEXISTENT;
3477 if (!playername.empty()) {
3478 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3481 peer_id = player->getPeerId();
3484 m_env->deleteParticleSpawner(id);
3485 SendDeleteParticleSpawner(peer_id, id);
3488 bool Server::dynamicAddMedia(std::string filepath,
3489 const u32 token, const std::string &to_player, bool ephemeral)
3491 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3492 auto it = m_media.find(filename);
3493 if (it != m_media.end()) {
3494 // Allow the same path to be "added" again in certain conditions
3495 if (ephemeral || it->second.path != filepath) {
3496 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3497 << "\" already exists in media cache" << std::endl;
3502 // Load the file and add it to our media cache
3503 std::string filedata, raw_hash;
3504 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3509 // Create a copy of the file and swap out the path, this removes the
3510 // requirement that mods keep the file accessible at the original path.
3511 filepath = fs::CreateTempFile();
3512 bool ok = ([&] () -> bool {
3513 if (filepath.empty())
3515 std::ofstream os(filepath.c_str(), std::ios::binary);
3523 errorstream << "Server: failed to create a copy of media file "
3524 << "\"" << filename << "\"" << std::endl;
3525 m_media.erase(filename);
3528 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3529 << filepath << std::endl;
3531 m_media[filename].path = filepath;
3532 m_media[filename].no_announce = true;
3533 // stepPendingDynMediaCallbacks will clean this up later.
3534 } else if (!to_player.empty()) {
3535 m_media[filename].no_announce = true;
3538 // Push file to existing clients
3539 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3540 pkt << raw_hash << filename << (bool)ephemeral;
3542 NetworkPacket legacy_pkt = pkt;
3544 // Newer clients get asked to fetch the file (asynchronous)
3546 // Older clients have an awful hack that just throws the data at them
3547 legacy_pkt.putLongString(filedata);
3549 std::unordered_set<session_t> delivered, waiting;
3551 for (auto &pair : m_clients.getClientList()) {
3552 if (pair.second->getState() < CS_DefinitionsSent)
3554 const auto proto_ver = pair.second->net_proto_version;
3558 const session_t peer_id = pair.second->peer_id;
3559 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3562 if (proto_ver < 40) {
3563 delivered.emplace(peer_id);
3565 The network layer only guarantees ordered delivery inside a channel.
3566 Since the very next packet could be one that uses the media, we have
3567 to push the media over ALL channels to ensure it is processed before
3568 it is used. In practice this means channels 1 and 0.
3570 m_clients.send(peer_id, 1, &legacy_pkt, true);
3571 m_clients.send(peer_id, 0, &legacy_pkt, true);
3573 waiting.emplace(peer_id);
3574 Send(peer_id, &pkt);
3579 // Run callback for players that already had the file delivered (legacy-only)
3580 for (session_t peer_id : delivered) {
3581 if (auto player = m_env->getPlayer(peer_id))
3582 getScriptIface()->on_dynamic_media_added(token, player->getName());
3585 // Save all others in our pending state
3586 auto &state = m_pending_dyn_media[token];
3587 state.waiting_players = std::move(waiting);
3588 // regardless of success throw away the callback after a while
3589 state.expiry_timer = 60.0f;
3591 state.filename = filename;
3596 // actions: time-reversed list
3597 // Return value: success/failure
3598 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3599 std::list<std::string> *log)
3601 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3602 ServerMap *map = (ServerMap*)(&m_env->getMap());
3604 // Fail if no actions to handle
3605 if (actions.empty()) {
3607 log->push_back("Nothing to do.");
3614 for (const RollbackAction &action : actions) {
3616 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3619 std::ostringstream os;
3620 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3621 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3623 log->push_back(os.str());
3625 std::ostringstream os;
3626 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3627 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3629 log->push_back(os.str());
3633 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3634 <<" failed"<<std::endl;
3636 // Call it done if less than half failed
3637 return num_failed <= num_tried/2;
3640 // IGameDef interface
3642 IItemDefManager *Server::getItemDefManager()
3647 const NodeDefManager *Server::getNodeDefManager()
3652 ICraftDefManager *Server::getCraftDefManager()
3657 u16 Server::allocateUnknownNodeId(const std::string &name)
3659 return m_nodedef->allocateDummy(name);
3662 IWritableItemDefManager *Server::getWritableItemDefManager()
3667 NodeDefManager *Server::getWritableNodeDefManager()
3672 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3677 const std::vector<ModSpec> & Server::getMods() const
3679 return m_modmgr->getMods();
3682 const ModSpec *Server::getModSpec(const std::string &modname) const
3684 return m_modmgr->getModSpec(modname);
3687 void Server::getModNames(std::vector<std::string> &modlist)
3689 m_modmgr->getModNames(modlist);
3692 std::string Server::getBuiltinLuaPath()
3694 return porting::path_share + DIR_DELIM + "builtin";
3697 v3f Server::findSpawnPos()
3699 ServerMap &map = m_env->getServerMap();
3701 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3702 return nodeposf * BS;
3704 bool is_good = false;
3705 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3706 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3708 // Try to find a good place a few times
3709 for (s32 i = 0; i < 4000 && !is_good; i++) {
3710 s32 range = MYMIN(1 + i, range_max);
3711 // We're going to try to throw the player to this position
3712 v2s16 nodepos2d = v2s16(
3713 -range + (myrand() % (range * 2)),
3714 -range + (myrand() % (range * 2)));
3715 // Get spawn level at point
3716 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3717 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3718 // signify an unsuitable spawn position, or if outside limits.
3719 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3720 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3723 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3724 // Consecutive empty nodes
3727 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3728 // avoid obstructions in already-generated mapblocks.
3729 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3730 // no obstructions, but mapgen decorations are generated after spawn so
3731 // the player may end up inside one.
3732 for (s32 i = 0; i < 8; i++) {
3733 v3s16 blockpos = getNodeBlockPos(nodepos);
3734 map.emergeBlock(blockpos, true);
3735 content_t c = map.getNode(nodepos).getContent();
3737 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3738 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3739 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3741 if (air_count >= 2) {
3742 // Spawn in lower empty node
3744 nodeposf = intToFloat(nodepos, BS);
3745 // Don't spawn the player outside map boundaries
3746 if (objectpos_over_limit(nodeposf))
3747 // Exit this loop, positions above are probably over limit
3750 // Good position found, cause an exit from main loop
3764 // No suitable spawn point found, return fallback 0,0,0
3765 return v3f(0.0f, 0.0f, 0.0f);
3768 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3770 if (delay == 0.0f) {
3771 // No delay, shutdown immediately
3772 m_shutdown_state.is_requested = true;
3773 // only print to the infostream, a chat message saying
3774 // "Server Shutting Down" is sent when the server destructs.
3775 infostream << "*** Immediate Server shutdown requested." << std::endl;
3776 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3777 // Negative delay, cancel shutdown if requested
3778 m_shutdown_state.reset();
3779 std::wstringstream ws;
3781 ws << L"*** Server shutdown canceled.";
3783 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3784 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3785 // m_shutdown_* are already handled, skip.
3787 } else if (delay > 0.0f) {
3788 // Positive delay, tell the clients when the server will shut down
3789 std::wstringstream ws;
3791 ws << L"*** Server shutting down in "
3792 << duration_to_string(myround(delay)).c_str()
3795 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3796 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3799 m_shutdown_state.trigger(delay, msg, reconnect);
3802 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3805 Try to get an existing player
3807 RemotePlayer *player = m_env->getPlayer(name);
3809 // If player is already connected, cancel
3810 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3811 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3816 If player with the wanted peer_id already exists, cancel.
3818 if (m_env->getPlayer(peer_id)) {
3819 infostream<<"emergePlayer(): Player with wrong name but same"
3820 " peer_id already exists"<<std::endl;
3825 player = new RemotePlayer(name, idef());
3828 bool newplayer = false;
3831 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3833 // Complete init with server parts
3834 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3835 player->protocol_version = proto_version;
3839 m_script->on_newplayer(playersao);
3845 bool Server::registerModStorage(ModMetadata *storage)
3847 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3848 errorstream << "Unable to register same mod storage twice. Storage name: "
3849 << storage->getModName() << std::endl;
3853 m_mod_storages[storage->getModName()] = storage;
3857 void Server::unregisterModStorage(const std::string &name)
3859 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3860 if (it != m_mod_storages.end())
3861 m_mod_storages.erase(name);
3864 void dedicated_server_loop(Server &server, bool &kill)
3866 verbosestream<<"dedicated_server_loop()"<<std::endl;
3868 IntervalLimiter m_profiler_interval;
3870 static thread_local const float steplen =
3871 g_settings->getFloat("dedicated_server_step");
3872 static thread_local const float profiler_print_interval =
3873 g_settings->getFloat("profiler_print_interval");
3876 * The dedicated server loop only does time-keeping (in Server::step) and
3877 * provides a way to main.cpp to kill the server externally (bool &kill).
3881 // This is kind of a hack but can be done like this
3882 // because server.step() is very light
3883 sleep_ms((int)(steplen*1000.0));
3884 server.step(steplen);
3886 if (server.isShutdownRequested() || kill)
3892 if (profiler_print_interval != 0) {
3893 if(m_profiler_interval.step(steplen, profiler_print_interval))
3895 infostream<<"Profiler:"<<std::endl;
3896 g_profiler->print(infostream);
3897 g_profiler->clear();
3902 infostream << "Dedicated server quitting" << std::endl;
3904 if (g_settings->getBool("server_announce"))
3905 ServerList::sendAnnounce(ServerList::AA_DELETE,
3906 server.m_bind_addr.getPort());
3915 bool Server::joinModChannel(const std::string &channel)
3917 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3918 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3921 bool Server::leaveModChannel(const std::string &channel)
3923 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3926 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3928 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3931 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3935 ModChannel* Server::getModChannel(const std::string &channel)
3937 return m_modchannel_mgr->getModChannel(channel);
3940 void Server::broadcastModChannelMessage(const std::string &channel,
3941 const std::string &message, session_t from_peer)
3943 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3947 if (message.size() > STRING_MAX_LEN) {
3948 warningstream << "ModChannel message too long, dropping before sending "
3949 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3950 << channel << ")" << std::endl;
3955 if (from_peer != PEER_ID_SERVER) {
3956 sender = getPlayerName(from_peer);
3959 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3960 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3961 resp_pkt << channel << sender << message;
3962 for (session_t peer_id : peers) {
3964 if (peer_id == from_peer)
3967 Send(peer_id, &resp_pkt);
3970 if (from_peer != PEER_ID_SERVER) {
3971 m_script->on_modchannel_message(channel, sender, message);
3975 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3977 if (lang_code.empty())
3980 auto it = server_translations.find(lang_code);
3981 if (it != server_translations.end())
3982 return &it->second; // Already loaded
3984 // [] will create an entry
3985 auto *translations = &server_translations[lang_code];
3987 std::string suffix = "." + lang_code + ".tr";
3988 for (const auto &i : m_media) {
3989 if (str_ends_with(i.first, suffix)) {
3991 if (fs::ReadFile(i.second.path, data)) {
3992 translations->loadTranslation(data);
3997 return translations;
4000 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
4002 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4004 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4005 throw BaseException("Cannot read world.mt!");
4007 std::string backend = world_mt.exists("mod_storage_backend") ?
4008 world_mt.get("mod_storage_backend") : "files";
4009 if (backend == "files")
4010 warningstream << "/!\\ You are using the old mod storage files backend. "
4011 << "This backend is deprecated and may be removed in a future release /!\\"
4012 << std::endl << "Switching to SQLite3 is advised, "
4013 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4015 return openModStorageDatabase(backend, world_path, world_mt);
4018 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
4019 const std::string &world_path, const Settings &world_mt)
4021 if (backend == "sqlite3")
4022 return new ModMetadataDatabaseSQLite3(world_path);
4024 if (backend == "files")
4025 return new ModMetadataDatabaseFiles(world_path);
4027 if (backend == "dummy")
4028 return new Database_Dummy();
4030 throw BaseException("Mod storage database backend " + backend + " not supported");
4033 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4035 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4037 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4038 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4039 errorstream << "Cannot read world.mt!" << std::endl;
4043 std::string backend = world_mt.exists("mod_storage_backend") ?
4044 world_mt.get("mod_storage_backend") : "files";
4045 if (backend == migrate_to) {
4046 errorstream << "Cannot migrate: new backend is same"
4047 << " as the old one" << std::endl;
4051 ModMetadataDatabase *srcdb = nullptr;
4052 ModMetadataDatabase *dstdb = nullptr;
4054 bool succeeded = false;
4057 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4058 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4062 std::vector<std::string> mod_list;
4063 srcdb->listMods(&mod_list);
4064 for (const std::string &modname : mod_list) {
4066 srcdb->getModEntries(modname, &meta);
4067 for (const auto &pair : meta) {
4068 dstdb->setModEntry(modname, pair.first, pair.second);
4076 actionstream << "Successfully migrated the metadata of "
4077 << mod_list.size() << " mods" << std::endl;
4078 world_mt.set("mod_storage_backend", migrate_to);
4079 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4080 errorstream << "Failed to update world.mt!" << std::endl;
4082 actionstream << "world.mt updated" << std::endl;
4084 } catch (BaseException &e) {
4085 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4091 if (succeeded && backend == "files") {
4093 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4094 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4095 if (!fs::Rename(storage_path, backup_path))
4096 warningstream << "After migration, " << storage_path
4097 << " could not be renamed to " << backup_path << std::endl;