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::make_unique<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)");
272 const std::string aom_types[] = {"reliable", "unreliable"};
273 for (u32 i = 0; i < ARRLEN(aom_types); i++) {
274 std::string help_str("Number of active object messages generated (");
275 help_str.append(aom_types[i]).append(")");
276 m_aom_buffer_counter[i] = m_metrics_backend->addCounter(
277 "minetest_core_aom_generated_count", help_str,
278 {{"type", aom_types[i]}});
281 m_packet_recv_counter = m_metrics_backend->addCounter(
282 "minetest_core_server_packet_recv",
283 "Processable packets received");
285 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
286 "minetest_core_server_packet_recv_processed",
287 "Valid received packets processed");
289 m_map_edit_event_counter = m_metrics_backend->addCounter(
290 "minetest_core_map_edit_events",
291 "Number of map edit events");
293 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
299 // Send shutdown message
300 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
301 L"*** Server shutting down"));
304 MutexAutoLock envlock(m_env_mutex);
306 infostream << "Server: Saving players" << std::endl;
307 m_env->saveLoadedPlayers();
309 infostream << "Server: Kicking players" << std::endl;
310 std::string kick_msg;
311 bool reconnect = false;
312 if (isShutdownRequested()) {
313 reconnect = m_shutdown_state.should_reconnect;
314 kick_msg = m_shutdown_state.message;
316 if (kick_msg.empty()) {
317 kick_msg = g_settings->get("kick_msg_shutdown");
319 m_env->saveLoadedPlayers(true);
320 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
321 kick_msg, reconnect);
324 actionstream << "Server: Shutting down" << std::endl;
326 // Do this before stopping the server in case mapgen callbacks need to access
327 // server-controlled resources (like ModStorages). Also do them before
328 // shutdown callbacks since they may modify state that is finalized in a
331 m_emerge->stopThreads();
334 MutexAutoLock envlock(m_env_mutex);
336 // Execute script shutdown hooks
337 infostream << "Executing shutdown hooks" << std::endl;
339 m_script->on_shutdown();
340 } catch (ModError &e) {
341 errorstream << "ModError: " << e.what() << std::endl;
342 if (m_on_shutdown_errmsg) {
343 if (m_on_shutdown_errmsg->empty()) {
344 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
346 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
351 infostream << "Server: Saving environment metadata" << std::endl;
361 // Write any changes before deletion.
362 if (m_mod_storage_database)
363 m_mod_storage_database->endSave();
365 // Delete things in the reverse order of creation
369 delete m_mod_storage_database;
375 // Deinitialize scripting
376 infostream << "Server: Deinitializing scripting" << std::endl;
378 delete m_startup_server_map; // if available
379 delete m_game_settings;
381 while (!m_unsent_map_edit_queue.empty()) {
382 delete m_unsent_map_edit_queue.front();
383 m_unsent_map_edit_queue.pop();
389 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
390 if (m_simple_singleplayer_mode)
391 infostream << " in simple singleplayer mode" << std::endl;
393 infostream << std::endl;
394 infostream << "- world: " << m_path_world << std::endl;
395 infostream << "- game: " << m_gamespec.path << std::endl;
397 m_game_settings = Settings::createLayer(SL_GAME);
399 // Create world if it doesn't exist
401 loadGameConfAndInitWorld(m_path_world,
402 fs::GetFilenameFromPath(m_path_world.c_str()),
404 } catch (const BaseException &e) {
405 throw ServerError(std::string("Failed to initialize world: ") + e.what());
408 // Create emerge manager
409 m_emerge = new EmergeManager(this, m_metrics_backend.get());
411 // Create ban manager
412 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
413 m_banmanager = new BanManager(ban_path);
415 // Create mod storage database and begin a save for later
416 m_mod_storage_database = openModStorageDatabase(m_path_world);
417 m_mod_storage_database->beginSave();
419 m_modmgr = std::make_unique<ServerModManager>(m_path_world);
420 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
421 // complain about mods with unsatisfied dependencies
422 if (!m_modmgr->isConsistent()) {
423 m_modmgr->printUnsatisfiedModsError();
427 MutexAutoLock envlock(m_env_mutex);
429 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
430 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
431 m_startup_server_map = servermap;
433 // Initialize scripting
434 infostream << "Server: Initializing Lua" << std::endl;
436 m_script = new ServerScripting(this);
438 // Must be created before mod loading because we have some inventory creation
439 m_inventory_mgr = std::make_unique<ServerInventoryManager>();
441 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
443 m_gamespec.checkAndLog();
444 m_modmgr->loadMods(m_script);
446 // Read Textures and calculate sha1 sums
449 // Apply item aliases in the node definition manager
450 m_nodedef->updateAliases(m_itemdef);
452 // Apply texture overrides from texturepack/override.txt
453 std::vector<std::string> paths;
454 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
455 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
456 for (const std::string &path : paths) {
457 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
458 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
459 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
462 m_nodedef->setNodeRegistrationStatus(true);
464 // Perform pending node name resolutions
465 m_nodedef->runNodeResolveCallbacks();
467 // unmap node names in cross-references
468 m_nodedef->resolveCrossrefs();
470 // init the recipe hashes to speed up crafting
471 m_craftdef->initHashes(this);
473 // Initialize Environment
474 m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
475 m_env = new ServerEnvironment(servermap, m_script, this,
476 m_path_world, m_metrics_backend.get());
478 m_inventory_mgr->setEnv(m_env);
479 m_clients.setEnv(m_env);
481 if (!servermap->settings_mgr.makeMapgenParams())
482 FATAL_ERROR("Couldn't create any mapgen type");
484 // Initialize mapgens
485 m_emerge->initMapgens(servermap->getMapgenParams());
487 if (g_settings->getBool("enable_rollback_recording")) {
488 // Create rollback manager
489 m_rollback = new RollbackManager(m_path_world, this);
492 // Give environment reference to scripting api
493 m_script->initializeEnvironment(m_env);
495 // Do this after regular script init is done
496 m_script->initAsync();
498 // Register us to receive map edit events
499 servermap->addEventReceiver(this);
503 // Those settings can be overwritten in world.mt, they are
504 // intended to be cached after environment loading.
505 m_liquid_transform_every = g_settings->getFloat("liquid_update");
506 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
507 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
508 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
515 infostream << "Starting server on " << m_bind_addr.serializeString()
516 << "..." << std::endl;
518 // Stop thread if already running
521 // Initialize connection
522 m_con->SetTimeoutMs(30);
523 m_con->Serve(m_bind_addr);
528 // ASCII art for the win!
530 << " __. __. __. " << std::endl
531 << " _____ |__| ____ _____ / |_ _____ _____ / |_ " << std::endl
532 << " / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\" << std::endl
533 << "| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | " << std::endl
534 << "|__|_| / |___| /\\______> | \\______>_____/| | " << std::endl
535 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
536 actionstream << "World at [" << m_path_world << "]" << std::endl;
537 actionstream << "Server for gameid=\"" << m_gamespec.id
538 << "\" listening on ";
539 m_bind_addr.print(actionstream);
540 actionstream << "." << std::endl;
545 infostream<<"Server: Stopping and waiting threads"<<std::endl;
547 // Stop threads (set run=false first so both start stopping)
551 infostream<<"Server: Threads stopped"<<std::endl;
554 void Server::step(float dtime)
560 MutexAutoLock lock(m_step_dtime_mutex);
561 m_step_dtime += dtime;
563 // Throw if fatal error occurred in thread
564 std::string async_err = m_async_fatal_error.get();
565 if (!async_err.empty()) {
566 if (!m_simple_singleplayer_mode) {
567 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
568 g_settings->get("kick_msg_crash"),
569 g_settings->getBool("ask_reconnect_on_crash"));
571 throw ServerError("AsyncErr: " + async_err);
575 void Server::AsyncRunStep(bool initial_step)
580 MutexAutoLock lock1(m_step_dtime_mutex);
581 dtime = m_step_dtime;
585 // Send blocks to clients
589 if((dtime < 0.001) && !initial_step)
592 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
595 MutexAutoLock lock1(m_step_dtime_mutex);
596 m_step_dtime -= dtime;
602 m_uptime_counter->increment(dtime);
607 Update time of day and overall game time
609 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
612 Send to clients at constant intervals
615 m_time_of_day_send_timer -= dtime;
616 if (m_time_of_day_send_timer < 0.0) {
617 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
618 u16 time = m_env->getTimeOfDay();
619 float time_speed = g_settings->getFloat("time_speed");
620 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
622 m_timeofday_gauge->set(time);
626 MutexAutoLock lock(m_env_mutex);
627 // Figure out and report maximum lag to environment
628 float max_lag = m_env->getMaxLagEstimate();
629 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
631 if(dtime > 0.1 && dtime > max_lag * 2.0)
632 infostream<<"Server: Maximum lag peaked to "<<dtime
636 m_env->reportMaxLagEstimate(max_lag);
642 static const float map_timer_and_unload_dtime = 2.92;
643 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
645 MutexAutoLock lock(m_env_mutex);
646 // Run Map's timers and unload unused data
647 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
648 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
649 g_settings->getFloat("server_unload_unused_data_timeout"),
654 Listen to the admin chat, if available
657 if (!m_admin_chat->command_queue.empty()) {
658 MutexAutoLock lock(m_env_mutex);
659 while (!m_admin_chat->command_queue.empty()) {
660 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
661 handleChatInterfaceEvent(evt);
665 m_admin_chat->outgoing_queue.push_back(
666 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
673 /* Transform liquids */
674 m_liquid_transform_timer += dtime;
675 if(m_liquid_transform_timer >= m_liquid_transform_every)
677 m_liquid_transform_timer -= m_liquid_transform_every;
679 MutexAutoLock lock(m_env_mutex);
681 ScopeProfiler sp(g_profiler, "Server: liquid transform");
683 std::map<v3s16, MapBlock*> modified_blocks;
684 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
687 Set the modified blocks unsent for all the clients
689 if (!modified_blocks.empty()) {
690 SetBlocksNotSent(modified_blocks);
693 m_clients.step(dtime);
695 // increase/decrease lag gauge gradually
696 if (m_lag_gauge->get() > dtime) {
697 m_lag_gauge->decrement(dtime/100);
699 m_lag_gauge->increment(dtime/100);
703 float &counter = m_step_pending_dyn_media_timer;
705 if (counter >= 5.0f) {
706 stepPendingDynMediaCallbacks(counter);
713 // send masterserver announce
715 float &counter = m_masterserver_timer;
716 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
717 g_settings->getBool("server_announce")) {
718 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
719 ServerList::AA_START,
720 m_bind_addr.getPort(),
721 m_clients.getPlayerNames(),
722 m_uptime_counter->get(),
723 m_env->getGameTime(),
726 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
736 Check added and deleted active objects
739 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
740 MutexAutoLock envlock(m_env_mutex);
743 ClientInterface::AutoLock clientlock(m_clients);
744 const RemoteClientMap &clients = m_clients.getClientList();
745 ScopeProfiler sp(g_profiler, "Server: update objects within range");
747 m_player_gauge->set(clients.size());
748 for (const auto &client_it : clients) {
749 RemoteClient *client = client_it.second;
751 if (client->getState() < CS_DefinitionsSent)
754 // This can happen if the client times out somehow
755 if (!m_env->getPlayer(client->peer_id))
758 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
762 SendActiveObjectRemoveAdd(client, playersao);
766 // Write changes to the mod storage
767 m_mod_storage_save_timer -= dtime;
768 if (m_mod_storage_save_timer <= 0.0f) {
769 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
770 m_mod_storage_database->endSave();
771 m_mod_storage_database->beginSave();
779 MutexAutoLock envlock(m_env_mutex);
780 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
783 // Value = data sent by object
784 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
786 // Get active object messages from environment
787 ActiveObjectMessage aom(0);
788 u32 count_reliable = 0, count_unreliable = 0;
790 if (!m_env->getActiveObjectMessage(&aom))
797 std::vector<ActiveObjectMessage>* message_list = nullptr;
798 auto n = buffered_messages.find(aom.id);
799 if (n == buffered_messages.end()) {
800 message_list = new std::vector<ActiveObjectMessage>;
801 buffered_messages[aom.id] = message_list;
803 message_list = n->second;
805 message_list->push_back(std::move(aom));
808 m_aom_buffer_counter[0]->increment(count_reliable);
809 m_aom_buffer_counter[1]->increment(count_unreliable);
812 ClientInterface::AutoLock clientlock(m_clients);
813 const RemoteClientMap &clients = m_clients.getClientList();
814 // Route data to every client
815 std::string reliable_data, unreliable_data;
816 for (const auto &client_it : clients) {
817 reliable_data.clear();
818 unreliable_data.clear();
819 RemoteClient *client = client_it.second;
820 PlayerSAO *player = getPlayerSAO(client->peer_id);
821 // Go through all objects in message buffer
822 for (const auto &buffered_message : buffered_messages) {
823 // If object does not exist or is not known by client, skip it
824 u16 id = buffered_message.first;
825 ServerActiveObject *sao = m_env->getActiveObject(id);
826 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
829 // Get message list of object
830 std::vector<ActiveObjectMessage>* list = buffered_message.second;
831 // Go through every message
832 for (const ActiveObjectMessage &aom : *list) {
833 // Send position updates to players who do not see the attachment
834 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
835 if (sao->getId() == player->getId())
838 // Do not send position updates for attached players
839 // as long the parent is known to the client
840 ServerActiveObject *parent = sao->getParent();
841 if (parent && client->m_known_objects.find(parent->getId()) !=
842 client->m_known_objects.end())
846 // Add full new data to appropriate buffer
847 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
849 writeU16((u8*) idbuf, aom.id);
852 buffer.append(idbuf, sizeof(idbuf));
853 buffer.append(serializeString16(aom.datastring));
857 reliable_data and unreliable_data are now ready.
860 if (!reliable_data.empty()) {
861 SendActiveObjectMessages(client->peer_id, reliable_data);
864 if (!unreliable_data.empty()) {
865 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
870 // Clear buffered_messages
871 for (auto &buffered_message : buffered_messages) {
872 delete buffered_message.second;
877 Send queued-for-sending map edit events.
880 // We will be accessing the environment
881 MutexAutoLock lock(m_env_mutex);
883 // Single change sending is disabled if queue size is big
884 bool disable_single_change_sending = false;
885 if(m_unsent_map_edit_queue.size() >= 4)
886 disable_single_change_sending = true;
888 const auto event_count = m_unsent_map_edit_queue.size();
889 m_map_edit_event_counter->increment(event_count);
891 // We'll log the amount of each
894 std::list<v3s16> node_meta_updates;
896 while (!m_unsent_map_edit_queue.empty()) {
897 MapEditEvent* event = m_unsent_map_edit_queue.front();
898 m_unsent_map_edit_queue.pop();
900 // Players far away from the change are stored here.
901 // Instead of sending the changes, MapBlocks are set not sent
903 std::unordered_set<u16> far_players;
905 switch (event->type) {
908 prof.add("MEET_ADDNODE", 1);
909 sendAddNode(event->p, event->n, &far_players,
910 disable_single_change_sending ? 5 : 30,
911 event->type == MEET_ADDNODE);
913 case MEET_REMOVENODE:
914 prof.add("MEET_REMOVENODE", 1);
915 sendRemoveNode(event->p, &far_players,
916 disable_single_change_sending ? 5 : 30);
918 case MEET_BLOCK_NODE_METADATA_CHANGED: {
919 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
920 if (!event->is_private_change) {
921 // Don't send the change yet. Collect them to eliminate dupes.
922 node_meta_updates.remove(event->p);
923 node_meta_updates.push_back(event->p);
926 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
927 getNodeBlockPos(event->p))) {
928 block->raiseModified(MOD_STATE_WRITE_NEEDED,
929 MOD_REASON_REPORT_META_CHANGE);
934 prof.add("MEET_OTHER", 1);
935 for (const v3s16 &modified_block : event->modified_blocks) {
936 m_clients.markBlockposAsNotSent(modified_block);
940 prof.add("unknown", 1);
941 warningstream << "Server: Unknown MapEditEvent "
942 << ((u32)event->type) << std::endl;
947 Set blocks not sent to far players
949 if (!far_players.empty()) {
950 // Convert list format to that wanted by SetBlocksNotSent
951 std::map<v3s16, MapBlock*> modified_blocks2;
952 for (const v3s16 &modified_block : event->modified_blocks) {
953 modified_blocks2[modified_block] =
954 m_env->getMap().getBlockNoCreateNoEx(modified_block);
957 // Set blocks not sent
958 for (const u16 far_player : far_players) {
959 if (RemoteClient *client = getClient(far_player))
960 client->SetBlocksNotSent(modified_blocks2);
967 if (event_count >= 5) {
968 infostream << "Server: MapEditEvents:" << std::endl;
969 prof.print(infostream);
970 } else if (event_count != 0) {
971 verbosestream << "Server: MapEditEvents:" << std::endl;
972 prof.print(verbosestream);
975 // Send all metadata updates
976 if (node_meta_updates.size())
977 sendMetadataChanged(node_meta_updates);
981 Trigger emerge thread
982 Doing this every 2s is left over from old code, unclear if this is still needed.
985 float &counter = m_emergethread_trigger_timer;
987 if (counter <= 0.0f) {
990 m_emerge->startThreads();
994 // Save map, players and auth stuff
996 float &counter = m_savemap_timer;
998 static thread_local const float save_interval =
999 g_settings->getFloat("server_map_save_interval");
1000 if (counter >= save_interval) {
1002 MutexAutoLock lock(m_env_mutex);
1004 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
1007 if (m_banmanager->isModified()) {
1008 m_banmanager->save();
1011 // Save changed parts of map
1012 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1015 m_env->saveLoadedPlayers();
1017 // Save environment metadata
1022 m_shutdown_state.tick(dtime, this);
1025 void Server::Receive()
1035 In the first iteration *wait* for a packet, afterwards process
1036 all packets that are immediately available (no waiting).
1039 m_con->Receive(&pkt);
1042 if (!m_con->TryReceive(&pkt))
1046 peer_id = pkt.getPeerId();
1047 m_packet_recv_counter->increment();
1049 m_packet_recv_processed_counter->increment();
1050 } catch (const con::InvalidIncomingDataException &e) {
1051 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1052 << e.what() << std::endl;
1053 } catch (const SerializationError &e) {
1054 infostream << "Server::Receive(): SerializationError: what()="
1055 << e.what() << std::endl;
1056 } catch (const ClientStateError &e) {
1057 errorstream << "ProcessData: peer=" << peer_id << " what()="
1058 << e.what() << std::endl;
1059 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1060 } catch (const con::PeerNotFoundException &e) {
1062 } catch (const con::NoIncomingDataException &e) {
1068 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1070 std::string playername;
1071 PlayerSAO *playersao = NULL;
1073 ClientInterface::AutoLock clientlock(m_clients);
1074 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1076 playername = client->getName();
1077 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1081 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1083 // If failed, cancel
1084 if (!playersao || !player) {
1085 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1086 actionstream << "Server: Failed to emerge player \"" << playername
1087 << "\" (player allocated to an another client)" << std::endl;
1088 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1090 errorstream << "Server: " << playername << ": Failed to emerge player"
1092 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1098 Send complete position information
1100 SendMovePlayer(peer_id);
1103 SendPlayerPrivileges(peer_id);
1105 // Send inventory formspec
1106 SendPlayerInventoryFormspec(peer_id);
1109 SendInventory(playersao, false);
1112 SendPlayerHP(playersao);
1114 // Send death screen
1115 if (playersao->isDead())
1116 SendDeathscreen(peer_id, false, v3f(0,0,0));
1119 SendPlayerBreath(playersao);
1122 Update player list and print action
1125 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1126 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1127 m_clients.sendToAll(¬ice_pkt);
1130 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1131 const auto &names = m_clients.getPlayerNames();
1133 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1134 for (const std::string &name : names)
1135 actionstream << name << " ";
1136 actionstream << player->getName() << std::endl;
1141 inline void Server::handleCommand(NetworkPacket *pkt)
1143 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1144 (this->*opHandle.handler)(pkt);
1147 void Server::ProcessData(NetworkPacket *pkt)
1149 // Environment is locked first.
1150 MutexAutoLock envlock(m_env_mutex);
1152 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1153 u32 peer_id = pkt->getPeerId();
1156 Address address = getPeerAddress(peer_id);
1157 std::string addr_s = address.serializeString();
1159 // FIXME: Isn't it a bit excessive to check this for every packet?
1160 if (m_banmanager->isIpBanned(addr_s)) {
1161 std::string ban_name = m_banmanager->getBanName(addr_s);
1162 infostream << "Server: A banned client tried to connect from "
1163 << addr_s << "; banned name was " << ban_name << std::endl;
1164 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1165 "Your IP is banned. Banned name was " + ban_name);
1168 } catch (con::PeerNotFoundException &e) {
1170 * no peer for this packet found
1171 * most common reason is peer timeout, e.g. peer didn't
1172 * respond for some time, your server was overloaded or
1175 infostream << "Server::ProcessData(): Canceling: peer "
1176 << peer_id << " not found" << std::endl;
1181 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1183 // Command must be handled into ToServerCommandHandler
1184 if (command >= TOSERVER_NUM_MSG_TYPES) {
1185 infostream << "Server: Ignoring unknown command "
1186 << command << std::endl;
1190 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1195 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1197 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1198 errorstream << "Server::ProcessData(): Cancelling: Peer"
1199 " serialization format invalid or not initialized."
1200 " Skipping incoming command=" << command << std::endl;
1204 /* Handle commands related to client startup */
1205 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1210 if (m_clients.getClientState(peer_id) < CS_Active) {
1211 if (command == TOSERVER_PLAYERPOS) return;
1213 errorstream << "Got packet command: " << command << " for peer id "
1214 << peer_id << " but client isn't active yet. Dropping packet "
1220 } catch (SendFailedException &e) {
1221 errorstream << "Server::ProcessData(): SendFailedException: "
1222 << "what=" << e.what()
1224 } catch (PacketError &e) {
1225 actionstream << "Server::ProcessData(): PacketError: "
1226 << "what=" << e.what()
1231 void Server::setTimeOfDay(u32 time)
1233 m_env->setTimeOfDay(time);
1234 m_time_of_day_send_timer = 0;
1237 void Server::onMapEditEvent(const MapEditEvent &event)
1239 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1242 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1245 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1247 std::vector<session_t> clients = m_clients.getClientIDs();
1248 ClientInterface::AutoLock clientlock(m_clients);
1249 // Set the modified blocks unsent for all the clients
1250 for (const session_t client_id : clients) {
1251 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1252 client->SetBlocksNotSent(block);
1256 void Server::peerAdded(con::Peer *peer)
1258 verbosestream<<"Server::peerAdded(): peer->id="
1259 <<peer->id<<std::endl;
1261 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1264 void Server::deletingPeer(con::Peer *peer, bool timeout)
1266 verbosestream<<"Server::deletingPeer(): peer->id="
1267 <<peer->id<<", timeout="<<timeout<<std::endl;
1269 m_clients.event(peer->id, CSE_Disconnect);
1270 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1273 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1275 *retval = m_con->getPeerStat(peer_id,type);
1276 return *retval != -1;
1279 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1281 ClientInterface::AutoLock clientlock(m_clients);
1282 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1287 ret.state = client->getState();
1288 ret.addr = client->getAddress();
1289 ret.uptime = client->uptime();
1290 ret.ser_vers = client->serialization_version;
1291 ret.prot_vers = client->net_proto_version;
1293 ret.major = client->getMajor();
1294 ret.minor = client->getMinor();
1295 ret.patch = client->getPatch();
1296 ret.vers_string = client->getFullVer();
1298 ret.lang_code = client->getLangCode();
1303 void Server::handlePeerChanges()
1305 while(!m_peer_change_queue.empty())
1307 con::PeerChange c = m_peer_change_queue.front();
1308 m_peer_change_queue.pop();
1310 verbosestream<<"Server: Handling peer change: "
1311 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1316 case con::PEER_ADDED:
1317 m_clients.CreateClient(c.peer_id);
1320 case con::PEER_REMOVED:
1321 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1325 FATAL_ERROR("Invalid peer change event received!");
1331 void Server::printToConsoleOnly(const std::string &text)
1334 m_admin_chat->outgoing_queue.push_back(
1335 new ChatEventChat("", utf8_to_wide(text)));
1337 std::cout << text << std::endl;
1341 void Server::Send(NetworkPacket *pkt)
1343 Send(pkt->getPeerId(), pkt);
1346 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1348 m_clients.send(peer_id,
1349 clientCommandFactoryTable[pkt->getCommand()].channel,
1351 clientCommandFactoryTable[pkt->getCommand()].reliable);
1354 void Server::SendMovement(session_t peer_id)
1356 std::ostringstream os(std::ios_base::binary);
1358 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1360 pkt << g_settings->getFloat("movement_acceleration_default");
1361 pkt << g_settings->getFloat("movement_acceleration_air");
1362 pkt << g_settings->getFloat("movement_acceleration_fast");
1363 pkt << g_settings->getFloat("movement_speed_walk");
1364 pkt << g_settings->getFloat("movement_speed_crouch");
1365 pkt << g_settings->getFloat("movement_speed_fast");
1366 pkt << g_settings->getFloat("movement_speed_climb");
1367 pkt << g_settings->getFloat("movement_speed_jump");
1368 pkt << g_settings->getFloat("movement_liquid_fluidity");
1369 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1370 pkt << g_settings->getFloat("movement_liquid_sink");
1371 pkt << g_settings->getFloat("movement_gravity");
1376 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1378 m_script->player_event(playersao, "health_changed");
1379 SendPlayerHP(playersao);
1381 // Send to other clients
1382 playersao->sendPunchCommand();
1384 if (playersao->isDead())
1385 HandlePlayerDeath(playersao, reason);
1388 void Server::SendPlayerHP(PlayerSAO *playersao)
1390 SendHP(playersao->getPeerID(), playersao->getHP());
1393 void Server::SendHP(session_t peer_id, u16 hp)
1395 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1400 void Server::SendBreath(session_t peer_id, u16 breath)
1402 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1403 pkt << (u16) breath;
1407 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1408 const std::string &custom_reason, bool reconnect)
1410 assert(reason < SERVER_ACCESSDENIED_MAX);
1412 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1414 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1415 pkt << custom_reason;
1416 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1417 reason == SERVER_ACCESSDENIED_CRASH)
1418 pkt << custom_reason << (u8)reconnect;
1422 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1423 v3f camera_point_target)
1425 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1426 pkt << set_camera_point_target << camera_point_target;
1430 void Server::SendItemDef(session_t peer_id,
1431 IItemDefManager *itemdef, u16 protocol_version)
1433 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1437 u32 length of the next item
1438 zlib-compressed serialized ItemDefManager
1440 std::ostringstream tmp_os(std::ios::binary);
1441 itemdef->serialize(tmp_os, protocol_version);
1442 std::ostringstream tmp_os2(std::ios::binary);
1443 compressZlib(tmp_os.str(), tmp_os2);
1444 pkt.putLongString(tmp_os2.str());
1447 verbosestream << "Server: Sending item definitions to id(" << peer_id
1448 << "): size=" << pkt.getSize() << std::endl;
1453 void Server::SendNodeDef(session_t peer_id,
1454 const NodeDefManager *nodedef, u16 protocol_version)
1456 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1460 u32 length of the next item
1461 zlib-compressed serialized NodeDefManager
1463 std::ostringstream tmp_os(std::ios::binary);
1464 nodedef->serialize(tmp_os, protocol_version);
1465 std::ostringstream tmp_os2(std::ios::binary);
1466 compressZlib(tmp_os.str(), tmp_os2);
1468 pkt.putLongString(tmp_os2.str());
1471 verbosestream << "Server: Sending node definitions to id(" << peer_id
1472 << "): size=" << pkt.getSize() << std::endl;
1478 Non-static send methods
1481 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1483 RemotePlayer *player = sao->getPlayer();
1485 // Do not send new format to old clients
1486 incremental &= player->protocol_version >= 38;
1488 UpdateCrafting(player);
1494 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1496 std::ostringstream os(std::ios::binary);
1497 sao->getInventory()->serialize(os, incremental);
1498 sao->getInventory()->setModified(false);
1499 player->setModified(true);
1501 const std::string &s = os.str();
1502 pkt.putRawString(s.c_str(), s.size());
1506 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1508 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1510 u8 type = message.type;
1511 pkt << version << type << message.sender << message.message
1512 << static_cast<u64>(message.timestamp);
1514 if (peer_id != PEER_ID_INEXISTENT) {
1515 RemotePlayer *player = m_env->getPlayer(peer_id);
1521 m_clients.sendToAll(&pkt);
1525 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1526 const std::string &formname)
1528 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1529 if (formspec.empty()){
1530 //the client should close the formspec
1531 //but make sure there wasn't another one open in meantime
1532 const auto it = m_formspec_state_data.find(peer_id);
1533 if (it != m_formspec_state_data.end() && it->second == formname) {
1534 m_formspec_state_data.erase(peer_id);
1536 pkt.putLongString("");
1538 m_formspec_state_data[peer_id] = formname;
1539 pkt.putLongString(formspec);
1546 // Spawns a particle on peer with peer_id
1547 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1548 const ParticleParameters &p)
1550 static thread_local const float radius =
1551 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1553 if (peer_id == PEER_ID_INEXISTENT) {
1554 std::vector<session_t> clients = m_clients.getClientIDs();
1555 const v3f pos = p.pos * BS;
1556 const float radius_sq = radius * radius;
1558 for (const session_t client_id : clients) {
1559 RemotePlayer *player = m_env->getPlayer(client_id);
1563 PlayerSAO *sao = player->getPlayerSAO();
1567 // Do not send to distant clients
1568 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1571 SendSpawnParticle(client_id, player->protocol_version, p);
1575 assert(protocol_version != 0);
1577 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1580 // NetworkPacket and iostreams are incompatible...
1581 std::ostringstream oss(std::ios_base::binary);
1582 p.serialize(oss, protocol_version);
1583 pkt.putRawString(oss.str());
1589 // Adds a ParticleSpawner on peer with peer_id
1590 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1591 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1593 static thread_local const float radius =
1594 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1596 if (peer_id == PEER_ID_INEXISTENT) {
1597 std::vector<session_t> clients = m_clients.getClientIDs();
1598 const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS;
1599 const float radius_sq = radius * radius;
1600 /* Don't send short-lived spawners to distant players.
1601 * This could be replaced with proper tracking at some point. */
1602 const bool distance_check = !attached_id && p.time <= 1.0f;
1604 for (const session_t client_id : clients) {
1605 RemotePlayer *player = m_env->getPlayer(client_id);
1609 if (distance_check) {
1610 PlayerSAO *sao = player->getPlayerSAO();
1613 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1617 SendAddParticleSpawner(client_id, player->protocol_version,
1618 p, attached_id, id);
1622 assert(protocol_version != 0);
1624 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1626 pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel
1627 << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime
1628 << p.minsize << p.maxsize << p.collisiondetection;
1630 pkt.putLongString(p.texture);
1632 pkt << id << p.vertical << p.collision_removal << attached_id;
1634 std::ostringstream os(std::ios_base::binary);
1635 p.animation.serialize(os, protocol_version);
1636 pkt.putRawString(os.str());
1638 pkt << p.glow << p.object_collision;
1639 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1644 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1646 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1650 if (peer_id != PEER_ID_INEXISTENT)
1653 m_clients.sendToAll(&pkt);
1657 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1659 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1661 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1662 << form->text << form->number << form->item << form->dir
1663 << form->align << form->offset << form->world_pos << form->size
1664 << form->z_index << form->text2 << form->style;
1669 void Server::SendHUDRemove(session_t peer_id, u32 id)
1671 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1676 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1678 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1679 pkt << id << (u8) stat;
1683 case HUD_STAT_SCALE:
1684 case HUD_STAT_ALIGN:
1685 case HUD_STAT_OFFSET:
1686 pkt << *(v2f *) value;
1690 case HUD_STAT_TEXT2:
1691 pkt << *(std::string *) value;
1693 case HUD_STAT_WORLD_POS:
1694 pkt << *(v3f *) value;
1697 pkt << *(v2s32 *) value;
1699 default: // all other types
1700 pkt << *(u32 *) value;
1707 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1709 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1711 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1713 pkt << flags << mask;
1718 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1720 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1721 pkt << param << value;
1725 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1727 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1729 // Handle prior clients here
1730 if (m_clients.getProtocolVersion(peer_id) < 39) {
1731 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1733 for (const std::string& texture : params.textures)
1736 pkt << params.clouds;
1737 } else { // Handle current clients and future clients
1738 pkt << params.bgcolor << params.type
1739 << params.clouds << params.fog_sun_tint
1740 << params.fog_moon_tint << params.fog_tint_type;
1742 if (params.type == "skybox") {
1743 pkt << (u16) params.textures.size();
1744 for (const std::string &texture : params.textures)
1746 } else if (params.type == "regular") {
1747 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1748 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1749 << params.sky_color.night_sky << params.sky_color.night_horizon
1750 << params.sky_color.indoors;
1757 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1759 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1760 pkt << params.visible << params.texture
1761 << params.tonemap << params.sunrise
1762 << params.sunrise_visible << params.scale;
1766 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1768 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1770 pkt << params.visible << params.texture
1771 << params.tonemap << params.scale;
1775 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1777 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1779 pkt << params.visible << params.count
1780 << params.starcolor << params.scale;
1785 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1787 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1788 pkt << params.density << params.color_bright << params.color_ambient
1789 << params.height << params.thickness << params.speed;
1793 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1796 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1799 pkt << do_override << (u16) (ratio * 65535);
1804 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1806 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1809 pkt << lighting.shadow_intensity;
1814 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1816 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1817 pkt << time << time_speed;
1819 if (peer_id == PEER_ID_INEXISTENT) {
1820 m_clients.sendToAll(&pkt);
1827 void Server::SendPlayerBreath(PlayerSAO *sao)
1831 m_script->player_event(sao, "breath_changed");
1832 SendBreath(sao->getPeerID(), sao->getBreath());
1835 void Server::SendMovePlayer(session_t peer_id)
1837 RemotePlayer *player = m_env->getPlayer(peer_id);
1839 PlayerSAO *sao = player->getPlayerSAO();
1842 // Send attachment updates instantly to the client prior updating position
1843 sao->sendOutdatedData();
1845 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1846 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1849 v3f pos = sao->getBasePosition();
1850 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1851 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1852 << " pitch=" << sao->getLookPitch()
1853 << " yaw=" << sao->getRotation().Y
1860 void Server::SendPlayerFov(session_t peer_id)
1862 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1864 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1865 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1870 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1871 f32 animation_speed)
1873 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1876 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1877 << animation_frames[3] << animation_speed;
1882 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1884 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1885 pkt << first << third;
1889 void Server::SendPlayerPrivileges(session_t peer_id)
1891 RemotePlayer *player = m_env->getPlayer(peer_id);
1893 if(player->getPeerId() == PEER_ID_INEXISTENT)
1896 std::set<std::string> privs;
1897 m_script->getAuth(player->getName(), NULL, &privs);
1899 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1900 pkt << (u16) privs.size();
1902 for (const std::string &priv : privs) {
1909 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1911 RemotePlayer *player = m_env->getPlayer(peer_id);
1913 if (player->getPeerId() == PEER_ID_INEXISTENT)
1916 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1917 pkt.putLongString(player->inventory_formspec);
1922 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1924 RemotePlayer *player = m_env->getPlayer(peer_id);
1926 if (player->getPeerId() == PEER_ID_INEXISTENT)
1929 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1930 pkt << player->formspec_prepend;
1934 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1936 // Radius inside which objects are active
1937 static thread_local const s16 radius =
1938 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1940 // Radius inside which players are active
1941 static thread_local const bool is_transfer_limited =
1942 g_settings->exists("unlimited_player_transfer_distance") &&
1943 !g_settings->getBool("unlimited_player_transfer_distance");
1945 static thread_local const s16 player_transfer_dist =
1946 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
1948 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
1949 radius : player_transfer_dist;
1951 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
1955 std::queue<u16> removed_objects, added_objects;
1956 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
1957 client->m_known_objects, removed_objects);
1958 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
1959 client->m_known_objects, added_objects);
1961 int removed_count = removed_objects.size();
1962 int added_count = added_objects.size();
1964 if (removed_objects.empty() && added_objects.empty())
1970 // Handle removed objects
1971 writeU16((u8*)buf, removed_objects.size());
1972 data.append(buf, 2);
1973 while (!removed_objects.empty()) {
1975 u16 id = removed_objects.front();
1976 ServerActiveObject* obj = m_env->getActiveObject(id);
1978 // Add to data buffer for sending
1979 writeU16((u8*)buf, id);
1980 data.append(buf, 2);
1982 // Remove from known objects
1983 client->m_known_objects.erase(id);
1985 if (obj && obj->m_known_by_count > 0)
1986 obj->m_known_by_count--;
1988 removed_objects.pop();
1991 // Handle added objects
1992 writeU16((u8*)buf, added_objects.size());
1993 data.append(buf, 2);
1994 while (!added_objects.empty()) {
1996 u16 id = added_objects.front();
1997 ServerActiveObject *obj = m_env->getActiveObject(id);
1998 added_objects.pop();
2001 warningstream << FUNCTION_NAME << ": NULL object id="
2002 << (int)id << std::endl;
2007 u8 type = obj->getSendType();
2009 // Add to data buffer for sending
2010 writeU16((u8*)buf, id);
2011 data.append(buf, 2);
2012 writeU8((u8*)buf, type);
2013 data.append(buf, 1);
2015 data.append(serializeString32(
2016 obj->getClientInitializationData(client->net_proto_version)));
2018 // Add to known objects
2019 client->m_known_objects.insert(id);
2021 obj->m_known_by_count++;
2024 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2025 pkt.putRawString(data.c_str(), data.size());
2028 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2029 << removed_count << " removed, " << added_count << " added, "
2030 << "packet size is " << pkt.getSize() << std::endl;
2033 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2036 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2037 datas.size(), peer_id);
2039 pkt.putRawString(datas.c_str(), datas.size());
2041 m_clients.send(pkt.getPeerId(),
2042 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2046 void Server::SendCSMRestrictionFlags(session_t peer_id)
2048 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2049 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2050 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2054 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2056 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2061 inline s32 Server::nextSoundId()
2063 s32 ret = m_next_sound_id;
2064 if (m_next_sound_id == INT32_MAX)
2065 m_next_sound_id = 0; // signed overflow is undefined
2071 s32 Server::playSound(const SimpleSoundSpec &spec,
2072 const ServerSoundParams ¶ms, bool ephemeral)
2074 // Find out initial position of sound
2075 bool pos_exists = false;
2076 v3f pos = params.getPos(m_env, &pos_exists);
2077 // If position is not found while it should be, cancel sound
2078 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2081 // Filter destination clients
2082 std::vector<session_t> dst_clients;
2083 if (!params.to_player.empty()) {
2084 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2086 infostream<<"Server::playSound: Player \""<<params.to_player
2087 <<"\" not found"<<std::endl;
2090 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2091 infostream<<"Server::playSound: Player \""<<params.to_player
2092 <<"\" not connected"<<std::endl;
2095 dst_clients.push_back(player->getPeerId());
2097 std::vector<session_t> clients = m_clients.getClientIDs();
2099 for (const session_t client_id : clients) {
2100 RemotePlayer *player = m_env->getPlayer(client_id);
2103 if (!params.exclude_player.empty() &&
2104 params.exclude_player == player->getName())
2107 PlayerSAO *sao = player->getPlayerSAO();
2112 if(sao->getBasePosition().getDistanceFrom(pos) >
2113 params.max_hear_distance)
2116 dst_clients.push_back(client_id);
2120 if(dst_clients.empty())
2125 ServerPlayingSound *psound = nullptr;
2127 id = -1; // old clients will still use this, so pick a reserved ID
2130 // The sound will exist as a reference in m_playing_sounds
2131 m_playing_sounds[id] = ServerPlayingSound();
2132 psound = &m_playing_sounds[id];
2133 psound->params = params;
2134 psound->spec = spec;
2137 float gain = params.gain * spec.gain;
2138 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2139 pkt << id << spec.name << gain
2140 << (u8) params.type << pos << params.object
2141 << params.loop << params.fade << params.pitch
2144 bool as_reliable = !ephemeral;
2146 for (const u16 dst_client : dst_clients) {
2148 psound->clients.insert(dst_client);
2149 m_clients.send(dst_client, 0, &pkt, as_reliable);
2153 void Server::stopSound(s32 handle)
2155 // Get sound reference
2156 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2157 m_playing_sounds.find(handle);
2158 if (i == m_playing_sounds.end())
2160 ServerPlayingSound &psound = i->second;
2162 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2165 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2166 si != psound.clients.end(); ++si) {
2168 m_clients.send(*si, 0, &pkt, true);
2170 // Remove sound reference
2171 m_playing_sounds.erase(i);
2174 void Server::fadeSound(s32 handle, float step, float gain)
2176 // Get sound reference
2177 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2178 m_playing_sounds.find(handle);
2179 if (i == m_playing_sounds.end())
2182 ServerPlayingSound &psound = i->second;
2183 psound.params.gain = gain;
2185 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2186 pkt << handle << step << gain;
2188 // Backwards compability
2189 bool play_sound = gain > 0;
2190 ServerPlayingSound compat_psound = psound;
2191 compat_psound.clients.clear();
2193 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2194 compat_pkt << handle;
2196 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2197 it != psound.clients.end();) {
2198 if (m_clients.getProtocolVersion(*it) >= 32) {
2200 m_clients.send(*it, 0, &pkt, true);
2203 compat_psound.clients.insert(*it);
2205 m_clients.send(*it, 0, &compat_pkt, true);
2206 psound.clients.erase(it++);
2210 // Remove sound reference
2211 if (!play_sound || psound.clients.empty())
2212 m_playing_sounds.erase(i);
2214 if (play_sound && !compat_psound.clients.empty()) {
2215 // Play new sound volume on older clients
2216 playSound(compat_psound.spec, compat_psound.params);
2220 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2223 float maxd = far_d_nodes * BS;
2224 v3f p_f = intToFloat(p, BS);
2225 v3s16 block_pos = getNodeBlockPos(p);
2227 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2230 std::vector<session_t> clients = m_clients.getClientIDs();
2231 ClientInterface::AutoLock clientlock(m_clients);
2233 for (session_t client_id : clients) {
2234 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2238 RemotePlayer *player = m_env->getPlayer(client_id);
2239 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2241 // If player is far away, only set modified blocks not sent
2242 if (!client->isBlockSent(block_pos) || (sao &&
2243 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2245 far_players->emplace(client_id);
2247 client->SetBlockNotSent(block_pos);
2252 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();
2268 ClientInterface::AutoLock clientlock(m_clients);
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);
2293 void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes)
2295 float maxd = far_d_nodes * BS;
2296 NodeMetadataList meta_updates_list(false);
2297 std::vector<session_t> clients = m_clients.getClientIDs();
2299 ClientInterface::AutoLock clientlock(m_clients);
2301 for (session_t i : clients) {
2302 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2306 ServerActiveObject *player = m_env->getActiveObject(i);
2307 v3f player_pos = player ? player->getBasePosition() : v3f();
2309 for (const v3s16 &pos : meta_updates) {
2310 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2315 v3s16 block_pos = getNodeBlockPos(pos);
2316 if (!client->isBlockSent(block_pos) || (player &&
2317 player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) {
2318 client->SetBlockNotSent(block_pos);
2322 // Add the change to send list
2323 meta_updates_list.set(pos, meta);
2325 if (meta_updates_list.size() == 0)
2328 // Send the meta changes
2329 std::ostringstream os(std::ios::binary);
2330 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2331 std::ostringstream oss(std::ios::binary);
2332 compressZlib(os.str(), oss);
2334 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0);
2335 pkt.putLongString(oss.str());
2336 m_clients.send(i, 0, &pkt, true);
2338 meta_updates_list.clear();
2342 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2343 u16 net_proto_version, SerializedBlockCache *cache)
2345 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2346 std::string s, *sptr = nullptr;
2349 auto it = cache->find({block->getPos(), ver});
2350 if (it != cache->end())
2354 // Serialize the block in the right format
2356 std::ostringstream os(std::ios_base::binary);
2357 block->serialize(os, ver, false, net_compression_level);
2358 block->serializeNetworkSpecific(os);
2363 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2364 pkt << block->getPos();
2365 pkt.putRawString(*sptr);
2368 // Store away in cache
2369 if (cache && sptr == &s)
2370 (*cache)[{block->getPos(), ver}] = std::move(s);
2373 void Server::SendBlocks(float dtime)
2375 MutexAutoLock envlock(m_env_mutex);
2376 //TODO check if one big lock could be faster then multiple small ones
2378 std::vector<PrioritySortedBlockTransfer> queue;
2380 u32 total_sending = 0, unique_clients = 0;
2383 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2385 std::vector<session_t> clients = m_clients.getClientIDs();
2387 ClientInterface::AutoLock clientlock(m_clients);
2388 for (const session_t client_id : clients) {
2389 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2394 total_sending += client->getSendingCount();
2395 const auto old_count = queue.size();
2396 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2397 unique_clients += queue.size() > old_count ? 1 : 0;
2402 // Lowest priority number comes first.
2403 // Lowest is most important.
2404 std::sort(queue.begin(), queue.end());
2406 ClientInterface::AutoLock clientlock(m_clients);
2408 // Maximal total count calculation
2409 // The per-client block sends is halved with the maximal online users
2410 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2411 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2413 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2414 Map &map = m_env->getMap();
2416 SerializedBlockCache cache, *cache_ptr = nullptr;
2417 if (unique_clients > 1) {
2418 // caching is pointless with a single client
2422 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2423 if (total_sending >= max_blocks_to_send)
2426 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2430 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2435 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2436 client->net_proto_version, cache_ptr);
2438 client->SentBlock(block_to_send.pos);
2443 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2445 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2449 ClientInterface::AutoLock clientlock(m_clients);
2450 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2451 if (!client || client->isBlockSent(blockpos))
2453 SendBlockNoLock(peer_id, block, client->serialization_version,
2454 client->net_proto_version);
2459 bool Server::addMediaFile(const std::string &filename,
2460 const std::string &filepath, std::string *filedata_to,
2461 std::string *digest_to)
2463 // If name contains illegal characters, ignore the file
2464 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2465 infostream << "Server: ignoring illegal file name: \""
2466 << filename << "\"" << std::endl;
2469 // If name is not in a supported format, ignore it
2470 const char *supported_ext[] = {
2471 ".png", ".jpg", ".bmp", ".tga",
2473 ".x", ".b3d", ".obj",
2474 // Custom translation file format
2478 if (removeStringEnd(filename, supported_ext).empty()) {
2479 infostream << "Server: ignoring unsupported file extension: \""
2480 << filename << "\"" << std::endl;
2483 // Ok, attempt to load the file and add to cache
2486 std::string filedata;
2487 if (!fs::ReadFile(filepath, filedata)) {
2488 errorstream << "Server::addMediaFile(): Failed to open \""
2489 << filename << "\" for reading" << std::endl;
2493 if (filedata.empty()) {
2494 errorstream << "Server::addMediaFile(): Empty file \""
2495 << filepath << "\"" << std::endl;
2500 sha1.addBytes(filedata.c_str(), filedata.length());
2502 unsigned char *digest = sha1.getDigest();
2503 std::string sha1_base64 = base64_encode(digest, 20);
2504 std::string sha1_hex = hex_encode((char*) digest, 20);
2506 *digest_to = std::string((char*) digest, 20);
2510 m_media[filename] = MediaInfo(filepath, sha1_base64);
2511 verbosestream << "Server: " << sha1_hex << " is " << filename
2515 *filedata_to = std::move(filedata);
2519 void Server::fillMediaCache()
2521 infostream << "Server: Calculating media file checksums" << std::endl;
2523 // Collect all media file paths
2524 std::vector<std::string> paths;
2526 // ordered in descending priority
2527 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2528 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2529 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2530 m_modmgr->getModsMediaPaths(paths);
2532 // Collect media file information from paths into cache
2533 for (const std::string &mediapath : paths) {
2534 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2535 for (const fs::DirListNode &dln : dirlist) {
2536 if (dln.dir) // Ignore dirs (already in paths)
2539 const std::string &filename = dln.name;
2540 if (m_media.find(filename) != m_media.end()) // Do not override
2543 std::string filepath = mediapath;
2544 filepath.append(DIR_DELIM).append(filename);
2545 addMediaFile(filename, filepath);
2549 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2552 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2555 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2558 std::string lang_suffix;
2559 lang_suffix.append(".").append(lang_code).append(".tr");
2560 for (const auto &i : m_media) {
2561 if (i.second.no_announce)
2563 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2570 for (const auto &i : m_media) {
2571 if (i.second.no_announce)
2573 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2575 pkt << i.first << i.second.sha1_digest;
2578 pkt << g_settings->get("remote_media");
2581 verbosestream << "Server: Announcing files to id(" << peer_id
2582 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2585 struct SendableMedia
2591 SendableMedia(const std::string &name, const std::string &path,
2592 std::string &&data):
2593 name(name), path(path), data(std::move(data))
2597 void Server::sendRequestedMedia(session_t peer_id,
2598 const std::vector<std::string> &tosend)
2600 verbosestream<<"Server::sendRequestedMedia(): "
2601 <<"Sending files to client"<<std::endl;
2605 // Put 5kB in one bunch (this is not accurate)
2606 u32 bytes_per_bunch = 5000;
2608 std::vector< std::vector<SendableMedia> > file_bunches;
2609 file_bunches.emplace_back();
2611 u32 file_size_bunch_total = 0;
2613 for (const std::string &name : tosend) {
2614 if (m_media.find(name) == m_media.end()) {
2615 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2616 <<"unknown file \""<<(name)<<"\""<<std::endl;
2620 const auto &m = m_media[name];
2624 if (!fs::ReadFile(m.path, data)) {
2625 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2626 << name << "\"" << std::endl;
2629 file_size_bunch_total += data.size();
2632 file_bunches.back().emplace_back(name, m.path, std::move(data));
2634 // Start next bunch if got enough data
2635 if(file_size_bunch_total >= bytes_per_bunch) {
2636 file_bunches.emplace_back();
2637 file_size_bunch_total = 0;
2642 /* Create and send packets */
2644 u16 num_bunches = file_bunches.size();
2645 for (u16 i = 0; i < num_bunches; i++) {
2648 u16 total number of texture bunches
2649 u16 index of this bunch
2650 u32 number of files in this bunch
2659 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2660 pkt << num_bunches << i << (u32) file_bunches[i].size();
2662 for (const SendableMedia &j : file_bunches[i]) {
2664 pkt.putLongString(j.data);
2667 verbosestream << "Server::sendRequestedMedia(): bunch "
2668 << i << "/" << num_bunches
2669 << " files=" << file_bunches[i].size()
2670 << " size=" << pkt.getSize() << std::endl;
2675 void Server::stepPendingDynMediaCallbacks(float dtime)
2677 MutexAutoLock lock(m_env_mutex);
2679 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2680 it->second.expiry_timer -= dtime;
2681 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2688 const auto &name = it->second.filename;
2689 if (!name.empty()) {
2690 assert(m_media.count(name));
2691 // if no_announce isn't set we're definitely deleting the wrong file!
2692 sanity_check(m_media[name].no_announce);
2694 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2695 m_media.erase(name);
2697 getScriptIface()->freeDynamicMediaCallback(it->first);
2698 it = m_pending_dyn_media.erase(it);
2702 void Server::SendMinimapModes(session_t peer_id,
2703 std::vector<MinimapMode> &modes, size_t wanted_mode)
2705 RemotePlayer *player = m_env->getPlayer(peer_id);
2707 if (player->getPeerId() == PEER_ID_INEXISTENT)
2710 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2711 pkt << (u16)modes.size() << (u16)wanted_mode;
2713 for (auto &mode : modes)
2714 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2719 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2721 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2725 pkt << false; // Remove inventory
2727 pkt << true; // Update inventory
2729 // Serialization & NetworkPacket isn't a love story
2730 std::ostringstream os(std::ios_base::binary);
2731 inventory->serialize(os);
2732 inventory->setModified(false);
2734 const std::string &os_str = os.str();
2735 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2736 pkt.putRawString(os_str);
2739 if (peer_id == PEER_ID_INEXISTENT)
2740 m_clients.sendToAll(&pkt);
2745 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2747 // Lookup player name, to filter detached inventories just after
2748 std::string peer_name;
2749 if (peer_id != PEER_ID_INEXISTENT) {
2750 peer_name = getClient(peer_id, CS_Created)->getName();
2753 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2754 sendDetachedInventory(inv, name, peer_id);
2757 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2764 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2766 infostream << "Server::DiePlayer(): Player "
2767 << playersao->getPlayer()->getName()
2768 << " dies" << std::endl;
2770 playersao->clearParentAttachment();
2772 // Trigger scripted stuff
2773 m_script->on_dieplayer(playersao, reason);
2775 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2778 void Server::RespawnPlayer(session_t peer_id)
2780 PlayerSAO *playersao = getPlayerSAO(peer_id);
2783 infostream << "Server::RespawnPlayer(): Player "
2784 << playersao->getPlayer()->getName()
2785 << " respawns" << std::endl;
2787 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2788 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2789 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2791 bool repositioned = m_script->on_respawnplayer(playersao);
2792 if (!repositioned) {
2793 // setPos will send the new position to client
2794 playersao->setPos(findSpawnPos());
2799 void Server::DenySudoAccess(session_t peer_id)
2801 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2806 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2807 const std::string &custom_reason, bool reconnect)
2809 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2810 m_clients.event(peer_id, CSE_SetDenied);
2811 DisconnectPeer(peer_id);
2814 void Server::DisconnectPeer(session_t peer_id)
2816 m_modchannel_mgr->leaveAllChannels(peer_id);
2817 m_con->DisconnectPeer(peer_id);
2820 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2823 RemoteClient* client = getClient(peer_id, CS_Invalid);
2825 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2827 // Right now, the auth mechs don't change between login and sudo mode.
2828 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2829 client->allowed_sudo_mechs = sudo_auth_mechs;
2831 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2832 << g_settings->getFloat("dedicated_server_step")
2836 m_clients.event(peer_id, CSE_AuthAccept);
2838 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2840 // We only support SRP right now
2841 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2843 resp_pkt << sudo_auth_mechs;
2845 m_clients.event(peer_id, CSE_SudoSuccess);
2849 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2851 std::wstring message;
2854 Clear references to playing sounds
2856 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2857 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2858 ServerPlayingSound &psound = i->second;
2859 psound.clients.erase(peer_id);
2860 if (psound.clients.empty())
2861 m_playing_sounds.erase(i++);
2866 // clear formspec info so the next client can't abuse the current state
2867 m_formspec_state_data.erase(peer_id);
2869 RemotePlayer *player = m_env->getPlayer(peer_id);
2871 /* Run scripts and remove from environment */
2873 PlayerSAO *playersao = player->getPlayerSAO();
2876 playersao->clearChildAttachments();
2877 playersao->clearParentAttachment();
2879 // inform connected clients
2880 const std::string &player_name = player->getName();
2881 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2882 // (u16) 1 + std::string represents a vector serialization representation
2883 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2884 m_clients.sendToAll(¬ice);
2886 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2888 playersao->disconnected();
2895 if (player && reason != CDR_DENY) {
2896 std::ostringstream os(std::ios_base::binary);
2897 std::vector<session_t> clients = m_clients.getClientIDs();
2899 for (const session_t client_id : clients) {
2901 RemotePlayer *player = m_env->getPlayer(client_id);
2905 // Get name of player
2906 os << player->getName() << " ";
2909 std::string name = player->getName();
2910 actionstream << name << " "
2911 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2912 << " List of players: " << os.str() << std::endl;
2914 m_admin_chat->outgoing_queue.push_back(
2915 new ChatEventNick(CET_NICK_REMOVE, name));
2919 MutexAutoLock env_lock(m_env_mutex);
2920 m_clients.DeleteClient(peer_id);
2924 // Send leave chat message to all remaining clients
2925 if (!message.empty()) {
2926 SendChatMessage(PEER_ID_INEXISTENT,
2927 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2931 void Server::UpdateCrafting(RemotePlayer *player)
2933 InventoryList *clist = player->inventory.getList("craft");
2934 if (!clist || clist->getSize() == 0)
2937 if (!clist->checkModified())
2940 // Get a preview for crafting
2942 InventoryLocation loc;
2943 loc.setPlayer(player->getName());
2944 std::vector<ItemStack> output_replacements;
2945 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2946 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2949 InventoryList *plist = player->inventory.getList("craftpreview");
2950 if (plist && plist->getSize() >= 1) {
2951 // Put the new preview in
2952 plist->changeItem(0, preview);
2956 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2958 if (evt->type == CET_NICK_ADD) {
2959 // The terminal informed us of its nick choice
2960 m_admin_nick = ((ChatEventNick *)evt)->nick;
2961 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2962 errorstream << "You haven't set up an account." << std::endl
2963 << "Please log in using the client as '"
2964 << m_admin_nick << "' with a secure password." << std::endl
2965 << "Until then, you can't execute admin tasks via the console," << std::endl
2966 << "and everybody can claim the user account instead of you," << std::endl
2967 << "giving them full control over this server." << std::endl;
2970 assert(evt->type == CET_CHAT);
2971 handleAdminChat((ChatEventChat *)evt);
2975 std::wstring Server::handleChat(const std::string &name,
2976 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2978 // If something goes wrong, this player is to blame
2979 RollbackScopeActor rollback_scope(m_rollback,
2980 std::string("player:") + name);
2982 if (g_settings->getBool("strip_color_codes"))
2983 wmessage = unescape_enriched(wmessage);
2986 switch (player->canSendChatMessage()) {
2987 case RPLAYER_CHATRESULT_FLOODING: {
2988 std::wstringstream ws;
2989 ws << L"You cannot send more messages. You are limited to "
2990 << g_settings->getFloat("chat_message_limit_per_10sec")
2991 << L" messages per 10 seconds.";
2994 case RPLAYER_CHATRESULT_KICK:
2995 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
2996 "You have been kicked due to message flooding.");
2998 case RPLAYER_CHATRESULT_OK:
3001 FATAL_ERROR("Unhandled chat filtering result found.");
3005 if (m_max_chatmessage_length > 0
3006 && wmessage.length() > m_max_chatmessage_length) {
3007 return L"Your message exceed the maximum chat message limit set on the server. "
3008 L"It was refused. Send a shorter message";
3011 auto message = trim(wide_to_utf8(wmessage));
3012 if (message.empty())
3015 if (message.find_first_of("\n\r") != std::wstring::npos) {
3016 return L"Newlines are not permitted in chat messages";
3019 // Run script hook, exit if script ate the chat message
3020 if (m_script->on_chat_message(name, message))
3025 // Whether to send line to the player that sent the message, or to all players
3026 bool broadcast_line = true;
3028 if (check_shout_priv && !checkPriv(name, "shout")) {
3029 line += L"-!- You don't have permission to shout.";
3030 broadcast_line = false;
3033 Workaround for fixing chat on Android. Lua doesn't handle
3034 the Cyrillic alphabet and some characters on older Android devices
3037 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3039 line += utf8_to_wide(m_script->formatChatMessage(name,
3040 wide_to_utf8(wmessage)));
3045 Tell calling method to send the message to sender
3047 if (!broadcast_line)
3051 Send the message to others
3053 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3055 ChatMessage chatmsg(line);
3057 std::vector<session_t> clients = m_clients.getClientIDs();
3058 for (u16 cid : clients)
3059 SendChatMessage(cid, chatmsg);
3064 void Server::handleAdminChat(const ChatEventChat *evt)
3066 std::string name = evt->nick;
3067 std::wstring wmessage = evt->evt_msg;
3069 std::wstring answer = handleChat(name, wmessage);
3071 // If asked to send answer to sender
3072 if (!answer.empty()) {
3073 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3077 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3079 RemoteClient *client = getClientNoEx(peer_id,state_min);
3081 throw ClientNotFoundException("Client not found");
3085 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3087 return m_clients.getClientNoEx(peer_id, state_min);
3090 std::string Server::getPlayerName(session_t peer_id)
3092 RemotePlayer *player = m_env->getPlayer(peer_id);
3094 return "[id="+itos(peer_id)+"]";
3095 return player->getName();
3098 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3100 RemotePlayer *player = m_env->getPlayer(peer_id);
3103 return player->getPlayerSAO();
3106 std::string Server::getStatusString()
3108 std::ostringstream os(std::ios_base::binary);
3111 os << "version: " << g_version_string;
3113 os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3115 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3117 os << " | max lag: " << std::setprecision(3);
3118 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3120 // Information about clients
3122 os << " | clients: ";
3124 std::vector<session_t> clients = m_clients.getClientIDs();
3125 for (session_t client_id : clients) {
3126 RemotePlayer *player = m_env->getPlayer(client_id);
3128 // Get name of player
3129 const char *name = player ? player->getName() : "<unknown>";
3131 // Add name to information string
3140 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3141 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3143 if (!g_settings->get("motd").empty())
3144 os << std::endl << "# Server: " << g_settings->get("motd");
3149 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3151 std::set<std::string> privs;
3152 m_script->getAuth(name, NULL, &privs);
3156 bool Server::checkPriv(const std::string &name, const std::string &priv)
3158 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3159 return (privs.count(priv) != 0);
3162 void Server::reportPrivsModified(const std::string &name)
3165 std::vector<session_t> clients = m_clients.getClientIDs();
3166 for (const session_t client_id : clients) {
3167 RemotePlayer *player = m_env->getPlayer(client_id);
3168 reportPrivsModified(player->getName());
3171 RemotePlayer *player = m_env->getPlayer(name.c_str());
3174 SendPlayerPrivileges(player->getPeerId());
3175 PlayerSAO *sao = player->getPlayerSAO();
3178 sao->updatePrivileges(
3179 getPlayerEffectivePrivs(name),
3184 void Server::reportInventoryFormspecModified(const std::string &name)
3186 RemotePlayer *player = m_env->getPlayer(name.c_str());
3189 SendPlayerInventoryFormspec(player->getPeerId());
3192 void Server::reportFormspecPrependModified(const std::string &name)
3194 RemotePlayer *player = m_env->getPlayer(name.c_str());
3197 SendPlayerFormspecPrepend(player->getPeerId());
3200 void Server::setIpBanned(const std::string &ip, const std::string &name)
3202 m_banmanager->add(ip, name);
3205 void Server::unsetIpBanned(const std::string &ip_or_name)
3207 m_banmanager->remove(ip_or_name);
3210 std::string Server::getBanDescription(const std::string &ip_or_name)
3212 return m_banmanager->getBanDescription(ip_or_name);
3215 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3217 // m_env will be NULL if the server is initializing
3221 if (m_admin_nick == name && !m_admin_nick.empty()) {
3222 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3225 RemotePlayer *player = m_env->getPlayer(name);
3230 if (player->getPeerId() == PEER_ID_INEXISTENT)
3233 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3236 bool Server::showFormspec(const char *playername, const std::string &formspec,
3237 const std::string &formname)
3239 // m_env will be NULL if the server is initializing
3243 RemotePlayer *player = m_env->getPlayer(playername);
3247 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3251 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3256 u32 id = player->addHud(form);
3258 SendHUDAdd(player->getPeerId(), id, form);
3263 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3267 HudElement* todel = player->removeHud(id);
3274 SendHUDRemove(player->getPeerId(), id);
3278 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3283 SendHUDChange(player->getPeerId(), id, stat, data);
3287 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3292 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3293 if (new_hud_flags == player->hud_flags) // no change
3296 SendHUDSetFlags(player->getPeerId(), flags, mask);
3297 player->hud_flags = new_hud_flags;
3299 PlayerSAO* playersao = player->getPlayerSAO();
3304 m_script->player_event(playersao, "hud_changed");
3308 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3313 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3316 player->setHotbarItemcount(hotbar_itemcount);
3317 std::ostringstream os(std::ios::binary);
3318 writeS32(os, hotbar_itemcount);
3319 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3323 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3328 player->setHotbarImage(name);
3329 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3332 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3337 player->setHotbarSelectedImage(name);
3338 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3341 Address Server::getPeerAddress(session_t peer_id)
3343 // Note that this is only set after Init was received in Server::handleCommand_Init
3344 return getClient(peer_id, CS_Invalid)->getAddress();
3347 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3348 v2s32 animation_frames[4], f32 frame_speed)
3350 sanity_check(player);
3351 player->setLocalAnimations(animation_frames, frame_speed);
3352 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3355 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3357 sanity_check(player);
3358 player->eye_offset_first = first;
3359 player->eye_offset_third = third;
3360 SendEyeOffset(player->getPeerId(), first, third);
3363 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3365 sanity_check(player);
3366 player->setSky(params);
3367 SendSetSky(player->getPeerId(), params);
3370 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3372 sanity_check(player);
3373 player->setSun(params);
3374 SendSetSun(player->getPeerId(), params);
3377 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3379 sanity_check(player);
3380 player->setMoon(params);
3381 SendSetMoon(player->getPeerId(), params);
3384 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3386 sanity_check(player);
3387 player->setStars(params);
3388 SendSetStars(player->getPeerId(), params);
3391 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3393 sanity_check(player);
3394 player->setCloudParams(params);
3395 SendCloudParams(player->getPeerId(), params);
3398 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3401 sanity_check(player);
3402 player->overrideDayNightRatio(do_override, ratio);
3403 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3406 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3408 sanity_check(player);
3409 player->setLighting(lighting);
3410 SendSetLighting(player->getPeerId(), lighting);
3413 void Server::notifyPlayers(const std::wstring &msg)
3415 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3418 void Server::spawnParticle(const std::string &playername,
3419 const ParticleParameters &p)
3421 // m_env will be NULL if the server is initializing
3425 session_t peer_id = PEER_ID_INEXISTENT;
3427 if (!playername.empty()) {
3428 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3431 peer_id = player->getPeerId();
3432 proto_ver = player->protocol_version;
3435 SendSpawnParticle(peer_id, proto_ver, p);
3438 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3439 ServerActiveObject *attached, const std::string &playername)
3441 // m_env will be NULL if the server is initializing
3445 session_t peer_id = PEER_ID_INEXISTENT;
3447 if (!playername.empty()) {
3448 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3451 peer_id = player->getPeerId();
3452 proto_ver = player->protocol_version;
3455 u16 attached_id = attached ? attached->getId() : 0;
3458 if (attached_id == 0)
3459 id = m_env->addParticleSpawner(p.time);
3461 id = m_env->addParticleSpawner(p.time, attached_id);
3463 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3467 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3469 // m_env will be NULL if the server is initializing
3471 throw ServerError("Can't delete particle spawners during initialisation!");
3473 session_t peer_id = PEER_ID_INEXISTENT;
3474 if (!playername.empty()) {
3475 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3478 peer_id = player->getPeerId();
3481 m_env->deleteParticleSpawner(id);
3482 SendDeleteParticleSpawner(peer_id, id);
3485 bool Server::dynamicAddMedia(std::string filepath,
3486 const u32 token, const std::string &to_player, bool ephemeral)
3488 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3489 auto it = m_media.find(filename);
3490 if (it != m_media.end()) {
3491 // Allow the same path to be "added" again in certain conditions
3492 if (ephemeral || it->second.path != filepath) {
3493 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3494 << "\" already exists in media cache" << std::endl;
3499 // Load the file and add it to our media cache
3500 std::string filedata, raw_hash;
3501 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3506 // Create a copy of the file and swap out the path, this removes the
3507 // requirement that mods keep the file accessible at the original path.
3508 filepath = fs::CreateTempFile();
3509 bool ok = ([&] () -> bool {
3510 if (filepath.empty())
3512 std::ofstream os(filepath.c_str(), std::ios::binary);
3520 errorstream << "Server: failed to create a copy of media file "
3521 << "\"" << filename << "\"" << std::endl;
3522 m_media.erase(filename);
3525 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3526 << filepath << std::endl;
3528 m_media[filename].path = filepath;
3529 m_media[filename].no_announce = true;
3530 // stepPendingDynMediaCallbacks will clean this up later.
3531 } else if (!to_player.empty()) {
3532 m_media[filename].no_announce = true;
3535 // Push file to existing clients
3536 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3537 pkt << raw_hash << filename << (bool)ephemeral;
3539 NetworkPacket legacy_pkt = pkt;
3541 // Newer clients get asked to fetch the file (asynchronous)
3543 // Older clients have an awful hack that just throws the data at them
3544 legacy_pkt.putLongString(filedata);
3546 std::unordered_set<session_t> delivered, waiting;
3548 ClientInterface::AutoLock clientlock(m_clients);
3549 for (auto &pair : m_clients.getClientList()) {
3550 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3552 If a client is in the DefinitionsSent state it is too late to
3553 transfer the file via sendMediaAnnouncement() but at the same
3554 time the client cannot accept a media push yet.
3555 Short of artificially delaying the joining process there is no
3556 way for the server to resolve this so we (currently) opt not to.
3558 warningstream << "The media \"" << filename << "\" (dynamic) could "
3559 "not be delivered to " << pair.second->getName()
3560 << " due to a race condition." << std::endl;
3563 if (pair.second->getState() < CS_Active)
3566 const auto proto_ver = pair.second->net_proto_version;
3570 const session_t peer_id = pair.second->peer_id;
3571 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3574 if (proto_ver < 40) {
3575 delivered.emplace(peer_id);
3577 The network layer only guarantees ordered delivery inside a channel.
3578 Since the very next packet could be one that uses the media, we have
3579 to push the media over ALL channels to ensure it is processed before
3580 it is used. In practice this means channels 1 and 0.
3582 m_clients.send(peer_id, 1, &legacy_pkt, true);
3583 m_clients.send(peer_id, 0, &legacy_pkt, true);
3585 waiting.emplace(peer_id);
3586 Send(peer_id, &pkt);
3591 // Run callback for players that already had the file delivered (legacy-only)
3592 for (session_t peer_id : delivered) {
3593 if (auto player = m_env->getPlayer(peer_id))
3594 getScriptIface()->on_dynamic_media_added(token, player->getName());
3597 // Save all others in our pending state
3598 auto &state = m_pending_dyn_media[token];
3599 state.waiting_players = std::move(waiting);
3600 // regardless of success throw away the callback after a while
3601 state.expiry_timer = 60.0f;
3603 state.filename = filename;
3608 // actions: time-reversed list
3609 // Return value: success/failure
3610 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3611 std::list<std::string> *log)
3613 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3614 ServerMap *map = (ServerMap*)(&m_env->getMap());
3616 // Fail if no actions to handle
3617 if (actions.empty()) {
3619 log->push_back("Nothing to do.");
3626 for (const RollbackAction &action : actions) {
3628 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3631 std::ostringstream os;
3632 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3633 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3635 log->push_back(os.str());
3637 std::ostringstream os;
3638 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3639 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3641 log->push_back(os.str());
3645 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3646 <<" failed"<<std::endl;
3648 // Call it done if less than half failed
3649 return num_failed <= num_tried/2;
3652 // IGameDef interface
3654 IItemDefManager *Server::getItemDefManager()
3659 const NodeDefManager *Server::getNodeDefManager()
3664 ICraftDefManager *Server::getCraftDefManager()
3669 u16 Server::allocateUnknownNodeId(const std::string &name)
3671 return m_nodedef->allocateDummy(name);
3674 IWritableItemDefManager *Server::getWritableItemDefManager()
3679 NodeDefManager *Server::getWritableNodeDefManager()
3684 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3689 const std::vector<ModSpec> & Server::getMods() const
3691 return m_modmgr->getMods();
3694 const ModSpec *Server::getModSpec(const std::string &modname) const
3696 return m_modmgr->getModSpec(modname);
3699 std::string Server::getBuiltinLuaPath()
3701 return porting::path_share + DIR_DELIM + "builtin";
3704 v3f Server::findSpawnPos()
3706 ServerMap &map = m_env->getServerMap();
3708 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3709 return nodeposf * BS;
3711 bool is_good = false;
3712 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3713 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3715 // Try to find a good place a few times
3716 for (s32 i = 0; i < 4000 && !is_good; i++) {
3717 s32 range = MYMIN(1 + i, range_max);
3718 // We're going to try to throw the player to this position
3719 v2s16 nodepos2d = v2s16(
3720 -range + (myrand() % (range * 2)),
3721 -range + (myrand() % (range * 2)));
3722 // Get spawn level at point
3723 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3724 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3725 // signify an unsuitable spawn position, or if outside limits.
3726 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3727 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3730 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3731 // Consecutive empty nodes
3734 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3735 // avoid obstructions in already-generated mapblocks.
3736 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3737 // no obstructions, but mapgen decorations are generated after spawn so
3738 // the player may end up inside one.
3739 for (s32 i = 0; i < 8; i++) {
3740 v3s16 blockpos = getNodeBlockPos(nodepos);
3741 map.emergeBlock(blockpos, true);
3742 content_t c = map.getNode(nodepos).getContent();
3744 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3745 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3746 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3748 if (air_count >= 2) {
3749 // Spawn in lower empty node
3751 nodeposf = intToFloat(nodepos, BS);
3752 // Don't spawn the player outside map boundaries
3753 if (objectpos_over_limit(nodeposf))
3754 // Exit this loop, positions above are probably over limit
3757 // Good position found, cause an exit from main loop
3771 // No suitable spawn point found, return fallback 0,0,0
3772 return v3f(0.0f, 0.0f, 0.0f);
3775 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3777 if (delay == 0.0f) {
3778 // No delay, shutdown immediately
3779 m_shutdown_state.is_requested = true;
3780 // only print to the infostream, a chat message saying
3781 // "Server Shutting Down" is sent when the server destructs.
3782 infostream << "*** Immediate Server shutdown requested." << std::endl;
3783 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3784 // Negative delay, cancel shutdown if requested
3785 m_shutdown_state.reset();
3786 std::wstringstream ws;
3788 ws << L"*** Server shutdown canceled.";
3790 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3791 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3792 // m_shutdown_* are already handled, skip.
3794 } else if (delay > 0.0f) {
3795 // Positive delay, tell the clients when the server will shut down
3796 std::wstringstream ws;
3798 ws << L"*** Server shutting down in "
3799 << duration_to_string(myround(delay)).c_str()
3802 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3803 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3806 m_shutdown_state.trigger(delay, msg, reconnect);
3809 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3812 Try to get an existing player
3814 RemotePlayer *player = m_env->getPlayer(name);
3816 // If player is already connected, cancel
3817 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3818 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3823 If player with the wanted peer_id already exists, cancel.
3825 if (m_env->getPlayer(peer_id)) {
3826 infostream<<"emergePlayer(): Player with wrong name but same"
3827 " peer_id already exists"<<std::endl;
3832 player = new RemotePlayer(name, idef());
3835 bool newplayer = false;
3838 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3840 // Complete init with server parts
3841 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3842 player->protocol_version = proto_version;
3846 m_script->on_newplayer(playersao);
3852 bool Server::registerModStorage(ModMetadata *storage)
3854 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3855 errorstream << "Unable to register same mod storage twice. Storage name: "
3856 << storage->getModName() << std::endl;
3860 m_mod_storages[storage->getModName()] = storage;
3864 void Server::unregisterModStorage(const std::string &name)
3866 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3867 if (it != m_mod_storages.end())
3868 m_mod_storages.erase(name);
3871 void dedicated_server_loop(Server &server, bool &kill)
3873 verbosestream<<"dedicated_server_loop()"<<std::endl;
3875 IntervalLimiter m_profiler_interval;
3877 static thread_local const float steplen =
3878 g_settings->getFloat("dedicated_server_step");
3879 static thread_local const float profiler_print_interval =
3880 g_settings->getFloat("profiler_print_interval");
3883 * The dedicated server loop only does time-keeping (in Server::step) and
3884 * provides a way to main.cpp to kill the server externally (bool &kill).
3888 // This is kind of a hack but can be done like this
3889 // because server.step() is very light
3890 sleep_ms((int)(steplen*1000.0));
3891 server.step(steplen);
3893 if (server.isShutdownRequested() || kill)
3899 if (profiler_print_interval != 0) {
3900 if(m_profiler_interval.step(steplen, profiler_print_interval))
3902 infostream<<"Profiler:"<<std::endl;
3903 g_profiler->print(infostream);
3904 g_profiler->clear();
3909 infostream << "Dedicated server quitting" << std::endl;
3911 if (g_settings->getBool("server_announce"))
3912 ServerList::sendAnnounce(ServerList::AA_DELETE,
3913 server.m_bind_addr.getPort());
3922 bool Server::joinModChannel(const std::string &channel)
3924 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3925 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3928 bool Server::leaveModChannel(const std::string &channel)
3930 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3933 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3935 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3938 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3942 ModChannel* Server::getModChannel(const std::string &channel)
3944 return m_modchannel_mgr->getModChannel(channel);
3947 void Server::broadcastModChannelMessage(const std::string &channel,
3948 const std::string &message, session_t from_peer)
3950 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3954 if (message.size() > STRING_MAX_LEN) {
3955 warningstream << "ModChannel message too long, dropping before sending "
3956 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3957 << channel << ")" << std::endl;
3962 if (from_peer != PEER_ID_SERVER) {
3963 sender = getPlayerName(from_peer);
3966 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3967 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3968 resp_pkt << channel << sender << message;
3969 for (session_t peer_id : peers) {
3971 if (peer_id == from_peer)
3974 Send(peer_id, &resp_pkt);
3977 if (from_peer != PEER_ID_SERVER) {
3978 m_script->on_modchannel_message(channel, sender, message);
3982 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3984 if (lang_code.empty())
3987 auto it = server_translations.find(lang_code);
3988 if (it != server_translations.end())
3989 return &it->second; // Already loaded
3991 // [] will create an entry
3992 auto *translations = &server_translations[lang_code];
3994 std::string suffix = "." + lang_code + ".tr";
3995 for (const auto &i : m_media) {
3996 if (str_ends_with(i.first, suffix)) {
3998 if (fs::ReadFile(i.second.path, data)) {
3999 translations->loadTranslation(data);
4004 return translations;
4007 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
4009 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4011 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4012 throw BaseException("Cannot read world.mt!");
4014 std::string backend = world_mt.exists("mod_storage_backend") ?
4015 world_mt.get("mod_storage_backend") : "files";
4016 if (backend == "files")
4017 warningstream << "/!\\ You are using the old mod storage files backend. "
4018 << "This backend is deprecated and may be removed in a future release /!\\"
4019 << std::endl << "Switching to SQLite3 is advised, "
4020 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4022 return openModStorageDatabase(backend, world_path, world_mt);
4025 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
4026 const std::string &world_path, const Settings &world_mt)
4028 if (backend == "sqlite3")
4029 return new ModMetadataDatabaseSQLite3(world_path);
4031 if (backend == "files")
4032 return new ModMetadataDatabaseFiles(world_path);
4034 if (backend == "dummy")
4035 return new Database_Dummy();
4037 throw BaseException("Mod storage database backend " + backend + " not supported");
4040 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4042 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4044 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4045 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4046 errorstream << "Cannot read world.mt!" << std::endl;
4050 std::string backend = world_mt.exists("mod_storage_backend") ?
4051 world_mt.get("mod_storage_backend") : "files";
4052 if (backend == migrate_to) {
4053 errorstream << "Cannot migrate: new backend is same"
4054 << " as the old one" << std::endl;
4058 ModMetadataDatabase *srcdb = nullptr;
4059 ModMetadataDatabase *dstdb = nullptr;
4061 bool succeeded = false;
4064 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4065 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4069 std::vector<std::string> mod_list;
4070 srcdb->listMods(&mod_list);
4071 for (const std::string &modname : mod_list) {
4073 srcdb->getModEntries(modname, &meta);
4074 for (const auto &pair : meta) {
4075 dstdb->setModEntry(modname, pair.first, pair.second);
4083 actionstream << "Successfully migrated the metadata of "
4084 << mod_list.size() << " mods" << std::endl;
4085 world_mt.set("mod_storage_backend", migrate_to);
4086 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4087 errorstream << "Failed to update world.mt!" << std::endl;
4089 actionstream << "world.mt updated" << std::endl;
4091 } catch (BaseException &e) {
4092 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4098 if (succeeded && backend == "files") {
4100 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4101 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4102 if (!fs::Rename(storage_path, backup_path))
4103 warningstream << "After migration, " << storage_path
4104 << " could not be renamed to " << backup_path << std::endl;