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"
71 #include "database/database-postgresql.h"
73 #include "database/database-files.h"
74 #include "database/database-dummy.h"
75 #include "gameparams.h"
77 class ClientNotFoundException : public BaseException
80 ClientNotFoundException(const char *s):
85 class ServerThread : public Thread
89 ServerThread(Server *server):
100 void *ServerThread::run()
102 BEGIN_DEBUG_EXCEPTION_HANDLER
105 * The real business of the server happens on the ServerThread.
107 * AsyncRunStep() runs an actual server step as soon as enough time has
108 * passed (dedicated_server_loop keeps track of that).
109 * Receive() blocks at least(!) 30ms waiting for a packet (so this loop
110 * doesn't busy wait) and will process any remaining packets.
114 m_server->AsyncRunStep(true);
115 } catch (con::ConnectionBindFailed &e) {
116 m_server->setAsyncFatalError(e.what());
117 } catch (LuaError &e) {
118 m_server->setAsyncFatalError(e);
121 while (!stopRequested()) {
123 m_server->AsyncRunStep();
127 } catch (con::PeerNotFoundException &e) {
128 infostream<<"Server: PeerNotFoundException"<<std::endl;
129 } catch (ClientNotFoundException &e) {
130 } catch (con::ConnectionBindFailed &e) {
131 m_server->setAsyncFatalError(e.what());
132 } catch (LuaError &e) {
133 m_server->setAsyncFatalError(e);
137 END_DEBUG_EXCEPTION_HANDLER
142 v3f ServerPlayingSound::getPos(ServerEnvironment *env, bool *pos_exists) const
148 case SoundLocation::Local:
150 case SoundLocation::Position:
154 case SoundLocation::Object:
158 ServerActiveObject *sao = env->getActiveObject(object);
163 return sao->getBasePosition();
170 void Server::ShutdownState::reset()
174 should_reconnect = false;
175 is_requested = false;
178 void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
182 should_reconnect = reconnect;
185 void Server::ShutdownState::tick(float dtime, Server *server)
191 static const float shutdown_msg_times[] =
193 1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
196 // Automated messages
197 if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
198 for (float t : shutdown_msg_times) {
199 // If shutdown timer matches an automessage, shot it
200 if (m_timer > t && m_timer - dtime < t) {
201 std::wstring periodicMsg = getShutdownTimerMessage();
203 infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
204 server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
211 if (m_timer < 0.0f) {
217 std::wstring Server::ShutdownState::getShutdownTimerMessage() const
219 std::wstringstream ws;
220 ws << L"*** Server shutting down in "
221 << duration_to_string(myround(m_timer)).c_str() << ".";
230 const std::string &path_world,
231 const SubgameSpec &gamespec,
232 bool simple_singleplayer_mode,
235 ChatInterface *iface,
236 std::string *on_shutdown_errmsg
238 m_bind_addr(bind_addr),
239 m_path_world(path_world),
240 m_gamespec(gamespec),
241 m_simple_singleplayer_mode(simple_singleplayer_mode),
242 m_dedicated(dedicated),
243 m_async_fatal_error(""),
244 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
247 m_bind_addr.isIPv6(),
249 m_itemdef(createItemDefManager()),
250 m_nodedef(createNodeDefManager()),
251 m_craftdef(createCraftDefManager()),
252 m_thread(new ServerThread(this)),
255 m_on_shutdown_errmsg(on_shutdown_errmsg),
256 m_modchannel_mgr(new ModChannelMgr())
258 if (m_path_world.empty())
259 throw ServerError("Supplied empty world path");
261 if (!gamespec.isValid())
262 throw ServerError("Supplied invalid gamespec");
265 m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
267 m_metrics_backend = std::make_unique<MetricsBackend>();
270 m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
271 m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
273 m_timeofday_gauge = m_metrics_backend->addGauge(
274 "minetest_core_timeofday",
275 "Time of day value");
277 m_lag_gauge = m_metrics_backend->addGauge(
278 "minetest_core_latency",
279 "Latency value (in seconds)");
282 const std::string aom_types[] = {"reliable", "unreliable"};
283 for (u32 i = 0; i < ARRLEN(aom_types); i++) {
284 std::string help_str("Number of active object messages generated (");
285 help_str.append(aom_types[i]).append(")");
286 m_aom_buffer_counter[i] = m_metrics_backend->addCounter(
287 "minetest_core_aom_generated_count", help_str,
288 {{"type", aom_types[i]}});
291 m_packet_recv_counter = m_metrics_backend->addCounter(
292 "minetest_core_server_packet_recv",
293 "Processable packets received");
295 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
296 "minetest_core_server_packet_recv_processed",
297 "Valid received packets processed");
299 m_map_edit_event_counter = m_metrics_backend->addCounter(
300 "minetest_core_map_edit_events",
301 "Number of map edit events");
303 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
309 // Send shutdown message
310 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
311 L"*** Server shutting down"));
314 MutexAutoLock envlock(m_env_mutex);
316 infostream << "Server: Saving players" << std::endl;
317 m_env->saveLoadedPlayers();
319 infostream << "Server: Kicking players" << std::endl;
320 std::string kick_msg;
321 bool reconnect = false;
322 if (isShutdownRequested()) {
323 reconnect = m_shutdown_state.should_reconnect;
324 kick_msg = m_shutdown_state.message;
326 if (kick_msg.empty()) {
327 kick_msg = g_settings->get("kick_msg_shutdown");
329 m_env->saveLoadedPlayers(true);
330 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
331 kick_msg, reconnect);
334 actionstream << "Server: Shutting down" << std::endl;
336 // Do this before stopping the server in case mapgen callbacks need to access
337 // server-controlled resources (like ModStorages). Also do them before
338 // shutdown callbacks since they may modify state that is finalized in a
341 m_emerge->stopThreads();
344 MutexAutoLock envlock(m_env_mutex);
346 // Execute script shutdown hooks
347 infostream << "Executing shutdown hooks" << std::endl;
349 m_script->on_shutdown();
350 } catch (ModError &e) {
351 errorstream << "ModError: " << e.what() << std::endl;
352 if (m_on_shutdown_errmsg) {
353 if (m_on_shutdown_errmsg->empty()) {
354 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
356 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
361 infostream << "Server: Saving environment metadata" << std::endl;
371 // Write any changes before deletion.
372 if (m_mod_storage_database)
373 m_mod_storage_database->endSave();
375 // Delete things in the reverse order of creation
379 delete m_mod_storage_database;
385 // Deinitialize scripting
386 infostream << "Server: Deinitializing scripting" << std::endl;
388 delete m_startup_server_map; // if available
389 delete m_game_settings;
391 while (!m_unsent_map_edit_queue.empty()) {
392 delete m_unsent_map_edit_queue.front();
393 m_unsent_map_edit_queue.pop();
399 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
400 if (m_simple_singleplayer_mode)
401 infostream << " in simple singleplayer mode" << std::endl;
403 infostream << std::endl;
404 infostream << "- world: " << m_path_world << std::endl;
405 infostream << "- game: " << m_gamespec.path << std::endl;
407 m_game_settings = Settings::createLayer(SL_GAME);
409 // Create world if it doesn't exist
411 loadGameConfAndInitWorld(m_path_world,
412 fs::GetFilenameFromPath(m_path_world.c_str()),
414 } catch (const BaseException &e) {
415 throw ServerError(std::string("Failed to initialize world: ") + e.what());
418 // Create emerge manager
419 m_emerge = new EmergeManager(this, m_metrics_backend.get());
421 // Create ban manager
422 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
423 m_banmanager = new BanManager(ban_path);
425 // Create mod storage database and begin a save for later
426 m_mod_storage_database = openModStorageDatabase(m_path_world);
427 m_mod_storage_database->beginSave();
429 m_modmgr = std::make_unique<ServerModManager>(m_path_world);
431 // complain about mods with unsatisfied dependencies
432 if (!m_modmgr->isConsistent()) {
433 std::string error = m_modmgr->getUnsatisfiedModsError();
434 throw ServerError(error);
438 MutexAutoLock envlock(m_env_mutex);
440 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
441 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
442 m_startup_server_map = servermap;
444 // Initialize scripting
445 infostream << "Server: Initializing Lua" << std::endl;
447 m_script = new ServerScripting(this);
449 // Must be created before mod loading because we have some inventory creation
450 m_inventory_mgr = std::make_unique<ServerInventoryManager>();
452 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
453 m_script->checkSetByBuiltin();
455 m_gamespec.checkAndLog();
456 m_modmgr->loadMods(m_script);
458 // Read Textures and calculate sha1 sums
461 // Apply item aliases in the node definition manager
462 m_nodedef->updateAliases(m_itemdef);
464 // Apply texture overrides from texturepack/override.txt
465 std::vector<std::string> paths;
466 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
467 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
468 for (const std::string &path : paths) {
469 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
470 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
471 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
474 m_nodedef->setNodeRegistrationStatus(true);
476 // Perform pending node name resolutions
477 m_nodedef->runNodeResolveCallbacks();
479 // unmap node names in cross-references
480 m_nodedef->resolveCrossrefs();
482 // init the recipe hashes to speed up crafting
483 m_craftdef->initHashes(this);
485 // Initialize Environment
486 m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
487 m_env = new ServerEnvironment(servermap, m_script, this,
488 m_path_world, m_metrics_backend.get());
491 m_inventory_mgr->setEnv(m_env);
492 m_clients.setEnv(m_env);
494 if (!servermap->settings_mgr.makeMapgenParams())
495 FATAL_ERROR("Couldn't create any mapgen type");
497 // Initialize mapgens
498 m_emerge->initMapgens(servermap->getMapgenParams());
500 if (g_settings->getBool("enable_rollback_recording")) {
501 // Create rollback manager
502 m_rollback = new RollbackManager(m_path_world, this);
505 // Give environment reference to scripting api
506 m_script->initializeEnvironment(m_env);
508 // Do this after regular script init is done
509 m_script->initAsync();
511 // Register us to receive map edit events
512 servermap->addEventReceiver(this);
516 // Those settings can be overwritten in world.mt, they are
517 // intended to be cached after environment loading.
518 m_liquid_transform_every = g_settings->getFloat("liquid_update");
519 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
520 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
521 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
528 infostream << "Starting server on " << m_bind_addr.serializeString()
529 << "..." << std::endl;
531 // Stop thread if already running
534 // Initialize connection
535 m_con->SetTimeoutMs(30);
536 m_con->Serve(m_bind_addr);
541 // ASCII art for the win!
543 << " __. __. __. " << std::endl
544 << " _____ |__| ____ _____ / |_ _____ _____ / |_ " << std::endl
545 << " / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\" << std::endl
546 << "| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | " << std::endl
547 << "|__|_| / |___| /\\______> | \\______>_____/| | " << std::endl
548 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
549 actionstream << "World at [" << m_path_world << "]" << std::endl;
550 actionstream << "Server for gameid=\"" << m_gamespec.id
551 << "\" listening on ";
552 m_bind_addr.print(actionstream);
553 actionstream << "." << std::endl;
558 infostream<<"Server: Stopping and waiting threads"<<std::endl;
560 // Stop threads (set run=false first so both start stopping)
564 infostream<<"Server: Threads stopped"<<std::endl;
567 void Server::step(float dtime)
573 MutexAutoLock lock(m_step_dtime_mutex);
574 m_step_dtime += dtime;
576 // Throw if fatal error occurred in thread
577 std::string async_err = m_async_fatal_error.get();
578 if (!async_err.empty()) {
579 if (!m_simple_singleplayer_mode) {
580 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
581 g_settings->get("kick_msg_crash"),
582 g_settings->getBool("ask_reconnect_on_crash"));
584 throw ServerError("AsyncErr: " + async_err);
588 void Server::AsyncRunStep(bool initial_step)
593 MutexAutoLock lock1(m_step_dtime_mutex);
594 dtime = m_step_dtime;
598 // Send blocks to clients
602 if((dtime < 0.001) && !initial_step)
605 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
608 MutexAutoLock lock1(m_step_dtime_mutex);
609 m_step_dtime -= dtime;
615 m_uptime_counter->increment(dtime);
620 Update time of day and overall game time
622 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
625 Send to clients at constant intervals
628 m_time_of_day_send_timer -= dtime;
629 if (m_time_of_day_send_timer < 0.0) {
630 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
631 u16 time = m_env->getTimeOfDay();
632 float time_speed = g_settings->getFloat("time_speed");
633 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
635 m_timeofday_gauge->set(time);
639 MutexAutoLock lock(m_env_mutex);
640 // Figure out and report maximum lag to environment
641 float max_lag = m_env->getMaxLagEstimate();
642 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
644 if(dtime > 0.1 && dtime > max_lag * 2.0)
645 infostream<<"Server: Maximum lag peaked to "<<dtime
649 m_env->reportMaxLagEstimate(max_lag);
655 static const float map_timer_and_unload_dtime = 2.92;
656 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
658 MutexAutoLock lock(m_env_mutex);
659 // Run Map's timers and unload unused data
660 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
661 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
662 std::max(g_settings->getFloat("server_unload_unused_data_timeout"), 0.0f),
667 Listen to the admin chat, if available
670 if (!m_admin_chat->command_queue.empty()) {
671 MutexAutoLock lock(m_env_mutex);
672 while (!m_admin_chat->command_queue.empty()) {
673 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
674 handleChatInterfaceEvent(evt);
678 m_admin_chat->outgoing_queue.push_back(
679 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
686 /* Transform liquids */
687 m_liquid_transform_timer += dtime;
688 if(m_liquid_transform_timer >= m_liquid_transform_every)
690 m_liquid_transform_timer -= m_liquid_transform_every;
692 MutexAutoLock lock(m_env_mutex);
694 ScopeProfiler sp(g_profiler, "Server: liquid transform");
696 std::map<v3s16, MapBlock*> modified_blocks;
697 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
700 Set the modified blocks unsent for all the clients
702 if (!modified_blocks.empty()) {
703 SetBlocksNotSent(modified_blocks);
706 m_clients.step(dtime);
708 // increase/decrease lag gauge gradually
709 if (m_lag_gauge->get() > dtime) {
710 m_lag_gauge->decrement(dtime/100);
712 m_lag_gauge->increment(dtime/100);
716 float &counter = m_step_pending_dyn_media_timer;
718 if (counter >= 5.0f) {
719 stepPendingDynMediaCallbacks(counter);
726 // send masterserver announce
728 float &counter = m_masterserver_timer;
729 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
730 g_settings->getBool("server_announce")) {
731 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
732 ServerList::AA_START,
733 m_bind_addr.getPort(),
734 m_clients.getPlayerNames(),
735 m_uptime_counter->get(),
736 m_env->getGameTime(),
739 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
749 Check added and deleted active objects
752 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
753 MutexAutoLock envlock(m_env_mutex);
756 ClientInterface::AutoLock clientlock(m_clients);
757 const RemoteClientMap &clients = m_clients.getClientList();
758 ScopeProfiler sp(g_profiler, "Server: update objects within range");
760 m_player_gauge->set(clients.size());
761 for (const auto &client_it : clients) {
762 RemoteClient *client = client_it.second;
764 if (client->getState() < CS_DefinitionsSent)
767 // This can happen if the client times out somehow
768 if (!m_env->getPlayer(client->peer_id))
771 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
775 SendActiveObjectRemoveAdd(client, playersao);
779 // Write changes to the mod storage
780 m_mod_storage_save_timer -= dtime;
781 if (m_mod_storage_save_timer <= 0.0f) {
782 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
783 m_mod_storage_database->endSave();
784 m_mod_storage_database->beginSave();
792 MutexAutoLock envlock(m_env_mutex);
793 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
796 // Value = data sent by object
797 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
799 // Get active object messages from environment
800 ActiveObjectMessage aom(0);
801 u32 count_reliable = 0, count_unreliable = 0;
803 if (!m_env->getActiveObjectMessage(&aom))
810 std::vector<ActiveObjectMessage>* message_list = nullptr;
811 auto n = buffered_messages.find(aom.id);
812 if (n == buffered_messages.end()) {
813 message_list = new std::vector<ActiveObjectMessage>;
814 buffered_messages[aom.id] = message_list;
816 message_list = n->second;
818 message_list->push_back(std::move(aom));
821 m_aom_buffer_counter[0]->increment(count_reliable);
822 m_aom_buffer_counter[1]->increment(count_unreliable);
825 ClientInterface::AutoLock clientlock(m_clients);
826 const RemoteClientMap &clients = m_clients.getClientList();
827 // Route data to every client
828 std::string reliable_data, unreliable_data;
829 for (const auto &client_it : clients) {
830 reliable_data.clear();
831 unreliable_data.clear();
832 RemoteClient *client = client_it.second;
833 PlayerSAO *player = getPlayerSAO(client->peer_id);
834 // Go through all objects in message buffer
835 for (const auto &buffered_message : buffered_messages) {
836 // If object does not exist or is not known by client, skip it
837 u16 id = buffered_message.first;
838 ServerActiveObject *sao = m_env->getActiveObject(id);
839 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
842 // Get message list of object
843 std::vector<ActiveObjectMessage>* list = buffered_message.second;
844 // Go through every message
845 for (const ActiveObjectMessage &aom : *list) {
846 // Send position updates to players who do not see the attachment
847 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
848 if (sao->getId() == player->getId())
851 // Do not send position updates for attached players
852 // as long the parent is known to the client
853 ServerActiveObject *parent = sao->getParent();
854 if (parent && client->m_known_objects.find(parent->getId()) !=
855 client->m_known_objects.end())
859 // Add full new data to appropriate buffer
860 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
862 writeU16((u8*) idbuf, aom.id);
865 buffer.append(idbuf, sizeof(idbuf));
866 buffer.append(serializeString16(aom.datastring));
870 reliable_data and unreliable_data are now ready.
873 if (!reliable_data.empty()) {
874 SendActiveObjectMessages(client->peer_id, reliable_data);
877 if (!unreliable_data.empty()) {
878 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
883 // Clear buffered_messages
884 for (auto &buffered_message : buffered_messages) {
885 delete buffered_message.second;
890 Send queued-for-sending map edit events.
893 // We will be accessing the environment
894 MutexAutoLock lock(m_env_mutex);
896 // Single change sending is disabled if queue size is big
897 bool disable_single_change_sending = false;
898 if(m_unsent_map_edit_queue.size() >= 4)
899 disable_single_change_sending = true;
901 const auto event_count = m_unsent_map_edit_queue.size();
902 m_map_edit_event_counter->increment(event_count);
904 // We'll log the amount of each
907 std::unordered_set<v3s16> node_meta_updates;
909 while (!m_unsent_map_edit_queue.empty()) {
910 MapEditEvent* event = m_unsent_map_edit_queue.front();
911 m_unsent_map_edit_queue.pop();
913 // Players far away from the change are stored here.
914 // Instead of sending the changes, MapBlocks are set not sent
916 std::unordered_set<u16> far_players;
918 switch (event->type) {
921 prof.add("MEET_ADDNODE", 1);
922 sendAddNode(event->p, event->n, &far_players,
923 disable_single_change_sending ? 5 : 30,
924 event->type == MEET_ADDNODE);
926 case MEET_REMOVENODE:
927 prof.add("MEET_REMOVENODE", 1);
928 sendRemoveNode(event->p, &far_players,
929 disable_single_change_sending ? 5 : 30);
931 case MEET_BLOCK_NODE_METADATA_CHANGED: {
932 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
933 if (!event->is_private_change) {
934 node_meta_updates.emplace(event->p);
937 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
938 getNodeBlockPos(event->p))) {
939 block->raiseModified(MOD_STATE_WRITE_NEEDED,
940 MOD_REASON_REPORT_META_CHANGE);
945 prof.add("MEET_OTHER", 1);
946 for (const v3s16 &modified_block : event->modified_blocks) {
947 m_clients.markBlockposAsNotSent(modified_block);
951 prof.add("unknown", 1);
952 warningstream << "Server: Unknown MapEditEvent "
953 << ((u32)event->type) << std::endl;
958 Set blocks not sent to far players
960 if (!far_players.empty()) {
961 // Convert list format to that wanted by SetBlocksNotSent
962 std::map<v3s16, MapBlock*> modified_blocks2;
963 for (const v3s16 &modified_block : event->modified_blocks) {
964 modified_blocks2[modified_block] =
965 m_env->getMap().getBlockNoCreateNoEx(modified_block);
968 // Set blocks not sent
969 for (const u16 far_player : far_players) {
970 if (RemoteClient *client = getClient(far_player))
971 client->SetBlocksNotSent(modified_blocks2);
978 if (event_count >= 5) {
979 infostream << "Server: MapEditEvents:" << std::endl;
980 prof.print(infostream);
981 } else if (event_count != 0) {
982 verbosestream << "Server: MapEditEvents:" << std::endl;
983 prof.print(verbosestream);
986 // Send all metadata updates
987 if (!node_meta_updates.empty())
988 sendMetadataChanged(node_meta_updates);
992 Trigger emerge thread
993 Doing this every 2s is left over from old code, unclear if this is still needed.
996 float &counter = m_emergethread_trigger_timer;
998 if (counter <= 0.0f) {
1001 m_emerge->startThreads();
1005 // Save map, players and auth stuff
1007 float &counter = m_savemap_timer;
1009 static thread_local const float save_interval =
1010 g_settings->getFloat("server_map_save_interval");
1011 if (counter >= save_interval) {
1013 MutexAutoLock lock(m_env_mutex);
1015 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
1018 if (m_banmanager->isModified()) {
1019 m_banmanager->save();
1022 // Save changed parts of map
1023 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1026 m_env->saveLoadedPlayers();
1028 // Save environment metadata
1033 m_shutdown_state.tick(dtime, this);
1036 void Server::Receive()
1046 In the first iteration *wait* for a packet, afterwards process
1047 all packets that are immediately available (no waiting).
1050 m_con->Receive(&pkt);
1053 if (!m_con->TryReceive(&pkt))
1057 peer_id = pkt.getPeerId();
1058 m_packet_recv_counter->increment();
1060 m_packet_recv_processed_counter->increment();
1061 } catch (const con::InvalidIncomingDataException &e) {
1062 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1063 << e.what() << std::endl;
1064 } catch (const SerializationError &e) {
1065 infostream << "Server::Receive(): SerializationError: what()="
1066 << e.what() << std::endl;
1067 } catch (const ClientStateError &e) {
1068 errorstream << "ProcessData: peer=" << peer_id << " what()="
1069 << e.what() << std::endl;
1070 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1071 } catch (const con::PeerNotFoundException &e) {
1073 } catch (const con::NoIncomingDataException &e) {
1079 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1081 std::string playername;
1082 PlayerSAO *playersao = NULL;
1084 ClientInterface::AutoLock clientlock(m_clients);
1085 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1087 playername = client->getName();
1088 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1092 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1094 // If failed, cancel
1095 if (!playersao || !player) {
1096 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1097 actionstream << "Server: Failed to emerge player \"" << playername
1098 << "\" (player allocated to another client)" << std::endl;
1099 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1101 errorstream << "Server: " << playername << ": Failed to emerge player"
1103 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1109 Send complete position information
1111 SendMovePlayer(peer_id);
1114 SendPlayerPrivileges(peer_id);
1116 // Send inventory formspec
1117 SendPlayerInventoryFormspec(peer_id);
1120 SendInventory(playersao, false);
1123 SendPlayerHP(playersao, false);
1125 // Send death screen
1126 if (playersao->isDead())
1127 SendDeathscreen(peer_id, false, v3f(0,0,0));
1130 SendPlayerBreath(playersao);
1133 Update player list and print action
1136 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1137 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1138 m_clients.sendToAll(¬ice_pkt);
1141 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1142 const auto &names = m_clients.getPlayerNames();
1144 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1145 for (const std::string &name : names)
1146 actionstream << name << " ";
1147 actionstream << player->getName() << std::endl;
1152 inline void Server::handleCommand(NetworkPacket *pkt)
1154 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1155 (this->*opHandle.handler)(pkt);
1158 void Server::ProcessData(NetworkPacket *pkt)
1160 // Environment is locked first.
1161 MutexAutoLock envlock(m_env_mutex);
1163 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1164 u32 peer_id = pkt->getPeerId();
1167 Address address = getPeerAddress(peer_id);
1168 std::string addr_s = address.serializeString();
1170 // FIXME: Isn't it a bit excessive to check this for every packet?
1171 if (m_banmanager->isIpBanned(addr_s)) {
1172 std::string ban_name = m_banmanager->getBanName(addr_s);
1173 infostream << "Server: A banned client tried to connect from "
1174 << addr_s << "; banned name was " << ban_name << std::endl;
1175 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1176 "Your IP is banned. Banned name was " + ban_name);
1179 } catch (con::PeerNotFoundException &e) {
1181 * no peer for this packet found
1182 * most common reason is peer timeout, e.g. peer didn't
1183 * respond for some time, your server was overloaded or
1186 infostream << "Server::ProcessData(): Canceling: peer "
1187 << peer_id << " not found" << std::endl;
1192 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1194 // Command must be handled into ToServerCommandHandler
1195 if (command >= TOSERVER_NUM_MSG_TYPES) {
1196 infostream << "Server: Ignoring unknown command "
1197 << command << std::endl;
1201 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1206 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1208 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1209 errorstream << "Server::ProcessData(): Cancelling: Peer"
1210 " serialization format invalid or not initialized."
1211 " Skipping incoming command=" << command << std::endl;
1215 /* Handle commands related to client startup */
1216 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1221 if (m_clients.getClientState(peer_id) < CS_Active) {
1222 if (command == TOSERVER_PLAYERPOS) return;
1224 errorstream << "Got packet command: " << command << " for peer id "
1225 << peer_id << " but client isn't active yet. Dropping packet "
1231 } catch (SendFailedException &e) {
1232 errorstream << "Server::ProcessData(): SendFailedException: "
1233 << "what=" << e.what()
1235 } catch (PacketError &e) {
1236 actionstream << "Server::ProcessData(): PacketError: "
1237 << "what=" << e.what()
1242 void Server::setTimeOfDay(u32 time)
1244 m_env->setTimeOfDay(time);
1245 m_time_of_day_send_timer = 0;
1248 void Server::onMapEditEvent(const MapEditEvent &event)
1250 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1253 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1256 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1258 std::vector<session_t> clients = m_clients.getClientIDs();
1259 ClientInterface::AutoLock clientlock(m_clients);
1260 // Set the modified blocks unsent for all the clients
1261 for (const session_t client_id : clients) {
1262 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1263 client->SetBlocksNotSent(block);
1267 void Server::peerAdded(con::Peer *peer)
1269 verbosestream<<"Server::peerAdded(): peer->id="
1270 <<peer->id<<std::endl;
1272 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1275 void Server::deletingPeer(con::Peer *peer, bool timeout)
1277 verbosestream<<"Server::deletingPeer(): peer->id="
1278 <<peer->id<<", timeout="<<timeout<<std::endl;
1280 m_clients.event(peer->id, CSE_Disconnect);
1281 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1284 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1286 *retval = m_con->getPeerStat(peer_id,type);
1287 return *retval != -1;
1290 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1292 ClientInterface::AutoLock clientlock(m_clients);
1293 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1298 ret.state = client->getState();
1299 ret.addr = client->getAddress();
1300 ret.uptime = client->uptime();
1301 ret.ser_vers = client->serialization_version;
1302 ret.prot_vers = client->net_proto_version;
1304 ret.major = client->getMajor();
1305 ret.minor = client->getMinor();
1306 ret.patch = client->getPatch();
1307 ret.vers_string = client->getFullVer();
1309 ret.lang_code = client->getLangCode();
1314 void Server::handlePeerChanges()
1316 while(!m_peer_change_queue.empty())
1318 con::PeerChange c = m_peer_change_queue.front();
1319 m_peer_change_queue.pop();
1321 verbosestream<<"Server: Handling peer change: "
1322 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1327 case con::PEER_ADDED:
1328 m_clients.CreateClient(c.peer_id);
1331 case con::PEER_REMOVED:
1332 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1336 FATAL_ERROR("Invalid peer change event received!");
1342 void Server::printToConsoleOnly(const std::string &text)
1345 m_admin_chat->outgoing_queue.push_back(
1346 new ChatEventChat("", utf8_to_wide(text)));
1348 std::cout << text << std::endl;
1352 void Server::Send(NetworkPacket *pkt)
1354 Send(pkt->getPeerId(), pkt);
1357 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1359 m_clients.send(peer_id,
1360 clientCommandFactoryTable[pkt->getCommand()].channel,
1362 clientCommandFactoryTable[pkt->getCommand()].reliable);
1365 void Server::SendMovement(session_t peer_id)
1367 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1369 pkt << g_settings->getFloat("movement_acceleration_default");
1370 pkt << g_settings->getFloat("movement_acceleration_air");
1371 pkt << g_settings->getFloat("movement_acceleration_fast");
1372 pkt << g_settings->getFloat("movement_speed_walk");
1373 pkt << g_settings->getFloat("movement_speed_crouch");
1374 pkt << g_settings->getFloat("movement_speed_fast");
1375 pkt << g_settings->getFloat("movement_speed_climb");
1376 pkt << g_settings->getFloat("movement_speed_jump");
1377 pkt << g_settings->getFloat("movement_liquid_fluidity");
1378 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1379 pkt << g_settings->getFloat("movement_liquid_sink");
1380 pkt << g_settings->getFloat("movement_gravity");
1385 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1387 m_script->player_event(playersao, "health_changed");
1388 SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX);
1390 // Send to other clients
1391 playersao->sendPunchCommand();
1393 if (playersao->isDead())
1394 HandlePlayerDeath(playersao, reason);
1397 void Server::SendPlayerHP(PlayerSAO *playersao, bool effect)
1399 SendHP(playersao->getPeerID(), playersao->getHP(), effect);
1402 void Server::SendHP(session_t peer_id, u16 hp, bool effect)
1404 NetworkPacket pkt(TOCLIENT_HP, 3, peer_id);
1405 pkt << hp << effect;
1409 void Server::SendBreath(session_t peer_id, u16 breath)
1411 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1412 pkt << (u16) breath;
1416 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1417 const std::string &custom_reason, bool reconnect)
1419 assert(reason < SERVER_ACCESSDENIED_MAX);
1421 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1423 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1424 pkt << custom_reason;
1425 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1426 reason == SERVER_ACCESSDENIED_CRASH)
1427 pkt << custom_reason << (u8)reconnect;
1431 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1432 v3f camera_point_target)
1434 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1435 pkt << set_camera_point_target << camera_point_target;
1439 void Server::SendItemDef(session_t peer_id,
1440 IItemDefManager *itemdef, u16 protocol_version)
1442 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1446 u32 length of the next item
1447 zlib-compressed serialized ItemDefManager
1449 std::ostringstream tmp_os(std::ios::binary);
1450 itemdef->serialize(tmp_os, protocol_version);
1451 std::ostringstream tmp_os2(std::ios::binary);
1452 compressZlib(tmp_os.str(), tmp_os2);
1453 pkt.putLongString(tmp_os2.str());
1456 verbosestream << "Server: Sending item definitions to id(" << peer_id
1457 << "): size=" << pkt.getSize() << std::endl;
1462 void Server::SendNodeDef(session_t peer_id,
1463 const NodeDefManager *nodedef, u16 protocol_version)
1465 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1469 u32 length of the next item
1470 zlib-compressed serialized NodeDefManager
1472 std::ostringstream tmp_os(std::ios::binary);
1473 nodedef->serialize(tmp_os, protocol_version);
1474 std::ostringstream tmp_os2(std::ios::binary);
1475 compressZlib(tmp_os.str(), tmp_os2);
1477 pkt.putLongString(tmp_os2.str());
1480 verbosestream << "Server: Sending node definitions to id(" << peer_id
1481 << "): size=" << pkt.getSize() << std::endl;
1487 Non-static send methods
1490 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1492 RemotePlayer *player = sao->getPlayer();
1494 // Do not send new format to old clients
1495 incremental &= player->protocol_version >= 38;
1497 UpdateCrafting(player);
1503 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1505 std::ostringstream os(std::ios::binary);
1506 sao->getInventory()->serialize(os, incremental);
1507 sao->getInventory()->setModified(false);
1508 player->setModified(true);
1510 const std::string &s = os.str();
1511 pkt.putRawString(s.c_str(), s.size());
1515 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1517 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1519 u8 type = message.type;
1520 pkt << version << type << message.sender << message.message
1521 << static_cast<u64>(message.timestamp);
1523 if (peer_id != PEER_ID_INEXISTENT) {
1524 RemotePlayer *player = m_env->getPlayer(peer_id);
1530 m_clients.sendToAll(&pkt);
1534 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1535 const std::string &formname)
1537 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1538 if (formspec.empty()){
1539 //the client should close the formspec
1540 //but make sure there wasn't another one open in meantime
1541 const auto it = m_formspec_state_data.find(peer_id);
1542 if (it != m_formspec_state_data.end() && it->second == formname) {
1543 m_formspec_state_data.erase(peer_id);
1545 pkt.putLongString("");
1547 m_formspec_state_data[peer_id] = formname;
1548 pkt.putLongString(formspec);
1555 // Spawns a particle on peer with peer_id
1556 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1557 const ParticleParameters &p)
1559 static thread_local const float radius =
1560 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1562 if (peer_id == PEER_ID_INEXISTENT) {
1563 std::vector<session_t> clients = m_clients.getClientIDs();
1564 const v3f pos = p.pos * BS;
1565 const float radius_sq = radius * radius;
1567 for (const session_t client_id : clients) {
1568 RemotePlayer *player = m_env->getPlayer(client_id);
1572 PlayerSAO *sao = player->getPlayerSAO();
1576 // Do not send to distant clients
1577 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1580 SendSpawnParticle(client_id, player->protocol_version, p);
1584 assert(protocol_version != 0);
1586 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1589 // NetworkPacket and iostreams are incompatible...
1590 std::ostringstream oss(std::ios_base::binary);
1591 p.serialize(oss, protocol_version);
1592 pkt.putRawString(oss.str());
1598 // Adds a ParticleSpawner on peer with peer_id
1599 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1600 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1602 static thread_local const float radius =
1603 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1605 if (peer_id == PEER_ID_INEXISTENT) {
1606 std::vector<session_t> clients = m_clients.getClientIDs();
1608 p.pos.start.min.val +
1609 p.pos.start.max.val +
1613 const float radius_sq = radius * radius;
1614 /* Don't send short-lived spawners to distant players.
1615 * This could be replaced with proper tracking at some point. */
1616 const bool distance_check = !attached_id && p.time <= 1.0f;
1618 for (const session_t client_id : clients) {
1619 RemotePlayer *player = m_env->getPlayer(client_id);
1623 if (distance_check) {
1624 PlayerSAO *sao = player->getPlayerSAO();
1627 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1631 SendAddParticleSpawner(client_id, player->protocol_version,
1632 p, attached_id, id);
1636 assert(protocol_version != 0);
1638 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1640 pkt << p.amount << p.time;
1641 { // serialize legacy fields
1642 std::ostringstream os(std::ios_base::binary);
1643 p.pos.start.legacySerialize(os);
1644 p.vel.start.legacySerialize(os);
1645 p.acc.start.legacySerialize(os);
1646 p.exptime.start.legacySerialize(os);
1647 p.size.start.legacySerialize(os);
1648 pkt.putRawString(os.str());
1650 pkt << p.collisiondetection;
1652 pkt.putLongString(p.texture.string);
1654 pkt << id << p.vertical << p.collision_removal << attached_id;
1656 std::ostringstream os(std::ios_base::binary);
1657 p.animation.serialize(os, protocol_version);
1658 pkt.putRawString(os.str());
1660 pkt << p.glow << p.object_collision;
1661 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1663 { // serialize new fields
1664 // initial bias for older properties
1665 pkt << p.pos.start.bias
1668 << p.exptime.start.bias
1669 << p.size.start.bias;
1671 std::ostringstream os(std::ios_base::binary);
1673 // final tween frames of older properties
1674 p.pos.end.serialize(os);
1675 p.vel.end.serialize(os);
1676 p.acc.end.serialize(os);
1677 p.exptime.end.serialize(os);
1678 p.size.end.serialize(os);
1680 // properties for legacy texture field
1681 p.texture.serialize(os, protocol_version, true);
1684 p.drag.serialize(os);
1685 p.jitter.serialize(os);
1686 p.bounce.serialize(os);
1687 ParticleParamTypes::serializeParameterValue(os, p.attractor_kind);
1688 if (p.attractor_kind != ParticleParamTypes::AttractorKind::none) {
1689 p.attract.serialize(os);
1690 p.attractor_origin.serialize(os);
1691 writeU16(os, p.attractor_attachment); /* object ID */
1692 writeU8(os, p.attractor_kill);
1693 if (p.attractor_kind != ParticleParamTypes::AttractorKind::point) {
1694 p.attractor_direction.serialize(os);
1695 writeU16(os, p.attractor_direction_attachment);
1698 p.radius.serialize(os);
1700 ParticleParamTypes::serializeParameterValue(os, (u16)p.texpool.size());
1701 for (const auto& tex : p.texpool) {
1702 tex.serialize(os, protocol_version);
1705 pkt.putRawString(os.str());
1711 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1713 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1717 if (peer_id != PEER_ID_INEXISTENT)
1720 m_clients.sendToAll(&pkt);
1724 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1726 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1728 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1729 << form->text << form->number << form->item << form->dir
1730 << form->align << form->offset << form->world_pos << form->size
1731 << form->z_index << form->text2 << form->style;
1736 void Server::SendHUDRemove(session_t peer_id, u32 id)
1738 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1743 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1745 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1746 pkt << id << (u8) stat;
1750 case HUD_STAT_SCALE:
1751 case HUD_STAT_ALIGN:
1752 case HUD_STAT_OFFSET:
1753 pkt << *(v2f *) value;
1757 case HUD_STAT_TEXT2:
1758 pkt << *(std::string *) value;
1760 case HUD_STAT_WORLD_POS:
1761 pkt << *(v3f *) value;
1764 pkt << *(v2s32 *) value;
1766 default: // all other types
1767 pkt << *(u32 *) value;
1774 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1776 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1778 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1780 pkt << flags << mask;
1785 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1787 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1788 pkt << param << value;
1792 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1794 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1796 // Handle prior clients here
1797 if (m_clients.getProtocolVersion(peer_id) < 39) {
1798 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1800 for (const std::string& texture : params.textures)
1803 pkt << params.clouds;
1804 } else { // Handle current clients and future clients
1805 pkt << params.bgcolor << params.type
1806 << params.clouds << params.fog_sun_tint
1807 << params.fog_moon_tint << params.fog_tint_type;
1809 if (params.type == "skybox") {
1810 pkt << (u16) params.textures.size();
1811 for (const std::string &texture : params.textures)
1813 } else if (params.type == "regular") {
1814 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1815 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1816 << params.sky_color.night_sky << params.sky_color.night_horizon
1817 << params.sky_color.indoors;
1824 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1826 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1827 pkt << params.visible << params.texture
1828 << params.tonemap << params.sunrise
1829 << params.sunrise_visible << params.scale;
1833 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1835 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1837 pkt << params.visible << params.texture
1838 << params.tonemap << params.scale;
1842 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1844 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1846 pkt << params.visible << params.count
1847 << params.starcolor << params.scale
1848 << params.day_opacity;
1853 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1855 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1856 pkt << params.density << params.color_bright << params.color_ambient
1857 << params.height << params.thickness << params.speed;
1861 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1864 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1867 pkt << do_override << (u16) (ratio * 65535);
1872 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1874 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1877 pkt << lighting.shadow_intensity;
1882 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1884 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1885 pkt << time << time_speed;
1887 if (peer_id == PEER_ID_INEXISTENT) {
1888 m_clients.sendToAll(&pkt);
1895 void Server::SendPlayerBreath(PlayerSAO *sao)
1899 m_script->player_event(sao, "breath_changed");
1900 SendBreath(sao->getPeerID(), sao->getBreath());
1903 void Server::SendMovePlayer(session_t peer_id)
1905 RemotePlayer *player = m_env->getPlayer(peer_id);
1907 PlayerSAO *sao = player->getPlayerSAO();
1910 // Send attachment updates instantly to the client prior updating position
1911 sao->sendOutdatedData();
1913 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1914 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1917 v3f pos = sao->getBasePosition();
1918 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1919 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1920 << " pitch=" << sao->getLookPitch()
1921 << " yaw=" << sao->getRotation().Y
1928 void Server::SendPlayerFov(session_t peer_id)
1930 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1932 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1933 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1938 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1939 f32 animation_speed)
1941 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1944 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1945 << animation_frames[3] << animation_speed;
1950 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1952 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1953 pkt << first << third;
1957 void Server::SendPlayerPrivileges(session_t peer_id)
1959 RemotePlayer *player = m_env->getPlayer(peer_id);
1961 if(player->getPeerId() == PEER_ID_INEXISTENT)
1964 std::set<std::string> privs;
1965 m_script->getAuth(player->getName(), NULL, &privs);
1967 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1968 pkt << (u16) privs.size();
1970 for (const std::string &priv : privs) {
1977 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1979 RemotePlayer *player = m_env->getPlayer(peer_id);
1981 if (player->getPeerId() == PEER_ID_INEXISTENT)
1984 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1985 pkt.putLongString(player->inventory_formspec);
1990 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1992 RemotePlayer *player = m_env->getPlayer(peer_id);
1994 if (player->getPeerId() == PEER_ID_INEXISTENT)
1997 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1998 pkt << player->formspec_prepend;
2002 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
2004 // Radius inside which objects are active
2005 static thread_local const s16 radius =
2006 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
2008 // Radius inside which players are active
2009 static thread_local const bool is_transfer_limited =
2010 g_settings->exists("unlimited_player_transfer_distance") &&
2011 !g_settings->getBool("unlimited_player_transfer_distance");
2013 static thread_local const s16 player_transfer_dist =
2014 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
2016 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
2017 radius : player_transfer_dist;
2019 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
2023 std::queue<u16> removed_objects, added_objects;
2024 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
2025 client->m_known_objects, removed_objects);
2026 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
2027 client->m_known_objects, added_objects);
2029 int removed_count = removed_objects.size();
2030 int added_count = added_objects.size();
2032 if (removed_objects.empty() && added_objects.empty())
2038 // Handle removed objects
2039 writeU16((u8*)buf, removed_objects.size());
2040 data.append(buf, 2);
2041 while (!removed_objects.empty()) {
2043 u16 id = removed_objects.front();
2044 ServerActiveObject* obj = m_env->getActiveObject(id);
2046 // Add to data buffer for sending
2047 writeU16((u8*)buf, id);
2048 data.append(buf, 2);
2050 // Remove from known objects
2051 client->m_known_objects.erase(id);
2053 if (obj && obj->m_known_by_count > 0)
2054 obj->m_known_by_count--;
2056 removed_objects.pop();
2059 // Handle added objects
2060 writeU16((u8*)buf, added_objects.size());
2061 data.append(buf, 2);
2062 while (!added_objects.empty()) {
2064 u16 id = added_objects.front();
2065 ServerActiveObject *obj = m_env->getActiveObject(id);
2066 added_objects.pop();
2069 warningstream << FUNCTION_NAME << ": NULL object id="
2070 << (int)id << std::endl;
2075 u8 type = obj->getSendType();
2077 // Add to data buffer for sending
2078 writeU16((u8*)buf, id);
2079 data.append(buf, 2);
2080 writeU8((u8*)buf, type);
2081 data.append(buf, 1);
2083 data.append(serializeString32(
2084 obj->getClientInitializationData(client->net_proto_version)));
2086 // Add to known objects
2087 client->m_known_objects.insert(id);
2089 obj->m_known_by_count++;
2092 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2093 pkt.putRawString(data.c_str(), data.size());
2096 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2097 << removed_count << " removed, " << added_count << " added, "
2098 << "packet size is " << pkt.getSize() << std::endl;
2101 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2104 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2105 datas.size(), peer_id);
2107 pkt.putRawString(datas.c_str(), datas.size());
2109 m_clients.send(pkt.getPeerId(),
2110 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2114 void Server::SendCSMRestrictionFlags(session_t peer_id)
2116 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2117 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2118 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2122 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2124 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2129 inline s32 Server::nextSoundId()
2131 s32 ret = m_next_sound_id;
2132 if (m_next_sound_id == INT32_MAX)
2133 m_next_sound_id = 0; // signed overflow is undefined
2139 s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
2141 // Find out initial position of sound
2142 bool pos_exists = false;
2143 const v3f pos = params.getPos(m_env, &pos_exists);
2144 // If position is not found while it should be, cancel sound
2145 if(pos_exists != (params.type != SoundLocation::Local))
2148 // Filter destination clients
2149 std::vector<session_t> dst_clients;
2150 if (!params.to_player.empty()) {
2151 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2153 infostream<<"Server::playSound: Player \""<<params.to_player
2154 <<"\" not found"<<std::endl;
2157 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2158 infostream<<"Server::playSound: Player \""<<params.to_player
2159 <<"\" not connected"<<std::endl;
2162 dst_clients.push_back(player->getPeerId());
2164 std::vector<session_t> clients = m_clients.getClientIDs();
2166 for (const session_t client_id : clients) {
2167 RemotePlayer *player = m_env->getPlayer(client_id);
2170 if (!params.exclude_player.empty() &&
2171 params.exclude_player == player->getName())
2174 PlayerSAO *sao = player->getPlayerSAO();
2179 if(sao->getBasePosition().getDistanceFrom(pos) >
2180 params.max_hear_distance)
2183 dst_clients.push_back(client_id);
2187 if(dst_clients.empty())
2190 // old clients will still use this, so pick a reserved ID (-1)
2191 const s32 id = ephemeral ? -1 : nextSoundId();
2193 float gain = params.gain * params.spec.gain;
2194 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2195 pkt << id << params.spec.name << gain
2196 << (u8) params.type << pos << params.object
2197 << params.spec.loop << params.spec.fade << params.spec.pitch
2200 bool as_reliable = !ephemeral;
2202 for (const session_t peer_id : dst_clients) {
2204 params.clients.insert(peer_id);
2205 m_clients.send(peer_id, 0, &pkt, as_reliable);
2209 m_playing_sounds[id] = std::move(params);
2212 void Server::stopSound(s32 handle)
2214 auto it = m_playing_sounds.find(handle);
2215 if (it == m_playing_sounds.end())
2218 ServerPlayingSound &psound = it->second;
2220 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2223 for (session_t peer_id : psound.clients) {
2225 m_clients.send(peer_id, 0, &pkt, true);
2228 // Remove sound reference
2229 m_playing_sounds.erase(it);
2232 void Server::fadeSound(s32 handle, float step, float gain)
2234 auto it = m_playing_sounds.find(handle);
2235 if (it == m_playing_sounds.end())
2238 ServerPlayingSound &psound = it->second;
2239 psound.gain = gain; // destination gain
2241 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2242 pkt << handle << step << gain;
2244 for (session_t peer_id : psound.clients) {
2246 m_clients.send(peer_id, 0, &pkt, true);
2249 // Remove sound reference
2250 if (gain <= 0 || psound.clients.empty())
2251 m_playing_sounds.erase(it);
2254 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2257 v3f p_f = intToFloat(p, BS);
2258 v3s16 block_pos = getNodeBlockPos(p);
2260 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2263 sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2266 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2267 float far_d_nodes, bool remove_metadata)
2269 v3f p_f = intToFloat(p, BS);
2270 v3s16 block_pos = getNodeBlockPos(p);
2272 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2273 pkt << p << n.param0 << n.param1 << n.param2
2274 << (u8) (remove_metadata ? 0 : 1);
2275 sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2278 void Server::sendNodeChangePkt(NetworkPacket &pkt, v3s16 block_pos,
2279 v3f p, float far_d_nodes, std::unordered_set<u16> *far_players)
2281 float maxd = far_d_nodes * BS;
2282 std::vector<session_t> clients = m_clients.getClientIDs();
2283 ClientInterface::AutoLock clientlock(m_clients);
2285 for (session_t client_id : clients) {
2286 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2290 RemotePlayer *player = m_env->getPlayer(client_id);
2291 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2293 // If player is far away, only set modified blocks not sent
2294 if (!client->isBlockSent(block_pos) || (sao &&
2295 sao->getBasePosition().getDistanceFrom(p) > maxd)) {
2297 far_players->emplace(client_id);
2299 client->SetBlockNotSent(block_pos);
2304 m_clients.send(client_id, 0, &pkt, true);
2308 void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
2310 NodeMetadataList meta_updates_list(false);
2311 std::ostringstream os(std::ios::binary);
2313 std::vector<session_t> clients = m_clients.getClientIDs();
2314 ClientInterface::AutoLock clientlock(m_clients);
2316 for (session_t i : clients) {
2317 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2321 ServerActiveObject *player = getPlayerSAO(i);
2324 player_pos = floatToInt(player->getBasePosition(), BS);
2326 for (const v3s16 pos : positions) {
2327 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2332 v3s16 block_pos = getNodeBlockPos(pos);
2333 if (!client->isBlockSent(block_pos) ||
2334 player_pos.getDistanceFrom(pos) > far_d_nodes) {
2335 client->SetBlockNotSent(block_pos);
2339 // Add the change to send list
2340 meta_updates_list.set(pos, meta);
2342 if (meta_updates_list.size() == 0)
2345 // Send the meta changes
2347 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2348 std::string raw = os.str();
2350 compressZlib(raw, os);
2352 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
2353 pkt.putLongString(os.str());
2356 meta_updates_list.clear();
2360 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2361 u16 net_proto_version, SerializedBlockCache *cache)
2363 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2364 std::string s, *sptr = nullptr;
2367 auto it = cache->find({block->getPos(), ver});
2368 if (it != cache->end())
2372 // Serialize the block in the right format
2374 std::ostringstream os(std::ios_base::binary);
2375 block->serialize(os, ver, false, net_compression_level);
2376 block->serializeNetworkSpecific(os);
2381 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2382 pkt << block->getPos();
2383 pkt.putRawString(*sptr);
2386 // Store away in cache
2387 if (cache && sptr == &s)
2388 (*cache)[{block->getPos(), ver}] = std::move(s);
2391 void Server::SendBlocks(float dtime)
2393 MutexAutoLock envlock(m_env_mutex);
2394 //TODO check if one big lock could be faster then multiple small ones
2396 std::vector<PrioritySortedBlockTransfer> queue;
2398 u32 total_sending = 0, unique_clients = 0;
2401 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2403 std::vector<session_t> clients = m_clients.getClientIDs();
2405 ClientInterface::AutoLock clientlock(m_clients);
2406 for (const session_t client_id : clients) {
2407 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2412 total_sending += client->getSendingCount();
2413 const auto old_count = queue.size();
2414 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2415 unique_clients += queue.size() > old_count ? 1 : 0;
2420 // Lowest priority number comes first.
2421 // Lowest is most important.
2422 std::sort(queue.begin(), queue.end());
2424 ClientInterface::AutoLock clientlock(m_clients);
2426 // Maximal total count calculation
2427 // The per-client block sends is halved with the maximal online users
2428 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2429 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2431 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2432 Map &map = m_env->getMap();
2434 SerializedBlockCache cache, *cache_ptr = nullptr;
2435 if (unique_clients > 1) {
2436 // caching is pointless with a single client
2440 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2441 if (total_sending >= max_blocks_to_send)
2444 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2448 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2453 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2454 client->net_proto_version, cache_ptr);
2456 client->SentBlock(block_to_send.pos);
2461 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2463 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2467 ClientInterface::AutoLock clientlock(m_clients);
2468 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2469 if (!client || client->isBlockSent(blockpos))
2471 SendBlockNoLock(peer_id, block, client->serialization_version,
2472 client->net_proto_version);
2477 bool Server::addMediaFile(const std::string &filename,
2478 const std::string &filepath, std::string *filedata_to,
2479 std::string *digest_to)
2481 // If name contains illegal characters, ignore the file
2482 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2483 infostream << "Server: ignoring illegal file name: \""
2484 << filename << "\"" << std::endl;
2487 // If name is not in a supported format, ignore it
2488 const char *supported_ext[] = {
2489 ".png", ".jpg", ".bmp", ".tga",
2491 ".x", ".b3d", ".obj",
2492 // Custom translation file format
2496 if (removeStringEnd(filename, supported_ext).empty()) {
2497 infostream << "Server: ignoring unsupported file extension: \""
2498 << filename << "\"" << std::endl;
2501 // Ok, attempt to load the file and add to cache
2504 std::string filedata;
2505 if (!fs::ReadFile(filepath, filedata)) {
2506 errorstream << "Server::addMediaFile(): Failed to open \""
2507 << filename << "\" for reading" << std::endl;
2511 if (filedata.empty()) {
2512 errorstream << "Server::addMediaFile(): Empty file \""
2513 << filepath << "\"" << std::endl;
2518 sha1.addBytes(filedata.c_str(), filedata.length());
2520 unsigned char *digest = sha1.getDigest();
2521 std::string sha1_base64 = base64_encode(digest, 20);
2522 std::string sha1_hex = hex_encode((char*) digest, 20);
2524 *digest_to = std::string((char*) digest, 20);
2528 m_media[filename] = MediaInfo(filepath, sha1_base64);
2529 verbosestream << "Server: " << sha1_hex << " is " << filename
2533 *filedata_to = std::move(filedata);
2537 void Server::fillMediaCache()
2539 infostream << "Server: Calculating media file checksums" << std::endl;
2541 // Collect all media file paths
2542 std::vector<std::string> paths;
2544 // ordered in descending priority
2545 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2546 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2547 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2548 m_modmgr->getModsMediaPaths(paths);
2550 // Collect media file information from paths into cache
2551 for (const std::string &mediapath : paths) {
2552 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2553 for (const fs::DirListNode &dln : dirlist) {
2554 if (dln.dir) // Ignore dirs (already in paths)
2557 const std::string &filename = dln.name;
2558 if (m_media.find(filename) != m_media.end()) // Do not override
2561 std::string filepath = mediapath;
2562 filepath.append(DIR_DELIM).append(filename);
2563 addMediaFile(filename, filepath);
2567 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2570 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2573 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2576 std::string lang_suffix;
2577 lang_suffix.append(".").append(lang_code).append(".tr");
2578 for (const auto &i : m_media) {
2579 if (i.second.no_announce)
2581 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2588 for (const auto &i : m_media) {
2589 if (i.second.no_announce)
2591 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2593 pkt << i.first << i.second.sha1_digest;
2596 pkt << g_settings->get("remote_media");
2599 verbosestream << "Server: Announcing files to id(" << peer_id
2600 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2603 struct SendableMedia
2609 SendableMedia(const std::string &name, const std::string &path,
2610 std::string &&data):
2611 name(name), path(path), data(std::move(data))
2615 void Server::sendRequestedMedia(session_t peer_id,
2616 const std::vector<std::string> &tosend)
2618 verbosestream<<"Server::sendRequestedMedia(): "
2619 <<"Sending files to client"<<std::endl;
2623 // Put 5kB in one bunch (this is not accurate)
2624 u32 bytes_per_bunch = 5000;
2626 std::vector< std::vector<SendableMedia> > file_bunches;
2627 file_bunches.emplace_back();
2629 u32 file_size_bunch_total = 0;
2631 for (const std::string &name : tosend) {
2632 if (m_media.find(name) == m_media.end()) {
2633 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2634 <<"unknown file \""<<(name)<<"\""<<std::endl;
2638 const auto &m = m_media[name];
2642 if (!fs::ReadFile(m.path, data)) {
2643 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2644 << name << "\"" << std::endl;
2647 file_size_bunch_total += data.size();
2650 file_bunches.back().emplace_back(name, m.path, std::move(data));
2652 // Start next bunch if got enough data
2653 if(file_size_bunch_total >= bytes_per_bunch) {
2654 file_bunches.emplace_back();
2655 file_size_bunch_total = 0;
2660 /* Create and send packets */
2662 u16 num_bunches = file_bunches.size();
2663 for (u16 i = 0; i < num_bunches; i++) {
2666 u16 total number of texture bunches
2667 u16 index of this bunch
2668 u32 number of files in this bunch
2677 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2678 pkt << num_bunches << i << (u32) file_bunches[i].size();
2680 for (const SendableMedia &j : file_bunches[i]) {
2682 pkt.putLongString(j.data);
2685 verbosestream << "Server::sendRequestedMedia(): bunch "
2686 << i << "/" << num_bunches
2687 << " files=" << file_bunches[i].size()
2688 << " size=" << pkt.getSize() << std::endl;
2693 void Server::stepPendingDynMediaCallbacks(float dtime)
2695 MutexAutoLock lock(m_env_mutex);
2697 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2698 it->second.expiry_timer -= dtime;
2699 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2706 const auto &name = it->second.filename;
2707 if (!name.empty()) {
2708 assert(m_media.count(name));
2709 // if no_announce isn't set we're definitely deleting the wrong file!
2710 sanity_check(m_media[name].no_announce);
2712 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2713 m_media.erase(name);
2715 getScriptIface()->freeDynamicMediaCallback(it->first);
2716 it = m_pending_dyn_media.erase(it);
2720 void Server::SendMinimapModes(session_t peer_id,
2721 std::vector<MinimapMode> &modes, size_t wanted_mode)
2723 RemotePlayer *player = m_env->getPlayer(peer_id);
2725 if (player->getPeerId() == PEER_ID_INEXISTENT)
2728 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2729 pkt << (u16)modes.size() << (u16)wanted_mode;
2731 for (auto &mode : modes)
2732 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2737 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2739 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2743 pkt << false; // Remove inventory
2745 pkt << true; // Update inventory
2747 // Serialization & NetworkPacket isn't a love story
2748 std::ostringstream os(std::ios_base::binary);
2749 inventory->serialize(os);
2750 inventory->setModified(false);
2752 const std::string &os_str = os.str();
2753 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2754 pkt.putRawString(os_str);
2757 if (peer_id == PEER_ID_INEXISTENT)
2758 m_clients.sendToAll(&pkt);
2763 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2765 // Lookup player name, to filter detached inventories just after
2766 std::string peer_name;
2767 if (peer_id != PEER_ID_INEXISTENT) {
2768 peer_name = getClient(peer_id, CS_Created)->getName();
2771 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2772 sendDetachedInventory(inv, name, peer_id);
2775 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2782 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2784 infostream << "Server::DiePlayer(): Player "
2785 << playersao->getPlayer()->getName()
2786 << " dies" << std::endl;
2788 playersao->clearParentAttachment();
2790 // Trigger scripted stuff
2791 m_script->on_dieplayer(playersao, reason);
2793 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2796 void Server::RespawnPlayer(session_t peer_id)
2798 PlayerSAO *playersao = getPlayerSAO(peer_id);
2801 infostream << "Server::RespawnPlayer(): Player "
2802 << playersao->getPlayer()->getName()
2803 << " respawns" << std::endl;
2805 const auto *prop = playersao->accessObjectProperties();
2806 playersao->setHP(prop->hp_max,
2807 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2808 playersao->setBreath(prop->breath_max);
2810 bool repositioned = m_script->on_respawnplayer(playersao);
2811 if (!repositioned) {
2812 // setPos will send the new position to client
2813 playersao->setPos(findSpawnPos());
2818 void Server::DenySudoAccess(session_t peer_id)
2820 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2825 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2826 const std::string &custom_reason, bool reconnect)
2828 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2829 m_clients.event(peer_id, CSE_SetDenied);
2830 DisconnectPeer(peer_id);
2833 void Server::DisconnectPeer(session_t peer_id)
2835 m_modchannel_mgr->leaveAllChannels(peer_id);
2836 m_con->DisconnectPeer(peer_id);
2839 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2842 RemoteClient* client = getClient(peer_id, CS_Invalid);
2844 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2846 // Right now, the auth mechs don't change between login and sudo mode.
2847 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2848 client->allowed_sudo_mechs = sudo_auth_mechs;
2850 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2851 << g_settings->getFloat("dedicated_server_step")
2855 m_clients.event(peer_id, CSE_AuthAccept);
2857 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2859 // We only support SRP right now
2860 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2862 resp_pkt << sudo_auth_mechs;
2864 m_clients.event(peer_id, CSE_SudoSuccess);
2868 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2870 std::wstring message;
2873 Clear references to playing sounds
2875 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2876 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2877 ServerPlayingSound &psound = i->second;
2878 psound.clients.erase(peer_id);
2879 if (psound.clients.empty())
2880 m_playing_sounds.erase(i++);
2885 // clear formspec info so the next client can't abuse the current state
2886 m_formspec_state_data.erase(peer_id);
2888 RemotePlayer *player = m_env->getPlayer(peer_id);
2890 /* Run scripts and remove from environment */
2892 PlayerSAO *playersao = player->getPlayerSAO();
2895 playersao->clearChildAttachments();
2896 playersao->clearParentAttachment();
2898 // inform connected clients
2899 const std::string &player_name = player->getName();
2900 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2901 // (u16) 1 + std::string represents a vector serialization representation
2902 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2903 m_clients.sendToAll(¬ice);
2905 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2907 playersao->disconnected();
2914 if (player && reason != CDR_DENY) {
2915 std::ostringstream os(std::ios_base::binary);
2916 std::vector<session_t> clients = m_clients.getClientIDs();
2918 for (const session_t client_id : clients) {
2920 RemotePlayer *player = m_env->getPlayer(client_id);
2924 // Get name of player
2925 os << player->getName() << " ";
2928 std::string name = player->getName();
2929 actionstream << name << " "
2930 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2931 << " List of players: " << os.str() << std::endl;
2933 m_admin_chat->outgoing_queue.push_back(
2934 new ChatEventNick(CET_NICK_REMOVE, name));
2938 MutexAutoLock env_lock(m_env_mutex);
2939 m_clients.DeleteClient(peer_id);
2943 // Send leave chat message to all remaining clients
2944 if (!message.empty()) {
2945 SendChatMessage(PEER_ID_INEXISTENT,
2946 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2950 void Server::UpdateCrafting(RemotePlayer *player)
2952 InventoryList *clist = player->inventory.getList("craft");
2953 if (!clist || clist->getSize() == 0)
2956 if (!clist->checkModified())
2959 // Get a preview for crafting
2961 InventoryLocation loc;
2962 loc.setPlayer(player->getName());
2963 std::vector<ItemStack> output_replacements;
2964 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2965 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2968 InventoryList *plist = player->inventory.getList("craftpreview");
2969 if (plist && plist->getSize() >= 1) {
2970 // Put the new preview in
2971 plist->changeItem(0, preview);
2975 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2977 if (evt->type == CET_NICK_ADD) {
2978 // The terminal informed us of its nick choice
2979 m_admin_nick = ((ChatEventNick *)evt)->nick;
2980 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2981 errorstream << "You haven't set up an account." << std::endl
2982 << "Please log in using the client as '"
2983 << m_admin_nick << "' with a secure password." << std::endl
2984 << "Until then, you can't execute admin tasks via the console," << std::endl
2985 << "and everybody can claim the user account instead of you," << std::endl
2986 << "giving them full control over this server." << std::endl;
2989 assert(evt->type == CET_CHAT);
2990 handleAdminChat((ChatEventChat *)evt);
2994 std::wstring Server::handleChat(const std::string &name,
2995 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2997 // If something goes wrong, this player is to blame
2998 RollbackScopeActor rollback_scope(m_rollback,
2999 std::string("player:") + name);
3001 if (g_settings->getBool("strip_color_codes"))
3002 wmessage = unescape_enriched(wmessage);
3005 switch (player->canSendChatMessage()) {
3006 case RPLAYER_CHATRESULT_FLOODING: {
3007 std::wstringstream ws;
3008 ws << L"You cannot send more messages. You are limited to "
3009 << g_settings->getFloat("chat_message_limit_per_10sec")
3010 << L" messages per 10 seconds.";
3013 case RPLAYER_CHATRESULT_KICK:
3014 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
3015 "You have been kicked due to message flooding.");
3017 case RPLAYER_CHATRESULT_OK:
3020 FATAL_ERROR("Unhandled chat filtering result found.");
3024 if (m_max_chatmessage_length > 0
3025 && wmessage.length() > m_max_chatmessage_length) {
3026 return L"Your message exceed the maximum chat message limit set on the server. "
3027 L"It was refused. Send a shorter message";
3030 auto message = trim(wide_to_utf8(wmessage));
3031 if (message.empty())
3034 if (message.find_first_of("\n\r") != std::wstring::npos) {
3035 return L"Newlines are not permitted in chat messages";
3038 // Run script hook, exit if script ate the chat message
3039 if (m_script->on_chat_message(name, message))
3044 // Whether to send line to the player that sent the message, or to all players
3045 bool broadcast_line = true;
3047 if (check_shout_priv && !checkPriv(name, "shout")) {
3048 line += L"-!- You don't have permission to shout.";
3049 broadcast_line = false;
3052 Workaround for fixing chat on Android. Lua doesn't handle
3053 the Cyrillic alphabet and some characters on older Android devices
3056 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3058 line += utf8_to_wide(m_script->formatChatMessage(name,
3059 wide_to_utf8(wmessage)));
3064 Tell calling method to send the message to sender
3066 if (!broadcast_line)
3070 Send the message to others
3072 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3074 ChatMessage chatmsg(line);
3076 std::vector<session_t> clients = m_clients.getClientIDs();
3077 for (u16 cid : clients)
3078 SendChatMessage(cid, chatmsg);
3083 void Server::handleAdminChat(const ChatEventChat *evt)
3085 std::string name = evt->nick;
3086 std::wstring wmessage = evt->evt_msg;
3088 std::wstring answer = handleChat(name, wmessage);
3090 // If asked to send answer to sender
3091 if (!answer.empty()) {
3092 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3096 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3098 RemoteClient *client = getClientNoEx(peer_id,state_min);
3100 throw ClientNotFoundException("Client not found");
3104 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3106 return m_clients.getClientNoEx(peer_id, state_min);
3109 std::string Server::getPlayerName(session_t peer_id)
3111 RemotePlayer *player = m_env->getPlayer(peer_id);
3113 return "[id="+itos(peer_id)+"]";
3114 return player->getName();
3117 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3119 RemotePlayer *player = m_env->getPlayer(peer_id);
3122 return player->getPlayerSAO();
3125 std::string Server::getStatusString()
3127 std::ostringstream os(std::ios_base::binary);
3130 os << "version: " << g_version_string;
3132 os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3134 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3136 os << " | max lag: " << std::setprecision(3);
3137 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3139 // Information about clients
3141 os << " | clients: ";
3143 std::vector<session_t> clients = m_clients.getClientIDs();
3144 for (session_t client_id : clients) {
3145 RemotePlayer *player = m_env->getPlayer(client_id);
3147 // Get name of player
3148 const char *name = player ? player->getName() : "<unknown>";
3150 // Add name to information string
3159 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3160 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3162 if (!g_settings->get("motd").empty())
3163 os << std::endl << "# Server: " << g_settings->get("motd");
3168 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3170 std::set<std::string> privs;
3171 m_script->getAuth(name, NULL, &privs);
3175 bool Server::checkPriv(const std::string &name, const std::string &priv)
3177 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3178 return (privs.count(priv) != 0);
3181 void Server::reportPrivsModified(const std::string &name)
3184 std::vector<session_t> clients = m_clients.getClientIDs();
3185 for (const session_t client_id : clients) {
3186 RemotePlayer *player = m_env->getPlayer(client_id);
3187 reportPrivsModified(player->getName());
3190 RemotePlayer *player = m_env->getPlayer(name.c_str());
3193 SendPlayerPrivileges(player->getPeerId());
3194 PlayerSAO *sao = player->getPlayerSAO();
3197 sao->updatePrivileges(
3198 getPlayerEffectivePrivs(name),
3203 void Server::reportInventoryFormspecModified(const std::string &name)
3205 RemotePlayer *player = m_env->getPlayer(name.c_str());
3208 SendPlayerInventoryFormspec(player->getPeerId());
3211 void Server::reportFormspecPrependModified(const std::string &name)
3213 RemotePlayer *player = m_env->getPlayer(name.c_str());
3216 SendPlayerFormspecPrepend(player->getPeerId());
3219 void Server::setIpBanned(const std::string &ip, const std::string &name)
3221 m_banmanager->add(ip, name);
3224 void Server::unsetIpBanned(const std::string &ip_or_name)
3226 m_banmanager->remove(ip_or_name);
3229 std::string Server::getBanDescription(const std::string &ip_or_name)
3231 return m_banmanager->getBanDescription(ip_or_name);
3234 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3236 // m_env will be NULL if the server is initializing
3240 if (m_admin_nick == name && !m_admin_nick.empty()) {
3241 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3244 RemotePlayer *player = m_env->getPlayer(name);
3249 if (player->getPeerId() == PEER_ID_INEXISTENT)
3252 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3255 bool Server::showFormspec(const char *playername, const std::string &formspec,
3256 const std::string &formname)
3258 // m_env will be NULL if the server is initializing
3262 RemotePlayer *player = m_env->getPlayer(playername);
3266 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3270 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3275 u32 id = player->addHud(form);
3277 SendHUDAdd(player->getPeerId(), id, form);
3282 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3286 HudElement* todel = player->removeHud(id);
3293 SendHUDRemove(player->getPeerId(), id);
3297 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3302 SendHUDChange(player->getPeerId(), id, stat, data);
3306 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3311 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3312 if (new_hud_flags == player->hud_flags) // no change
3315 SendHUDSetFlags(player->getPeerId(), flags, mask);
3316 player->hud_flags = new_hud_flags;
3318 PlayerSAO* playersao = player->getPlayerSAO();
3323 m_script->player_event(playersao, "hud_changed");
3327 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3332 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3335 player->setHotbarItemcount(hotbar_itemcount);
3336 std::ostringstream os(std::ios::binary);
3337 writeS32(os, hotbar_itemcount);
3338 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3342 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3347 player->setHotbarImage(name);
3348 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3351 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3356 player->setHotbarSelectedImage(name);
3357 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3360 Address Server::getPeerAddress(session_t peer_id)
3362 // Note that this is only set after Init was received in Server::handleCommand_Init
3363 return getClient(peer_id, CS_Invalid)->getAddress();
3366 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3367 v2s32 animation_frames[4], f32 frame_speed)
3369 sanity_check(player);
3370 player->setLocalAnimations(animation_frames, frame_speed);
3371 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3374 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3376 sanity_check(player);
3377 player->eye_offset_first = first;
3378 player->eye_offset_third = third;
3379 SendEyeOffset(player->getPeerId(), first, third);
3382 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3384 sanity_check(player);
3385 player->setSky(params);
3386 SendSetSky(player->getPeerId(), params);
3389 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3391 sanity_check(player);
3392 player->setSun(params);
3393 SendSetSun(player->getPeerId(), params);
3396 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3398 sanity_check(player);
3399 player->setMoon(params);
3400 SendSetMoon(player->getPeerId(), params);
3403 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3405 sanity_check(player);
3406 player->setStars(params);
3407 SendSetStars(player->getPeerId(), params);
3410 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3412 sanity_check(player);
3413 player->setCloudParams(params);
3414 SendCloudParams(player->getPeerId(), params);
3417 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3420 sanity_check(player);
3421 player->overrideDayNightRatio(do_override, ratio);
3422 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3425 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3427 sanity_check(player);
3428 player->setLighting(lighting);
3429 SendSetLighting(player->getPeerId(), lighting);
3432 void Server::notifyPlayers(const std::wstring &msg)
3434 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3437 void Server::spawnParticle(const std::string &playername,
3438 const ParticleParameters &p)
3440 // m_env will be NULL if the server is initializing
3444 session_t peer_id = PEER_ID_INEXISTENT;
3446 if (!playername.empty()) {
3447 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3450 peer_id = player->getPeerId();
3451 proto_ver = player->protocol_version;
3454 SendSpawnParticle(peer_id, proto_ver, p);
3457 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3458 ServerActiveObject *attached, const std::string &playername)
3460 // m_env will be NULL if the server is initializing
3464 session_t peer_id = PEER_ID_INEXISTENT;
3466 if (!playername.empty()) {
3467 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3470 peer_id = player->getPeerId();
3471 proto_ver = player->protocol_version;
3474 u16 attached_id = attached ? attached->getId() : 0;
3477 if (attached_id == 0)
3478 id = m_env->addParticleSpawner(p.time);
3480 id = m_env->addParticleSpawner(p.time, attached_id);
3482 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3486 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3488 // m_env will be NULL if the server is initializing
3490 throw ServerError("Can't delete particle spawners during initialisation!");
3492 session_t peer_id = PEER_ID_INEXISTENT;
3493 if (!playername.empty()) {
3494 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3497 peer_id = player->getPeerId();
3500 m_env->deleteParticleSpawner(id);
3501 SendDeleteParticleSpawner(peer_id, id);
3504 bool Server::dynamicAddMedia(std::string filepath,
3505 const u32 token, const std::string &to_player, bool ephemeral)
3507 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3508 auto it = m_media.find(filename);
3509 if (it != m_media.end()) {
3510 // Allow the same path to be "added" again in certain conditions
3511 if (ephemeral || it->second.path != filepath) {
3512 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3513 << "\" already exists in media cache" << std::endl;
3518 // Load the file and add it to our media cache
3519 std::string filedata, raw_hash;
3520 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3525 // Create a copy of the file and swap out the path, this removes the
3526 // requirement that mods keep the file accessible at the original path.
3527 filepath = fs::CreateTempFile();
3528 bool ok = ([&] () -> bool {
3529 if (filepath.empty())
3531 std::ofstream os(filepath.c_str(), std::ios::binary);
3539 errorstream << "Server: failed to create a copy of media file "
3540 << "\"" << filename << "\"" << std::endl;
3541 m_media.erase(filename);
3544 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3545 << filepath << std::endl;
3547 m_media[filename].path = filepath;
3548 m_media[filename].no_announce = true;
3549 // stepPendingDynMediaCallbacks will clean this up later.
3550 } else if (!to_player.empty()) {
3551 m_media[filename].no_announce = true;
3554 // Push file to existing clients
3555 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3556 pkt << raw_hash << filename << (bool)ephemeral;
3558 NetworkPacket legacy_pkt = pkt;
3560 // Newer clients get asked to fetch the file (asynchronous)
3562 // Older clients have an awful hack that just throws the data at them
3563 legacy_pkt.putLongString(filedata);
3565 std::unordered_set<session_t> delivered, waiting;
3567 ClientInterface::AutoLock clientlock(m_clients);
3568 for (auto &pair : m_clients.getClientList()) {
3569 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3571 If a client is in the DefinitionsSent state it is too late to
3572 transfer the file via sendMediaAnnouncement() but at the same
3573 time the client cannot accept a media push yet.
3574 Short of artificially delaying the joining process there is no
3575 way for the server to resolve this so we (currently) opt not to.
3577 warningstream << "The media \"" << filename << "\" (dynamic) could "
3578 "not be delivered to " << pair.second->getName()
3579 << " due to a race condition." << std::endl;
3582 if (pair.second->getState() < CS_Active)
3585 const auto proto_ver = pair.second->net_proto_version;
3589 const session_t peer_id = pair.second->peer_id;
3590 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3593 if (proto_ver < 40) {
3594 delivered.emplace(peer_id);
3596 The network layer only guarantees ordered delivery inside a channel.
3597 Since the very next packet could be one that uses the media, we have
3598 to push the media over ALL channels to ensure it is processed before
3599 it is used. In practice this means channels 1 and 0.
3601 m_clients.send(peer_id, 1, &legacy_pkt, true);
3602 m_clients.send(peer_id, 0, &legacy_pkt, true);
3604 waiting.emplace(peer_id);
3605 Send(peer_id, &pkt);
3610 // Run callback for players that already had the file delivered (legacy-only)
3611 for (session_t peer_id : delivered) {
3612 if (auto player = m_env->getPlayer(peer_id))
3613 getScriptIface()->on_dynamic_media_added(token, player->getName());
3616 // Save all others in our pending state
3617 auto &state = m_pending_dyn_media[token];
3618 state.waiting_players = std::move(waiting);
3619 // regardless of success throw away the callback after a while
3620 state.expiry_timer = 60.0f;
3622 state.filename = filename;
3627 // actions: time-reversed list
3628 // Return value: success/failure
3629 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3630 std::list<std::string> *log)
3632 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3633 ServerMap *map = (ServerMap*)(&m_env->getMap());
3635 // Fail if no actions to handle
3636 if (actions.empty()) {
3638 log->push_back("Nothing to do.");
3645 for (const RollbackAction &action : actions) {
3647 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3650 std::ostringstream os;
3651 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3652 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3654 log->push_back(os.str());
3656 std::ostringstream os;
3657 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3658 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3660 log->push_back(os.str());
3664 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3665 <<" failed"<<std::endl;
3667 // Call it done if less than half failed
3668 return num_failed <= num_tried/2;
3671 // IGameDef interface
3673 IItemDefManager *Server::getItemDefManager()
3678 const NodeDefManager *Server::getNodeDefManager()
3683 ICraftDefManager *Server::getCraftDefManager()
3688 u16 Server::allocateUnknownNodeId(const std::string &name)
3690 return m_nodedef->allocateDummy(name);
3693 IWritableItemDefManager *Server::getWritableItemDefManager()
3698 NodeDefManager *Server::getWritableNodeDefManager()
3703 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3708 const std::vector<ModSpec> & Server::getMods() const
3710 return m_modmgr->getMods();
3713 const ModSpec *Server::getModSpec(const std::string &modname) const
3715 return m_modmgr->getModSpec(modname);
3718 std::string Server::getBuiltinLuaPath()
3720 return porting::path_share + DIR_DELIM + "builtin";
3723 v3f Server::findSpawnPos()
3725 ServerMap &map = m_env->getServerMap();
3727 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3728 return nodeposf * BS;
3730 bool is_good = false;
3731 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3732 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3734 // Try to find a good place a few times
3735 for (s32 i = 0; i < 4000 && !is_good; i++) {
3736 s32 range = MYMIN(1 + i, range_max);
3737 // We're going to try to throw the player to this position
3738 v2s16 nodepos2d = v2s16(
3739 -range + myrand_range(0, range*2),
3740 -range + myrand_range(0, range*2));
3741 // Get spawn level at point
3742 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3743 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3744 // signify an unsuitable spawn position, or if outside limits.
3745 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3746 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3749 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3750 // Consecutive empty nodes
3753 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3754 // avoid obstructions in already-generated mapblocks.
3755 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3756 // no obstructions, but mapgen decorations are generated after spawn so
3757 // the player may end up inside one.
3758 for (s32 i = 0; i < 8; i++) {
3759 v3s16 blockpos = getNodeBlockPos(nodepos);
3760 map.emergeBlock(blockpos, true);
3761 content_t c = map.getNode(nodepos).getContent();
3763 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3764 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3765 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3767 if (air_count >= 2) {
3768 // Spawn in lower empty node
3770 nodeposf = intToFloat(nodepos, BS);
3771 // Don't spawn the player outside map boundaries
3772 if (objectpos_over_limit(nodeposf))
3773 // Exit this loop, positions above are probably over limit
3776 // Good position found, cause an exit from main loop
3790 // No suitable spawn point found, return fallback 0,0,0
3791 return v3f(0.0f, 0.0f, 0.0f);
3794 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3796 if (delay == 0.0f) {
3797 // No delay, shutdown immediately
3798 m_shutdown_state.is_requested = true;
3799 // only print to the infostream, a chat message saying
3800 // "Server Shutting Down" is sent when the server destructs.
3801 infostream << "*** Immediate Server shutdown requested." << std::endl;
3802 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3803 // Negative delay, cancel shutdown if requested
3804 m_shutdown_state.reset();
3805 std::wstringstream ws;
3807 ws << L"*** Server shutdown canceled.";
3809 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3810 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3811 // m_shutdown_* are already handled, skip.
3813 } else if (delay > 0.0f) {
3814 // Positive delay, tell the clients when the server will shut down
3815 std::wstringstream ws;
3817 ws << L"*** Server shutting down in "
3818 << duration_to_string(myround(delay)).c_str()
3821 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3822 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3825 m_shutdown_state.trigger(delay, msg, reconnect);
3828 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3831 Try to get an existing player
3833 RemotePlayer *player = m_env->getPlayer(name);
3835 // If player is already connected, cancel
3836 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3837 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3842 If player with the wanted peer_id already exists, cancel.
3844 if (m_env->getPlayer(peer_id)) {
3845 infostream<<"emergePlayer(): Player with wrong name but same"
3846 " peer_id already exists"<<std::endl;
3851 player = new RemotePlayer(name, idef());
3854 bool newplayer = false;
3857 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3859 // Complete init with server parts
3860 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3861 player->protocol_version = proto_version;
3865 m_script->on_newplayer(playersao);
3871 void dedicated_server_loop(Server &server, bool &kill)
3873 verbosestream<<"dedicated_server_loop()"<<std::endl;
3875 IntervalLimiter m_profiler_interval;
3877 static thread_local const float steplen =
3878 g_settings->getFloat("dedicated_server_step");
3879 static thread_local const float profiler_print_interval =
3880 g_settings->getFloat("profiler_print_interval");
3883 * The dedicated server loop only does time-keeping (in Server::step) and
3884 * provides a way to main.cpp to kill the server externally (bool &kill).
3888 // This is kind of a hack but can be done like this
3889 // because server.step() is very light
3890 sleep_ms((int)(steplen*1000.0));
3891 server.step(steplen);
3893 if (server.isShutdownRequested() || kill)
3899 if (profiler_print_interval != 0) {
3900 if(m_profiler_interval.step(steplen, profiler_print_interval))
3902 infostream<<"Profiler:"<<std::endl;
3903 g_profiler->print(infostream);
3904 g_profiler->clear();
3909 infostream << "Dedicated server quitting" << std::endl;
3911 if (g_settings->getBool("server_announce"))
3912 ServerList::sendAnnounce(ServerList::AA_DELETE,
3913 server.m_bind_addr.getPort());
3922 bool Server::joinModChannel(const std::string &channel)
3924 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3925 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3928 bool Server::leaveModChannel(const std::string &channel)
3930 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3933 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3935 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3938 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3942 ModChannel* Server::getModChannel(const std::string &channel)
3944 return m_modchannel_mgr->getModChannel(channel);
3947 void Server::broadcastModChannelMessage(const std::string &channel,
3948 const std::string &message, session_t from_peer)
3950 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3954 if (message.size() > STRING_MAX_LEN) {
3955 warningstream << "ModChannel message too long, dropping before sending "
3956 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3957 << channel << ")" << std::endl;
3962 if (from_peer != PEER_ID_SERVER) {
3963 sender = getPlayerName(from_peer);
3966 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3967 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3968 resp_pkt << channel << sender << message;
3969 for (session_t peer_id : peers) {
3971 if (peer_id == from_peer)
3974 Send(peer_id, &resp_pkt);
3977 if (from_peer != PEER_ID_SERVER) {
3978 m_script->on_modchannel_message(channel, sender, message);
3982 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3984 if (lang_code.empty())
3987 auto it = server_translations.find(lang_code);
3988 if (it != server_translations.end())
3989 return &it->second; // Already loaded
3991 // [] will create an entry
3992 auto *translations = &server_translations[lang_code];
3994 std::string suffix = "." + lang_code + ".tr";
3995 for (const auto &i : m_media) {
3996 if (str_ends_with(i.first, suffix)) {
3998 if (fs::ReadFile(i.second.path, data)) {
3999 translations->loadTranslation(data);
4004 return translations;
4007 ModStorageDatabase *Server::openModStorageDatabase(const std::string &world_path)
4009 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4011 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4012 throw BaseException("Cannot read world.mt!");
4014 std::string backend = world_mt.exists("mod_storage_backend") ?
4015 world_mt.get("mod_storage_backend") : "files";
4016 if (backend == "files")
4017 warningstream << "/!\\ You are using the old mod storage files backend. "
4018 << "This backend is deprecated and may be removed in a future release /!\\"
4019 << std::endl << "Switching to SQLite3 is advised, "
4020 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4022 return openModStorageDatabase(backend, world_path, world_mt);
4025 ModStorageDatabase *Server::openModStorageDatabase(const std::string &backend,
4026 const std::string &world_path, const Settings &world_mt)
4028 if (backend == "sqlite3")
4029 return new ModStorageDatabaseSQLite3(world_path);
4032 if (backend == "postgresql") {
4033 std::string connect_string;
4034 world_mt.getNoEx("pgsql_mod_storage_connection", connect_string);
4035 return new ModStorageDatabasePostgreSQL(connect_string);
4037 #endif // USE_POSTGRESQL
4039 if (backend == "files")
4040 return new ModStorageDatabaseFiles(world_path);
4042 if (backend == "dummy")
4043 return new Database_Dummy();
4045 throw BaseException("Mod storage database backend " + backend + " not supported");
4048 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4050 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4052 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4053 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4054 errorstream << "Cannot read world.mt!" << std::endl;
4058 std::string backend = world_mt.exists("mod_storage_backend") ?
4059 world_mt.get("mod_storage_backend") : "files";
4060 if (backend == migrate_to) {
4061 errorstream << "Cannot migrate: new backend is same"
4062 << " as the old one" << std::endl;
4066 ModStorageDatabase *srcdb = nullptr;
4067 ModStorageDatabase *dstdb = nullptr;
4069 bool succeeded = false;
4072 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4073 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4077 std::vector<std::string> mod_list;
4078 srcdb->listMods(&mod_list);
4079 for (const std::string &modname : mod_list) {
4081 srcdb->getModEntries(modname, &meta);
4082 for (const auto &pair : meta) {
4083 dstdb->setModEntry(modname, pair.first, pair.second);
4091 actionstream << "Successfully migrated the metadata of "
4092 << mod_list.size() << " mods" << std::endl;
4093 world_mt.set("mod_storage_backend", migrate_to);
4094 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4095 errorstream << "Failed to update world.mt!" << std::endl;
4097 actionstream << "world.mt updated" << std::endl;
4099 } catch (BaseException &e) {
4100 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4106 if (succeeded && backend == "files") {
4108 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4109 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4110 if (!fs::Rename(storage_path, backup_path))
4111 warningstream << "After migration, " << storage_path
4112 << " could not be renamed to " << backup_path << std::endl;