3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "network/connection.h"
25 #include "network/networkprotocol.h"
26 #include "network/serveropcodes.h"
28 #include "environment.h"
30 #include "threading/mutex_auto_lock.h"
31 #include "constants.h"
37 #include "server/serveractiveobject.h"
41 #include "scripting_server.h"
46 #include "mapgen/mapgen.h"
47 #include "mapgen/mg_biome.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content/mods.h"
51 #include "modchannels.h"
52 #include "serverlist.h"
53 #include "util/string.h"
55 #include "util/serialize.h"
56 #include "util/thread.h"
57 #include "defaultsettings.h"
58 #include "server/mods.h"
59 #include "util/base64.h"
60 #include "util/sha1.h"
62 #include "database/database.h"
63 #include "chatmessage.h"
64 #include "chat_interface.h"
65 #include "remoteplayer.h"
66 #include "server/player_sao.h"
67 #include "server/serverinventorymgr.h"
68 #include "translation.h"
69 #include "database/database-sqlite3.h"
70 #include "database/database-files.h"
71 #include "database/database-dummy.h"
72 #include "gameparams.h"
74 class ClientNotFoundException : public BaseException
77 ClientNotFoundException(const char *s):
82 class ServerThread : public Thread
86 ServerThread(Server *server):
97 void *ServerThread::run()
99 BEGIN_DEBUG_EXCEPTION_HANDLER
102 * The real business of the server happens on the ServerThread.
104 * AsyncRunStep() runs an actual server step as soon as enough time has
105 * passed (dedicated_server_loop keeps track of that).
106 * Receive() blocks at least(!) 30ms waiting for a packet (so this loop
107 * doesn't busy wait) and will process any remaining packets.
111 m_server->AsyncRunStep(true);
112 } catch (con::ConnectionBindFailed &e) {
113 m_server->setAsyncFatalError(e.what());
114 } catch (LuaError &e) {
115 m_server->setAsyncFatalError(e);
118 while (!stopRequested()) {
120 m_server->AsyncRunStep();
124 } catch (con::PeerNotFoundException &e) {
125 infostream<<"Server: PeerNotFoundException"<<std::endl;
126 } catch (ClientNotFoundException &e) {
127 } catch (con::ConnectionBindFailed &e) {
128 m_server->setAsyncFatalError(e.what());
129 } catch (LuaError &e) {
130 m_server->setAsyncFatalError(e);
134 END_DEBUG_EXCEPTION_HANDLER
139 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
141 if(pos_exists) *pos_exists = false;
146 if(pos_exists) *pos_exists = true;
151 ServerActiveObject *sao = env->getActiveObject(object);
154 if(pos_exists) *pos_exists = true;
155 return sao->getBasePosition(); }
160 void Server::ShutdownState::reset()
164 should_reconnect = false;
165 is_requested = false;
168 void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
172 should_reconnect = reconnect;
175 void Server::ShutdownState::tick(float dtime, Server *server)
181 static const float shutdown_msg_times[] =
183 1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
186 // Automated messages
187 if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
188 for (float t : shutdown_msg_times) {
189 // If shutdown timer matches an automessage, shot it
190 if (m_timer > t && m_timer - dtime < t) {
191 std::wstring periodicMsg = getShutdownTimerMessage();
193 infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
194 server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
201 if (m_timer < 0.0f) {
207 std::wstring Server::ShutdownState::getShutdownTimerMessage() const
209 std::wstringstream ws;
210 ws << L"*** Server shutting down in "
211 << duration_to_string(myround(m_timer)).c_str() << ".";
220 const std::string &path_world,
221 const SubgameSpec &gamespec,
222 bool simple_singleplayer_mode,
225 ChatInterface *iface,
226 std::string *on_shutdown_errmsg
228 m_bind_addr(bind_addr),
229 m_path_world(path_world),
230 m_gamespec(gamespec),
231 m_simple_singleplayer_mode(simple_singleplayer_mode),
232 m_dedicated(dedicated),
233 m_async_fatal_error(""),
234 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
237 m_bind_addr.isIPv6(),
239 m_itemdef(createItemDefManager()),
240 m_nodedef(createNodeDefManager()),
241 m_craftdef(createCraftDefManager()),
242 m_thread(new ServerThread(this)),
245 m_on_shutdown_errmsg(on_shutdown_errmsg),
246 m_async_globals_data(""),
247 m_modchannel_mgr(new ModChannelMgr())
249 if (m_path_world.empty())
250 throw ServerError("Supplied empty world path");
252 if (!gamespec.isValid())
253 throw ServerError("Supplied invalid gamespec");
256 m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
258 m_metrics_backend = std::make_unique<MetricsBackend>();
261 m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
262 m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
264 m_timeofday_gauge = m_metrics_backend->addGauge(
265 "minetest_core_timeofday",
266 "Time of day value");
268 m_lag_gauge = m_metrics_backend->addGauge(
269 "minetest_core_latency",
270 "Latency value (in seconds)");
272 m_aom_buffer_counter = m_metrics_backend->addCounter(
273 "minetest_core_aom_generated_count",
274 "Number of active object messages generated");
276 m_packet_recv_counter = m_metrics_backend->addCounter(
277 "minetest_core_server_packet_recv",
278 "Processable packets received");
280 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
281 "minetest_core_server_packet_recv_processed",
282 "Valid received packets processed");
284 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
290 // Send shutdown message
291 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
292 L"*** Server shutting down"));
295 MutexAutoLock envlock(m_env_mutex);
297 infostream << "Server: Saving players" << std::endl;
298 m_env->saveLoadedPlayers();
300 infostream << "Server: Kicking players" << std::endl;
301 std::string kick_msg;
302 bool reconnect = false;
303 if (isShutdownRequested()) {
304 reconnect = m_shutdown_state.should_reconnect;
305 kick_msg = m_shutdown_state.message;
307 if (kick_msg.empty()) {
308 kick_msg = g_settings->get("kick_msg_shutdown");
310 m_env->saveLoadedPlayers(true);
311 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
312 kick_msg, reconnect);
315 actionstream << "Server: Shutting down" << std::endl;
317 // Do this before stopping the server in case mapgen callbacks need to access
318 // server-controlled resources (like ModStorages). Also do them before
319 // shutdown callbacks since they may modify state that is finalized in a
322 m_emerge->stopThreads();
325 MutexAutoLock envlock(m_env_mutex);
327 // Execute script shutdown hooks
328 infostream << "Executing shutdown hooks" << std::endl;
330 m_script->on_shutdown();
331 } catch (ModError &e) {
332 errorstream << "ModError: " << e.what() << std::endl;
333 if (m_on_shutdown_errmsg) {
334 if (m_on_shutdown_errmsg->empty()) {
335 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
337 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
342 infostream << "Server: Saving environment metadata" << std::endl;
352 // Write any changes before deletion.
353 if (m_mod_storage_database)
354 m_mod_storage_database->endSave();
356 // Delete things in the reverse order of creation
360 delete m_mod_storage_database;
366 // Deinitialize scripting
367 infostream << "Server: Deinitializing scripting" << std::endl;
369 delete m_startup_server_map; // if available
370 delete m_game_settings;
372 while (!m_unsent_map_edit_queue.empty()) {
373 delete m_unsent_map_edit_queue.front();
374 m_unsent_map_edit_queue.pop();
380 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
381 if (m_simple_singleplayer_mode)
382 infostream << " in simple singleplayer mode" << std::endl;
384 infostream << std::endl;
385 infostream << "- world: " << m_path_world << std::endl;
386 infostream << "- game: " << m_gamespec.path << std::endl;
388 m_game_settings = Settings::createLayer(SL_GAME);
390 // Create world if it doesn't exist
392 loadGameConfAndInitWorld(m_path_world,
393 fs::GetFilenameFromPath(m_path_world.c_str()),
395 } catch (const BaseException &e) {
396 throw ServerError(std::string("Failed to initialize world: ") + e.what());
399 // Create emerge manager
400 m_emerge = new EmergeManager(this);
402 // Create ban manager
403 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
404 m_banmanager = new BanManager(ban_path);
406 // Create mod storage database and begin a save for later
407 m_mod_storage_database = openModStorageDatabase(m_path_world);
408 m_mod_storage_database->beginSave();
410 m_modmgr = std::make_unique<ServerModManager>(m_path_world);
411 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
412 // complain about mods with unsatisfied dependencies
413 if (!m_modmgr->isConsistent()) {
414 m_modmgr->printUnsatisfiedModsError();
418 MutexAutoLock envlock(m_env_mutex);
420 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
421 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
422 m_startup_server_map = servermap;
424 // Initialize scripting
425 infostream << "Server: Initializing Lua" << std::endl;
427 m_script = new ServerScripting(this);
429 // Must be created before mod loading because we have some inventory creation
430 m_inventory_mgr = std::make_unique<ServerInventoryManager>();
432 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
434 m_modmgr->loadMods(m_script);
436 // Read Textures and calculate sha1 sums
439 // Apply item aliases in the node definition manager
440 m_nodedef->updateAliases(m_itemdef);
442 // Apply texture overrides from texturepack/override.txt
443 std::vector<std::string> paths;
444 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
445 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
446 for (const std::string &path : paths) {
447 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
448 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
449 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
452 m_nodedef->setNodeRegistrationStatus(true);
454 // Perform pending node name resolutions
455 m_nodedef->runNodeResolveCallbacks();
457 // unmap node names in cross-references
458 m_nodedef->resolveCrossrefs();
460 // init the recipe hashes to speed up crafting
461 m_craftdef->initHashes(this);
463 // Initialize Environment
464 m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
465 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
467 m_inventory_mgr->setEnv(m_env);
468 m_clients.setEnv(m_env);
470 if (!servermap->settings_mgr.makeMapgenParams())
471 FATAL_ERROR("Couldn't create any mapgen type");
473 // Initialize mapgens
474 m_emerge->initMapgens(servermap->getMapgenParams());
476 if (g_settings->getBool("enable_rollback_recording")) {
477 // Create rollback manager
478 m_rollback = new RollbackManager(m_path_world, this);
481 // Give environment reference to scripting api
482 m_script->initializeEnvironment(m_env);
484 // Do this after regular script init is done
485 m_script->initAsync();
487 // Register us to receive map edit events
488 servermap->addEventReceiver(this);
492 // Those settings can be overwritten in world.mt, they are
493 // intended to be cached after environment loading.
494 m_liquid_transform_every = g_settings->getFloat("liquid_update");
495 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
496 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
497 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
504 infostream << "Starting server on " << m_bind_addr.serializeString()
505 << "..." << std::endl;
507 // Stop thread if already running
510 // Initialize connection
511 m_con->SetTimeoutMs(30);
512 m_con->Serve(m_bind_addr);
517 // ASCII art for the win!
519 << " __. __. __. " << std::endl
520 << " _____ |__| ____ _____ / |_ _____ _____ / |_ " << std::endl
521 << " / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\" << std::endl
522 << "| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | " << std::endl
523 << "|__|_| / |___| /\\______> | \\______>_____/| | " << std::endl
524 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
525 actionstream << "World at [" << m_path_world << "]" << std::endl;
526 actionstream << "Server for gameid=\"" << m_gamespec.id
527 << "\" listening on ";
528 m_bind_addr.print(actionstream);
529 actionstream << "." << std::endl;
534 infostream<<"Server: Stopping and waiting threads"<<std::endl;
536 // Stop threads (set run=false first so both start stopping)
540 infostream<<"Server: Threads stopped"<<std::endl;
543 void Server::step(float dtime)
549 MutexAutoLock lock(m_step_dtime_mutex);
550 m_step_dtime += dtime;
552 // Throw if fatal error occurred in thread
553 std::string async_err = m_async_fatal_error.get();
554 if (!async_err.empty()) {
555 if (!m_simple_singleplayer_mode) {
556 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
557 g_settings->get("kick_msg_crash"),
558 g_settings->getBool("ask_reconnect_on_crash"));
560 throw ServerError("AsyncErr: " + async_err);
564 void Server::AsyncRunStep(bool initial_step)
569 MutexAutoLock lock1(m_step_dtime_mutex);
570 dtime = m_step_dtime;
574 // Send blocks to clients
578 if((dtime < 0.001) && !initial_step)
581 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
584 MutexAutoLock lock1(m_step_dtime_mutex);
585 m_step_dtime -= dtime;
591 m_uptime_counter->increment(dtime);
596 Update time of day and overall game time
598 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
601 Send to clients at constant intervals
604 m_time_of_day_send_timer -= dtime;
605 if (m_time_of_day_send_timer < 0.0) {
606 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
607 u16 time = m_env->getTimeOfDay();
608 float time_speed = g_settings->getFloat("time_speed");
609 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
611 m_timeofday_gauge->set(time);
615 MutexAutoLock lock(m_env_mutex);
616 // Figure out and report maximum lag to environment
617 float max_lag = m_env->getMaxLagEstimate();
618 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
620 if(dtime > 0.1 && dtime > max_lag * 2.0)
621 infostream<<"Server: Maximum lag peaked to "<<dtime
625 m_env->reportMaxLagEstimate(max_lag);
630 static const float map_timer_and_unload_dtime = 2.92;
631 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
633 MutexAutoLock lock(m_env_mutex);
634 // Run Map's timers and unload unused data
635 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
636 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
637 g_settings->getFloat("server_unload_unused_data_timeout"),
642 Listen to the admin chat, if available
645 if (!m_admin_chat->command_queue.empty()) {
646 MutexAutoLock lock(m_env_mutex);
647 while (!m_admin_chat->command_queue.empty()) {
648 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
649 handleChatInterfaceEvent(evt);
653 m_admin_chat->outgoing_queue.push_back(
654 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
661 /* Transform liquids */
662 m_liquid_transform_timer += dtime;
663 if(m_liquid_transform_timer >= m_liquid_transform_every)
665 m_liquid_transform_timer -= m_liquid_transform_every;
667 MutexAutoLock lock(m_env_mutex);
669 ScopeProfiler sp(g_profiler, "Server: liquid transform");
671 std::map<v3s16, MapBlock*> modified_blocks;
672 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
675 Set the modified blocks unsent for all the clients
677 if (!modified_blocks.empty()) {
678 SetBlocksNotSent(modified_blocks);
681 m_clients.step(dtime);
683 // increase/decrease lag gauge gradually
684 if (m_lag_gauge->get() > dtime) {
685 m_lag_gauge->decrement(dtime/100);
687 m_lag_gauge->increment(dtime/100);
691 float &counter = m_step_pending_dyn_media_timer;
693 if (counter >= 5.0f) {
694 stepPendingDynMediaCallbacks(counter);
701 // send masterserver announce
703 float &counter = m_masterserver_timer;
704 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
705 g_settings->getBool("server_announce")) {
706 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
707 ServerList::AA_START,
708 m_bind_addr.getPort(),
709 m_clients.getPlayerNames(),
710 m_uptime_counter->get(),
711 m_env->getGameTime(),
714 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
724 Check added and deleted active objects
727 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
728 MutexAutoLock envlock(m_env_mutex);
731 ClientInterface::AutoLock clientlock(m_clients);
732 const RemoteClientMap &clients = m_clients.getClientList();
733 ScopeProfiler sp(g_profiler, "Server: update objects within range");
735 m_player_gauge->set(clients.size());
736 for (const auto &client_it : clients) {
737 RemoteClient *client = client_it.second;
739 if (client->getState() < CS_DefinitionsSent)
742 // This can happen if the client times out somehow
743 if (!m_env->getPlayer(client->peer_id))
746 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
750 SendActiveObjectRemoveAdd(client, playersao);
754 // Write changes to the mod storage
755 m_mod_storage_save_timer -= dtime;
756 if (m_mod_storage_save_timer <= 0.0f) {
757 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
758 m_mod_storage_database->endSave();
759 m_mod_storage_database->beginSave();
767 MutexAutoLock envlock(m_env_mutex);
768 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
771 // Value = data sent by object
772 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
774 // Get active object messages from environment
775 ActiveObjectMessage aom(0);
778 if (!m_env->getActiveObjectMessage(&aom))
781 std::vector<ActiveObjectMessage>* message_list = nullptr;
782 auto n = buffered_messages.find(aom.id);
783 if (n == buffered_messages.end()) {
784 message_list = new std::vector<ActiveObjectMessage>;
785 buffered_messages[aom.id] = message_list;
787 message_list = n->second;
789 message_list->push_back(std::move(aom));
793 m_aom_buffer_counter->increment(aom_count);
796 ClientInterface::AutoLock clientlock(m_clients);
797 const RemoteClientMap &clients = m_clients.getClientList();
798 // Route data to every client
799 std::string reliable_data, unreliable_data;
800 for (const auto &client_it : clients) {
801 reliable_data.clear();
802 unreliable_data.clear();
803 RemoteClient *client = client_it.second;
804 PlayerSAO *player = getPlayerSAO(client->peer_id);
805 // Go through all objects in message buffer
806 for (const auto &buffered_message : buffered_messages) {
807 // If object does not exist or is not known by client, skip it
808 u16 id = buffered_message.first;
809 ServerActiveObject *sao = m_env->getActiveObject(id);
810 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
813 // Get message list of object
814 std::vector<ActiveObjectMessage>* list = buffered_message.second;
815 // Go through every message
816 for (const ActiveObjectMessage &aom : *list) {
817 // Send position updates to players who do not see the attachment
818 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
819 if (sao->getId() == player->getId())
822 // Do not send position updates for attached players
823 // as long the parent is known to the client
824 ServerActiveObject *parent = sao->getParent();
825 if (parent && client->m_known_objects.find(parent->getId()) !=
826 client->m_known_objects.end())
830 // Add full new data to appropriate buffer
831 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
833 writeU16((u8*) idbuf, aom.id);
836 buffer.append(idbuf, sizeof(idbuf));
837 buffer.append(serializeString16(aom.datastring));
841 reliable_data and unreliable_data are now ready.
844 if (!reliable_data.empty()) {
845 SendActiveObjectMessages(client->peer_id, reliable_data);
848 if (!unreliable_data.empty()) {
849 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
854 // Clear buffered_messages
855 for (auto &buffered_message : buffered_messages) {
856 delete buffered_message.second;
861 Send queued-for-sending map edit events.
864 // We will be accessing the environment
865 MutexAutoLock lock(m_env_mutex);
867 // Don't send too many at a time
870 // Single change sending is disabled if queue size is not small
871 bool disable_single_change_sending = false;
872 if(m_unsent_map_edit_queue.size() >= 4)
873 disable_single_change_sending = true;
875 int event_count = m_unsent_map_edit_queue.size();
877 // We'll log the amount of each
880 std::list<v3s16> node_meta_updates;
882 while (!m_unsent_map_edit_queue.empty()) {
883 MapEditEvent* event = m_unsent_map_edit_queue.front();
884 m_unsent_map_edit_queue.pop();
886 // Players far away from the change are stored here.
887 // Instead of sending the changes, MapBlocks are set not sent
889 std::unordered_set<u16> far_players;
891 switch (event->type) {
894 prof.add("MEET_ADDNODE", 1);
895 sendAddNode(event->p, event->n, &far_players,
896 disable_single_change_sending ? 5 : 30,
897 event->type == MEET_ADDNODE);
899 case MEET_REMOVENODE:
900 prof.add("MEET_REMOVENODE", 1);
901 sendRemoveNode(event->p, &far_players,
902 disable_single_change_sending ? 5 : 30);
904 case MEET_BLOCK_NODE_METADATA_CHANGED: {
905 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
906 if (!event->is_private_change) {
907 // Don't send the change yet. Collect them to eliminate dupes.
908 node_meta_updates.remove(event->p);
909 node_meta_updates.push_back(event->p);
912 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
913 getNodeBlockPos(event->p))) {
914 block->raiseModified(MOD_STATE_WRITE_NEEDED,
915 MOD_REASON_REPORT_META_CHANGE);
920 prof.add("MEET_OTHER", 1);
921 for (const v3s16 &modified_block : event->modified_blocks) {
922 m_clients.markBlockposAsNotSent(modified_block);
926 prof.add("unknown", 1);
927 warningstream << "Server: Unknown MapEditEvent "
928 << ((u32)event->type) << std::endl;
933 Set blocks not sent to far players
935 if (!far_players.empty()) {
936 // Convert list format to that wanted by SetBlocksNotSent
937 std::map<v3s16, MapBlock*> modified_blocks2;
938 for (const v3s16 &modified_block : event->modified_blocks) {
939 modified_blocks2[modified_block] =
940 m_env->getMap().getBlockNoCreateNoEx(modified_block);
943 // Set blocks not sent
944 for (const u16 far_player : far_players) {
945 if (RemoteClient *client = getClient(far_player))
946 client->SetBlocksNotSent(modified_blocks2);
953 if (event_count >= 5) {
954 infostream << "Server: MapEditEvents:" << std::endl;
955 prof.print(infostream);
956 } else if (event_count != 0) {
957 verbosestream << "Server: MapEditEvents:" << std::endl;
958 prof.print(verbosestream);
961 // Send all metadata updates
962 if (node_meta_updates.size())
963 sendMetadataChanged(node_meta_updates);
967 Trigger emerge thread
968 Doing this every 2s is left over from old code, unclear if this is still needed.
971 float &counter = m_emergethread_trigger_timer;
973 if (counter <= 0.0f) {
976 m_emerge->startThreads();
980 // Save map, players and auth stuff
982 float &counter = m_savemap_timer;
984 static thread_local const float save_interval =
985 g_settings->getFloat("server_map_save_interval");
986 if (counter >= save_interval) {
988 MutexAutoLock lock(m_env_mutex);
990 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
993 if (m_banmanager->isModified()) {
994 m_banmanager->save();
997 // Save changed parts of map
998 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1001 m_env->saveLoadedPlayers();
1003 // Save environment metadata
1008 m_shutdown_state.tick(dtime, this);
1011 void Server::Receive()
1021 In the first iteration *wait* for a packet, afterwards process
1022 all packets that are immediately available (no waiting).
1025 m_con->Receive(&pkt);
1028 if (!m_con->TryReceive(&pkt))
1032 peer_id = pkt.getPeerId();
1033 m_packet_recv_counter->increment();
1035 m_packet_recv_processed_counter->increment();
1036 } catch (const con::InvalidIncomingDataException &e) {
1037 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1038 << e.what() << std::endl;
1039 } catch (const SerializationError &e) {
1040 infostream << "Server::Receive(): SerializationError: what()="
1041 << e.what() << std::endl;
1042 } catch (const ClientStateError &e) {
1043 errorstream << "ProcessData: peer=" << peer_id << " what()="
1044 << e.what() << std::endl;
1045 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1046 } catch (const con::PeerNotFoundException &e) {
1048 } catch (const con::NoIncomingDataException &e) {
1054 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1056 std::string playername;
1057 PlayerSAO *playersao = NULL;
1059 ClientInterface::AutoLock clientlock(m_clients);
1060 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1062 playername = client->getName();
1063 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1067 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1069 // If failed, cancel
1070 if (!playersao || !player) {
1071 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1072 actionstream << "Server: Failed to emerge player \"" << playername
1073 << "\" (player allocated to an another client)" << std::endl;
1074 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1076 errorstream << "Server: " << playername << ": Failed to emerge player"
1078 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1084 Send complete position information
1086 SendMovePlayer(peer_id);
1089 SendPlayerPrivileges(peer_id);
1091 // Send inventory formspec
1092 SendPlayerInventoryFormspec(peer_id);
1095 SendInventory(playersao, false);
1098 SendPlayerHP(playersao);
1100 // Send death screen
1101 if (playersao->isDead())
1102 SendDeathscreen(peer_id, false, v3f(0,0,0));
1105 SendPlayerBreath(playersao);
1108 Update player list and print action
1111 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1112 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1113 m_clients.sendToAll(¬ice_pkt);
1116 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1117 const auto &names = m_clients.getPlayerNames();
1119 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1120 for (const std::string &name : names)
1121 actionstream << name << " ";
1122 actionstream << player->getName() << std::endl;
1127 inline void Server::handleCommand(NetworkPacket *pkt)
1129 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1130 (this->*opHandle.handler)(pkt);
1133 void Server::ProcessData(NetworkPacket *pkt)
1135 // Environment is locked first.
1136 MutexAutoLock envlock(m_env_mutex);
1138 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1139 u32 peer_id = pkt->getPeerId();
1142 Address address = getPeerAddress(peer_id);
1143 std::string addr_s = address.serializeString();
1145 // FIXME: Isn't it a bit excessive to check this for every packet?
1146 if (m_banmanager->isIpBanned(addr_s)) {
1147 std::string ban_name = m_banmanager->getBanName(addr_s);
1148 infostream << "Server: A banned client tried to connect from "
1149 << addr_s << "; banned name was " << ban_name << std::endl;
1150 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1151 "Your IP is banned. Banned name was " + ban_name);
1154 } catch (con::PeerNotFoundException &e) {
1156 * no peer for this packet found
1157 * most common reason is peer timeout, e.g. peer didn't
1158 * respond for some time, your server was overloaded or
1161 infostream << "Server::ProcessData(): Canceling: peer "
1162 << peer_id << " not found" << std::endl;
1167 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1169 // Command must be handled into ToServerCommandHandler
1170 if (command >= TOSERVER_NUM_MSG_TYPES) {
1171 infostream << "Server: Ignoring unknown command "
1172 << command << std::endl;
1176 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1181 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1183 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1184 errorstream << "Server::ProcessData(): Cancelling: Peer"
1185 " serialization format invalid or not initialized."
1186 " Skipping incoming command=" << command << std::endl;
1190 /* Handle commands related to client startup */
1191 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1196 if (m_clients.getClientState(peer_id) < CS_Active) {
1197 if (command == TOSERVER_PLAYERPOS) return;
1199 errorstream << "Got packet command: " << command << " for peer id "
1200 << peer_id << " but client isn't active yet. Dropping packet "
1206 } catch (SendFailedException &e) {
1207 errorstream << "Server::ProcessData(): SendFailedException: "
1208 << "what=" << e.what()
1210 } catch (PacketError &e) {
1211 actionstream << "Server::ProcessData(): PacketError: "
1212 << "what=" << e.what()
1217 void Server::setTimeOfDay(u32 time)
1219 m_env->setTimeOfDay(time);
1220 m_time_of_day_send_timer = 0;
1223 void Server::onMapEditEvent(const MapEditEvent &event)
1225 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1228 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1231 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1233 std::vector<session_t> clients = m_clients.getClientIDs();
1234 ClientInterface::AutoLock clientlock(m_clients);
1235 // Set the modified blocks unsent for all the clients
1236 for (const session_t client_id : clients) {
1237 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1238 client->SetBlocksNotSent(block);
1242 void Server::peerAdded(con::Peer *peer)
1244 verbosestream<<"Server::peerAdded(): peer->id="
1245 <<peer->id<<std::endl;
1247 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1250 void Server::deletingPeer(con::Peer *peer, bool timeout)
1252 verbosestream<<"Server::deletingPeer(): peer->id="
1253 <<peer->id<<", timeout="<<timeout<<std::endl;
1255 m_clients.event(peer->id, CSE_Disconnect);
1256 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1259 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1261 *retval = m_con->getPeerStat(peer_id,type);
1262 return *retval != -1;
1265 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1267 ClientInterface::AutoLock clientlock(m_clients);
1268 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1273 ret.state = client->getState();
1274 ret.addr = client->getAddress();
1275 ret.uptime = client->uptime();
1276 ret.ser_vers = client->serialization_version;
1277 ret.prot_vers = client->net_proto_version;
1279 ret.major = client->getMajor();
1280 ret.minor = client->getMinor();
1281 ret.patch = client->getPatch();
1282 ret.vers_string = client->getFullVer();
1284 ret.lang_code = client->getLangCode();
1289 void Server::handlePeerChanges()
1291 while(!m_peer_change_queue.empty())
1293 con::PeerChange c = m_peer_change_queue.front();
1294 m_peer_change_queue.pop();
1296 verbosestream<<"Server: Handling peer change: "
1297 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1302 case con::PEER_ADDED:
1303 m_clients.CreateClient(c.peer_id);
1306 case con::PEER_REMOVED:
1307 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1311 FATAL_ERROR("Invalid peer change event received!");
1317 void Server::printToConsoleOnly(const std::string &text)
1320 m_admin_chat->outgoing_queue.push_back(
1321 new ChatEventChat("", utf8_to_wide(text)));
1323 std::cout << text << std::endl;
1327 void Server::Send(NetworkPacket *pkt)
1329 Send(pkt->getPeerId(), pkt);
1332 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1334 m_clients.send(peer_id,
1335 clientCommandFactoryTable[pkt->getCommand()].channel,
1337 clientCommandFactoryTable[pkt->getCommand()].reliable);
1340 void Server::SendMovement(session_t peer_id)
1342 std::ostringstream os(std::ios_base::binary);
1344 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1346 pkt << g_settings->getFloat("movement_acceleration_default");
1347 pkt << g_settings->getFloat("movement_acceleration_air");
1348 pkt << g_settings->getFloat("movement_acceleration_fast");
1349 pkt << g_settings->getFloat("movement_speed_walk");
1350 pkt << g_settings->getFloat("movement_speed_crouch");
1351 pkt << g_settings->getFloat("movement_speed_fast");
1352 pkt << g_settings->getFloat("movement_speed_climb");
1353 pkt << g_settings->getFloat("movement_speed_jump");
1354 pkt << g_settings->getFloat("movement_liquid_fluidity");
1355 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1356 pkt << g_settings->getFloat("movement_liquid_sink");
1357 pkt << g_settings->getFloat("movement_gravity");
1362 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1364 m_script->player_event(playersao, "health_changed");
1365 SendPlayerHP(playersao);
1367 // Send to other clients
1368 playersao->sendPunchCommand();
1370 if (playersao->isDead())
1371 HandlePlayerDeath(playersao, reason);
1374 void Server::SendPlayerHP(PlayerSAO *playersao)
1376 SendHP(playersao->getPeerID(), playersao->getHP());
1379 void Server::SendHP(session_t peer_id, u16 hp)
1381 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1386 void Server::SendBreath(session_t peer_id, u16 breath)
1388 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1389 pkt << (u16) breath;
1393 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1394 const std::string &custom_reason, bool reconnect)
1396 assert(reason < SERVER_ACCESSDENIED_MAX);
1398 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1400 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1401 pkt << custom_reason;
1402 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1403 reason == SERVER_ACCESSDENIED_CRASH)
1404 pkt << custom_reason << (u8)reconnect;
1408 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1409 v3f camera_point_target)
1411 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1412 pkt << set_camera_point_target << camera_point_target;
1416 void Server::SendItemDef(session_t peer_id,
1417 IItemDefManager *itemdef, u16 protocol_version)
1419 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1423 u32 length of the next item
1424 zlib-compressed serialized ItemDefManager
1426 std::ostringstream tmp_os(std::ios::binary);
1427 itemdef->serialize(tmp_os, protocol_version);
1428 std::ostringstream tmp_os2(std::ios::binary);
1429 compressZlib(tmp_os.str(), tmp_os2);
1430 pkt.putLongString(tmp_os2.str());
1433 verbosestream << "Server: Sending item definitions to id(" << peer_id
1434 << "): size=" << pkt.getSize() << std::endl;
1439 void Server::SendNodeDef(session_t peer_id,
1440 const NodeDefManager *nodedef, u16 protocol_version)
1442 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1446 u32 length of the next item
1447 zlib-compressed serialized NodeDefManager
1449 std::ostringstream tmp_os(std::ios::binary);
1450 nodedef->serialize(tmp_os, protocol_version);
1451 std::ostringstream tmp_os2(std::ios::binary);
1452 compressZlib(tmp_os.str(), tmp_os2);
1454 pkt.putLongString(tmp_os2.str());
1457 verbosestream << "Server: Sending node definitions to id(" << peer_id
1458 << "): size=" << pkt.getSize() << std::endl;
1464 Non-static send methods
1467 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1469 RemotePlayer *player = sao->getPlayer();
1471 // Do not send new format to old clients
1472 incremental &= player->protocol_version >= 38;
1474 UpdateCrafting(player);
1480 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1482 std::ostringstream os(std::ios::binary);
1483 sao->getInventory()->serialize(os, incremental);
1484 sao->getInventory()->setModified(false);
1485 player->setModified(true);
1487 const std::string &s = os.str();
1488 pkt.putRawString(s.c_str(), s.size());
1492 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1494 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1496 u8 type = message.type;
1497 pkt << version << type << message.sender << message.message
1498 << static_cast<u64>(message.timestamp);
1500 if (peer_id != PEER_ID_INEXISTENT) {
1501 RemotePlayer *player = m_env->getPlayer(peer_id);
1507 m_clients.sendToAll(&pkt);
1511 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1512 const std::string &formname)
1514 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1515 if (formspec.empty()){
1516 //the client should close the formspec
1517 //but make sure there wasn't another one open in meantime
1518 const auto it = m_formspec_state_data.find(peer_id);
1519 if (it != m_formspec_state_data.end() && it->second == formname) {
1520 m_formspec_state_data.erase(peer_id);
1522 pkt.putLongString("");
1524 m_formspec_state_data[peer_id] = formname;
1525 pkt.putLongString(formspec);
1532 // Spawns a particle on peer with peer_id
1533 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1534 const ParticleParameters &p)
1536 static thread_local const float radius =
1537 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1539 if (peer_id == PEER_ID_INEXISTENT) {
1540 std::vector<session_t> clients = m_clients.getClientIDs();
1541 const v3f pos = p.pos * BS;
1542 const float radius_sq = radius * radius;
1544 for (const session_t client_id : clients) {
1545 RemotePlayer *player = m_env->getPlayer(client_id);
1549 PlayerSAO *sao = player->getPlayerSAO();
1553 // Do not send to distant clients
1554 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1557 SendSpawnParticle(client_id, player->protocol_version, p);
1561 assert(protocol_version != 0);
1563 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1566 // NetworkPacket and iostreams are incompatible...
1567 std::ostringstream oss(std::ios_base::binary);
1568 p.serialize(oss, protocol_version);
1569 pkt.putRawString(oss.str());
1575 // Adds a ParticleSpawner on peer with peer_id
1576 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1577 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1579 static thread_local const float radius =
1580 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1582 if (peer_id == PEER_ID_INEXISTENT) {
1583 std::vector<session_t> clients = m_clients.getClientIDs();
1584 const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS;
1585 const float radius_sq = radius * radius;
1586 /* Don't send short-lived spawners to distant players.
1587 * This could be replaced with proper tracking at some point. */
1588 const bool distance_check = !attached_id && p.time <= 1.0f;
1590 for (const session_t client_id : clients) {
1591 RemotePlayer *player = m_env->getPlayer(client_id);
1595 if (distance_check) {
1596 PlayerSAO *sao = player->getPlayerSAO();
1599 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1603 SendAddParticleSpawner(client_id, player->protocol_version,
1604 p, attached_id, id);
1608 assert(protocol_version != 0);
1610 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1612 pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel
1613 << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime
1614 << p.minsize << p.maxsize << p.collisiondetection;
1616 pkt.putLongString(p.texture);
1618 pkt << id << p.vertical << p.collision_removal << attached_id;
1620 std::ostringstream os(std::ios_base::binary);
1621 p.animation.serialize(os, protocol_version);
1622 pkt.putRawString(os.str());
1624 pkt << p.glow << p.object_collision;
1625 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1630 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1632 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1636 if (peer_id != PEER_ID_INEXISTENT)
1639 m_clients.sendToAll(&pkt);
1643 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1645 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1647 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1648 << form->text << form->number << form->item << form->dir
1649 << form->align << form->offset << form->world_pos << form->size
1650 << form->z_index << form->text2 << form->style;
1655 void Server::SendHUDRemove(session_t peer_id, u32 id)
1657 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1662 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1664 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1665 pkt << id << (u8) stat;
1669 case HUD_STAT_SCALE:
1670 case HUD_STAT_ALIGN:
1671 case HUD_STAT_OFFSET:
1672 pkt << *(v2f *) value;
1676 case HUD_STAT_TEXT2:
1677 pkt << *(std::string *) value;
1679 case HUD_STAT_WORLD_POS:
1680 pkt << *(v3f *) value;
1683 pkt << *(v2s32 *) value;
1685 default: // all other types
1686 pkt << *(u32 *) value;
1693 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1695 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1697 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1699 pkt << flags << mask;
1704 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1706 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1707 pkt << param << value;
1711 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1713 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1715 // Handle prior clients here
1716 if (m_clients.getProtocolVersion(peer_id) < 39) {
1717 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1719 for (const std::string& texture : params.textures)
1722 pkt << params.clouds;
1723 } else { // Handle current clients and future clients
1724 pkt << params.bgcolor << params.type
1725 << params.clouds << params.fog_sun_tint
1726 << params.fog_moon_tint << params.fog_tint_type;
1728 if (params.type == "skybox") {
1729 pkt << (u16) params.textures.size();
1730 for (const std::string &texture : params.textures)
1732 } else if (params.type == "regular") {
1733 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1734 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1735 << params.sky_color.night_sky << params.sky_color.night_horizon
1736 << params.sky_color.indoors;
1743 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1745 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1746 pkt << params.visible << params.texture
1747 << params.tonemap << params.sunrise
1748 << params.sunrise_visible << params.scale;
1752 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1754 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1756 pkt << params.visible << params.texture
1757 << params.tonemap << params.scale;
1761 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1763 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1765 pkt << params.visible << params.count
1766 << params.starcolor << params.scale;
1771 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1773 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1774 pkt << params.density << params.color_bright << params.color_ambient
1775 << params.height << params.thickness << params.speed;
1779 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1782 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1785 pkt << do_override << (u16) (ratio * 65535);
1790 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1792 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1795 pkt << lighting.shadow_intensity;
1800 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1802 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1803 pkt << time << time_speed;
1805 if (peer_id == PEER_ID_INEXISTENT) {
1806 m_clients.sendToAll(&pkt);
1813 void Server::SendPlayerBreath(PlayerSAO *sao)
1817 m_script->player_event(sao, "breath_changed");
1818 SendBreath(sao->getPeerID(), sao->getBreath());
1821 void Server::SendMovePlayer(session_t peer_id)
1823 RemotePlayer *player = m_env->getPlayer(peer_id);
1825 PlayerSAO *sao = player->getPlayerSAO();
1828 // Send attachment updates instantly to the client prior updating position
1829 sao->sendOutdatedData();
1831 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1832 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1835 v3f pos = sao->getBasePosition();
1836 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1837 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1838 << " pitch=" << sao->getLookPitch()
1839 << " yaw=" << sao->getRotation().Y
1846 void Server::SendPlayerFov(session_t peer_id)
1848 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1850 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1851 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1856 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1857 f32 animation_speed)
1859 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1862 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1863 << animation_frames[3] << animation_speed;
1868 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1870 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1871 pkt << first << third;
1875 void Server::SendPlayerPrivileges(session_t peer_id)
1877 RemotePlayer *player = m_env->getPlayer(peer_id);
1879 if(player->getPeerId() == PEER_ID_INEXISTENT)
1882 std::set<std::string> privs;
1883 m_script->getAuth(player->getName(), NULL, &privs);
1885 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1886 pkt << (u16) privs.size();
1888 for (const std::string &priv : privs) {
1895 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1897 RemotePlayer *player = m_env->getPlayer(peer_id);
1899 if (player->getPeerId() == PEER_ID_INEXISTENT)
1902 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1903 pkt.putLongString(player->inventory_formspec);
1908 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1910 RemotePlayer *player = m_env->getPlayer(peer_id);
1912 if (player->getPeerId() == PEER_ID_INEXISTENT)
1915 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1916 pkt << player->formspec_prepend;
1920 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1922 // Radius inside which objects are active
1923 static thread_local const s16 radius =
1924 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1926 // Radius inside which players are active
1927 static thread_local const bool is_transfer_limited =
1928 g_settings->exists("unlimited_player_transfer_distance") &&
1929 !g_settings->getBool("unlimited_player_transfer_distance");
1931 static thread_local const s16 player_transfer_dist =
1932 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
1934 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
1935 radius : player_transfer_dist;
1937 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
1941 std::queue<u16> removed_objects, added_objects;
1942 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
1943 client->m_known_objects, removed_objects);
1944 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
1945 client->m_known_objects, added_objects);
1947 int removed_count = removed_objects.size();
1948 int added_count = added_objects.size();
1950 if (removed_objects.empty() && added_objects.empty())
1956 // Handle removed objects
1957 writeU16((u8*)buf, removed_objects.size());
1958 data.append(buf, 2);
1959 while (!removed_objects.empty()) {
1961 u16 id = removed_objects.front();
1962 ServerActiveObject* obj = m_env->getActiveObject(id);
1964 // Add to data buffer for sending
1965 writeU16((u8*)buf, id);
1966 data.append(buf, 2);
1968 // Remove from known objects
1969 client->m_known_objects.erase(id);
1971 if (obj && obj->m_known_by_count > 0)
1972 obj->m_known_by_count--;
1974 removed_objects.pop();
1977 // Handle added objects
1978 writeU16((u8*)buf, added_objects.size());
1979 data.append(buf, 2);
1980 while (!added_objects.empty()) {
1982 u16 id = added_objects.front();
1983 ServerActiveObject *obj = m_env->getActiveObject(id);
1984 added_objects.pop();
1987 warningstream << FUNCTION_NAME << ": NULL object id="
1988 << (int)id << std::endl;
1993 u8 type = obj->getSendType();
1995 // Add to data buffer for sending
1996 writeU16((u8*)buf, id);
1997 data.append(buf, 2);
1998 writeU8((u8*)buf, type);
1999 data.append(buf, 1);
2001 data.append(serializeString32(
2002 obj->getClientInitializationData(client->net_proto_version)));
2004 // Add to known objects
2005 client->m_known_objects.insert(id);
2007 obj->m_known_by_count++;
2010 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2011 pkt.putRawString(data.c_str(), data.size());
2014 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2015 << removed_count << " removed, " << added_count << " added, "
2016 << "packet size is " << pkt.getSize() << std::endl;
2019 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2022 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2023 datas.size(), peer_id);
2025 pkt.putRawString(datas.c_str(), datas.size());
2027 m_clients.send(pkt.getPeerId(),
2028 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2032 void Server::SendCSMRestrictionFlags(session_t peer_id)
2034 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2035 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2036 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2040 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2042 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2047 inline s32 Server::nextSoundId()
2049 s32 ret = m_next_sound_id;
2050 if (m_next_sound_id == INT32_MAX)
2051 m_next_sound_id = 0; // signed overflow is undefined
2057 s32 Server::playSound(const SimpleSoundSpec &spec,
2058 const ServerSoundParams ¶ms, bool ephemeral)
2060 // Find out initial position of sound
2061 bool pos_exists = false;
2062 v3f pos = params.getPos(m_env, &pos_exists);
2063 // If position is not found while it should be, cancel sound
2064 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2067 // Filter destination clients
2068 std::vector<session_t> dst_clients;
2069 if (!params.to_player.empty()) {
2070 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2072 infostream<<"Server::playSound: Player \""<<params.to_player
2073 <<"\" not found"<<std::endl;
2076 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2077 infostream<<"Server::playSound: Player \""<<params.to_player
2078 <<"\" not connected"<<std::endl;
2081 dst_clients.push_back(player->getPeerId());
2083 std::vector<session_t> clients = m_clients.getClientIDs();
2085 for (const session_t client_id : clients) {
2086 RemotePlayer *player = m_env->getPlayer(client_id);
2089 if (!params.exclude_player.empty() &&
2090 params.exclude_player == player->getName())
2093 PlayerSAO *sao = player->getPlayerSAO();
2098 if(sao->getBasePosition().getDistanceFrom(pos) >
2099 params.max_hear_distance)
2102 dst_clients.push_back(client_id);
2106 if(dst_clients.empty())
2111 ServerPlayingSound *psound = nullptr;
2113 id = -1; // old clients will still use this, so pick a reserved ID
2116 // The sound will exist as a reference in m_playing_sounds
2117 m_playing_sounds[id] = ServerPlayingSound();
2118 psound = &m_playing_sounds[id];
2119 psound->params = params;
2120 psound->spec = spec;
2123 float gain = params.gain * spec.gain;
2124 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2125 pkt << id << spec.name << gain
2126 << (u8) params.type << pos << params.object
2127 << params.loop << params.fade << params.pitch
2130 bool as_reliable = !ephemeral;
2132 for (const u16 dst_client : dst_clients) {
2134 psound->clients.insert(dst_client);
2135 m_clients.send(dst_client, 0, &pkt, as_reliable);
2139 void Server::stopSound(s32 handle)
2141 // Get sound reference
2142 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2143 m_playing_sounds.find(handle);
2144 if (i == m_playing_sounds.end())
2146 ServerPlayingSound &psound = i->second;
2148 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2151 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2152 si != psound.clients.end(); ++si) {
2154 m_clients.send(*si, 0, &pkt, true);
2156 // Remove sound reference
2157 m_playing_sounds.erase(i);
2160 void Server::fadeSound(s32 handle, float step, float gain)
2162 // Get sound reference
2163 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2164 m_playing_sounds.find(handle);
2165 if (i == m_playing_sounds.end())
2168 ServerPlayingSound &psound = i->second;
2169 psound.params.gain = gain;
2171 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2172 pkt << handle << step << gain;
2174 // Backwards compability
2175 bool play_sound = gain > 0;
2176 ServerPlayingSound compat_psound = psound;
2177 compat_psound.clients.clear();
2179 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2180 compat_pkt << handle;
2182 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2183 it != psound.clients.end();) {
2184 if (m_clients.getProtocolVersion(*it) >= 32) {
2186 m_clients.send(*it, 0, &pkt, true);
2189 compat_psound.clients.insert(*it);
2191 m_clients.send(*it, 0, &compat_pkt, true);
2192 psound.clients.erase(it++);
2196 // Remove sound reference
2197 if (!play_sound || psound.clients.empty())
2198 m_playing_sounds.erase(i);
2200 if (play_sound && !compat_psound.clients.empty()) {
2201 // Play new sound volume on older clients
2202 playSound(compat_psound.spec, compat_psound.params);
2206 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2209 float maxd = far_d_nodes * BS;
2210 v3f p_f = intToFloat(p, BS);
2211 v3s16 block_pos = getNodeBlockPos(p);
2213 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2216 std::vector<session_t> clients = m_clients.getClientIDs();
2217 ClientInterface::AutoLock clientlock(m_clients);
2219 for (session_t client_id : clients) {
2220 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2224 RemotePlayer *player = m_env->getPlayer(client_id);
2225 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2227 // If player is far away, only set modified blocks not sent
2228 if (!client->isBlockSent(block_pos) || (sao &&
2229 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2231 far_players->emplace(client_id);
2233 client->SetBlockNotSent(block_pos);
2238 m_clients.send(client_id, 0, &pkt, true);
2242 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2243 float far_d_nodes, bool remove_metadata)
2245 float maxd = far_d_nodes * BS;
2246 v3f p_f = intToFloat(p, BS);
2247 v3s16 block_pos = getNodeBlockPos(p);
2249 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2250 pkt << p << n.param0 << n.param1 << n.param2
2251 << (u8) (remove_metadata ? 0 : 1);
2253 std::vector<session_t> clients = m_clients.getClientIDs();
2254 ClientInterface::AutoLock clientlock(m_clients);
2256 for (session_t client_id : clients) {
2257 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2261 RemotePlayer *player = m_env->getPlayer(client_id);
2262 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2264 // If player is far away, only set modified blocks not sent
2265 if (!client->isBlockSent(block_pos) || (sao &&
2266 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2268 far_players->emplace(client_id);
2270 client->SetBlockNotSent(block_pos);
2275 m_clients.send(client_id, 0, &pkt, true);
2279 void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes)
2281 float maxd = far_d_nodes * BS;
2282 NodeMetadataList meta_updates_list(false);
2283 std::vector<session_t> clients = m_clients.getClientIDs();
2285 ClientInterface::AutoLock clientlock(m_clients);
2287 for (session_t i : clients) {
2288 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2292 ServerActiveObject *player = m_env->getActiveObject(i);
2293 v3f player_pos = player ? player->getBasePosition() : v3f();
2295 for (const v3s16 &pos : meta_updates) {
2296 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2301 v3s16 block_pos = getNodeBlockPos(pos);
2302 if (!client->isBlockSent(block_pos) || (player &&
2303 player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) {
2304 client->SetBlockNotSent(block_pos);
2308 // Add the change to send list
2309 meta_updates_list.set(pos, meta);
2311 if (meta_updates_list.size() == 0)
2314 // Send the meta changes
2315 std::ostringstream os(std::ios::binary);
2316 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2317 std::ostringstream oss(std::ios::binary);
2318 compressZlib(os.str(), oss);
2320 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0);
2321 pkt.putLongString(oss.str());
2322 m_clients.send(i, 0, &pkt, true);
2324 meta_updates_list.clear();
2328 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2329 u16 net_proto_version, SerializedBlockCache *cache)
2331 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2332 std::string s, *sptr = nullptr;
2335 auto it = cache->find({block->getPos(), ver});
2336 if (it != cache->end())
2340 // Serialize the block in the right format
2342 std::ostringstream os(std::ios_base::binary);
2343 block->serialize(os, ver, false, net_compression_level);
2344 block->serializeNetworkSpecific(os);
2349 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2350 pkt << block->getPos();
2351 pkt.putRawString(*sptr);
2354 // Store away in cache
2355 if (cache && sptr == &s)
2356 (*cache)[{block->getPos(), ver}] = std::move(s);
2359 void Server::SendBlocks(float dtime)
2361 MutexAutoLock envlock(m_env_mutex);
2362 //TODO check if one big lock could be faster then multiple small ones
2364 std::vector<PrioritySortedBlockTransfer> queue;
2366 u32 total_sending = 0, unique_clients = 0;
2369 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2371 std::vector<session_t> clients = m_clients.getClientIDs();
2373 ClientInterface::AutoLock clientlock(m_clients);
2374 for (const session_t client_id : clients) {
2375 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2380 total_sending += client->getSendingCount();
2381 const auto old_count = queue.size();
2382 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2383 unique_clients += queue.size() > old_count ? 1 : 0;
2388 // Lowest priority number comes first.
2389 // Lowest is most important.
2390 std::sort(queue.begin(), queue.end());
2392 ClientInterface::AutoLock clientlock(m_clients);
2394 // Maximal total count calculation
2395 // The per-client block sends is halved with the maximal online users
2396 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2397 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2399 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2400 Map &map = m_env->getMap();
2402 SerializedBlockCache cache, *cache_ptr = nullptr;
2403 if (unique_clients > 1) {
2404 // caching is pointless with a single client
2408 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2409 if (total_sending >= max_blocks_to_send)
2412 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2416 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2421 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2422 client->net_proto_version, cache_ptr);
2424 client->SentBlock(block_to_send.pos);
2429 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2431 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2435 ClientInterface::AutoLock clientlock(m_clients);
2436 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2437 if (!client || client->isBlockSent(blockpos))
2439 SendBlockNoLock(peer_id, block, client->serialization_version,
2440 client->net_proto_version);
2445 bool Server::addMediaFile(const std::string &filename,
2446 const std::string &filepath, std::string *filedata_to,
2447 std::string *digest_to)
2449 // If name contains illegal characters, ignore the file
2450 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2451 infostream << "Server: ignoring illegal file name: \""
2452 << filename << "\"" << std::endl;
2455 // If name is not in a supported format, ignore it
2456 const char *supported_ext[] = {
2457 ".png", ".jpg", ".bmp", ".tga",
2459 ".x", ".b3d", ".obj",
2460 // Custom translation file format
2464 if (removeStringEnd(filename, supported_ext).empty()) {
2465 infostream << "Server: ignoring unsupported file extension: \""
2466 << filename << "\"" << std::endl;
2469 // Ok, attempt to load the file and add to cache
2472 std::string filedata;
2473 if (!fs::ReadFile(filepath, filedata)) {
2474 errorstream << "Server::addMediaFile(): Failed to open \""
2475 << filename << "\" for reading" << std::endl;
2479 if (filedata.empty()) {
2480 errorstream << "Server::addMediaFile(): Empty file \""
2481 << filepath << "\"" << std::endl;
2486 sha1.addBytes(filedata.c_str(), filedata.length());
2488 unsigned char *digest = sha1.getDigest();
2489 std::string sha1_base64 = base64_encode(digest, 20);
2490 std::string sha1_hex = hex_encode((char*) digest, 20);
2492 *digest_to = std::string((char*) digest, 20);
2496 m_media[filename] = MediaInfo(filepath, sha1_base64);
2497 verbosestream << "Server: " << sha1_hex << " is " << filename
2501 *filedata_to = std::move(filedata);
2505 void Server::fillMediaCache()
2507 infostream << "Server: Calculating media file checksums" << std::endl;
2509 // Collect all media file paths
2510 std::vector<std::string> paths;
2512 // ordered in descending priority
2513 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2514 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2515 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2516 m_modmgr->getModsMediaPaths(paths);
2518 // Collect media file information from paths into cache
2519 for (const std::string &mediapath : paths) {
2520 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2521 for (const fs::DirListNode &dln : dirlist) {
2522 if (dln.dir) // Ignore dirs (already in paths)
2525 const std::string &filename = dln.name;
2526 if (m_media.find(filename) != m_media.end()) // Do not override
2529 std::string filepath = mediapath;
2530 filepath.append(DIR_DELIM).append(filename);
2531 addMediaFile(filename, filepath);
2535 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2538 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2541 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2544 std::string lang_suffix;
2545 lang_suffix.append(".").append(lang_code).append(".tr");
2546 for (const auto &i : m_media) {
2547 if (i.second.no_announce)
2549 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2556 for (const auto &i : m_media) {
2557 if (i.second.no_announce)
2559 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2561 pkt << i.first << i.second.sha1_digest;
2564 pkt << g_settings->get("remote_media");
2567 verbosestream << "Server: Announcing files to id(" << peer_id
2568 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2571 struct SendableMedia
2577 SendableMedia(const std::string &name, const std::string &path,
2578 std::string &&data):
2579 name(name), path(path), data(std::move(data))
2583 void Server::sendRequestedMedia(session_t peer_id,
2584 const std::vector<std::string> &tosend)
2586 verbosestream<<"Server::sendRequestedMedia(): "
2587 <<"Sending files to client"<<std::endl;
2591 // Put 5kB in one bunch (this is not accurate)
2592 u32 bytes_per_bunch = 5000;
2594 std::vector< std::vector<SendableMedia> > file_bunches;
2595 file_bunches.emplace_back();
2597 u32 file_size_bunch_total = 0;
2599 for (const std::string &name : tosend) {
2600 if (m_media.find(name) == m_media.end()) {
2601 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2602 <<"unknown file \""<<(name)<<"\""<<std::endl;
2606 const auto &m = m_media[name];
2610 if (!fs::ReadFile(m.path, data)) {
2611 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2612 << name << "\"" << std::endl;
2615 file_size_bunch_total += data.size();
2618 file_bunches.back().emplace_back(name, m.path, std::move(data));
2620 // Start next bunch if got enough data
2621 if(file_size_bunch_total >= bytes_per_bunch) {
2622 file_bunches.emplace_back();
2623 file_size_bunch_total = 0;
2628 /* Create and send packets */
2630 u16 num_bunches = file_bunches.size();
2631 for (u16 i = 0; i < num_bunches; i++) {
2634 u16 total number of texture bunches
2635 u16 index of this bunch
2636 u32 number of files in this bunch
2645 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2646 pkt << num_bunches << i << (u32) file_bunches[i].size();
2648 for (const SendableMedia &j : file_bunches[i]) {
2650 pkt.putLongString(j.data);
2653 verbosestream << "Server::sendRequestedMedia(): bunch "
2654 << i << "/" << num_bunches
2655 << " files=" << file_bunches[i].size()
2656 << " size=" << pkt.getSize() << std::endl;
2661 void Server::stepPendingDynMediaCallbacks(float dtime)
2663 MutexAutoLock lock(m_env_mutex);
2665 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2666 it->second.expiry_timer -= dtime;
2667 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2674 const auto &name = it->second.filename;
2675 if (!name.empty()) {
2676 assert(m_media.count(name));
2677 // if no_announce isn't set we're definitely deleting the wrong file!
2678 sanity_check(m_media[name].no_announce);
2680 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2681 m_media.erase(name);
2683 getScriptIface()->freeDynamicMediaCallback(it->first);
2684 it = m_pending_dyn_media.erase(it);
2688 void Server::SendMinimapModes(session_t peer_id,
2689 std::vector<MinimapMode> &modes, size_t wanted_mode)
2691 RemotePlayer *player = m_env->getPlayer(peer_id);
2693 if (player->getPeerId() == PEER_ID_INEXISTENT)
2696 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2697 pkt << (u16)modes.size() << (u16)wanted_mode;
2699 for (auto &mode : modes)
2700 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2705 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2707 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2711 pkt << false; // Remove inventory
2713 pkt << true; // Update inventory
2715 // Serialization & NetworkPacket isn't a love story
2716 std::ostringstream os(std::ios_base::binary);
2717 inventory->serialize(os);
2718 inventory->setModified(false);
2720 const std::string &os_str = os.str();
2721 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2722 pkt.putRawString(os_str);
2725 if (peer_id == PEER_ID_INEXISTENT)
2726 m_clients.sendToAll(&pkt);
2731 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2733 // Lookup player name, to filter detached inventories just after
2734 std::string peer_name;
2735 if (peer_id != PEER_ID_INEXISTENT) {
2736 peer_name = getClient(peer_id, CS_Created)->getName();
2739 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2740 sendDetachedInventory(inv, name, peer_id);
2743 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2750 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2752 infostream << "Server::DiePlayer(): Player "
2753 << playersao->getPlayer()->getName()
2754 << " dies" << std::endl;
2756 playersao->clearParentAttachment();
2758 // Trigger scripted stuff
2759 m_script->on_dieplayer(playersao, reason);
2761 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2764 void Server::RespawnPlayer(session_t peer_id)
2766 PlayerSAO *playersao = getPlayerSAO(peer_id);
2769 infostream << "Server::RespawnPlayer(): Player "
2770 << playersao->getPlayer()->getName()
2771 << " respawns" << std::endl;
2773 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2774 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2775 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2777 bool repositioned = m_script->on_respawnplayer(playersao);
2778 if (!repositioned) {
2779 // setPos will send the new position to client
2780 playersao->setPos(findSpawnPos());
2785 void Server::DenySudoAccess(session_t peer_id)
2787 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2792 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2793 const std::string &custom_reason, bool reconnect)
2795 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2796 m_clients.event(peer_id, CSE_SetDenied);
2797 DisconnectPeer(peer_id);
2800 void Server::DisconnectPeer(session_t peer_id)
2802 m_modchannel_mgr->leaveAllChannels(peer_id);
2803 m_con->DisconnectPeer(peer_id);
2806 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2809 RemoteClient* client = getClient(peer_id, CS_Invalid);
2811 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2813 // Right now, the auth mechs don't change between login and sudo mode.
2814 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2815 client->allowed_sudo_mechs = sudo_auth_mechs;
2817 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2818 << g_settings->getFloat("dedicated_server_step")
2822 m_clients.event(peer_id, CSE_AuthAccept);
2824 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2826 // We only support SRP right now
2827 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2829 resp_pkt << sudo_auth_mechs;
2831 m_clients.event(peer_id, CSE_SudoSuccess);
2835 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2837 std::wstring message;
2840 Clear references to playing sounds
2842 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2843 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2844 ServerPlayingSound &psound = i->second;
2845 psound.clients.erase(peer_id);
2846 if (psound.clients.empty())
2847 m_playing_sounds.erase(i++);
2852 // clear formspec info so the next client can't abuse the current state
2853 m_formspec_state_data.erase(peer_id);
2855 RemotePlayer *player = m_env->getPlayer(peer_id);
2857 /* Run scripts and remove from environment */
2859 PlayerSAO *playersao = player->getPlayerSAO();
2862 playersao->clearChildAttachments();
2863 playersao->clearParentAttachment();
2865 // inform connected clients
2866 const std::string &player_name = player->getName();
2867 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2868 // (u16) 1 + std::string represents a vector serialization representation
2869 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2870 m_clients.sendToAll(¬ice);
2872 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2874 playersao->disconnected();
2881 if (player && reason != CDR_DENY) {
2882 std::ostringstream os(std::ios_base::binary);
2883 std::vector<session_t> clients = m_clients.getClientIDs();
2885 for (const session_t client_id : clients) {
2887 RemotePlayer *player = m_env->getPlayer(client_id);
2891 // Get name of player
2892 os << player->getName() << " ";
2895 std::string name = player->getName();
2896 actionstream << name << " "
2897 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2898 << " List of players: " << os.str() << std::endl;
2900 m_admin_chat->outgoing_queue.push_back(
2901 new ChatEventNick(CET_NICK_REMOVE, name));
2905 MutexAutoLock env_lock(m_env_mutex);
2906 m_clients.DeleteClient(peer_id);
2910 // Send leave chat message to all remaining clients
2911 if (!message.empty()) {
2912 SendChatMessage(PEER_ID_INEXISTENT,
2913 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2917 void Server::UpdateCrafting(RemotePlayer *player)
2919 InventoryList *clist = player->inventory.getList("craft");
2920 if (!clist || clist->getSize() == 0)
2923 if (!clist->checkModified())
2926 // Get a preview for crafting
2928 InventoryLocation loc;
2929 loc.setPlayer(player->getName());
2930 std::vector<ItemStack> output_replacements;
2931 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2932 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2935 InventoryList *plist = player->inventory.getList("craftpreview");
2936 if (plist && plist->getSize() >= 1) {
2937 // Put the new preview in
2938 plist->changeItem(0, preview);
2942 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2944 if (evt->type == CET_NICK_ADD) {
2945 // The terminal informed us of its nick choice
2946 m_admin_nick = ((ChatEventNick *)evt)->nick;
2947 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2948 errorstream << "You haven't set up an account." << std::endl
2949 << "Please log in using the client as '"
2950 << m_admin_nick << "' with a secure password." << std::endl
2951 << "Until then, you can't execute admin tasks via the console," << std::endl
2952 << "and everybody can claim the user account instead of you," << std::endl
2953 << "giving them full control over this server." << std::endl;
2956 assert(evt->type == CET_CHAT);
2957 handleAdminChat((ChatEventChat *)evt);
2961 std::wstring Server::handleChat(const std::string &name,
2962 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2964 // If something goes wrong, this player is to blame
2965 RollbackScopeActor rollback_scope(m_rollback,
2966 std::string("player:") + name);
2968 if (g_settings->getBool("strip_color_codes"))
2969 wmessage = unescape_enriched(wmessage);
2972 switch (player->canSendChatMessage()) {
2973 case RPLAYER_CHATRESULT_FLOODING: {
2974 std::wstringstream ws;
2975 ws << L"You cannot send more messages. You are limited to "
2976 << g_settings->getFloat("chat_message_limit_per_10sec")
2977 << L" messages per 10 seconds.";
2980 case RPLAYER_CHATRESULT_KICK:
2981 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
2982 "You have been kicked due to message flooding.");
2984 case RPLAYER_CHATRESULT_OK:
2987 FATAL_ERROR("Unhandled chat filtering result found.");
2991 if (m_max_chatmessage_length > 0
2992 && wmessage.length() > m_max_chatmessage_length) {
2993 return L"Your message exceed the maximum chat message limit set on the server. "
2994 L"It was refused. Send a shorter message";
2997 auto message = trim(wide_to_utf8(wmessage));
2998 if (message.empty())
3001 if (message.find_first_of("\n\r") != std::wstring::npos) {
3002 return L"Newlines are not permitted in chat messages";
3005 // Run script hook, exit if script ate the chat message
3006 if (m_script->on_chat_message(name, message))
3011 // Whether to send line to the player that sent the message, or to all players
3012 bool broadcast_line = true;
3014 if (check_shout_priv && !checkPriv(name, "shout")) {
3015 line += L"-!- You don't have permission to shout.";
3016 broadcast_line = false;
3019 Workaround for fixing chat on Android. Lua doesn't handle
3020 the Cyrillic alphabet and some characters on older Android devices
3023 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3025 line += utf8_to_wide(m_script->formatChatMessage(name,
3026 wide_to_utf8(wmessage)));
3031 Tell calling method to send the message to sender
3033 if (!broadcast_line)
3037 Send the message to others
3039 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3041 ChatMessage chatmsg(line);
3043 std::vector<session_t> clients = m_clients.getClientIDs();
3044 for (u16 cid : clients)
3045 SendChatMessage(cid, chatmsg);
3050 void Server::handleAdminChat(const ChatEventChat *evt)
3052 std::string name = evt->nick;
3053 std::wstring wmessage = evt->evt_msg;
3055 std::wstring answer = handleChat(name, wmessage);
3057 // If asked to send answer to sender
3058 if (!answer.empty()) {
3059 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3063 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3065 RemoteClient *client = getClientNoEx(peer_id,state_min);
3067 throw ClientNotFoundException("Client not found");
3071 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3073 return m_clients.getClientNoEx(peer_id, state_min);
3076 std::string Server::getPlayerName(session_t peer_id)
3078 RemotePlayer *player = m_env->getPlayer(peer_id);
3080 return "[id="+itos(peer_id)+"]";
3081 return player->getName();
3084 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3086 RemotePlayer *player = m_env->getPlayer(peer_id);
3089 return player->getPlayerSAO();
3092 std::string Server::getStatusString()
3094 std::ostringstream os(std::ios_base::binary);
3097 os << "version: " << g_version_string;
3099 os << " | game: " << (m_gamespec.name.empty() ? m_gamespec.id : m_gamespec.name);
3101 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3103 os << " | max lag: " << std::setprecision(3);
3104 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3106 // Information about clients
3108 os << " | clients: ";
3110 std::vector<session_t> clients = m_clients.getClientIDs();
3111 for (session_t client_id : clients) {
3112 RemotePlayer *player = m_env->getPlayer(client_id);
3114 // Get name of player
3115 const char *name = player ? player->getName() : "<unknown>";
3117 // Add name to information string
3126 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3127 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3129 if (!g_settings->get("motd").empty())
3130 os << std::endl << "# Server: " << g_settings->get("motd");
3135 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3137 std::set<std::string> privs;
3138 m_script->getAuth(name, NULL, &privs);
3142 bool Server::checkPriv(const std::string &name, const std::string &priv)
3144 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3145 return (privs.count(priv) != 0);
3148 void Server::reportPrivsModified(const std::string &name)
3151 std::vector<session_t> clients = m_clients.getClientIDs();
3152 for (const session_t client_id : clients) {
3153 RemotePlayer *player = m_env->getPlayer(client_id);
3154 reportPrivsModified(player->getName());
3157 RemotePlayer *player = m_env->getPlayer(name.c_str());
3160 SendPlayerPrivileges(player->getPeerId());
3161 PlayerSAO *sao = player->getPlayerSAO();
3164 sao->updatePrivileges(
3165 getPlayerEffectivePrivs(name),
3170 void Server::reportInventoryFormspecModified(const std::string &name)
3172 RemotePlayer *player = m_env->getPlayer(name.c_str());
3175 SendPlayerInventoryFormspec(player->getPeerId());
3178 void Server::reportFormspecPrependModified(const std::string &name)
3180 RemotePlayer *player = m_env->getPlayer(name.c_str());
3183 SendPlayerFormspecPrepend(player->getPeerId());
3186 void Server::setIpBanned(const std::string &ip, const std::string &name)
3188 m_banmanager->add(ip, name);
3191 void Server::unsetIpBanned(const std::string &ip_or_name)
3193 m_banmanager->remove(ip_or_name);
3196 std::string Server::getBanDescription(const std::string &ip_or_name)
3198 return m_banmanager->getBanDescription(ip_or_name);
3201 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3203 // m_env will be NULL if the server is initializing
3207 if (m_admin_nick == name && !m_admin_nick.empty()) {
3208 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3211 RemotePlayer *player = m_env->getPlayer(name);
3216 if (player->getPeerId() == PEER_ID_INEXISTENT)
3219 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3222 bool Server::showFormspec(const char *playername, const std::string &formspec,
3223 const std::string &formname)
3225 // m_env will be NULL if the server is initializing
3229 RemotePlayer *player = m_env->getPlayer(playername);
3233 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3237 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3242 u32 id = player->addHud(form);
3244 SendHUDAdd(player->getPeerId(), id, form);
3249 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3253 HudElement* todel = player->removeHud(id);
3260 SendHUDRemove(player->getPeerId(), id);
3264 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3269 SendHUDChange(player->getPeerId(), id, stat, data);
3273 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3278 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3279 if (new_hud_flags == player->hud_flags) // no change
3282 SendHUDSetFlags(player->getPeerId(), flags, mask);
3283 player->hud_flags = new_hud_flags;
3285 PlayerSAO* playersao = player->getPlayerSAO();
3290 m_script->player_event(playersao, "hud_changed");
3294 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3299 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3302 player->setHotbarItemcount(hotbar_itemcount);
3303 std::ostringstream os(std::ios::binary);
3304 writeS32(os, hotbar_itemcount);
3305 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3309 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3314 player->setHotbarImage(name);
3315 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3318 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3323 player->setHotbarSelectedImage(name);
3324 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3327 Address Server::getPeerAddress(session_t peer_id)
3329 // Note that this is only set after Init was received in Server::handleCommand_Init
3330 return getClient(peer_id, CS_Invalid)->getAddress();
3333 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3334 v2s32 animation_frames[4], f32 frame_speed)
3336 sanity_check(player);
3337 player->setLocalAnimations(animation_frames, frame_speed);
3338 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3341 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3343 sanity_check(player);
3344 player->eye_offset_first = first;
3345 player->eye_offset_third = third;
3346 SendEyeOffset(player->getPeerId(), first, third);
3349 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3351 sanity_check(player);
3352 player->setSky(params);
3353 SendSetSky(player->getPeerId(), params);
3356 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3358 sanity_check(player);
3359 player->setSun(params);
3360 SendSetSun(player->getPeerId(), params);
3363 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3365 sanity_check(player);
3366 player->setMoon(params);
3367 SendSetMoon(player->getPeerId(), params);
3370 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3372 sanity_check(player);
3373 player->setStars(params);
3374 SendSetStars(player->getPeerId(), params);
3377 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3379 sanity_check(player);
3380 player->setCloudParams(params);
3381 SendCloudParams(player->getPeerId(), params);
3384 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3387 sanity_check(player);
3388 player->overrideDayNightRatio(do_override, ratio);
3389 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3392 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3394 sanity_check(player);
3395 player->setLighting(lighting);
3396 SendSetLighting(player->getPeerId(), lighting);
3399 void Server::notifyPlayers(const std::wstring &msg)
3401 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3404 void Server::spawnParticle(const std::string &playername,
3405 const ParticleParameters &p)
3407 // m_env will be NULL if the server is initializing
3411 session_t peer_id = PEER_ID_INEXISTENT;
3413 if (!playername.empty()) {
3414 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3417 peer_id = player->getPeerId();
3418 proto_ver = player->protocol_version;
3421 SendSpawnParticle(peer_id, proto_ver, p);
3424 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3425 ServerActiveObject *attached, const std::string &playername)
3427 // m_env will be NULL if the server is initializing
3431 session_t peer_id = PEER_ID_INEXISTENT;
3433 if (!playername.empty()) {
3434 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3437 peer_id = player->getPeerId();
3438 proto_ver = player->protocol_version;
3441 u16 attached_id = attached ? attached->getId() : 0;
3444 if (attached_id == 0)
3445 id = m_env->addParticleSpawner(p.time);
3447 id = m_env->addParticleSpawner(p.time, attached_id);
3449 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3453 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3455 // m_env will be NULL if the server is initializing
3457 throw ServerError("Can't delete particle spawners during initialisation!");
3459 session_t peer_id = PEER_ID_INEXISTENT;
3460 if (!playername.empty()) {
3461 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3464 peer_id = player->getPeerId();
3467 m_env->deleteParticleSpawner(id);
3468 SendDeleteParticleSpawner(peer_id, id);
3471 bool Server::dynamicAddMedia(std::string filepath,
3472 const u32 token, const std::string &to_player, bool ephemeral)
3474 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3475 auto it = m_media.find(filename);
3476 if (it != m_media.end()) {
3477 // Allow the same path to be "added" again in certain conditions
3478 if (ephemeral || it->second.path != filepath) {
3479 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3480 << "\" already exists in media cache" << std::endl;
3485 // Load the file and add it to our media cache
3486 std::string filedata, raw_hash;
3487 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3492 // Create a copy of the file and swap out the path, this removes the
3493 // requirement that mods keep the file accessible at the original path.
3494 filepath = fs::CreateTempFile();
3495 bool ok = ([&] () -> bool {
3496 if (filepath.empty())
3498 std::ofstream os(filepath.c_str(), std::ios::binary);
3506 errorstream << "Server: failed to create a copy of media file "
3507 << "\"" << filename << "\"" << std::endl;
3508 m_media.erase(filename);
3511 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3512 << filepath << std::endl;
3514 m_media[filename].path = filepath;
3515 m_media[filename].no_announce = true;
3516 // stepPendingDynMediaCallbacks will clean this up later.
3517 } else if (!to_player.empty()) {
3518 m_media[filename].no_announce = true;
3521 // Push file to existing clients
3522 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3523 pkt << raw_hash << filename << (bool)ephemeral;
3525 NetworkPacket legacy_pkt = pkt;
3527 // Newer clients get asked to fetch the file (asynchronous)
3529 // Older clients have an awful hack that just throws the data at them
3530 legacy_pkt.putLongString(filedata);
3532 std::unordered_set<session_t> delivered, waiting;
3534 ClientInterface::AutoLock clientlock(m_clients);
3535 for (auto &pair : m_clients.getClientList()) {
3536 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3538 If a client is in the DefinitionsSent state it is too late to
3539 transfer the file via sendMediaAnnouncement() but at the same
3540 time the client cannot accept a media push yet.
3541 Short of artificially delaying the joining process there is no
3542 way for the server to resolve this so we (currently) opt not to.
3544 warningstream << "The media \"" << filename << "\" (dynamic) could "
3545 "not be delivered to " << pair.second->getName()
3546 << " due to a race condition." << std::endl;
3549 if (pair.second->getState() < CS_Active)
3552 const auto proto_ver = pair.second->net_proto_version;
3556 const session_t peer_id = pair.second->peer_id;
3557 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3560 if (proto_ver < 40) {
3561 delivered.emplace(peer_id);
3563 The network layer only guarantees ordered delivery inside a channel.
3564 Since the very next packet could be one that uses the media, we have
3565 to push the media over ALL channels to ensure it is processed before
3566 it is used. In practice this means channels 1 and 0.
3568 m_clients.send(peer_id, 1, &legacy_pkt, true);
3569 m_clients.send(peer_id, 0, &legacy_pkt, true);
3571 waiting.emplace(peer_id);
3572 Send(peer_id, &pkt);
3577 // Run callback for players that already had the file delivered (legacy-only)
3578 for (session_t peer_id : delivered) {
3579 if (auto player = m_env->getPlayer(peer_id))
3580 getScriptIface()->on_dynamic_media_added(token, player->getName());
3583 // Save all others in our pending state
3584 auto &state = m_pending_dyn_media[token];
3585 state.waiting_players = std::move(waiting);
3586 // regardless of success throw away the callback after a while
3587 state.expiry_timer = 60.0f;
3589 state.filename = filename;
3594 // actions: time-reversed list
3595 // Return value: success/failure
3596 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3597 std::list<std::string> *log)
3599 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3600 ServerMap *map = (ServerMap*)(&m_env->getMap());
3602 // Fail if no actions to handle
3603 if (actions.empty()) {
3605 log->push_back("Nothing to do.");
3612 for (const RollbackAction &action : actions) {
3614 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3617 std::ostringstream os;
3618 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3619 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3621 log->push_back(os.str());
3623 std::ostringstream os;
3624 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3625 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3627 log->push_back(os.str());
3631 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3632 <<" failed"<<std::endl;
3634 // Call it done if less than half failed
3635 return num_failed <= num_tried/2;
3638 // IGameDef interface
3640 IItemDefManager *Server::getItemDefManager()
3645 const NodeDefManager *Server::getNodeDefManager()
3650 ICraftDefManager *Server::getCraftDefManager()
3655 u16 Server::allocateUnknownNodeId(const std::string &name)
3657 return m_nodedef->allocateDummy(name);
3660 IWritableItemDefManager *Server::getWritableItemDefManager()
3665 NodeDefManager *Server::getWritableNodeDefManager()
3670 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3675 const std::vector<ModSpec> & Server::getMods() const
3677 return m_modmgr->getMods();
3680 const ModSpec *Server::getModSpec(const std::string &modname) const
3682 return m_modmgr->getModSpec(modname);
3685 std::string Server::getBuiltinLuaPath()
3687 return porting::path_share + DIR_DELIM + "builtin";
3690 v3f Server::findSpawnPos()
3692 ServerMap &map = m_env->getServerMap();
3694 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3695 return nodeposf * BS;
3697 bool is_good = false;
3698 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3699 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3701 // Try to find a good place a few times
3702 for (s32 i = 0; i < 4000 && !is_good; i++) {
3703 s32 range = MYMIN(1 + i, range_max);
3704 // We're going to try to throw the player to this position
3705 v2s16 nodepos2d = v2s16(
3706 -range + (myrand() % (range * 2)),
3707 -range + (myrand() % (range * 2)));
3708 // Get spawn level at point
3709 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3710 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3711 // signify an unsuitable spawn position, or if outside limits.
3712 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3713 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3716 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3717 // Consecutive empty nodes
3720 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3721 // avoid obstructions in already-generated mapblocks.
3722 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3723 // no obstructions, but mapgen decorations are generated after spawn so
3724 // the player may end up inside one.
3725 for (s32 i = 0; i < 8; i++) {
3726 v3s16 blockpos = getNodeBlockPos(nodepos);
3727 map.emergeBlock(blockpos, true);
3728 content_t c = map.getNode(nodepos).getContent();
3730 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3731 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3732 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3734 if (air_count >= 2) {
3735 // Spawn in lower empty node
3737 nodeposf = intToFloat(nodepos, BS);
3738 // Don't spawn the player outside map boundaries
3739 if (objectpos_over_limit(nodeposf))
3740 // Exit this loop, positions above are probably over limit
3743 // Good position found, cause an exit from main loop
3757 // No suitable spawn point found, return fallback 0,0,0
3758 return v3f(0.0f, 0.0f, 0.0f);
3761 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3763 if (delay == 0.0f) {
3764 // No delay, shutdown immediately
3765 m_shutdown_state.is_requested = true;
3766 // only print to the infostream, a chat message saying
3767 // "Server Shutting Down" is sent when the server destructs.
3768 infostream << "*** Immediate Server shutdown requested." << std::endl;
3769 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3770 // Negative delay, cancel shutdown if requested
3771 m_shutdown_state.reset();
3772 std::wstringstream ws;
3774 ws << L"*** Server shutdown canceled.";
3776 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3777 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3778 // m_shutdown_* are already handled, skip.
3780 } else if (delay > 0.0f) {
3781 // Positive delay, tell the clients when the server will shut down
3782 std::wstringstream ws;
3784 ws << L"*** Server shutting down in "
3785 << duration_to_string(myround(delay)).c_str()
3788 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3789 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3792 m_shutdown_state.trigger(delay, msg, reconnect);
3795 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3798 Try to get an existing player
3800 RemotePlayer *player = m_env->getPlayer(name);
3802 // If player is already connected, cancel
3803 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3804 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3809 If player with the wanted peer_id already exists, cancel.
3811 if (m_env->getPlayer(peer_id)) {
3812 infostream<<"emergePlayer(): Player with wrong name but same"
3813 " peer_id already exists"<<std::endl;
3818 player = new RemotePlayer(name, idef());
3821 bool newplayer = false;
3824 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3826 // Complete init with server parts
3827 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3828 player->protocol_version = proto_version;
3832 m_script->on_newplayer(playersao);
3838 bool Server::registerModStorage(ModMetadata *storage)
3840 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3841 errorstream << "Unable to register same mod storage twice. Storage name: "
3842 << storage->getModName() << std::endl;
3846 m_mod_storages[storage->getModName()] = storage;
3850 void Server::unregisterModStorage(const std::string &name)
3852 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3853 if (it != m_mod_storages.end())
3854 m_mod_storages.erase(name);
3857 void dedicated_server_loop(Server &server, bool &kill)
3859 verbosestream<<"dedicated_server_loop()"<<std::endl;
3861 IntervalLimiter m_profiler_interval;
3863 static thread_local const float steplen =
3864 g_settings->getFloat("dedicated_server_step");
3865 static thread_local const float profiler_print_interval =
3866 g_settings->getFloat("profiler_print_interval");
3869 * The dedicated server loop only does time-keeping (in Server::step) and
3870 * provides a way to main.cpp to kill the server externally (bool &kill).
3874 // This is kind of a hack but can be done like this
3875 // because server.step() is very light
3876 sleep_ms((int)(steplen*1000.0));
3877 server.step(steplen);
3879 if (server.isShutdownRequested() || kill)
3885 if (profiler_print_interval != 0) {
3886 if(m_profiler_interval.step(steplen, profiler_print_interval))
3888 infostream<<"Profiler:"<<std::endl;
3889 g_profiler->print(infostream);
3890 g_profiler->clear();
3895 infostream << "Dedicated server quitting" << std::endl;
3897 if (g_settings->getBool("server_announce"))
3898 ServerList::sendAnnounce(ServerList::AA_DELETE,
3899 server.m_bind_addr.getPort());
3908 bool Server::joinModChannel(const std::string &channel)
3910 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3911 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3914 bool Server::leaveModChannel(const std::string &channel)
3916 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3919 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3921 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3924 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3928 ModChannel* Server::getModChannel(const std::string &channel)
3930 return m_modchannel_mgr->getModChannel(channel);
3933 void Server::broadcastModChannelMessage(const std::string &channel,
3934 const std::string &message, session_t from_peer)
3936 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3940 if (message.size() > STRING_MAX_LEN) {
3941 warningstream << "ModChannel message too long, dropping before sending "
3942 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3943 << channel << ")" << std::endl;
3948 if (from_peer != PEER_ID_SERVER) {
3949 sender = getPlayerName(from_peer);
3952 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3953 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3954 resp_pkt << channel << sender << message;
3955 for (session_t peer_id : peers) {
3957 if (peer_id == from_peer)
3960 Send(peer_id, &resp_pkt);
3963 if (from_peer != PEER_ID_SERVER) {
3964 m_script->on_modchannel_message(channel, sender, message);
3968 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3970 if (lang_code.empty())
3973 auto it = server_translations.find(lang_code);
3974 if (it != server_translations.end())
3975 return &it->second; // Already loaded
3977 // [] will create an entry
3978 auto *translations = &server_translations[lang_code];
3980 std::string suffix = "." + lang_code + ".tr";
3981 for (const auto &i : m_media) {
3982 if (str_ends_with(i.first, suffix)) {
3984 if (fs::ReadFile(i.second.path, data)) {
3985 translations->loadTranslation(data);
3990 return translations;
3993 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
3995 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
3997 if (!world_mt.readConfigFile(world_mt_path.c_str()))
3998 throw BaseException("Cannot read world.mt!");
4000 std::string backend = world_mt.exists("mod_storage_backend") ?
4001 world_mt.get("mod_storage_backend") : "files";
4002 if (backend == "files")
4003 warningstream << "/!\\ You are using the old mod storage files backend. "
4004 << "This backend is deprecated and may be removed in a future release /!\\"
4005 << std::endl << "Switching to SQLite3 is advised, "
4006 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4008 return openModStorageDatabase(backend, world_path, world_mt);
4011 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
4012 const std::string &world_path, const Settings &world_mt)
4014 if (backend == "sqlite3")
4015 return new ModMetadataDatabaseSQLite3(world_path);
4017 if (backend == "files")
4018 return new ModMetadataDatabaseFiles(world_path);
4020 if (backend == "dummy")
4021 return new Database_Dummy();
4023 throw BaseException("Mod storage database backend " + backend + " not supported");
4026 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4028 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4030 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4031 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4032 errorstream << "Cannot read world.mt!" << std::endl;
4036 std::string backend = world_mt.exists("mod_storage_backend") ?
4037 world_mt.get("mod_storage_backend") : "files";
4038 if (backend == migrate_to) {
4039 errorstream << "Cannot migrate: new backend is same"
4040 << " as the old one" << std::endl;
4044 ModMetadataDatabase *srcdb = nullptr;
4045 ModMetadataDatabase *dstdb = nullptr;
4047 bool succeeded = false;
4050 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4051 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4055 std::vector<std::string> mod_list;
4056 srcdb->listMods(&mod_list);
4057 for (const std::string &modname : mod_list) {
4059 srcdb->getModEntries(modname, &meta);
4060 for (const auto &pair : meta) {
4061 dstdb->setModEntry(modname, pair.first, pair.second);
4069 actionstream << "Successfully migrated the metadata of "
4070 << mod_list.size() << " mods" << std::endl;
4071 world_mt.set("mod_storage_backend", migrate_to);
4072 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4073 errorstream << "Failed to update world.mt!" << std::endl;
4075 actionstream << "world.mt updated" << std::endl;
4077 } catch (BaseException &e) {
4078 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4084 if (succeeded && backend == "files") {
4086 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4087 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4088 if (!fs::Rename(storage_path, backup_path))
4089 warningstream << "After migration, " << storage_path
4090 << " could not be renamed to " << backup_path << std::endl;