3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "network/connection.h"
25 #include "network/networkprotocol.h"
26 #include "network/serveropcodes.h"
28 #include "environment.h"
30 #include "threading/mutex_auto_lock.h"
31 #include "constants.h"
37 #include "server/serveractiveobject.h"
41 #include "scripting_server.h"
46 #include "mapgen/mapgen.h"
47 #include "mapgen/mg_biome.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content/mods.h"
51 #include "modchannels.h"
52 #include "serverlist.h"
53 #include "util/string.h"
55 #include "util/serialize.h"
56 #include "util/thread.h"
57 #include "defaultsettings.h"
58 #include "server/mods.h"
59 #include "util/base64.h"
60 #include "util/sha1.h"
62 #include "database/database.h"
63 #include "chatmessage.h"
64 #include "chat_interface.h"
65 #include "remoteplayer.h"
66 #include "server/player_sao.h"
67 #include "server/serverinventorymgr.h"
68 #include "translation.h"
69 #include "database/database-sqlite3.h"
70 #include "database/database-files.h"
71 #include "database/database-dummy.h"
72 #include "gameparams.h"
74 class ClientNotFoundException : public BaseException
77 ClientNotFoundException(const char *s):
82 class ServerThread : public Thread
86 ServerThread(Server *server):
97 void *ServerThread::run()
99 BEGIN_DEBUG_EXCEPTION_HANDLER
102 * The real business of the server happens on the ServerThread.
104 * AsyncRunStep() runs an actual server step as soon as enough time has
105 * passed (dedicated_server_loop keeps track of that).
106 * Receive() blocks at least(!) 30ms waiting for a packet (so this loop
107 * doesn't busy wait) and will process any remaining packets.
111 m_server->AsyncRunStep(true);
112 } catch (con::ConnectionBindFailed &e) {
113 m_server->setAsyncFatalError(e.what());
114 } catch (LuaError &e) {
115 m_server->setAsyncFatalError(e);
118 while (!stopRequested()) {
120 m_server->AsyncRunStep();
124 } catch (con::PeerNotFoundException &e) {
125 infostream<<"Server: PeerNotFoundException"<<std::endl;
126 } catch (ClientNotFoundException &e) {
127 } catch (con::ConnectionBindFailed &e) {
128 m_server->setAsyncFatalError(e.what());
129 } catch (LuaError &e) {
130 m_server->setAsyncFatalError(e);
134 END_DEBUG_EXCEPTION_HANDLER
139 v3f ServerPlayingSound::getPos(ServerEnvironment *env, bool *pos_exists) const
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::unordered_set<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 node_meta_updates.emplace(event->p);
924 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
925 getNodeBlockPos(event->p))) {
926 block->raiseModified(MOD_STATE_WRITE_NEEDED,
927 MOD_REASON_REPORT_META_CHANGE);
932 prof.add("MEET_OTHER", 1);
933 for (const v3s16 &modified_block : event->modified_blocks) {
934 m_clients.markBlockposAsNotSent(modified_block);
938 prof.add("unknown", 1);
939 warningstream << "Server: Unknown MapEditEvent "
940 << ((u32)event->type) << std::endl;
945 Set blocks not sent to far players
947 if (!far_players.empty()) {
948 // Convert list format to that wanted by SetBlocksNotSent
949 std::map<v3s16, MapBlock*> modified_blocks2;
950 for (const v3s16 &modified_block : event->modified_blocks) {
951 modified_blocks2[modified_block] =
952 m_env->getMap().getBlockNoCreateNoEx(modified_block);
955 // Set blocks not sent
956 for (const u16 far_player : far_players) {
957 if (RemoteClient *client = getClient(far_player))
958 client->SetBlocksNotSent(modified_blocks2);
965 if (event_count >= 5) {
966 infostream << "Server: MapEditEvents:" << std::endl;
967 prof.print(infostream);
968 } else if (event_count != 0) {
969 verbosestream << "Server: MapEditEvents:" << std::endl;
970 prof.print(verbosestream);
973 // Send all metadata updates
974 if (!node_meta_updates.empty())
975 sendMetadataChanged(node_meta_updates);
979 Trigger emerge thread
980 Doing this every 2s is left over from old code, unclear if this is still needed.
983 float &counter = m_emergethread_trigger_timer;
985 if (counter <= 0.0f) {
988 m_emerge->startThreads();
992 // Save map, players and auth stuff
994 float &counter = m_savemap_timer;
996 static thread_local const float save_interval =
997 g_settings->getFloat("server_map_save_interval");
998 if (counter >= save_interval) {
1000 MutexAutoLock lock(m_env_mutex);
1002 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
1005 if (m_banmanager->isModified()) {
1006 m_banmanager->save();
1009 // Save changed parts of map
1010 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1013 m_env->saveLoadedPlayers();
1015 // Save environment metadata
1020 m_shutdown_state.tick(dtime, this);
1023 void Server::Receive()
1033 In the first iteration *wait* for a packet, afterwards process
1034 all packets that are immediately available (no waiting).
1037 m_con->Receive(&pkt);
1040 if (!m_con->TryReceive(&pkt))
1044 peer_id = pkt.getPeerId();
1045 m_packet_recv_counter->increment();
1047 m_packet_recv_processed_counter->increment();
1048 } catch (const con::InvalidIncomingDataException &e) {
1049 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1050 << e.what() << std::endl;
1051 } catch (const SerializationError &e) {
1052 infostream << "Server::Receive(): SerializationError: what()="
1053 << e.what() << std::endl;
1054 } catch (const ClientStateError &e) {
1055 errorstream << "ProcessData: peer=" << peer_id << " what()="
1056 << e.what() << std::endl;
1057 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1058 } catch (const con::PeerNotFoundException &e) {
1060 } catch (const con::NoIncomingDataException &e) {
1066 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1068 std::string playername;
1069 PlayerSAO *playersao = NULL;
1071 ClientInterface::AutoLock clientlock(m_clients);
1072 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1074 playername = client->getName();
1075 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1079 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1081 // If failed, cancel
1082 if (!playersao || !player) {
1083 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1084 actionstream << "Server: Failed to emerge player \"" << playername
1085 << "\" (player allocated to an another client)" << std::endl;
1086 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1088 errorstream << "Server: " << playername << ": Failed to emerge player"
1090 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1096 Send complete position information
1098 SendMovePlayer(peer_id);
1101 SendPlayerPrivileges(peer_id);
1103 // Send inventory formspec
1104 SendPlayerInventoryFormspec(peer_id);
1107 SendInventory(playersao, false);
1110 SendPlayerHP(playersao, false);
1112 // Send death screen
1113 if (playersao->isDead())
1114 SendDeathscreen(peer_id, false, v3f(0,0,0));
1117 SendPlayerBreath(playersao);
1120 Update player list and print action
1123 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1124 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1125 m_clients.sendToAll(¬ice_pkt);
1128 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1129 const auto &names = m_clients.getPlayerNames();
1131 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1132 for (const std::string &name : names)
1133 actionstream << name << " ";
1134 actionstream << player->getName() << std::endl;
1139 inline void Server::handleCommand(NetworkPacket *pkt)
1141 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1142 (this->*opHandle.handler)(pkt);
1145 void Server::ProcessData(NetworkPacket *pkt)
1147 // Environment is locked first.
1148 MutexAutoLock envlock(m_env_mutex);
1150 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1151 u32 peer_id = pkt->getPeerId();
1154 Address address = getPeerAddress(peer_id);
1155 std::string addr_s = address.serializeString();
1157 // FIXME: Isn't it a bit excessive to check this for every packet?
1158 if (m_banmanager->isIpBanned(addr_s)) {
1159 std::string ban_name = m_banmanager->getBanName(addr_s);
1160 infostream << "Server: A banned client tried to connect from "
1161 << addr_s << "; banned name was " << ban_name << std::endl;
1162 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1163 "Your IP is banned. Banned name was " + ban_name);
1166 } catch (con::PeerNotFoundException &e) {
1168 * no peer for this packet found
1169 * most common reason is peer timeout, e.g. peer didn't
1170 * respond for some time, your server was overloaded or
1173 infostream << "Server::ProcessData(): Canceling: peer "
1174 << peer_id << " not found" << std::endl;
1179 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1181 // Command must be handled into ToServerCommandHandler
1182 if (command >= TOSERVER_NUM_MSG_TYPES) {
1183 infostream << "Server: Ignoring unknown command "
1184 << command << std::endl;
1188 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1193 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1195 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1196 errorstream << "Server::ProcessData(): Cancelling: Peer"
1197 " serialization format invalid or not initialized."
1198 " Skipping incoming command=" << command << std::endl;
1202 /* Handle commands related to client startup */
1203 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1208 if (m_clients.getClientState(peer_id) < CS_Active) {
1209 if (command == TOSERVER_PLAYERPOS) return;
1211 errorstream << "Got packet command: " << command << " for peer id "
1212 << peer_id << " but client isn't active yet. Dropping packet "
1218 } catch (SendFailedException &e) {
1219 errorstream << "Server::ProcessData(): SendFailedException: "
1220 << "what=" << e.what()
1222 } catch (PacketError &e) {
1223 actionstream << "Server::ProcessData(): PacketError: "
1224 << "what=" << e.what()
1229 void Server::setTimeOfDay(u32 time)
1231 m_env->setTimeOfDay(time);
1232 m_time_of_day_send_timer = 0;
1235 void Server::onMapEditEvent(const MapEditEvent &event)
1237 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1240 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1243 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1245 std::vector<session_t> clients = m_clients.getClientIDs();
1246 ClientInterface::AutoLock clientlock(m_clients);
1247 // Set the modified blocks unsent for all the clients
1248 for (const session_t client_id : clients) {
1249 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1250 client->SetBlocksNotSent(block);
1254 void Server::peerAdded(con::Peer *peer)
1256 verbosestream<<"Server::peerAdded(): peer->id="
1257 <<peer->id<<std::endl;
1259 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1262 void Server::deletingPeer(con::Peer *peer, bool timeout)
1264 verbosestream<<"Server::deletingPeer(): peer->id="
1265 <<peer->id<<", timeout="<<timeout<<std::endl;
1267 m_clients.event(peer->id, CSE_Disconnect);
1268 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1271 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1273 *retval = m_con->getPeerStat(peer_id,type);
1274 return *retval != -1;
1277 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1279 ClientInterface::AutoLock clientlock(m_clients);
1280 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1285 ret.state = client->getState();
1286 ret.addr = client->getAddress();
1287 ret.uptime = client->uptime();
1288 ret.ser_vers = client->serialization_version;
1289 ret.prot_vers = client->net_proto_version;
1291 ret.major = client->getMajor();
1292 ret.minor = client->getMinor();
1293 ret.patch = client->getPatch();
1294 ret.vers_string = client->getFullVer();
1296 ret.lang_code = client->getLangCode();
1301 void Server::handlePeerChanges()
1303 while(!m_peer_change_queue.empty())
1305 con::PeerChange c = m_peer_change_queue.front();
1306 m_peer_change_queue.pop();
1308 verbosestream<<"Server: Handling peer change: "
1309 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1314 case con::PEER_ADDED:
1315 m_clients.CreateClient(c.peer_id);
1318 case con::PEER_REMOVED:
1319 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1323 FATAL_ERROR("Invalid peer change event received!");
1329 void Server::printToConsoleOnly(const std::string &text)
1332 m_admin_chat->outgoing_queue.push_back(
1333 new ChatEventChat("", utf8_to_wide(text)));
1335 std::cout << text << std::endl;
1339 void Server::Send(NetworkPacket *pkt)
1341 Send(pkt->getPeerId(), pkt);
1344 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1346 m_clients.send(peer_id,
1347 clientCommandFactoryTable[pkt->getCommand()].channel,
1349 clientCommandFactoryTable[pkt->getCommand()].reliable);
1352 void Server::SendMovement(session_t peer_id)
1354 std::ostringstream os(std::ios_base::binary);
1356 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1358 pkt << g_settings->getFloat("movement_acceleration_default");
1359 pkt << g_settings->getFloat("movement_acceleration_air");
1360 pkt << g_settings->getFloat("movement_acceleration_fast");
1361 pkt << g_settings->getFloat("movement_speed_walk");
1362 pkt << g_settings->getFloat("movement_speed_crouch");
1363 pkt << g_settings->getFloat("movement_speed_fast");
1364 pkt << g_settings->getFloat("movement_speed_climb");
1365 pkt << g_settings->getFloat("movement_speed_jump");
1366 pkt << g_settings->getFloat("movement_liquid_fluidity");
1367 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1368 pkt << g_settings->getFloat("movement_liquid_sink");
1369 pkt << g_settings->getFloat("movement_gravity");
1374 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1376 m_script->player_event(playersao, "health_changed");
1377 SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX);
1379 // Send to other clients
1380 playersao->sendPunchCommand();
1382 if (playersao->isDead())
1383 HandlePlayerDeath(playersao, reason);
1386 void Server::SendPlayerHP(PlayerSAO *playersao, bool effect)
1388 SendHP(playersao->getPeerID(), playersao->getHP(), effect);
1391 void Server::SendHP(session_t peer_id, u16 hp, bool effect)
1393 NetworkPacket pkt(TOCLIENT_HP, 3, peer_id);
1394 pkt << hp << effect;
1398 void Server::SendBreath(session_t peer_id, u16 breath)
1400 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1401 pkt << (u16) breath;
1405 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1406 const std::string &custom_reason, bool reconnect)
1408 assert(reason < SERVER_ACCESSDENIED_MAX);
1410 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1412 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1413 pkt << custom_reason;
1414 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1415 reason == SERVER_ACCESSDENIED_CRASH)
1416 pkt << custom_reason << (u8)reconnect;
1420 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1421 v3f camera_point_target)
1423 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1424 pkt << set_camera_point_target << camera_point_target;
1428 void Server::SendItemDef(session_t peer_id,
1429 IItemDefManager *itemdef, u16 protocol_version)
1431 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1435 u32 length of the next item
1436 zlib-compressed serialized ItemDefManager
1438 std::ostringstream tmp_os(std::ios::binary);
1439 itemdef->serialize(tmp_os, protocol_version);
1440 std::ostringstream tmp_os2(std::ios::binary);
1441 compressZlib(tmp_os.str(), tmp_os2);
1442 pkt.putLongString(tmp_os2.str());
1445 verbosestream << "Server: Sending item definitions to id(" << peer_id
1446 << "): size=" << pkt.getSize() << std::endl;
1451 void Server::SendNodeDef(session_t peer_id,
1452 const NodeDefManager *nodedef, u16 protocol_version)
1454 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1458 u32 length of the next item
1459 zlib-compressed serialized NodeDefManager
1461 std::ostringstream tmp_os(std::ios::binary);
1462 nodedef->serialize(tmp_os, protocol_version);
1463 std::ostringstream tmp_os2(std::ios::binary);
1464 compressZlib(tmp_os.str(), tmp_os2);
1466 pkt.putLongString(tmp_os2.str());
1469 verbosestream << "Server: Sending node definitions to id(" << peer_id
1470 << "): size=" << pkt.getSize() << std::endl;
1476 Non-static send methods
1479 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1481 RemotePlayer *player = sao->getPlayer();
1483 // Do not send new format to old clients
1484 incremental &= player->protocol_version >= 38;
1486 UpdateCrafting(player);
1492 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1494 std::ostringstream os(std::ios::binary);
1495 sao->getInventory()->serialize(os, incremental);
1496 sao->getInventory()->setModified(false);
1497 player->setModified(true);
1499 const std::string &s = os.str();
1500 pkt.putRawString(s.c_str(), s.size());
1504 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1506 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1508 u8 type = message.type;
1509 pkt << version << type << message.sender << message.message
1510 << static_cast<u64>(message.timestamp);
1512 if (peer_id != PEER_ID_INEXISTENT) {
1513 RemotePlayer *player = m_env->getPlayer(peer_id);
1519 m_clients.sendToAll(&pkt);
1523 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1524 const std::string &formname)
1526 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1527 if (formspec.empty()){
1528 //the client should close the formspec
1529 //but make sure there wasn't another one open in meantime
1530 const auto it = m_formspec_state_data.find(peer_id);
1531 if (it != m_formspec_state_data.end() && it->second == formname) {
1532 m_formspec_state_data.erase(peer_id);
1534 pkt.putLongString("");
1536 m_formspec_state_data[peer_id] = formname;
1537 pkt.putLongString(formspec);
1544 // Spawns a particle on peer with peer_id
1545 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1546 const ParticleParameters &p)
1548 static thread_local const float radius =
1549 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1551 if (peer_id == PEER_ID_INEXISTENT) {
1552 std::vector<session_t> clients = m_clients.getClientIDs();
1553 const v3f pos = p.pos * BS;
1554 const float radius_sq = radius * radius;
1556 for (const session_t client_id : clients) {
1557 RemotePlayer *player = m_env->getPlayer(client_id);
1561 PlayerSAO *sao = player->getPlayerSAO();
1565 // Do not send to distant clients
1566 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1569 SendSpawnParticle(client_id, player->protocol_version, p);
1573 assert(protocol_version != 0);
1575 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1578 // NetworkPacket and iostreams are incompatible...
1579 std::ostringstream oss(std::ios_base::binary);
1580 p.serialize(oss, protocol_version);
1581 pkt.putRawString(oss.str());
1587 // Adds a ParticleSpawner on peer with peer_id
1588 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1589 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1591 static thread_local const float radius =
1592 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1594 if (peer_id == PEER_ID_INEXISTENT) {
1595 std::vector<session_t> clients = m_clients.getClientIDs();
1596 const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS;
1597 const float radius_sq = radius * radius;
1598 /* Don't send short-lived spawners to distant players.
1599 * This could be replaced with proper tracking at some point. */
1600 const bool distance_check = !attached_id && p.time <= 1.0f;
1602 for (const session_t client_id : clients) {
1603 RemotePlayer *player = m_env->getPlayer(client_id);
1607 if (distance_check) {
1608 PlayerSAO *sao = player->getPlayerSAO();
1611 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1615 SendAddParticleSpawner(client_id, player->protocol_version,
1616 p, attached_id, id);
1620 assert(protocol_version != 0);
1622 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1624 pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel
1625 << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime
1626 << p.minsize << p.maxsize << p.collisiondetection;
1628 pkt.putLongString(p.texture);
1630 pkt << id << p.vertical << p.collision_removal << attached_id;
1632 std::ostringstream os(std::ios_base::binary);
1633 p.animation.serialize(os, protocol_version);
1634 pkt.putRawString(os.str());
1636 pkt << p.glow << p.object_collision;
1637 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1642 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1644 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1648 if (peer_id != PEER_ID_INEXISTENT)
1651 m_clients.sendToAll(&pkt);
1655 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1657 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1659 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1660 << form->text << form->number << form->item << form->dir
1661 << form->align << form->offset << form->world_pos << form->size
1662 << form->z_index << form->text2 << form->style;
1667 void Server::SendHUDRemove(session_t peer_id, u32 id)
1669 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1674 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1676 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1677 pkt << id << (u8) stat;
1681 case HUD_STAT_SCALE:
1682 case HUD_STAT_ALIGN:
1683 case HUD_STAT_OFFSET:
1684 pkt << *(v2f *) value;
1688 case HUD_STAT_TEXT2:
1689 pkt << *(std::string *) value;
1691 case HUD_STAT_WORLD_POS:
1692 pkt << *(v3f *) value;
1695 pkt << *(v2s32 *) value;
1697 default: // all other types
1698 pkt << *(u32 *) value;
1705 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1707 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1709 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1711 pkt << flags << mask;
1716 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1718 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1719 pkt << param << value;
1723 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1725 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1727 // Handle prior clients here
1728 if (m_clients.getProtocolVersion(peer_id) < 39) {
1729 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1731 for (const std::string& texture : params.textures)
1734 pkt << params.clouds;
1735 } else { // Handle current clients and future clients
1736 pkt << params.bgcolor << params.type
1737 << params.clouds << params.fog_sun_tint
1738 << params.fog_moon_tint << params.fog_tint_type;
1740 if (params.type == "skybox") {
1741 pkt << (u16) params.textures.size();
1742 for (const std::string &texture : params.textures)
1744 } else if (params.type == "regular") {
1745 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1746 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1747 << params.sky_color.night_sky << params.sky_color.night_horizon
1748 << params.sky_color.indoors;
1755 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1757 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1758 pkt << params.visible << params.texture
1759 << params.tonemap << params.sunrise
1760 << params.sunrise_visible << params.scale;
1764 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1766 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1768 pkt << params.visible << params.texture
1769 << params.tonemap << params.scale;
1773 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1775 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1777 pkt << params.visible << params.count
1778 << params.starcolor << params.scale;
1783 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1785 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1786 pkt << params.density << params.color_bright << params.color_ambient
1787 << params.height << params.thickness << params.speed;
1791 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1794 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1797 pkt << do_override << (u16) (ratio * 65535);
1802 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1804 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1807 pkt << lighting.shadow_intensity;
1812 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1814 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1815 pkt << time << time_speed;
1817 if (peer_id == PEER_ID_INEXISTENT) {
1818 m_clients.sendToAll(&pkt);
1825 void Server::SendPlayerBreath(PlayerSAO *sao)
1829 m_script->player_event(sao, "breath_changed");
1830 SendBreath(sao->getPeerID(), sao->getBreath());
1833 void Server::SendMovePlayer(session_t peer_id)
1835 RemotePlayer *player = m_env->getPlayer(peer_id);
1837 PlayerSAO *sao = player->getPlayerSAO();
1840 // Send attachment updates instantly to the client prior updating position
1841 sao->sendOutdatedData();
1843 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1844 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1847 v3f pos = sao->getBasePosition();
1848 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1849 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1850 << " pitch=" << sao->getLookPitch()
1851 << " yaw=" << sao->getRotation().Y
1858 void Server::SendPlayerFov(session_t peer_id)
1860 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1862 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1863 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1868 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1869 f32 animation_speed)
1871 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1874 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1875 << animation_frames[3] << animation_speed;
1880 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1882 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1883 pkt << first << third;
1887 void Server::SendPlayerPrivileges(session_t peer_id)
1889 RemotePlayer *player = m_env->getPlayer(peer_id);
1891 if(player->getPeerId() == PEER_ID_INEXISTENT)
1894 std::set<std::string> privs;
1895 m_script->getAuth(player->getName(), NULL, &privs);
1897 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1898 pkt << (u16) privs.size();
1900 for (const std::string &priv : privs) {
1907 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1909 RemotePlayer *player = m_env->getPlayer(peer_id);
1911 if (player->getPeerId() == PEER_ID_INEXISTENT)
1914 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1915 pkt.putLongString(player->inventory_formspec);
1920 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1922 RemotePlayer *player = m_env->getPlayer(peer_id);
1924 if (player->getPeerId() == PEER_ID_INEXISTENT)
1927 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1928 pkt << player->formspec_prepend;
1932 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1934 // Radius inside which objects are active
1935 static thread_local const s16 radius =
1936 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1938 // Radius inside which players are active
1939 static thread_local const bool is_transfer_limited =
1940 g_settings->exists("unlimited_player_transfer_distance") &&
1941 !g_settings->getBool("unlimited_player_transfer_distance");
1943 static thread_local const s16 player_transfer_dist =
1944 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
1946 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
1947 radius : player_transfer_dist;
1949 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
1953 std::queue<u16> removed_objects, added_objects;
1954 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
1955 client->m_known_objects, removed_objects);
1956 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
1957 client->m_known_objects, added_objects);
1959 int removed_count = removed_objects.size();
1960 int added_count = added_objects.size();
1962 if (removed_objects.empty() && added_objects.empty())
1968 // Handle removed objects
1969 writeU16((u8*)buf, removed_objects.size());
1970 data.append(buf, 2);
1971 while (!removed_objects.empty()) {
1973 u16 id = removed_objects.front();
1974 ServerActiveObject* obj = m_env->getActiveObject(id);
1976 // Add to data buffer for sending
1977 writeU16((u8*)buf, id);
1978 data.append(buf, 2);
1980 // Remove from known objects
1981 client->m_known_objects.erase(id);
1983 if (obj && obj->m_known_by_count > 0)
1984 obj->m_known_by_count--;
1986 removed_objects.pop();
1989 // Handle added objects
1990 writeU16((u8*)buf, added_objects.size());
1991 data.append(buf, 2);
1992 while (!added_objects.empty()) {
1994 u16 id = added_objects.front();
1995 ServerActiveObject *obj = m_env->getActiveObject(id);
1996 added_objects.pop();
1999 warningstream << FUNCTION_NAME << ": NULL object id="
2000 << (int)id << std::endl;
2005 u8 type = obj->getSendType();
2007 // Add to data buffer for sending
2008 writeU16((u8*)buf, id);
2009 data.append(buf, 2);
2010 writeU8((u8*)buf, type);
2011 data.append(buf, 1);
2013 data.append(serializeString32(
2014 obj->getClientInitializationData(client->net_proto_version)));
2016 // Add to known objects
2017 client->m_known_objects.insert(id);
2019 obj->m_known_by_count++;
2022 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2023 pkt.putRawString(data.c_str(), data.size());
2026 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2027 << removed_count << " removed, " << added_count << " added, "
2028 << "packet size is " << pkt.getSize() << std::endl;
2031 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2034 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2035 datas.size(), peer_id);
2037 pkt.putRawString(datas.c_str(), datas.size());
2039 m_clients.send(pkt.getPeerId(),
2040 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2044 void Server::SendCSMRestrictionFlags(session_t peer_id)
2046 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2047 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2048 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2052 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2054 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2059 inline s32 Server::nextSoundId()
2061 s32 ret = m_next_sound_id;
2062 if (m_next_sound_id == INT32_MAX)
2063 m_next_sound_id = 0; // signed overflow is undefined
2069 s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
2071 // Find out initial position of sound
2072 bool pos_exists = false;
2073 v3f pos = params.getPos(m_env, &pos_exists);
2074 // If position is not found while it should be, cancel sound
2075 if(pos_exists != (params.type != ServerPlayingSound::SSP_LOCAL))
2078 // Filter destination clients
2079 std::vector<session_t> dst_clients;
2080 if (!params.to_player.empty()) {
2081 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2083 infostream<<"Server::playSound: Player \""<<params.to_player
2084 <<"\" not found"<<std::endl;
2087 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2088 infostream<<"Server::playSound: Player \""<<params.to_player
2089 <<"\" not connected"<<std::endl;
2092 dst_clients.push_back(player->getPeerId());
2094 std::vector<session_t> clients = m_clients.getClientIDs();
2096 for (const session_t client_id : clients) {
2097 RemotePlayer *player = m_env->getPlayer(client_id);
2100 if (!params.exclude_player.empty() &&
2101 params.exclude_player == player->getName())
2104 PlayerSAO *sao = player->getPlayerSAO();
2109 if(sao->getBasePosition().getDistanceFrom(pos) >
2110 params.max_hear_distance)
2113 dst_clients.push_back(client_id);
2117 if(dst_clients.empty())
2120 // old clients will still use this, so pick a reserved ID (-1)
2121 const s32 id = ephemeral ? -1 : nextSoundId();
2123 float gain = params.gain * params.spec.gain;
2124 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2125 pkt << id << params.spec.name << gain
2126 << (u8) params.type << pos << params.object
2127 << params.spec.loop << params.spec.fade << params.spec.pitch
2130 bool as_reliable = !ephemeral;
2132 for (const session_t peer_id : dst_clients) {
2134 params.clients.insert(peer_id);
2135 m_clients.send(peer_id, 0, &pkt, as_reliable);
2139 m_playing_sounds[id] = std::move(params);
2142 void Server::stopSound(s32 handle)
2144 auto it = m_playing_sounds.find(handle);
2145 if (it == m_playing_sounds.end())
2148 ServerPlayingSound &psound = it->second;
2150 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2153 for (session_t peer_id : psound.clients) {
2155 m_clients.send(peer_id, 0, &pkt, true);
2158 // Remove sound reference
2159 m_playing_sounds.erase(it);
2162 void Server::fadeSound(s32 handle, float step, float gain)
2164 auto it = m_playing_sounds.find(handle);
2165 if (it == m_playing_sounds.end())
2168 ServerPlayingSound &psound = it->second;
2169 psound.gain = gain; // destination gain
2171 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2172 pkt << handle << step << gain;
2174 for (session_t peer_id : psound.clients) {
2176 m_clients.send(peer_id, 0, &pkt, true);
2179 // Remove sound reference
2180 if (gain <= 0 || psound.clients.empty())
2181 m_playing_sounds.erase(it);
2184 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2187 float maxd = far_d_nodes * BS;
2188 v3f p_f = intToFloat(p, BS);
2189 v3s16 block_pos = getNodeBlockPos(p);
2191 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2194 std::vector<session_t> clients = m_clients.getClientIDs();
2195 ClientInterface::AutoLock clientlock(m_clients);
2197 for (session_t client_id : clients) {
2198 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2202 RemotePlayer *player = m_env->getPlayer(client_id);
2203 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2205 // If player is far away, only set modified blocks not sent
2206 if (!client->isBlockSent(block_pos) || (sao &&
2207 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2209 far_players->emplace(client_id);
2211 client->SetBlockNotSent(block_pos);
2216 m_clients.send(client_id, 0, &pkt, true);
2220 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2221 float far_d_nodes, bool remove_metadata)
2223 float maxd = far_d_nodes * BS;
2224 v3f p_f = intToFloat(p, BS);
2225 v3s16 block_pos = getNodeBlockPos(p);
2227 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2228 pkt << p << n.param0 << n.param1 << n.param2
2229 << (u8) (remove_metadata ? 0 : 1);
2231 std::vector<session_t> clients = m_clients.getClientIDs();
2232 ClientInterface::AutoLock clientlock(m_clients);
2234 for (session_t client_id : clients) {
2235 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2239 RemotePlayer *player = m_env->getPlayer(client_id);
2240 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2242 // If player is far away, only set modified blocks not sent
2243 if (!client->isBlockSent(block_pos) || (sao &&
2244 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2246 far_players->emplace(client_id);
2248 client->SetBlockNotSent(block_pos);
2253 m_clients.send(client_id, 0, &pkt, true);
2257 void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
2259 NodeMetadataList meta_updates_list(false);
2260 std::ostringstream os(std::ios::binary);
2262 std::vector<session_t> clients = m_clients.getClientIDs();
2263 ClientInterface::AutoLock clientlock(m_clients);
2265 for (session_t i : clients) {
2266 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2270 ServerActiveObject *player = getPlayerSAO(i);
2273 player_pos = floatToInt(player->getBasePosition(), BS);
2275 for (const v3s16 pos : positions) {
2276 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2281 v3s16 block_pos = getNodeBlockPos(pos);
2282 if (!client->isBlockSent(block_pos) ||
2283 player_pos.getDistanceFrom(pos) > far_d_nodes) {
2284 client->SetBlockNotSent(block_pos);
2288 // Add the change to send list
2289 meta_updates_list.set(pos, meta);
2291 if (meta_updates_list.size() == 0)
2294 // Send the meta changes
2296 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2297 std::string raw = os.str();
2299 compressZlib(raw, os);
2301 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
2302 pkt.putLongString(os.str());
2305 meta_updates_list.clear();
2309 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2310 u16 net_proto_version, SerializedBlockCache *cache)
2312 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2313 std::string s, *sptr = nullptr;
2316 auto it = cache->find({block->getPos(), ver});
2317 if (it != cache->end())
2321 // Serialize the block in the right format
2323 std::ostringstream os(std::ios_base::binary);
2324 block->serialize(os, ver, false, net_compression_level);
2325 block->serializeNetworkSpecific(os);
2330 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2331 pkt << block->getPos();
2332 pkt.putRawString(*sptr);
2335 // Store away in cache
2336 if (cache && sptr == &s)
2337 (*cache)[{block->getPos(), ver}] = std::move(s);
2340 void Server::SendBlocks(float dtime)
2342 MutexAutoLock envlock(m_env_mutex);
2343 //TODO check if one big lock could be faster then multiple small ones
2345 std::vector<PrioritySortedBlockTransfer> queue;
2347 u32 total_sending = 0, unique_clients = 0;
2350 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2352 std::vector<session_t> clients = m_clients.getClientIDs();
2354 ClientInterface::AutoLock clientlock(m_clients);
2355 for (const session_t client_id : clients) {
2356 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2361 total_sending += client->getSendingCount();
2362 const auto old_count = queue.size();
2363 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2364 unique_clients += queue.size() > old_count ? 1 : 0;
2369 // Lowest priority number comes first.
2370 // Lowest is most important.
2371 std::sort(queue.begin(), queue.end());
2373 ClientInterface::AutoLock clientlock(m_clients);
2375 // Maximal total count calculation
2376 // The per-client block sends is halved with the maximal online users
2377 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2378 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2380 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2381 Map &map = m_env->getMap();
2383 SerializedBlockCache cache, *cache_ptr = nullptr;
2384 if (unique_clients > 1) {
2385 // caching is pointless with a single client
2389 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2390 if (total_sending >= max_blocks_to_send)
2393 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2397 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2402 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2403 client->net_proto_version, cache_ptr);
2405 client->SentBlock(block_to_send.pos);
2410 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2412 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2416 ClientInterface::AutoLock clientlock(m_clients);
2417 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2418 if (!client || client->isBlockSent(blockpos))
2420 SendBlockNoLock(peer_id, block, client->serialization_version,
2421 client->net_proto_version);
2426 bool Server::addMediaFile(const std::string &filename,
2427 const std::string &filepath, std::string *filedata_to,
2428 std::string *digest_to)
2430 // If name contains illegal characters, ignore the file
2431 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2432 infostream << "Server: ignoring illegal file name: \""
2433 << filename << "\"" << std::endl;
2436 // If name is not in a supported format, ignore it
2437 const char *supported_ext[] = {
2438 ".png", ".jpg", ".bmp", ".tga",
2440 ".x", ".b3d", ".obj",
2441 // Custom translation file format
2445 if (removeStringEnd(filename, supported_ext).empty()) {
2446 infostream << "Server: ignoring unsupported file extension: \""
2447 << filename << "\"" << std::endl;
2450 // Ok, attempt to load the file and add to cache
2453 std::string filedata;
2454 if (!fs::ReadFile(filepath, filedata)) {
2455 errorstream << "Server::addMediaFile(): Failed to open \""
2456 << filename << "\" for reading" << std::endl;
2460 if (filedata.empty()) {
2461 errorstream << "Server::addMediaFile(): Empty file \""
2462 << filepath << "\"" << std::endl;
2467 sha1.addBytes(filedata.c_str(), filedata.length());
2469 unsigned char *digest = sha1.getDigest();
2470 std::string sha1_base64 = base64_encode(digest, 20);
2471 std::string sha1_hex = hex_encode((char*) digest, 20);
2473 *digest_to = std::string((char*) digest, 20);
2477 m_media[filename] = MediaInfo(filepath, sha1_base64);
2478 verbosestream << "Server: " << sha1_hex << " is " << filename
2482 *filedata_to = std::move(filedata);
2486 void Server::fillMediaCache()
2488 infostream << "Server: Calculating media file checksums" << std::endl;
2490 // Collect all media file paths
2491 std::vector<std::string> paths;
2493 // ordered in descending priority
2494 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2495 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2496 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2497 m_modmgr->getModsMediaPaths(paths);
2499 // Collect media file information from paths into cache
2500 for (const std::string &mediapath : paths) {
2501 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2502 for (const fs::DirListNode &dln : dirlist) {
2503 if (dln.dir) // Ignore dirs (already in paths)
2506 const std::string &filename = dln.name;
2507 if (m_media.find(filename) != m_media.end()) // Do not override
2510 std::string filepath = mediapath;
2511 filepath.append(DIR_DELIM).append(filename);
2512 addMediaFile(filename, filepath);
2516 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2519 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2522 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2525 std::string lang_suffix;
2526 lang_suffix.append(".").append(lang_code).append(".tr");
2527 for (const auto &i : m_media) {
2528 if (i.second.no_announce)
2530 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2537 for (const auto &i : m_media) {
2538 if (i.second.no_announce)
2540 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2542 pkt << i.first << i.second.sha1_digest;
2545 pkt << g_settings->get("remote_media");
2548 verbosestream << "Server: Announcing files to id(" << peer_id
2549 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2552 struct SendableMedia
2558 SendableMedia(const std::string &name, const std::string &path,
2559 std::string &&data):
2560 name(name), path(path), data(std::move(data))
2564 void Server::sendRequestedMedia(session_t peer_id,
2565 const std::vector<std::string> &tosend)
2567 verbosestream<<"Server::sendRequestedMedia(): "
2568 <<"Sending files to client"<<std::endl;
2572 // Put 5kB in one bunch (this is not accurate)
2573 u32 bytes_per_bunch = 5000;
2575 std::vector< std::vector<SendableMedia> > file_bunches;
2576 file_bunches.emplace_back();
2578 u32 file_size_bunch_total = 0;
2580 for (const std::string &name : tosend) {
2581 if (m_media.find(name) == m_media.end()) {
2582 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2583 <<"unknown file \""<<(name)<<"\""<<std::endl;
2587 const auto &m = m_media[name];
2591 if (!fs::ReadFile(m.path, data)) {
2592 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2593 << name << "\"" << std::endl;
2596 file_size_bunch_total += data.size();
2599 file_bunches.back().emplace_back(name, m.path, std::move(data));
2601 // Start next bunch if got enough data
2602 if(file_size_bunch_total >= bytes_per_bunch) {
2603 file_bunches.emplace_back();
2604 file_size_bunch_total = 0;
2609 /* Create and send packets */
2611 u16 num_bunches = file_bunches.size();
2612 for (u16 i = 0; i < num_bunches; i++) {
2615 u16 total number of texture bunches
2616 u16 index of this bunch
2617 u32 number of files in this bunch
2626 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2627 pkt << num_bunches << i << (u32) file_bunches[i].size();
2629 for (const SendableMedia &j : file_bunches[i]) {
2631 pkt.putLongString(j.data);
2634 verbosestream << "Server::sendRequestedMedia(): bunch "
2635 << i << "/" << num_bunches
2636 << " files=" << file_bunches[i].size()
2637 << " size=" << pkt.getSize() << std::endl;
2642 void Server::stepPendingDynMediaCallbacks(float dtime)
2644 MutexAutoLock lock(m_env_mutex);
2646 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2647 it->second.expiry_timer -= dtime;
2648 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2655 const auto &name = it->second.filename;
2656 if (!name.empty()) {
2657 assert(m_media.count(name));
2658 // if no_announce isn't set we're definitely deleting the wrong file!
2659 sanity_check(m_media[name].no_announce);
2661 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2662 m_media.erase(name);
2664 getScriptIface()->freeDynamicMediaCallback(it->first);
2665 it = m_pending_dyn_media.erase(it);
2669 void Server::SendMinimapModes(session_t peer_id,
2670 std::vector<MinimapMode> &modes, size_t wanted_mode)
2672 RemotePlayer *player = m_env->getPlayer(peer_id);
2674 if (player->getPeerId() == PEER_ID_INEXISTENT)
2677 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2678 pkt << (u16)modes.size() << (u16)wanted_mode;
2680 for (auto &mode : modes)
2681 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2686 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2688 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2692 pkt << false; // Remove inventory
2694 pkt << true; // Update inventory
2696 // Serialization & NetworkPacket isn't a love story
2697 std::ostringstream os(std::ios_base::binary);
2698 inventory->serialize(os);
2699 inventory->setModified(false);
2701 const std::string &os_str = os.str();
2702 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2703 pkt.putRawString(os_str);
2706 if (peer_id == PEER_ID_INEXISTENT)
2707 m_clients.sendToAll(&pkt);
2712 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2714 // Lookup player name, to filter detached inventories just after
2715 std::string peer_name;
2716 if (peer_id != PEER_ID_INEXISTENT) {
2717 peer_name = getClient(peer_id, CS_Created)->getName();
2720 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2721 sendDetachedInventory(inv, name, peer_id);
2724 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2731 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2733 infostream << "Server::DiePlayer(): Player "
2734 << playersao->getPlayer()->getName()
2735 << " dies" << std::endl;
2737 playersao->clearParentAttachment();
2739 // Trigger scripted stuff
2740 m_script->on_dieplayer(playersao, reason);
2742 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2745 void Server::RespawnPlayer(session_t peer_id)
2747 PlayerSAO *playersao = getPlayerSAO(peer_id);
2750 infostream << "Server::RespawnPlayer(): Player "
2751 << playersao->getPlayer()->getName()
2752 << " respawns" << std::endl;
2754 const auto *prop = playersao->accessObjectProperties();
2755 playersao->setHP(prop->hp_max,
2756 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2757 playersao->setBreath(prop->breath_max);
2759 bool repositioned = m_script->on_respawnplayer(playersao);
2760 if (!repositioned) {
2761 // setPos will send the new position to client
2762 playersao->setPos(findSpawnPos());
2767 void Server::DenySudoAccess(session_t peer_id)
2769 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2774 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2775 const std::string &custom_reason, bool reconnect)
2777 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2778 m_clients.event(peer_id, CSE_SetDenied);
2779 DisconnectPeer(peer_id);
2782 void Server::DisconnectPeer(session_t peer_id)
2784 m_modchannel_mgr->leaveAllChannels(peer_id);
2785 m_con->DisconnectPeer(peer_id);
2788 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2791 RemoteClient* client = getClient(peer_id, CS_Invalid);
2793 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2795 // Right now, the auth mechs don't change between login and sudo mode.
2796 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2797 client->allowed_sudo_mechs = sudo_auth_mechs;
2799 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2800 << g_settings->getFloat("dedicated_server_step")
2804 m_clients.event(peer_id, CSE_AuthAccept);
2806 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2808 // We only support SRP right now
2809 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2811 resp_pkt << sudo_auth_mechs;
2813 m_clients.event(peer_id, CSE_SudoSuccess);
2817 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2819 std::wstring message;
2822 Clear references to playing sounds
2824 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2825 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2826 ServerPlayingSound &psound = i->second;
2827 psound.clients.erase(peer_id);
2828 if (psound.clients.empty())
2829 m_playing_sounds.erase(i++);
2834 // clear formspec info so the next client can't abuse the current state
2835 m_formspec_state_data.erase(peer_id);
2837 RemotePlayer *player = m_env->getPlayer(peer_id);
2839 /* Run scripts and remove from environment */
2841 PlayerSAO *playersao = player->getPlayerSAO();
2844 playersao->clearChildAttachments();
2845 playersao->clearParentAttachment();
2847 // inform connected clients
2848 const std::string &player_name = player->getName();
2849 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2850 // (u16) 1 + std::string represents a vector serialization representation
2851 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2852 m_clients.sendToAll(¬ice);
2854 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2856 playersao->disconnected();
2863 if (player && reason != CDR_DENY) {
2864 std::ostringstream os(std::ios_base::binary);
2865 std::vector<session_t> clients = m_clients.getClientIDs();
2867 for (const session_t client_id : clients) {
2869 RemotePlayer *player = m_env->getPlayer(client_id);
2873 // Get name of player
2874 os << player->getName() << " ";
2877 std::string name = player->getName();
2878 actionstream << name << " "
2879 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2880 << " List of players: " << os.str() << std::endl;
2882 m_admin_chat->outgoing_queue.push_back(
2883 new ChatEventNick(CET_NICK_REMOVE, name));
2887 MutexAutoLock env_lock(m_env_mutex);
2888 m_clients.DeleteClient(peer_id);
2892 // Send leave chat message to all remaining clients
2893 if (!message.empty()) {
2894 SendChatMessage(PEER_ID_INEXISTENT,
2895 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2899 void Server::UpdateCrafting(RemotePlayer *player)
2901 InventoryList *clist = player->inventory.getList("craft");
2902 if (!clist || clist->getSize() == 0)
2905 if (!clist->checkModified())
2908 // Get a preview for crafting
2910 InventoryLocation loc;
2911 loc.setPlayer(player->getName());
2912 std::vector<ItemStack> output_replacements;
2913 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2914 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2917 InventoryList *plist = player->inventory.getList("craftpreview");
2918 if (plist && plist->getSize() >= 1) {
2919 // Put the new preview in
2920 plist->changeItem(0, preview);
2924 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2926 if (evt->type == CET_NICK_ADD) {
2927 // The terminal informed us of its nick choice
2928 m_admin_nick = ((ChatEventNick *)evt)->nick;
2929 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2930 errorstream << "You haven't set up an account." << std::endl
2931 << "Please log in using the client as '"
2932 << m_admin_nick << "' with a secure password." << std::endl
2933 << "Until then, you can't execute admin tasks via the console," << std::endl
2934 << "and everybody can claim the user account instead of you," << std::endl
2935 << "giving them full control over this server." << std::endl;
2938 assert(evt->type == CET_CHAT);
2939 handleAdminChat((ChatEventChat *)evt);
2943 std::wstring Server::handleChat(const std::string &name,
2944 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2946 // If something goes wrong, this player is to blame
2947 RollbackScopeActor rollback_scope(m_rollback,
2948 std::string("player:") + name);
2950 if (g_settings->getBool("strip_color_codes"))
2951 wmessage = unescape_enriched(wmessage);
2954 switch (player->canSendChatMessage()) {
2955 case RPLAYER_CHATRESULT_FLOODING: {
2956 std::wstringstream ws;
2957 ws << L"You cannot send more messages. You are limited to "
2958 << g_settings->getFloat("chat_message_limit_per_10sec")
2959 << L" messages per 10 seconds.";
2962 case RPLAYER_CHATRESULT_KICK:
2963 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
2964 "You have been kicked due to message flooding.");
2966 case RPLAYER_CHATRESULT_OK:
2969 FATAL_ERROR("Unhandled chat filtering result found.");
2973 if (m_max_chatmessage_length > 0
2974 && wmessage.length() > m_max_chatmessage_length) {
2975 return L"Your message exceed the maximum chat message limit set on the server. "
2976 L"It was refused. Send a shorter message";
2979 auto message = trim(wide_to_utf8(wmessage));
2980 if (message.empty())
2983 if (message.find_first_of("\n\r") != std::wstring::npos) {
2984 return L"Newlines are not permitted in chat messages";
2987 // Run script hook, exit if script ate the chat message
2988 if (m_script->on_chat_message(name, message))
2993 // Whether to send line to the player that sent the message, or to all players
2994 bool broadcast_line = true;
2996 if (check_shout_priv && !checkPriv(name, "shout")) {
2997 line += L"-!- You don't have permission to shout.";
2998 broadcast_line = false;
3001 Workaround for fixing chat on Android. Lua doesn't handle
3002 the Cyrillic alphabet and some characters on older Android devices
3005 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3007 line += utf8_to_wide(m_script->formatChatMessage(name,
3008 wide_to_utf8(wmessage)));
3013 Tell calling method to send the message to sender
3015 if (!broadcast_line)
3019 Send the message to others
3021 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3023 ChatMessage chatmsg(line);
3025 std::vector<session_t> clients = m_clients.getClientIDs();
3026 for (u16 cid : clients)
3027 SendChatMessage(cid, chatmsg);
3032 void Server::handleAdminChat(const ChatEventChat *evt)
3034 std::string name = evt->nick;
3035 std::wstring wmessage = evt->evt_msg;
3037 std::wstring answer = handleChat(name, wmessage);
3039 // If asked to send answer to sender
3040 if (!answer.empty()) {
3041 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3045 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3047 RemoteClient *client = getClientNoEx(peer_id,state_min);
3049 throw ClientNotFoundException("Client not found");
3053 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3055 return m_clients.getClientNoEx(peer_id, state_min);
3058 std::string Server::getPlayerName(session_t peer_id)
3060 RemotePlayer *player = m_env->getPlayer(peer_id);
3062 return "[id="+itos(peer_id)+"]";
3063 return player->getName();
3066 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3068 RemotePlayer *player = m_env->getPlayer(peer_id);
3071 return player->getPlayerSAO();
3074 std::string Server::getStatusString()
3076 std::ostringstream os(std::ios_base::binary);
3079 os << "version: " << g_version_string;
3081 os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3083 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3085 os << " | max lag: " << std::setprecision(3);
3086 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3088 // Information about clients
3090 os << " | clients: ";
3092 std::vector<session_t> clients = m_clients.getClientIDs();
3093 for (session_t client_id : clients) {
3094 RemotePlayer *player = m_env->getPlayer(client_id);
3096 // Get name of player
3097 const char *name = player ? player->getName() : "<unknown>";
3099 // Add name to information string
3108 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3109 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3111 if (!g_settings->get("motd").empty())
3112 os << std::endl << "# Server: " << g_settings->get("motd");
3117 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3119 std::set<std::string> privs;
3120 m_script->getAuth(name, NULL, &privs);
3124 bool Server::checkPriv(const std::string &name, const std::string &priv)
3126 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3127 return (privs.count(priv) != 0);
3130 void Server::reportPrivsModified(const std::string &name)
3133 std::vector<session_t> clients = m_clients.getClientIDs();
3134 for (const session_t client_id : clients) {
3135 RemotePlayer *player = m_env->getPlayer(client_id);
3136 reportPrivsModified(player->getName());
3139 RemotePlayer *player = m_env->getPlayer(name.c_str());
3142 SendPlayerPrivileges(player->getPeerId());
3143 PlayerSAO *sao = player->getPlayerSAO();
3146 sao->updatePrivileges(
3147 getPlayerEffectivePrivs(name),
3152 void Server::reportInventoryFormspecModified(const std::string &name)
3154 RemotePlayer *player = m_env->getPlayer(name.c_str());
3157 SendPlayerInventoryFormspec(player->getPeerId());
3160 void Server::reportFormspecPrependModified(const std::string &name)
3162 RemotePlayer *player = m_env->getPlayer(name.c_str());
3165 SendPlayerFormspecPrepend(player->getPeerId());
3168 void Server::setIpBanned(const std::string &ip, const std::string &name)
3170 m_banmanager->add(ip, name);
3173 void Server::unsetIpBanned(const std::string &ip_or_name)
3175 m_banmanager->remove(ip_or_name);
3178 std::string Server::getBanDescription(const std::string &ip_or_name)
3180 return m_banmanager->getBanDescription(ip_or_name);
3183 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3185 // m_env will be NULL if the server is initializing
3189 if (m_admin_nick == name && !m_admin_nick.empty()) {
3190 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3193 RemotePlayer *player = m_env->getPlayer(name);
3198 if (player->getPeerId() == PEER_ID_INEXISTENT)
3201 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3204 bool Server::showFormspec(const char *playername, const std::string &formspec,
3205 const std::string &formname)
3207 // m_env will be NULL if the server is initializing
3211 RemotePlayer *player = m_env->getPlayer(playername);
3215 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3219 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3224 u32 id = player->addHud(form);
3226 SendHUDAdd(player->getPeerId(), id, form);
3231 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3235 HudElement* todel = player->removeHud(id);
3242 SendHUDRemove(player->getPeerId(), id);
3246 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3251 SendHUDChange(player->getPeerId(), id, stat, data);
3255 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3260 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3261 if (new_hud_flags == player->hud_flags) // no change
3264 SendHUDSetFlags(player->getPeerId(), flags, mask);
3265 player->hud_flags = new_hud_flags;
3267 PlayerSAO* playersao = player->getPlayerSAO();
3272 m_script->player_event(playersao, "hud_changed");
3276 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3281 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3284 player->setHotbarItemcount(hotbar_itemcount);
3285 std::ostringstream os(std::ios::binary);
3286 writeS32(os, hotbar_itemcount);
3287 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3291 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3296 player->setHotbarImage(name);
3297 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3300 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3305 player->setHotbarSelectedImage(name);
3306 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3309 Address Server::getPeerAddress(session_t peer_id)
3311 // Note that this is only set after Init was received in Server::handleCommand_Init
3312 return getClient(peer_id, CS_Invalid)->getAddress();
3315 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3316 v2s32 animation_frames[4], f32 frame_speed)
3318 sanity_check(player);
3319 player->setLocalAnimations(animation_frames, frame_speed);
3320 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3323 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3325 sanity_check(player);
3326 player->eye_offset_first = first;
3327 player->eye_offset_third = third;
3328 SendEyeOffset(player->getPeerId(), first, third);
3331 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3333 sanity_check(player);
3334 player->setSky(params);
3335 SendSetSky(player->getPeerId(), params);
3338 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3340 sanity_check(player);
3341 player->setSun(params);
3342 SendSetSun(player->getPeerId(), params);
3345 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3347 sanity_check(player);
3348 player->setMoon(params);
3349 SendSetMoon(player->getPeerId(), params);
3352 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3354 sanity_check(player);
3355 player->setStars(params);
3356 SendSetStars(player->getPeerId(), params);
3359 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3361 sanity_check(player);
3362 player->setCloudParams(params);
3363 SendCloudParams(player->getPeerId(), params);
3366 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3369 sanity_check(player);
3370 player->overrideDayNightRatio(do_override, ratio);
3371 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3374 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3376 sanity_check(player);
3377 player->setLighting(lighting);
3378 SendSetLighting(player->getPeerId(), lighting);
3381 void Server::notifyPlayers(const std::wstring &msg)
3383 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3386 void Server::spawnParticle(const std::string &playername,
3387 const ParticleParameters &p)
3389 // m_env will be NULL if the server is initializing
3393 session_t peer_id = PEER_ID_INEXISTENT;
3395 if (!playername.empty()) {
3396 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3399 peer_id = player->getPeerId();
3400 proto_ver = player->protocol_version;
3403 SendSpawnParticle(peer_id, proto_ver, p);
3406 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3407 ServerActiveObject *attached, const std::string &playername)
3409 // m_env will be NULL if the server is initializing
3413 session_t peer_id = PEER_ID_INEXISTENT;
3415 if (!playername.empty()) {
3416 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3419 peer_id = player->getPeerId();
3420 proto_ver = player->protocol_version;
3423 u16 attached_id = attached ? attached->getId() : 0;
3426 if (attached_id == 0)
3427 id = m_env->addParticleSpawner(p.time);
3429 id = m_env->addParticleSpawner(p.time, attached_id);
3431 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3435 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3437 // m_env will be NULL if the server is initializing
3439 throw ServerError("Can't delete particle spawners during initialisation!");
3441 session_t peer_id = PEER_ID_INEXISTENT;
3442 if (!playername.empty()) {
3443 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3446 peer_id = player->getPeerId();
3449 m_env->deleteParticleSpawner(id);
3450 SendDeleteParticleSpawner(peer_id, id);
3453 bool Server::dynamicAddMedia(std::string filepath,
3454 const u32 token, const std::string &to_player, bool ephemeral)
3456 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3457 auto it = m_media.find(filename);
3458 if (it != m_media.end()) {
3459 // Allow the same path to be "added" again in certain conditions
3460 if (ephemeral || it->second.path != filepath) {
3461 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3462 << "\" already exists in media cache" << std::endl;
3467 // Load the file and add it to our media cache
3468 std::string filedata, raw_hash;
3469 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3474 // Create a copy of the file and swap out the path, this removes the
3475 // requirement that mods keep the file accessible at the original path.
3476 filepath = fs::CreateTempFile();
3477 bool ok = ([&] () -> bool {
3478 if (filepath.empty())
3480 std::ofstream os(filepath.c_str(), std::ios::binary);
3488 errorstream << "Server: failed to create a copy of media file "
3489 << "\"" << filename << "\"" << std::endl;
3490 m_media.erase(filename);
3493 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3494 << filepath << std::endl;
3496 m_media[filename].path = filepath;
3497 m_media[filename].no_announce = true;
3498 // stepPendingDynMediaCallbacks will clean this up later.
3499 } else if (!to_player.empty()) {
3500 m_media[filename].no_announce = true;
3503 // Push file to existing clients
3504 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3505 pkt << raw_hash << filename << (bool)ephemeral;
3507 NetworkPacket legacy_pkt = pkt;
3509 // Newer clients get asked to fetch the file (asynchronous)
3511 // Older clients have an awful hack that just throws the data at them
3512 legacy_pkt.putLongString(filedata);
3514 std::unordered_set<session_t> delivered, waiting;
3516 ClientInterface::AutoLock clientlock(m_clients);
3517 for (auto &pair : m_clients.getClientList()) {
3518 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3520 If a client is in the DefinitionsSent state it is too late to
3521 transfer the file via sendMediaAnnouncement() but at the same
3522 time the client cannot accept a media push yet.
3523 Short of artificially delaying the joining process there is no
3524 way for the server to resolve this so we (currently) opt not to.
3526 warningstream << "The media \"" << filename << "\" (dynamic) could "
3527 "not be delivered to " << pair.second->getName()
3528 << " due to a race condition." << std::endl;
3531 if (pair.second->getState() < CS_Active)
3534 const auto proto_ver = pair.second->net_proto_version;
3538 const session_t peer_id = pair.second->peer_id;
3539 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3542 if (proto_ver < 40) {
3543 delivered.emplace(peer_id);
3545 The network layer only guarantees ordered delivery inside a channel.
3546 Since the very next packet could be one that uses the media, we have
3547 to push the media over ALL channels to ensure it is processed before
3548 it is used. In practice this means channels 1 and 0.
3550 m_clients.send(peer_id, 1, &legacy_pkt, true);
3551 m_clients.send(peer_id, 0, &legacy_pkt, true);
3553 waiting.emplace(peer_id);
3554 Send(peer_id, &pkt);
3559 // Run callback for players that already had the file delivered (legacy-only)
3560 for (session_t peer_id : delivered) {
3561 if (auto player = m_env->getPlayer(peer_id))
3562 getScriptIface()->on_dynamic_media_added(token, player->getName());
3565 // Save all others in our pending state
3566 auto &state = m_pending_dyn_media[token];
3567 state.waiting_players = std::move(waiting);
3568 // regardless of success throw away the callback after a while
3569 state.expiry_timer = 60.0f;
3571 state.filename = filename;
3576 // actions: time-reversed list
3577 // Return value: success/failure
3578 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3579 std::list<std::string> *log)
3581 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3582 ServerMap *map = (ServerMap*)(&m_env->getMap());
3584 // Fail if no actions to handle
3585 if (actions.empty()) {
3587 log->push_back("Nothing to do.");
3594 for (const RollbackAction &action : actions) {
3596 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3599 std::ostringstream os;
3600 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3601 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3603 log->push_back(os.str());
3605 std::ostringstream os;
3606 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3607 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3609 log->push_back(os.str());
3613 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3614 <<" failed"<<std::endl;
3616 // Call it done if less than half failed
3617 return num_failed <= num_tried/2;
3620 // IGameDef interface
3622 IItemDefManager *Server::getItemDefManager()
3627 const NodeDefManager *Server::getNodeDefManager()
3632 ICraftDefManager *Server::getCraftDefManager()
3637 u16 Server::allocateUnknownNodeId(const std::string &name)
3639 return m_nodedef->allocateDummy(name);
3642 IWritableItemDefManager *Server::getWritableItemDefManager()
3647 NodeDefManager *Server::getWritableNodeDefManager()
3652 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3657 const std::vector<ModSpec> & Server::getMods() const
3659 return m_modmgr->getMods();
3662 const ModSpec *Server::getModSpec(const std::string &modname) const
3664 return m_modmgr->getModSpec(modname);
3667 std::string Server::getBuiltinLuaPath()
3669 return porting::path_share + DIR_DELIM + "builtin";
3672 v3f Server::findSpawnPos()
3674 ServerMap &map = m_env->getServerMap();
3676 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3677 return nodeposf * BS;
3679 bool is_good = false;
3680 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3681 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3683 // Try to find a good place a few times
3684 for (s32 i = 0; i < 4000 && !is_good; i++) {
3685 s32 range = MYMIN(1 + i, range_max);
3686 // We're going to try to throw the player to this position
3687 v2s16 nodepos2d = v2s16(
3688 -range + (myrand() % (range * 2)),
3689 -range + (myrand() % (range * 2)));
3690 // Get spawn level at point
3691 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3692 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3693 // signify an unsuitable spawn position, or if outside limits.
3694 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3695 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3698 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3699 // Consecutive empty nodes
3702 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3703 // avoid obstructions in already-generated mapblocks.
3704 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3705 // no obstructions, but mapgen decorations are generated after spawn so
3706 // the player may end up inside one.
3707 for (s32 i = 0; i < 8; i++) {
3708 v3s16 blockpos = getNodeBlockPos(nodepos);
3709 map.emergeBlock(blockpos, true);
3710 content_t c = map.getNode(nodepos).getContent();
3712 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3713 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3714 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3716 if (air_count >= 2) {
3717 // Spawn in lower empty node
3719 nodeposf = intToFloat(nodepos, BS);
3720 // Don't spawn the player outside map boundaries
3721 if (objectpos_over_limit(nodeposf))
3722 // Exit this loop, positions above are probably over limit
3725 // Good position found, cause an exit from main loop
3739 // No suitable spawn point found, return fallback 0,0,0
3740 return v3f(0.0f, 0.0f, 0.0f);
3743 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3745 if (delay == 0.0f) {
3746 // No delay, shutdown immediately
3747 m_shutdown_state.is_requested = true;
3748 // only print to the infostream, a chat message saying
3749 // "Server Shutting Down" is sent when the server destructs.
3750 infostream << "*** Immediate Server shutdown requested." << std::endl;
3751 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3752 // Negative delay, cancel shutdown if requested
3753 m_shutdown_state.reset();
3754 std::wstringstream ws;
3756 ws << L"*** Server shutdown canceled.";
3758 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3759 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3760 // m_shutdown_* are already handled, skip.
3762 } else if (delay > 0.0f) {
3763 // Positive delay, tell the clients when the server will shut down
3764 std::wstringstream ws;
3766 ws << L"*** Server shutting down in "
3767 << duration_to_string(myround(delay)).c_str()
3770 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3771 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3774 m_shutdown_state.trigger(delay, msg, reconnect);
3777 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3780 Try to get an existing player
3782 RemotePlayer *player = m_env->getPlayer(name);
3784 // If player is already connected, cancel
3785 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3786 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3791 If player with the wanted peer_id already exists, cancel.
3793 if (m_env->getPlayer(peer_id)) {
3794 infostream<<"emergePlayer(): Player with wrong name but same"
3795 " peer_id already exists"<<std::endl;
3800 player = new RemotePlayer(name, idef());
3803 bool newplayer = false;
3806 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3808 // Complete init with server parts
3809 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3810 player->protocol_version = proto_version;
3814 m_script->on_newplayer(playersao);
3820 bool Server::registerModStorage(ModMetadata *storage)
3822 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3823 errorstream << "Unable to register same mod storage twice. Storage name: "
3824 << storage->getModName() << std::endl;
3828 m_mod_storages[storage->getModName()] = storage;
3832 void Server::unregisterModStorage(const std::string &name)
3834 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3835 if (it != m_mod_storages.end())
3836 m_mod_storages.erase(name);
3839 void dedicated_server_loop(Server &server, bool &kill)
3841 verbosestream<<"dedicated_server_loop()"<<std::endl;
3843 IntervalLimiter m_profiler_interval;
3845 static thread_local const float steplen =
3846 g_settings->getFloat("dedicated_server_step");
3847 static thread_local const float profiler_print_interval =
3848 g_settings->getFloat("profiler_print_interval");
3851 * The dedicated server loop only does time-keeping (in Server::step) and
3852 * provides a way to main.cpp to kill the server externally (bool &kill).
3856 // This is kind of a hack but can be done like this
3857 // because server.step() is very light
3858 sleep_ms((int)(steplen*1000.0));
3859 server.step(steplen);
3861 if (server.isShutdownRequested() || kill)
3867 if (profiler_print_interval != 0) {
3868 if(m_profiler_interval.step(steplen, profiler_print_interval))
3870 infostream<<"Profiler:"<<std::endl;
3871 g_profiler->print(infostream);
3872 g_profiler->clear();
3877 infostream << "Dedicated server quitting" << std::endl;
3879 if (g_settings->getBool("server_announce"))
3880 ServerList::sendAnnounce(ServerList::AA_DELETE,
3881 server.m_bind_addr.getPort());
3890 bool Server::joinModChannel(const std::string &channel)
3892 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3893 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3896 bool Server::leaveModChannel(const std::string &channel)
3898 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3901 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3903 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3906 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3910 ModChannel* Server::getModChannel(const std::string &channel)
3912 return m_modchannel_mgr->getModChannel(channel);
3915 void Server::broadcastModChannelMessage(const std::string &channel,
3916 const std::string &message, session_t from_peer)
3918 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3922 if (message.size() > STRING_MAX_LEN) {
3923 warningstream << "ModChannel message too long, dropping before sending "
3924 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3925 << channel << ")" << std::endl;
3930 if (from_peer != PEER_ID_SERVER) {
3931 sender = getPlayerName(from_peer);
3934 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3935 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3936 resp_pkt << channel << sender << message;
3937 for (session_t peer_id : peers) {
3939 if (peer_id == from_peer)
3942 Send(peer_id, &resp_pkt);
3945 if (from_peer != PEER_ID_SERVER) {
3946 m_script->on_modchannel_message(channel, sender, message);
3950 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3952 if (lang_code.empty())
3955 auto it = server_translations.find(lang_code);
3956 if (it != server_translations.end())
3957 return &it->second; // Already loaded
3959 // [] will create an entry
3960 auto *translations = &server_translations[lang_code];
3962 std::string suffix = "." + lang_code + ".tr";
3963 for (const auto &i : m_media) {
3964 if (str_ends_with(i.first, suffix)) {
3966 if (fs::ReadFile(i.second.path, data)) {
3967 translations->loadTranslation(data);
3972 return translations;
3975 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
3977 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
3979 if (!world_mt.readConfigFile(world_mt_path.c_str()))
3980 throw BaseException("Cannot read world.mt!");
3982 std::string backend = world_mt.exists("mod_storage_backend") ?
3983 world_mt.get("mod_storage_backend") : "files";
3984 if (backend == "files")
3985 warningstream << "/!\\ You are using the old mod storage files backend. "
3986 << "This backend is deprecated and may be removed in a future release /!\\"
3987 << std::endl << "Switching to SQLite3 is advised, "
3988 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
3990 return openModStorageDatabase(backend, world_path, world_mt);
3993 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
3994 const std::string &world_path, const Settings &world_mt)
3996 if (backend == "sqlite3")
3997 return new ModMetadataDatabaseSQLite3(world_path);
3999 if (backend == "files")
4000 return new ModMetadataDatabaseFiles(world_path);
4002 if (backend == "dummy")
4003 return new Database_Dummy();
4005 throw BaseException("Mod storage database backend " + backend + " not supported");
4008 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4010 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4012 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4013 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4014 errorstream << "Cannot read world.mt!" << std::endl;
4018 std::string backend = world_mt.exists("mod_storage_backend") ?
4019 world_mt.get("mod_storage_backend") : "files";
4020 if (backend == migrate_to) {
4021 errorstream << "Cannot migrate: new backend is same"
4022 << " as the old one" << std::endl;
4026 ModMetadataDatabase *srcdb = nullptr;
4027 ModMetadataDatabase *dstdb = nullptr;
4029 bool succeeded = false;
4032 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4033 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4037 std::vector<std::string> mod_list;
4038 srcdb->listMods(&mod_list);
4039 for (const std::string &modname : mod_list) {
4041 srcdb->getModEntries(modname, &meta);
4042 for (const auto &pair : meta) {
4043 dstdb->setModEntry(modname, pair.first, pair.second);
4051 actionstream << "Successfully migrated the metadata of "
4052 << mod_list.size() << " mods" << std::endl;
4053 world_mt.set("mod_storage_backend", migrate_to);
4054 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4055 errorstream << "Failed to update world.mt!" << std::endl;
4057 actionstream << "world.mt updated" << std::endl;
4059 } catch (BaseException &e) {
4060 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4066 if (succeeded && backend == "files") {
4068 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4069 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4070 if (!fs::Rename(storage_path, backup_path))
4071 warningstream << "After migration, " << storage_path
4072 << " could not be renamed to " << backup_path << std::endl;