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 if (!simple_singleplayer_mode)
266 m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
271 m_metrics_backend = std::make_unique<MetricsBackend>();
273 m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
274 m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
276 m_timeofday_gauge = m_metrics_backend->addGauge(
277 "minetest_core_timeofday",
278 "Time of day value");
280 m_lag_gauge = m_metrics_backend->addGauge(
281 "minetest_core_latency",
282 "Latency value (in seconds)");
285 const std::string aom_types[] = {"reliable", "unreliable"};
286 for (u32 i = 0; i < ARRLEN(aom_types); i++) {
287 std::string help_str("Number of active object messages generated (");
288 help_str.append(aom_types[i]).append(")");
289 m_aom_buffer_counter[i] = m_metrics_backend->addCounter(
290 "minetest_core_aom_generated_count", help_str,
291 {{"type", aom_types[i]}});
294 m_packet_recv_counter = m_metrics_backend->addCounter(
295 "minetest_core_server_packet_recv",
296 "Processable packets received");
298 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
299 "minetest_core_server_packet_recv_processed",
300 "Valid received packets processed");
302 m_map_edit_event_counter = m_metrics_backend->addCounter(
303 "minetest_core_map_edit_events",
304 "Number of map edit events");
306 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
312 // Send shutdown message
313 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
314 L"*** Server shutting down"));
317 MutexAutoLock envlock(m_env_mutex);
319 infostream << "Server: Saving players" << std::endl;
320 m_env->saveLoadedPlayers();
322 infostream << "Server: Kicking players" << std::endl;
323 std::string kick_msg;
324 bool reconnect = false;
325 if (isShutdownRequested()) {
326 reconnect = m_shutdown_state.should_reconnect;
327 kick_msg = m_shutdown_state.message;
329 if (kick_msg.empty()) {
330 kick_msg = g_settings->get("kick_msg_shutdown");
332 m_env->saveLoadedPlayers(true);
333 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
334 kick_msg, reconnect);
337 actionstream << "Server: Shutting down" << std::endl;
339 // Do this before stopping the server in case mapgen callbacks need to access
340 // server-controlled resources (like ModStorages). Also do them before
341 // shutdown callbacks since they may modify state that is finalized in a
344 m_emerge->stopThreads();
347 MutexAutoLock envlock(m_env_mutex);
349 // Execute script shutdown hooks
350 infostream << "Executing shutdown hooks" << std::endl;
352 m_script->on_shutdown();
353 } catch (ModError &e) {
354 errorstream << "ModError: " << e.what() << std::endl;
355 if (m_on_shutdown_errmsg) {
356 if (m_on_shutdown_errmsg->empty()) {
357 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
359 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
364 infostream << "Server: Saving environment metadata" << std::endl;
374 // Write any changes before deletion.
375 if (m_mod_storage_database)
376 m_mod_storage_database->endSave();
378 // Delete things in the reverse order of creation
382 delete m_mod_storage_database;
388 // Deinitialize scripting
389 infostream << "Server: Deinitializing scripting" << std::endl;
391 delete m_startup_server_map; // if available
392 delete m_game_settings;
394 while (!m_unsent_map_edit_queue.empty()) {
395 delete m_unsent_map_edit_queue.front();
396 m_unsent_map_edit_queue.pop();
402 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
403 if (m_simple_singleplayer_mode)
404 infostream << " in simple singleplayer mode" << std::endl;
406 infostream << std::endl;
407 infostream << "- world: " << m_path_world << std::endl;
408 infostream << "- game: " << m_gamespec.path << std::endl;
410 m_game_settings = Settings::createLayer(SL_GAME);
412 // Create world if it doesn't exist
414 loadGameConfAndInitWorld(m_path_world,
415 fs::GetFilenameFromPath(m_path_world.c_str()),
417 } catch (const BaseException &e) {
418 throw ServerError(std::string("Failed to initialize world: ") + e.what());
421 // Create emerge manager
422 m_emerge = new EmergeManager(this, m_metrics_backend.get());
424 // Create ban manager
425 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
426 m_banmanager = new BanManager(ban_path);
428 // Create mod storage database and begin a save for later
429 m_mod_storage_database = openModStorageDatabase(m_path_world);
430 m_mod_storage_database->beginSave();
432 m_modmgr = std::make_unique<ServerModManager>(m_path_world);
434 // complain about mods with unsatisfied dependencies
435 if (!m_modmgr->isConsistent()) {
436 std::string error = m_modmgr->getUnsatisfiedModsError();
437 throw ServerError(error);
441 MutexAutoLock envlock(m_env_mutex);
443 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
444 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
445 m_startup_server_map = servermap;
447 // Initialize scripting
448 infostream << "Server: Initializing Lua" << std::endl;
450 m_script = new ServerScripting(this);
452 // Must be created before mod loading because we have some inventory creation
453 m_inventory_mgr = std::make_unique<ServerInventoryManager>();
455 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
456 m_script->checkSetByBuiltin();
458 m_gamespec.checkAndLog();
459 m_modmgr->loadMods(m_script);
461 // Read Textures and calculate sha1 sums
464 // Apply item aliases in the node definition manager
465 m_nodedef->updateAliases(m_itemdef);
467 // Apply texture overrides from texturepack/override.txt
468 std::vector<std::string> paths;
469 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
470 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
471 for (const std::string &path : paths) {
472 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
473 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
474 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
477 m_nodedef->setNodeRegistrationStatus(true);
479 // Perform pending node name resolutions
480 m_nodedef->runNodeResolveCallbacks();
482 // unmap node names in cross-references
483 m_nodedef->resolveCrossrefs();
485 // init the recipe hashes to speed up crafting
486 m_craftdef->initHashes(this);
488 // Initialize Environment
489 m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
490 m_env = new ServerEnvironment(servermap, m_script, this,
491 m_path_world, m_metrics_backend.get());
494 m_inventory_mgr->setEnv(m_env);
495 m_clients.setEnv(m_env);
497 if (!servermap->settings_mgr.makeMapgenParams())
498 FATAL_ERROR("Couldn't create any mapgen type");
500 // Initialize mapgens
501 m_emerge->initMapgens(servermap->getMapgenParams());
503 if (g_settings->getBool("enable_rollback_recording")) {
504 // Create rollback manager
505 m_rollback = new RollbackManager(m_path_world, this);
508 // Give environment reference to scripting api
509 m_script->initializeEnvironment(m_env);
511 // Do this after regular script init is done
512 m_script->initAsync();
514 // Register us to receive map edit events
515 servermap->addEventReceiver(this);
519 // Those settings can be overwritten in world.mt, they are
520 // intended to be cached after environment loading.
521 m_liquid_transform_every = g_settings->getFloat("liquid_update");
522 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
523 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
524 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
531 infostream << "Starting server on " << m_bind_addr.serializeString()
532 << "..." << std::endl;
534 // Stop thread if already running
537 // Initialize connection
538 m_con->SetTimeoutMs(30);
539 m_con->Serve(m_bind_addr);
544 // ASCII art for the win!
546 << " __. __. __. " << std::endl
547 << " _____ |__| ____ _____ / |_ _____ _____ / |_ " << std::endl
548 << " / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\" << std::endl
549 << "| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | " << std::endl
550 << "|__|_| / |___| /\\______> | \\______>_____/| | " << std::endl
551 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
552 actionstream << "World at [" << m_path_world << "]" << std::endl;
553 actionstream << "Server for gameid=\"" << m_gamespec.id
554 << "\" listening on ";
555 m_bind_addr.print(actionstream);
556 actionstream << "." << std::endl;
561 infostream<<"Server: Stopping and waiting threads"<<std::endl;
563 // Stop threads (set run=false first so both start stopping)
567 infostream<<"Server: Threads stopped"<<std::endl;
570 void Server::step(float dtime)
576 MutexAutoLock lock(m_step_dtime_mutex);
577 m_step_dtime += dtime;
579 // Throw if fatal error occurred in thread
580 std::string async_err = m_async_fatal_error.get();
581 if (!async_err.empty()) {
582 if (!m_simple_singleplayer_mode) {
583 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
584 g_settings->get("kick_msg_crash"),
585 g_settings->getBool("ask_reconnect_on_crash"));
587 throw ServerError("AsyncErr: " + async_err);
591 void Server::AsyncRunStep(bool initial_step)
596 MutexAutoLock lock1(m_step_dtime_mutex);
597 dtime = m_step_dtime;
601 // Send blocks to clients
605 if((dtime < 0.001) && !initial_step)
608 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
611 MutexAutoLock lock1(m_step_dtime_mutex);
612 m_step_dtime -= dtime;
618 m_uptime_counter->increment(dtime);
623 Update time of day and overall game time
625 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
628 Send to clients at constant intervals
631 m_time_of_day_send_timer -= dtime;
632 if (m_time_of_day_send_timer < 0.0) {
633 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
634 u16 time = m_env->getTimeOfDay();
635 float time_speed = g_settings->getFloat("time_speed");
636 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
638 m_timeofday_gauge->set(time);
642 MutexAutoLock lock(m_env_mutex);
643 // Figure out and report maximum lag to environment
644 float max_lag = m_env->getMaxLagEstimate();
645 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
647 if(dtime > 0.1 && dtime > max_lag * 2.0)
648 infostream<<"Server: Maximum lag peaked to "<<dtime
652 m_env->reportMaxLagEstimate(max_lag);
658 static const float map_timer_and_unload_dtime = 2.92;
659 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
661 MutexAutoLock lock(m_env_mutex);
662 // Run Map's timers and unload unused data
663 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
664 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
665 std::max(g_settings->getFloat("server_unload_unused_data_timeout"), 0.0f),
670 Listen to the admin chat, if available
673 if (!m_admin_chat->command_queue.empty()) {
674 MutexAutoLock lock(m_env_mutex);
675 while (!m_admin_chat->command_queue.empty()) {
676 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
677 handleChatInterfaceEvent(evt);
681 m_admin_chat->outgoing_queue.push_back(
682 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
689 /* Transform liquids */
690 m_liquid_transform_timer += dtime;
691 if(m_liquid_transform_timer >= m_liquid_transform_every)
693 m_liquid_transform_timer -= m_liquid_transform_every;
695 MutexAutoLock lock(m_env_mutex);
697 ScopeProfiler sp(g_profiler, "Server: liquid transform");
699 std::map<v3s16, MapBlock*> modified_blocks;
700 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
702 if (!modified_blocks.empty()) {
704 event.type = MEET_OTHER;
705 event.setModifiedBlocks(modified_blocks);
706 m_env->getMap().dispatchEvent(event);
709 m_clients.step(dtime);
711 // increase/decrease lag gauge gradually
712 if (m_lag_gauge->get() > dtime) {
713 m_lag_gauge->decrement(dtime/100);
715 m_lag_gauge->increment(dtime/100);
719 float &counter = m_step_pending_dyn_media_timer;
721 if (counter >= 5.0f) {
722 stepPendingDynMediaCallbacks(counter);
729 // send masterserver announce
731 float &counter = m_masterserver_timer;
732 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
733 g_settings->getBool("server_announce")) {
734 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
735 ServerList::AA_START,
736 m_bind_addr.getPort(),
737 m_clients.getPlayerNames(),
738 m_uptime_counter->get(),
739 m_env->getGameTime(),
742 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
752 Check added and deleted active objects
755 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
756 MutexAutoLock envlock(m_env_mutex);
759 ClientInterface::AutoLock clientlock(m_clients);
760 const RemoteClientMap &clients = m_clients.getClientList();
761 ScopeProfiler sp(g_profiler, "Server: update objects within range");
763 m_player_gauge->set(clients.size());
764 for (const auto &client_it : clients) {
765 RemoteClient *client = client_it.second;
767 if (client->getState() < CS_DefinitionsSent)
770 // This can happen if the client times out somehow
771 if (!m_env->getPlayer(client->peer_id))
774 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
778 SendActiveObjectRemoveAdd(client, playersao);
782 // Write changes to the mod storage
783 m_mod_storage_save_timer -= dtime;
784 if (m_mod_storage_save_timer <= 0.0f) {
785 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
786 m_mod_storage_database->endSave();
787 m_mod_storage_database->beginSave();
795 MutexAutoLock envlock(m_env_mutex);
796 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
799 // Value = data sent by object
800 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
802 // Get active object messages from environment
803 ActiveObjectMessage aom(0);
804 u32 count_reliable = 0, count_unreliable = 0;
806 if (!m_env->getActiveObjectMessage(&aom))
813 std::vector<ActiveObjectMessage>* message_list = nullptr;
814 auto n = buffered_messages.find(aom.id);
815 if (n == buffered_messages.end()) {
816 message_list = new std::vector<ActiveObjectMessage>;
817 buffered_messages[aom.id] = message_list;
819 message_list = n->second;
821 message_list->push_back(std::move(aom));
824 m_aom_buffer_counter[0]->increment(count_reliable);
825 m_aom_buffer_counter[1]->increment(count_unreliable);
828 ClientInterface::AutoLock clientlock(m_clients);
829 const RemoteClientMap &clients = m_clients.getClientList();
830 // Route data to every client
831 std::string reliable_data, unreliable_data;
832 for (const auto &client_it : clients) {
833 reliable_data.clear();
834 unreliable_data.clear();
835 RemoteClient *client = client_it.second;
836 PlayerSAO *player = getPlayerSAO(client->peer_id);
837 // Go through all objects in message buffer
838 for (const auto &buffered_message : buffered_messages) {
839 // If object does not exist or is not known by client, skip it
840 u16 id = buffered_message.first;
841 ServerActiveObject *sao = m_env->getActiveObject(id);
842 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
845 // Get message list of object
846 std::vector<ActiveObjectMessage>* list = buffered_message.second;
847 // Go through every message
848 for (const ActiveObjectMessage &aom : *list) {
849 // Send position updates to players who do not see the attachment
850 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
851 if (sao->getId() == player->getId())
854 // Do not send position updates for attached players
855 // as long the parent is known to the client
856 ServerActiveObject *parent = sao->getParent();
857 if (parent && client->m_known_objects.find(parent->getId()) !=
858 client->m_known_objects.end())
862 // Add full new data to appropriate buffer
863 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
865 writeU16((u8*) idbuf, aom.id);
868 buffer.append(idbuf, sizeof(idbuf));
869 buffer.append(serializeString16(aom.datastring));
873 reliable_data and unreliable_data are now ready.
876 if (!reliable_data.empty()) {
877 SendActiveObjectMessages(client->peer_id, reliable_data);
880 if (!unreliable_data.empty()) {
881 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
886 // Clear buffered_messages
887 for (auto &buffered_message : buffered_messages) {
888 delete buffered_message.second;
893 Send queued-for-sending map edit events.
896 // We will be accessing the environment
897 MutexAutoLock lock(m_env_mutex);
899 // Single change sending is disabled if queue size is big
900 bool disable_single_change_sending = false;
901 if(m_unsent_map_edit_queue.size() >= 4)
902 disable_single_change_sending = true;
904 const auto event_count = m_unsent_map_edit_queue.size();
905 m_map_edit_event_counter->increment(event_count);
907 // We'll log the amount of each
910 std::unordered_set<v3s16> node_meta_updates;
912 while (!m_unsent_map_edit_queue.empty()) {
913 MapEditEvent* event = m_unsent_map_edit_queue.front();
914 m_unsent_map_edit_queue.pop();
916 // Players far away from the change are stored here.
917 // Instead of sending the changes, MapBlocks are set not sent
919 std::unordered_set<u16> far_players;
921 switch (event->type) {
924 prof.add("MEET_ADDNODE", 1);
925 sendAddNode(event->p, event->n, &far_players,
926 disable_single_change_sending ? 5 : 30,
927 event->type == MEET_ADDNODE);
929 case MEET_REMOVENODE:
930 prof.add("MEET_REMOVENODE", 1);
931 sendRemoveNode(event->p, &far_players,
932 disable_single_change_sending ? 5 : 30);
934 case MEET_BLOCK_NODE_METADATA_CHANGED: {
935 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
936 if (!event->is_private_change) {
937 node_meta_updates.emplace(event->p);
940 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
941 getNodeBlockPos(event->p))) {
942 block->raiseModified(MOD_STATE_WRITE_NEEDED,
943 MOD_REASON_REPORT_META_CHANGE);
948 prof.add("MEET_OTHER", 1);
949 for (const v3s16 &modified_block : event->modified_blocks) {
950 m_clients.markBlockposAsNotSent(modified_block);
954 prof.add("unknown", 1);
955 warningstream << "Server: Unknown MapEditEvent "
956 << ((u32)event->type) << std::endl;
961 Set blocks not sent to far players
963 if (!far_players.empty()) {
964 // Convert list format to that wanted by SetBlocksNotSent
965 std::map<v3s16, MapBlock*> modified_blocks2;
966 for (const v3s16 &modified_block : event->modified_blocks) {
967 modified_blocks2[modified_block] =
968 m_env->getMap().getBlockNoCreateNoEx(modified_block);
971 // Set blocks not sent
972 for (const u16 far_player : far_players) {
973 if (RemoteClient *client = getClient(far_player))
974 client->SetBlocksNotSent(modified_blocks2);
981 if (event_count >= 5) {
982 infostream << "Server: MapEditEvents:" << std::endl;
983 prof.print(infostream);
984 } else if (event_count != 0) {
985 verbosestream << "Server: MapEditEvents:" << std::endl;
986 prof.print(verbosestream);
989 // Send all metadata updates
990 if (!node_meta_updates.empty())
991 sendMetadataChanged(node_meta_updates);
995 Trigger emerge thread
996 Doing this every 2s is left over from old code, unclear if this is still needed.
999 float &counter = m_emergethread_trigger_timer;
1001 if (counter <= 0.0f) {
1004 m_emerge->startThreads();
1008 // Save map, players and auth stuff
1010 float &counter = m_savemap_timer;
1012 static thread_local const float save_interval =
1013 g_settings->getFloat("server_map_save_interval");
1014 if (counter >= save_interval) {
1016 MutexAutoLock lock(m_env_mutex);
1018 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
1021 if (m_banmanager->isModified()) {
1022 m_banmanager->save();
1025 // Save changed parts of map
1026 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1029 m_env->saveLoadedPlayers();
1031 // Save environment metadata
1036 m_shutdown_state.tick(dtime, this);
1039 void Server::Receive()
1049 In the first iteration *wait* for a packet, afterwards process
1050 all packets that are immediately available (no waiting).
1053 m_con->Receive(&pkt);
1056 if (!m_con->TryReceive(&pkt))
1060 peer_id = pkt.getPeerId();
1061 m_packet_recv_counter->increment();
1063 m_packet_recv_processed_counter->increment();
1064 } catch (const con::InvalidIncomingDataException &e) {
1065 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1066 << e.what() << std::endl;
1067 } catch (const SerializationError &e) {
1068 infostream << "Server::Receive(): SerializationError: what()="
1069 << e.what() << std::endl;
1070 } catch (const ClientStateError &e) {
1071 errorstream << "ProcessData: peer=" << peer_id << " what()="
1072 << e.what() << std::endl;
1073 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1074 } catch (const con::PeerNotFoundException &e) {
1076 } catch (const con::NoIncomingDataException &e) {
1082 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1084 std::string playername;
1085 PlayerSAO *playersao = NULL;
1087 ClientInterface::AutoLock clientlock(m_clients);
1088 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1090 playername = client->getName();
1091 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1095 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1097 // If failed, cancel
1098 if (!playersao || !player) {
1099 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1100 actionstream << "Server: Failed to emerge player \"" << playername
1101 << "\" (player allocated to another client)" << std::endl;
1102 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1104 errorstream << "Server: " << playername << ": Failed to emerge player"
1106 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1112 Send complete position information
1114 SendMovePlayer(peer_id);
1117 SendPlayerPrivileges(peer_id);
1119 // Send inventory formspec
1120 SendPlayerInventoryFormspec(peer_id);
1123 SendInventory(playersao, false);
1126 SendPlayerHP(playersao, false);
1128 // Send death screen
1129 if (playersao->isDead())
1130 SendDeathscreen(peer_id, false, v3f(0,0,0));
1133 SendPlayerBreath(playersao);
1136 Update player list and print action
1139 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1140 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1141 m_clients.sendToAll(¬ice_pkt);
1144 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1145 const auto &names = m_clients.getPlayerNames();
1147 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1148 for (const std::string &name : names)
1149 actionstream << name << " ";
1150 actionstream << player->getName() << std::endl;
1155 inline void Server::handleCommand(NetworkPacket *pkt)
1157 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1158 (this->*opHandle.handler)(pkt);
1161 void Server::ProcessData(NetworkPacket *pkt)
1163 // Environment is locked first.
1164 MutexAutoLock envlock(m_env_mutex);
1166 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1167 u32 peer_id = pkt->getPeerId();
1170 Address address = getPeerAddress(peer_id);
1171 std::string addr_s = address.serializeString();
1173 // FIXME: Isn't it a bit excessive to check this for every packet?
1174 if (m_banmanager->isIpBanned(addr_s)) {
1175 std::string ban_name = m_banmanager->getBanName(addr_s);
1176 infostream << "Server: A banned client tried to connect from "
1177 << addr_s << "; banned name was " << ban_name << std::endl;
1178 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1179 "Your IP is banned. Banned name was " + ban_name);
1182 } catch (con::PeerNotFoundException &e) {
1184 * no peer for this packet found
1185 * most common reason is peer timeout, e.g. peer didn't
1186 * respond for some time, your server was overloaded or
1189 infostream << "Server::ProcessData(): Canceling: peer "
1190 << peer_id << " not found" << std::endl;
1195 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1197 // Command must be handled into ToServerCommandHandler
1198 if (command >= TOSERVER_NUM_MSG_TYPES) {
1199 infostream << "Server: Ignoring unknown command "
1200 << command << std::endl;
1204 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1209 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1211 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1212 errorstream << "Server::ProcessData(): Cancelling: Peer"
1213 " serialization format invalid or not initialized."
1214 " Skipping incoming command=" << command << std::endl;
1218 /* Handle commands related to client startup */
1219 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1224 if (m_clients.getClientState(peer_id) < CS_Active) {
1225 if (command == TOSERVER_PLAYERPOS) return;
1227 errorstream << "Got packet command: " << command << " for peer id "
1228 << peer_id << " but client isn't active yet. Dropping packet "
1234 } catch (SendFailedException &e) {
1235 errorstream << "Server::ProcessData(): SendFailedException: "
1236 << "what=" << e.what()
1238 } catch (PacketError &e) {
1239 actionstream << "Server::ProcessData(): PacketError: "
1240 << "what=" << e.what()
1245 void Server::setTimeOfDay(u32 time)
1247 m_env->setTimeOfDay(time);
1248 m_time_of_day_send_timer = 0;
1251 void Server::onMapEditEvent(const MapEditEvent &event)
1253 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1256 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1259 void Server::peerAdded(con::Peer *peer)
1261 verbosestream<<"Server::peerAdded(): peer->id="
1262 <<peer->id<<std::endl;
1264 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1267 void Server::deletingPeer(con::Peer *peer, bool timeout)
1269 verbosestream<<"Server::deletingPeer(): peer->id="
1270 <<peer->id<<", timeout="<<timeout<<std::endl;
1272 m_clients.event(peer->id, CSE_Disconnect);
1273 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1276 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1278 *retval = m_con->getPeerStat(peer_id,type);
1279 return *retval != -1;
1282 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1284 ClientInterface::AutoLock clientlock(m_clients);
1285 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1290 ret.state = client->getState();
1291 ret.addr = client->getAddress();
1292 ret.uptime = client->uptime();
1293 ret.ser_vers = client->serialization_version;
1294 ret.prot_vers = client->net_proto_version;
1296 ret.major = client->getMajor();
1297 ret.minor = client->getMinor();
1298 ret.patch = client->getPatch();
1299 ret.vers_string = client->getFullVer();
1301 ret.lang_code = client->getLangCode();
1306 void Server::handlePeerChanges()
1308 while(!m_peer_change_queue.empty())
1310 con::PeerChange c = m_peer_change_queue.front();
1311 m_peer_change_queue.pop();
1313 verbosestream<<"Server: Handling peer change: "
1314 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1319 case con::PEER_ADDED:
1320 m_clients.CreateClient(c.peer_id);
1323 case con::PEER_REMOVED:
1324 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1328 FATAL_ERROR("Invalid peer change event received!");
1334 void Server::printToConsoleOnly(const std::string &text)
1337 m_admin_chat->outgoing_queue.push_back(
1338 new ChatEventChat("", utf8_to_wide(text)));
1340 std::cout << text << std::endl;
1344 void Server::Send(NetworkPacket *pkt)
1346 Send(pkt->getPeerId(), pkt);
1349 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1351 m_clients.send(peer_id,
1352 clientCommandFactoryTable[pkt->getCommand()].channel,
1354 clientCommandFactoryTable[pkt->getCommand()].reliable);
1357 void Server::SendMovement(session_t peer_id)
1359 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1361 pkt << g_settings->getFloat("movement_acceleration_default");
1362 pkt << g_settings->getFloat("movement_acceleration_air");
1363 pkt << g_settings->getFloat("movement_acceleration_fast");
1364 pkt << g_settings->getFloat("movement_speed_walk");
1365 pkt << g_settings->getFloat("movement_speed_crouch");
1366 pkt << g_settings->getFloat("movement_speed_fast");
1367 pkt << g_settings->getFloat("movement_speed_climb");
1368 pkt << g_settings->getFloat("movement_speed_jump");
1369 pkt << g_settings->getFloat("movement_liquid_fluidity");
1370 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1371 pkt << g_settings->getFloat("movement_liquid_sink");
1372 pkt << g_settings->getFloat("movement_gravity");
1377 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1379 m_script->player_event(playersao, "health_changed");
1380 SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX);
1382 // Send to other clients
1383 playersao->sendPunchCommand();
1385 if (playersao->isDead())
1386 HandlePlayerDeath(playersao, reason);
1389 void Server::SendPlayerHP(PlayerSAO *playersao, bool effect)
1391 SendHP(playersao->getPeerID(), playersao->getHP(), effect);
1394 void Server::SendHP(session_t peer_id, u16 hp, bool effect)
1396 NetworkPacket pkt(TOCLIENT_HP, 3, peer_id);
1397 pkt << hp << effect;
1401 void Server::SendBreath(session_t peer_id, u16 breath)
1403 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1404 pkt << (u16) breath;
1408 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1409 const std::string &custom_reason, bool reconnect)
1411 assert(reason < SERVER_ACCESSDENIED_MAX);
1413 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1415 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1416 pkt << custom_reason;
1417 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1418 reason == SERVER_ACCESSDENIED_CRASH)
1419 pkt << custom_reason << (u8)reconnect;
1423 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1424 v3f camera_point_target)
1426 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1427 pkt << set_camera_point_target << camera_point_target;
1431 void Server::SendItemDef(session_t peer_id,
1432 IItemDefManager *itemdef, u16 protocol_version)
1434 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1438 u32 length of the next item
1439 zlib-compressed serialized ItemDefManager
1441 std::ostringstream tmp_os(std::ios::binary);
1442 itemdef->serialize(tmp_os, protocol_version);
1443 std::ostringstream tmp_os2(std::ios::binary);
1444 compressZlib(tmp_os.str(), tmp_os2);
1445 pkt.putLongString(tmp_os2.str());
1448 verbosestream << "Server: Sending item definitions to id(" << peer_id
1449 << "): size=" << pkt.getSize() << std::endl;
1454 void Server::SendNodeDef(session_t peer_id,
1455 const NodeDefManager *nodedef, u16 protocol_version)
1457 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1461 u32 length of the next item
1462 zlib-compressed serialized NodeDefManager
1464 std::ostringstream tmp_os(std::ios::binary);
1465 nodedef->serialize(tmp_os, protocol_version);
1466 std::ostringstream tmp_os2(std::ios::binary);
1467 compressZlib(tmp_os.str(), tmp_os2);
1469 pkt.putLongString(tmp_os2.str());
1472 verbosestream << "Server: Sending node definitions to id(" << peer_id
1473 << "): size=" << pkt.getSize() << std::endl;
1479 Non-static send methods
1482 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1484 RemotePlayer *player = sao->getPlayer();
1486 // Do not send new format to old clients
1487 incremental &= player->protocol_version >= 38;
1489 UpdateCrafting(player);
1495 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1497 std::ostringstream os(std::ios::binary);
1498 sao->getInventory()->serialize(os, incremental);
1499 sao->getInventory()->setModified(false);
1500 player->setModified(true);
1502 const std::string &s = os.str();
1503 pkt.putRawString(s.c_str(), s.size());
1507 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1509 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1511 u8 type = message.type;
1512 pkt << version << type << message.sender << message.message
1513 << static_cast<u64>(message.timestamp);
1515 if (peer_id != PEER_ID_INEXISTENT) {
1516 RemotePlayer *player = m_env->getPlayer(peer_id);
1522 m_clients.sendToAll(&pkt);
1526 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1527 const std::string &formname)
1529 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1530 if (formspec.empty()){
1531 //the client should close the formspec
1532 //but make sure there wasn't another one open in meantime
1533 const auto it = m_formspec_state_data.find(peer_id);
1534 if (it != m_formspec_state_data.end() && it->second == formname) {
1535 m_formspec_state_data.erase(peer_id);
1537 pkt.putLongString("");
1539 m_formspec_state_data[peer_id] = formname;
1540 pkt.putLongString(formspec);
1547 // Spawns a particle on peer with peer_id
1548 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1549 const ParticleParameters &p)
1551 static thread_local const float radius =
1552 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1554 if (peer_id == PEER_ID_INEXISTENT) {
1555 std::vector<session_t> clients = m_clients.getClientIDs();
1556 const v3f pos = p.pos * BS;
1557 const float radius_sq = radius * radius;
1559 for (const session_t client_id : clients) {
1560 RemotePlayer *player = m_env->getPlayer(client_id);
1564 PlayerSAO *sao = player->getPlayerSAO();
1568 // Do not send to distant clients
1569 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1572 SendSpawnParticle(client_id, player->protocol_version, p);
1576 assert(protocol_version != 0);
1578 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1581 // NetworkPacket and iostreams are incompatible...
1582 std::ostringstream oss(std::ios_base::binary);
1583 p.serialize(oss, protocol_version);
1584 pkt.putRawString(oss.str());
1590 // Adds a ParticleSpawner on peer with peer_id
1591 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1592 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1594 static thread_local const float radius =
1595 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1597 if (peer_id == PEER_ID_INEXISTENT) {
1598 std::vector<session_t> clients = m_clients.getClientIDs();
1600 p.pos.start.min.val +
1601 p.pos.start.max.val +
1605 const float radius_sq = radius * radius;
1606 /* Don't send short-lived spawners to distant players.
1607 * This could be replaced with proper tracking at some point. */
1608 const bool distance_check = !attached_id && p.time <= 1.0f;
1610 for (const session_t client_id : clients) {
1611 RemotePlayer *player = m_env->getPlayer(client_id);
1615 if (distance_check) {
1616 PlayerSAO *sao = player->getPlayerSAO();
1619 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1623 SendAddParticleSpawner(client_id, player->protocol_version,
1624 p, attached_id, id);
1628 assert(protocol_version != 0);
1630 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1632 pkt << p.amount << p.time;
1633 { // serialize legacy fields
1634 std::ostringstream os(std::ios_base::binary);
1635 p.pos.start.legacySerialize(os);
1636 p.vel.start.legacySerialize(os);
1637 p.acc.start.legacySerialize(os);
1638 p.exptime.start.legacySerialize(os);
1639 p.size.start.legacySerialize(os);
1640 pkt.putRawString(os.str());
1642 pkt << p.collisiondetection;
1644 pkt.putLongString(p.texture.string);
1646 pkt << id << p.vertical << p.collision_removal << attached_id;
1648 std::ostringstream os(std::ios_base::binary);
1649 p.animation.serialize(os, protocol_version);
1650 pkt.putRawString(os.str());
1652 pkt << p.glow << p.object_collision;
1653 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1655 { // serialize new fields
1656 // initial bias for older properties
1657 pkt << p.pos.start.bias
1660 << p.exptime.start.bias
1661 << p.size.start.bias;
1663 std::ostringstream os(std::ios_base::binary);
1665 // final tween frames of older properties
1666 p.pos.end.serialize(os);
1667 p.vel.end.serialize(os);
1668 p.acc.end.serialize(os);
1669 p.exptime.end.serialize(os);
1670 p.size.end.serialize(os);
1672 // properties for legacy texture field
1673 p.texture.serialize(os, protocol_version, true);
1676 p.drag.serialize(os);
1677 p.jitter.serialize(os);
1678 p.bounce.serialize(os);
1679 ParticleParamTypes::serializeParameterValue(os, p.attractor_kind);
1680 if (p.attractor_kind != ParticleParamTypes::AttractorKind::none) {
1681 p.attract.serialize(os);
1682 p.attractor_origin.serialize(os);
1683 writeU16(os, p.attractor_attachment); /* object ID */
1684 writeU8(os, p.attractor_kill);
1685 if (p.attractor_kind != ParticleParamTypes::AttractorKind::point) {
1686 p.attractor_direction.serialize(os);
1687 writeU16(os, p.attractor_direction_attachment);
1690 p.radius.serialize(os);
1692 ParticleParamTypes::serializeParameterValue(os, (u16)p.texpool.size());
1693 for (const auto& tex : p.texpool) {
1694 tex.serialize(os, protocol_version);
1697 pkt.putRawString(os.str());
1703 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1705 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1709 if (peer_id != PEER_ID_INEXISTENT)
1712 m_clients.sendToAll(&pkt);
1716 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1718 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1720 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1721 << form->text << form->number << form->item << form->dir
1722 << form->align << form->offset << form->world_pos << form->size
1723 << form->z_index << form->text2 << form->style;
1728 void Server::SendHUDRemove(session_t peer_id, u32 id)
1730 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1735 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1737 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1738 pkt << id << (u8) stat;
1742 case HUD_STAT_SCALE:
1743 case HUD_STAT_ALIGN:
1744 case HUD_STAT_OFFSET:
1745 pkt << *(v2f *) value;
1749 case HUD_STAT_TEXT2:
1750 pkt << *(std::string *) value;
1752 case HUD_STAT_WORLD_POS:
1753 pkt << *(v3f *) value;
1756 pkt << *(v2s32 *) value;
1758 default: // all other types
1759 pkt << *(u32 *) value;
1766 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1768 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1770 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1772 pkt << flags << mask;
1777 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1779 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1780 pkt << param << value;
1784 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1786 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1788 // Handle prior clients here
1789 if (m_clients.getProtocolVersion(peer_id) < 39) {
1790 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1792 for (const std::string& texture : params.textures)
1795 pkt << params.clouds;
1796 } else { // Handle current clients and future clients
1797 pkt << params.bgcolor << params.type
1798 << params.clouds << params.fog_sun_tint
1799 << params.fog_moon_tint << params.fog_tint_type;
1801 if (params.type == "skybox") {
1802 pkt << (u16) params.textures.size();
1803 for (const std::string &texture : params.textures)
1805 } else if (params.type == "regular") {
1806 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1807 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1808 << params.sky_color.night_sky << params.sky_color.night_horizon
1809 << params.sky_color.indoors;
1812 pkt << params.body_orbit_tilt;
1818 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1820 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1821 pkt << params.visible << params.texture
1822 << params.tonemap << params.sunrise
1823 << params.sunrise_visible << params.scale;
1827 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1829 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1831 pkt << params.visible << params.texture
1832 << params.tonemap << params.scale;
1836 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1838 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1840 pkt << params.visible << params.count
1841 << params.starcolor << params.scale
1842 << params.day_opacity;
1847 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1849 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1850 pkt << params.density << params.color_bright << params.color_ambient
1851 << params.height << params.thickness << params.speed;
1855 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1858 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1861 pkt << do_override << (u16) (ratio * 65535);
1866 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1868 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1871 pkt << lighting.shadow_intensity;
1872 pkt << lighting.saturation;
1874 pkt << lighting.exposure.luminance_min
1875 << lighting.exposure.luminance_max
1876 << lighting.exposure.exposure_correction
1877 << lighting.exposure.speed_dark_bright
1878 << lighting.exposure.speed_bright_dark
1879 << lighting.exposure.center_weight_power;
1884 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1886 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1887 pkt << time << time_speed;
1889 if (peer_id == PEER_ID_INEXISTENT) {
1890 m_clients.sendToAll(&pkt);
1897 void Server::SendPlayerBreath(PlayerSAO *sao)
1901 m_script->player_event(sao, "breath_changed");
1902 SendBreath(sao->getPeerID(), sao->getBreath());
1905 void Server::SendMovePlayer(session_t peer_id)
1907 RemotePlayer *player = m_env->getPlayer(peer_id);
1909 PlayerSAO *sao = player->getPlayerSAO();
1912 // Send attachment updates instantly to the client prior updating position
1913 sao->sendOutdatedData();
1915 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1916 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1919 v3f pos = sao->getBasePosition();
1920 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1921 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1922 << " pitch=" << sao->getLookPitch()
1923 << " yaw=" << sao->getRotation().Y
1930 void Server::SendPlayerFov(session_t peer_id)
1932 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1934 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1935 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1940 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1941 f32 animation_speed)
1943 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1946 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1947 << animation_frames[3] << animation_speed;
1952 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1954 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1955 pkt << first << third;
1959 void Server::SendPlayerPrivileges(session_t peer_id)
1961 RemotePlayer *player = m_env->getPlayer(peer_id);
1963 if(player->getPeerId() == PEER_ID_INEXISTENT)
1966 std::set<std::string> privs;
1967 m_script->getAuth(player->getName(), NULL, &privs);
1969 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1970 pkt << (u16) privs.size();
1972 for (const std::string &priv : privs) {
1979 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1981 RemotePlayer *player = m_env->getPlayer(peer_id);
1983 if (player->getPeerId() == PEER_ID_INEXISTENT)
1986 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1987 pkt.putLongString(player->inventory_formspec);
1992 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1994 RemotePlayer *player = m_env->getPlayer(peer_id);
1996 if (player->getPeerId() == PEER_ID_INEXISTENT)
1999 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
2000 pkt << player->formspec_prepend;
2004 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
2006 // Radius inside which objects are active
2007 static thread_local const s16 radius =
2008 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
2010 // Radius inside which players are active
2011 static thread_local const bool is_transfer_limited =
2012 g_settings->exists("unlimited_player_transfer_distance") &&
2013 !g_settings->getBool("unlimited_player_transfer_distance");
2015 static thread_local const s16 player_transfer_dist =
2016 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
2018 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
2019 radius : player_transfer_dist;
2021 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
2025 std::queue<u16> removed_objects, added_objects;
2026 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
2027 client->m_known_objects, removed_objects);
2028 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
2029 client->m_known_objects, added_objects);
2031 int removed_count = removed_objects.size();
2032 int added_count = added_objects.size();
2034 if (removed_objects.empty() && added_objects.empty())
2040 // Handle removed objects
2041 writeU16((u8*)buf, removed_objects.size());
2042 data.append(buf, 2);
2043 while (!removed_objects.empty()) {
2045 u16 id = removed_objects.front();
2046 ServerActiveObject* obj = m_env->getActiveObject(id);
2048 // Add to data buffer for sending
2049 writeU16((u8*)buf, id);
2050 data.append(buf, 2);
2052 // Remove from known objects
2053 client->m_known_objects.erase(id);
2055 if (obj && obj->m_known_by_count > 0)
2056 obj->m_known_by_count--;
2058 removed_objects.pop();
2061 // Handle added objects
2062 writeU16((u8*)buf, added_objects.size());
2063 data.append(buf, 2);
2064 while (!added_objects.empty()) {
2066 u16 id = added_objects.front();
2067 ServerActiveObject *obj = m_env->getActiveObject(id);
2068 added_objects.pop();
2071 warningstream << FUNCTION_NAME << ": NULL object id="
2072 << (int)id << std::endl;
2077 u8 type = obj->getSendType();
2079 // Add to data buffer for sending
2080 writeU16((u8*)buf, id);
2081 data.append(buf, 2);
2082 writeU8((u8*)buf, type);
2083 data.append(buf, 1);
2085 data.append(serializeString32(
2086 obj->getClientInitializationData(client->net_proto_version)));
2088 // Add to known objects
2089 client->m_known_objects.insert(id);
2091 obj->m_known_by_count++;
2094 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2095 pkt.putRawString(data.c_str(), data.size());
2098 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2099 << removed_count << " removed, " << added_count << " added, "
2100 << "packet size is " << pkt.getSize() << std::endl;
2103 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2106 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2107 datas.size(), peer_id);
2109 pkt.putRawString(datas.c_str(), datas.size());
2111 m_clients.send(pkt.getPeerId(),
2112 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2116 void Server::SendCSMRestrictionFlags(session_t peer_id)
2118 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2119 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2120 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2124 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2126 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2131 inline s32 Server::nextSoundId()
2133 s32 ret = m_next_sound_id;
2134 if (m_next_sound_id == INT32_MAX)
2135 m_next_sound_id = 0; // signed overflow is undefined
2141 s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
2143 // Find out initial position of sound
2144 bool pos_exists = false;
2145 const v3f pos = params.getPos(m_env, &pos_exists);
2146 // If position is not found while it should be, cancel sound
2147 if(pos_exists != (params.type != SoundLocation::Local))
2150 // Filter destination clients
2151 std::vector<session_t> dst_clients;
2152 if (!params.to_player.empty()) {
2153 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2155 infostream<<"Server::playSound: Player \""<<params.to_player
2156 <<"\" not found"<<std::endl;
2159 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2160 infostream<<"Server::playSound: Player \""<<params.to_player
2161 <<"\" not connected"<<std::endl;
2164 dst_clients.push_back(player->getPeerId());
2166 std::vector<session_t> clients = m_clients.getClientIDs();
2168 for (const session_t client_id : clients) {
2169 RemotePlayer *player = m_env->getPlayer(client_id);
2172 if (!params.exclude_player.empty() &&
2173 params.exclude_player == player->getName())
2176 PlayerSAO *sao = player->getPlayerSAO();
2181 if(sao->getBasePosition().getDistanceFrom(pos) >
2182 params.max_hear_distance)
2185 dst_clients.push_back(client_id);
2189 if(dst_clients.empty())
2192 // old clients will still use this, so pick a reserved ID (-1)
2193 const s32 id = ephemeral ? -1 : nextSoundId();
2195 float gain = params.gain * params.spec.gain;
2196 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2197 pkt << id << params.spec.name << gain
2198 << (u8) params.type << pos << params.object
2199 << params.spec.loop << params.spec.fade << params.spec.pitch
2202 bool as_reliable = !ephemeral;
2204 for (const session_t peer_id : dst_clients) {
2206 params.clients.insert(peer_id);
2207 m_clients.send(peer_id, 0, &pkt, as_reliable);
2211 m_playing_sounds[id] = std::move(params);
2214 void Server::stopSound(s32 handle)
2216 auto it = m_playing_sounds.find(handle);
2217 if (it == m_playing_sounds.end())
2220 ServerPlayingSound &psound = it->second;
2222 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2225 for (session_t peer_id : psound.clients) {
2227 m_clients.send(peer_id, 0, &pkt, true);
2230 // Remove sound reference
2231 m_playing_sounds.erase(it);
2234 void Server::fadeSound(s32 handle, float step, float gain)
2236 auto it = m_playing_sounds.find(handle);
2237 if (it == m_playing_sounds.end())
2240 ServerPlayingSound &psound = it->second;
2241 psound.gain = gain; // destination gain
2243 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2244 pkt << handle << step << gain;
2246 for (session_t peer_id : psound.clients) {
2248 m_clients.send(peer_id, 0, &pkt, true);
2251 // Remove sound reference
2252 if (gain <= 0 || psound.clients.empty())
2253 m_playing_sounds.erase(it);
2256 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2259 v3f p_f = intToFloat(p, BS);
2260 v3s16 block_pos = getNodeBlockPos(p);
2262 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2265 sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2268 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2269 float far_d_nodes, bool remove_metadata)
2271 v3f p_f = intToFloat(p, BS);
2272 v3s16 block_pos = getNodeBlockPos(p);
2274 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2275 pkt << p << n.param0 << n.param1 << n.param2
2276 << (u8) (remove_metadata ? 0 : 1);
2277 sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2280 void Server::sendNodeChangePkt(NetworkPacket &pkt, v3s16 block_pos,
2281 v3f p, float far_d_nodes, std::unordered_set<u16> *far_players)
2283 float maxd = far_d_nodes * BS;
2284 std::vector<session_t> clients = m_clients.getClientIDs();
2285 ClientInterface::AutoLock clientlock(m_clients);
2287 for (session_t client_id : clients) {
2288 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2292 RemotePlayer *player = m_env->getPlayer(client_id);
2293 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2295 // If player is far away, only set modified blocks not sent
2296 if (!client->isBlockSent(block_pos) || (sao &&
2297 sao->getBasePosition().getDistanceFrom(p) > maxd)) {
2299 far_players->emplace(client_id);
2301 client->SetBlockNotSent(block_pos);
2306 m_clients.send(client_id, 0, &pkt, true);
2310 void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
2312 NodeMetadataList meta_updates_list(false);
2313 std::ostringstream os(std::ios::binary);
2315 std::vector<session_t> clients = m_clients.getClientIDs();
2316 ClientInterface::AutoLock clientlock(m_clients);
2318 for (session_t i : clients) {
2319 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2323 ServerActiveObject *player = getPlayerSAO(i);
2326 player_pos = floatToInt(player->getBasePosition(), BS);
2328 for (const v3s16 pos : positions) {
2329 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2334 v3s16 block_pos = getNodeBlockPos(pos);
2335 if (!client->isBlockSent(block_pos) ||
2336 player_pos.getDistanceFrom(pos) > far_d_nodes) {
2337 client->SetBlockNotSent(block_pos);
2341 // Add the change to send list
2342 meta_updates_list.set(pos, meta);
2344 if (meta_updates_list.size() == 0)
2347 // Send the meta changes
2349 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2350 std::string raw = os.str();
2352 compressZlib(raw, os);
2354 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
2355 pkt.putLongString(os.str());
2358 meta_updates_list.clear();
2362 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2363 u16 net_proto_version, SerializedBlockCache *cache)
2365 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2366 std::string s, *sptr = nullptr;
2369 auto it = cache->find({block->getPos(), ver});
2370 if (it != cache->end())
2374 // Serialize the block in the right format
2376 std::ostringstream os(std::ios_base::binary);
2377 block->serialize(os, ver, false, net_compression_level);
2378 block->serializeNetworkSpecific(os);
2383 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2384 pkt << block->getPos();
2385 pkt.putRawString(*sptr);
2388 // Store away in cache
2389 if (cache && sptr == &s)
2390 (*cache)[{block->getPos(), ver}] = std::move(s);
2393 void Server::SendBlocks(float dtime)
2395 MutexAutoLock envlock(m_env_mutex);
2396 //TODO check if one big lock could be faster then multiple small ones
2398 std::vector<PrioritySortedBlockTransfer> queue;
2400 u32 total_sending = 0, unique_clients = 0;
2403 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2405 std::vector<session_t> clients = m_clients.getClientIDs();
2407 ClientInterface::AutoLock clientlock(m_clients);
2408 for (const session_t client_id : clients) {
2409 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2414 total_sending += client->getSendingCount();
2415 const auto old_count = queue.size();
2416 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2417 unique_clients += queue.size() > old_count ? 1 : 0;
2422 // Lowest priority number comes first.
2423 // Lowest is most important.
2424 std::sort(queue.begin(), queue.end());
2426 ClientInterface::AutoLock clientlock(m_clients);
2428 // Maximal total count calculation
2429 // The per-client block sends is halved with the maximal online users
2430 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2431 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2433 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2434 Map &map = m_env->getMap();
2436 SerializedBlockCache cache, *cache_ptr = nullptr;
2437 if (unique_clients > 1) {
2438 // caching is pointless with a single client
2442 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2443 if (total_sending >= max_blocks_to_send)
2446 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2450 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2455 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2456 client->net_proto_version, cache_ptr);
2458 client->SentBlock(block_to_send.pos);
2463 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2465 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2469 ClientInterface::AutoLock clientlock(m_clients);
2470 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2471 if (!client || client->isBlockSent(blockpos))
2473 SendBlockNoLock(peer_id, block, client->serialization_version,
2474 client->net_proto_version);
2479 bool Server::addMediaFile(const std::string &filename,
2480 const std::string &filepath, std::string *filedata_to,
2481 std::string *digest_to)
2483 // If name contains illegal characters, ignore the file
2484 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2485 infostream << "Server: ignoring illegal file name: \""
2486 << filename << "\"" << std::endl;
2489 // If name is not in a supported format, ignore it
2490 const char *supported_ext[] = {
2491 ".png", ".jpg", ".bmp", ".tga",
2493 ".x", ".b3d", ".obj",
2494 // Custom translation file format
2498 if (removeStringEnd(filename, supported_ext).empty()) {
2499 infostream << "Server: ignoring unsupported file extension: \""
2500 << filename << "\"" << std::endl;
2503 // Ok, attempt to load the file and add to cache
2506 std::string filedata;
2507 if (!fs::ReadFile(filepath, filedata)) {
2508 errorstream << "Server::addMediaFile(): Failed to open \""
2509 << filename << "\" for reading" << std::endl;
2513 if (filedata.empty()) {
2514 errorstream << "Server::addMediaFile(): Empty file \""
2515 << filepath << "\"" << std::endl;
2520 sha1.addBytes(filedata.c_str(), filedata.length());
2522 unsigned char *digest = sha1.getDigest();
2523 std::string sha1_base64 = base64_encode(digest, 20);
2524 std::string sha1_hex = hex_encode((char*) digest, 20);
2526 *digest_to = std::string((char*) digest, 20);
2530 m_media[filename] = MediaInfo(filepath, sha1_base64);
2531 verbosestream << "Server: " << sha1_hex << " is " << filename
2535 *filedata_to = std::move(filedata);
2539 void Server::fillMediaCache()
2541 infostream << "Server: Calculating media file checksums" << std::endl;
2543 // Collect all media file paths
2544 std::vector<std::string> paths;
2546 // ordered in descending priority
2547 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2548 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2549 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2550 m_modmgr->getModsMediaPaths(paths);
2552 // Collect media file information from paths into cache
2553 for (const std::string &mediapath : paths) {
2554 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2555 for (const fs::DirListNode &dln : dirlist) {
2556 if (dln.dir) // Ignore dirs (already in paths)
2559 const std::string &filename = dln.name;
2560 if (m_media.find(filename) != m_media.end()) // Do not override
2563 std::string filepath = mediapath;
2564 filepath.append(DIR_DELIM).append(filename);
2565 addMediaFile(filename, filepath);
2569 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2572 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2575 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2578 std::string lang_suffix;
2579 lang_suffix.append(".").append(lang_code).append(".tr");
2580 for (const auto &i : m_media) {
2581 if (i.second.no_announce)
2583 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2590 for (const auto &i : m_media) {
2591 if (i.second.no_announce)
2593 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2595 pkt << i.first << i.second.sha1_digest;
2598 pkt << g_settings->get("remote_media");
2601 verbosestream << "Server: Announcing files to id(" << peer_id
2602 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2605 struct SendableMedia
2611 SendableMedia(const std::string &name, const std::string &path,
2612 std::string &&data):
2613 name(name), path(path), data(std::move(data))
2617 void Server::sendRequestedMedia(session_t peer_id,
2618 const std::vector<std::string> &tosend)
2620 verbosestream<<"Server::sendRequestedMedia(): "
2621 <<"Sending files to client"<<std::endl;
2625 // Put 5kB in one bunch (this is not accurate)
2626 u32 bytes_per_bunch = 5000;
2628 std::vector< std::vector<SendableMedia> > file_bunches;
2629 file_bunches.emplace_back();
2631 u32 file_size_bunch_total = 0;
2633 for (const std::string &name : tosend) {
2634 if (m_media.find(name) == m_media.end()) {
2635 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2636 <<"unknown file \""<<(name)<<"\""<<std::endl;
2640 const auto &m = m_media[name];
2644 if (!fs::ReadFile(m.path, data)) {
2645 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2646 << name << "\"" << std::endl;
2649 file_size_bunch_total += data.size();
2652 file_bunches.back().emplace_back(name, m.path, std::move(data));
2654 // Start next bunch if got enough data
2655 if(file_size_bunch_total >= bytes_per_bunch) {
2656 file_bunches.emplace_back();
2657 file_size_bunch_total = 0;
2662 /* Create and send packets */
2664 u16 num_bunches = file_bunches.size();
2665 for (u16 i = 0; i < num_bunches; i++) {
2668 u16 total number of texture bunches
2669 u16 index of this bunch
2670 u32 number of files in this bunch
2679 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2680 pkt << num_bunches << i << (u32) file_bunches[i].size();
2682 for (const SendableMedia &j : file_bunches[i]) {
2684 pkt.putLongString(j.data);
2687 verbosestream << "Server::sendRequestedMedia(): bunch "
2688 << i << "/" << num_bunches
2689 << " files=" << file_bunches[i].size()
2690 << " size=" << pkt.getSize() << std::endl;
2695 void Server::stepPendingDynMediaCallbacks(float dtime)
2697 MutexAutoLock lock(m_env_mutex);
2699 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2700 it->second.expiry_timer -= dtime;
2701 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2708 const auto &name = it->second.filename;
2709 if (!name.empty()) {
2710 assert(m_media.count(name));
2711 // if no_announce isn't set we're definitely deleting the wrong file!
2712 sanity_check(m_media[name].no_announce);
2714 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2715 m_media.erase(name);
2717 getScriptIface()->freeDynamicMediaCallback(it->first);
2718 it = m_pending_dyn_media.erase(it);
2722 void Server::SendMinimapModes(session_t peer_id,
2723 std::vector<MinimapMode> &modes, size_t wanted_mode)
2725 RemotePlayer *player = m_env->getPlayer(peer_id);
2727 if (player->getPeerId() == PEER_ID_INEXISTENT)
2730 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2731 pkt << (u16)modes.size() << (u16)wanted_mode;
2733 for (auto &mode : modes)
2734 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2739 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2741 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2745 pkt << false; // Remove inventory
2747 pkt << true; // Update inventory
2749 // Serialization & NetworkPacket isn't a love story
2750 std::ostringstream os(std::ios_base::binary);
2751 inventory->serialize(os);
2752 inventory->setModified(false);
2754 const std::string &os_str = os.str();
2755 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2756 pkt.putRawString(os_str);
2759 if (peer_id == PEER_ID_INEXISTENT)
2760 m_clients.sendToAll(&pkt);
2765 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2767 // Lookup player name, to filter detached inventories just after
2768 std::string peer_name;
2769 if (peer_id != PEER_ID_INEXISTENT) {
2770 peer_name = getClient(peer_id, CS_Created)->getName();
2773 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2774 sendDetachedInventory(inv, name, peer_id);
2777 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2784 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2786 infostream << "Server::DiePlayer(): Player "
2787 << playersao->getPlayer()->getName()
2788 << " dies" << std::endl;
2790 playersao->clearParentAttachment();
2792 // Trigger scripted stuff
2793 m_script->on_dieplayer(playersao, reason);
2795 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2798 void Server::RespawnPlayer(session_t peer_id)
2800 PlayerSAO *playersao = getPlayerSAO(peer_id);
2803 infostream << "Server::RespawnPlayer(): Player "
2804 << playersao->getPlayer()->getName()
2805 << " respawns" << std::endl;
2807 const auto *prop = playersao->accessObjectProperties();
2808 playersao->setHP(prop->hp_max,
2809 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2810 playersao->setBreath(prop->breath_max);
2812 bool repositioned = m_script->on_respawnplayer(playersao);
2813 if (!repositioned) {
2814 // setPos will send the new position to client
2815 playersao->setPos(findSpawnPos());
2820 void Server::DenySudoAccess(session_t peer_id)
2822 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2827 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2828 const std::string &custom_reason, bool reconnect)
2830 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2831 m_clients.event(peer_id, CSE_SetDenied);
2832 DisconnectPeer(peer_id);
2835 void Server::DisconnectPeer(session_t peer_id)
2837 m_modchannel_mgr->leaveAllChannels(peer_id);
2838 m_con->DisconnectPeer(peer_id);
2841 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2844 RemoteClient* client = getClient(peer_id, CS_Invalid);
2846 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2848 // Right now, the auth mechs don't change between login and sudo mode.
2849 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2850 client->allowed_sudo_mechs = sudo_auth_mechs;
2852 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2853 << g_settings->getFloat("dedicated_server_step")
2857 m_clients.event(peer_id, CSE_AuthAccept);
2859 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2861 // We only support SRP right now
2862 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2864 resp_pkt << sudo_auth_mechs;
2866 m_clients.event(peer_id, CSE_SudoSuccess);
2870 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2872 std::wstring message;
2875 Clear references to playing sounds
2877 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2878 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2879 ServerPlayingSound &psound = i->second;
2880 psound.clients.erase(peer_id);
2881 if (psound.clients.empty())
2882 m_playing_sounds.erase(i++);
2887 // clear formspec info so the next client can't abuse the current state
2888 m_formspec_state_data.erase(peer_id);
2890 RemotePlayer *player = m_env->getPlayer(peer_id);
2892 /* Run scripts and remove from environment */
2894 PlayerSAO *playersao = player->getPlayerSAO();
2897 playersao->clearChildAttachments();
2898 playersao->clearParentAttachment();
2900 // inform connected clients
2901 const std::string &player_name = player->getName();
2902 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2903 // (u16) 1 + std::string represents a vector serialization representation
2904 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2905 m_clients.sendToAll(¬ice);
2907 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2909 playersao->disconnected();
2916 if (player && reason != CDR_DENY) {
2917 std::ostringstream os(std::ios_base::binary);
2918 std::vector<session_t> clients = m_clients.getClientIDs();
2920 for (const session_t client_id : clients) {
2922 RemotePlayer *player = m_env->getPlayer(client_id);
2926 // Get name of player
2927 os << player->getName() << " ";
2930 std::string name = player->getName();
2931 actionstream << name << " "
2932 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2933 << " List of players: " << os.str() << std::endl;
2935 m_admin_chat->outgoing_queue.push_back(
2936 new ChatEventNick(CET_NICK_REMOVE, name));
2940 MutexAutoLock env_lock(m_env_mutex);
2941 m_clients.DeleteClient(peer_id);
2945 // Send leave chat message to all remaining clients
2946 if (!message.empty()) {
2947 SendChatMessage(PEER_ID_INEXISTENT,
2948 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2952 void Server::UpdateCrafting(RemotePlayer *player)
2954 InventoryList *clist = player->inventory.getList("craft");
2955 if (!clist || clist->getSize() == 0)
2958 if (!clist->checkModified())
2961 // Get a preview for crafting
2963 InventoryLocation loc;
2964 loc.setPlayer(player->getName());
2965 std::vector<ItemStack> output_replacements;
2966 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2967 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2970 InventoryList *plist = player->inventory.getList("craftpreview");
2971 if (plist && plist->getSize() >= 1) {
2972 // Put the new preview in
2973 plist->changeItem(0, preview);
2977 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2979 if (evt->type == CET_NICK_ADD) {
2980 // The terminal informed us of its nick choice
2981 m_admin_nick = ((ChatEventNick *)evt)->nick;
2982 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2983 errorstream << "You haven't set up an account." << std::endl
2984 << "Please log in using the client as '"
2985 << m_admin_nick << "' with a secure password." << std::endl
2986 << "Until then, you can't execute admin tasks via the console," << std::endl
2987 << "and everybody can claim the user account instead of you," << std::endl
2988 << "giving them full control over this server." << std::endl;
2991 assert(evt->type == CET_CHAT);
2992 handleAdminChat((ChatEventChat *)evt);
2996 std::wstring Server::handleChat(const std::string &name,
2997 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2999 // If something goes wrong, this player is to blame
3000 RollbackScopeActor rollback_scope(m_rollback,
3001 std::string("player:") + name);
3003 if (g_settings->getBool("strip_color_codes"))
3004 wmessage = unescape_enriched(wmessage);
3007 switch (player->canSendChatMessage()) {
3008 case RPLAYER_CHATRESULT_FLOODING: {
3009 std::wstringstream ws;
3010 ws << L"You cannot send more messages. You are limited to "
3011 << g_settings->getFloat("chat_message_limit_per_10sec")
3012 << L" messages per 10 seconds.";
3015 case RPLAYER_CHATRESULT_KICK:
3016 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
3017 "You have been kicked due to message flooding.");
3019 case RPLAYER_CHATRESULT_OK:
3022 FATAL_ERROR("Unhandled chat filtering result found.");
3026 if (m_max_chatmessage_length > 0
3027 && wmessage.length() > m_max_chatmessage_length) {
3028 return L"Your message exceed the maximum chat message limit set on the server. "
3029 L"It was refused. Send a shorter message";
3032 auto message = trim(wide_to_utf8(wmessage));
3033 if (message.empty())
3036 if (message.find_first_of("\n\r") != std::wstring::npos) {
3037 return L"Newlines are not permitted in chat messages";
3040 // Run script hook, exit if script ate the chat message
3041 if (m_script->on_chat_message(name, message))
3046 // Whether to send line to the player that sent the message, or to all players
3047 bool broadcast_line = true;
3049 if (check_shout_priv && !checkPriv(name, "shout")) {
3050 line += L"-!- You don't have permission to shout.";
3051 broadcast_line = false;
3054 Workaround for fixing chat on Android. Lua doesn't handle
3055 the Cyrillic alphabet and some characters on older Android devices
3058 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3060 line += utf8_to_wide(m_script->formatChatMessage(name,
3061 wide_to_utf8(wmessage)));
3066 Tell calling method to send the message to sender
3068 if (!broadcast_line)
3072 Send the message to others
3074 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3076 ChatMessage chatmsg(line);
3078 std::vector<session_t> clients = m_clients.getClientIDs();
3079 for (u16 cid : clients)
3080 SendChatMessage(cid, chatmsg);
3085 void Server::handleAdminChat(const ChatEventChat *evt)
3087 std::string name = evt->nick;
3088 std::wstring wmessage = evt->evt_msg;
3090 std::wstring answer = handleChat(name, wmessage);
3092 // If asked to send answer to sender
3093 if (!answer.empty()) {
3094 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3098 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3100 RemoteClient *client = getClientNoEx(peer_id,state_min);
3102 throw ClientNotFoundException("Client not found");
3106 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3108 return m_clients.getClientNoEx(peer_id, state_min);
3111 std::string Server::getPlayerName(session_t peer_id)
3113 RemotePlayer *player = m_env->getPlayer(peer_id);
3115 return "[id="+itos(peer_id)+"]";
3116 return player->getName();
3119 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3121 RemotePlayer *player = m_env->getPlayer(peer_id);
3124 return player->getPlayerSAO();
3127 std::string Server::getStatusString()
3129 std::ostringstream os(std::ios_base::binary);
3132 os << "version: " << g_version_string;
3134 os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3136 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3138 os << " | max lag: " << std::setprecision(3);
3139 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3141 // Information about clients
3143 os << " | clients: ";
3145 std::vector<session_t> clients = m_clients.getClientIDs();
3146 for (session_t client_id : clients) {
3147 RemotePlayer *player = m_env->getPlayer(client_id);
3149 // Get name of player
3150 const char *name = player ? player->getName() : "<unknown>";
3152 // Add name to information string
3161 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3162 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3164 if (!g_settings->get("motd").empty())
3165 os << std::endl << "# Server: " << g_settings->get("motd");
3170 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3172 std::set<std::string> privs;
3173 m_script->getAuth(name, NULL, &privs);
3177 bool Server::checkPriv(const std::string &name, const std::string &priv)
3179 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3180 return (privs.count(priv) != 0);
3183 void Server::reportPrivsModified(const std::string &name)
3186 std::vector<session_t> clients = m_clients.getClientIDs();
3187 for (const session_t client_id : clients) {
3188 RemotePlayer *player = m_env->getPlayer(client_id);
3189 reportPrivsModified(player->getName());
3192 RemotePlayer *player = m_env->getPlayer(name.c_str());
3195 SendPlayerPrivileges(player->getPeerId());
3196 PlayerSAO *sao = player->getPlayerSAO();
3199 sao->updatePrivileges(
3200 getPlayerEffectivePrivs(name),
3205 void Server::reportInventoryFormspecModified(const std::string &name)
3207 RemotePlayer *player = m_env->getPlayer(name.c_str());
3210 SendPlayerInventoryFormspec(player->getPeerId());
3213 void Server::reportFormspecPrependModified(const std::string &name)
3215 RemotePlayer *player = m_env->getPlayer(name.c_str());
3218 SendPlayerFormspecPrepend(player->getPeerId());
3221 void Server::setIpBanned(const std::string &ip, const std::string &name)
3223 m_banmanager->add(ip, name);
3226 void Server::unsetIpBanned(const std::string &ip_or_name)
3228 m_banmanager->remove(ip_or_name);
3231 std::string Server::getBanDescription(const std::string &ip_or_name)
3233 return m_banmanager->getBanDescription(ip_or_name);
3236 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3238 // m_env will be NULL if the server is initializing
3242 if (m_admin_nick == name && !m_admin_nick.empty()) {
3243 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3246 RemotePlayer *player = m_env->getPlayer(name);
3251 if (player->getPeerId() == PEER_ID_INEXISTENT)
3254 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3257 bool Server::showFormspec(const char *playername, const std::string &formspec,
3258 const std::string &formname)
3260 // m_env will be NULL if the server is initializing
3264 RemotePlayer *player = m_env->getPlayer(playername);
3268 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3272 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3277 u32 id = player->addHud(form);
3279 SendHUDAdd(player->getPeerId(), id, form);
3284 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3288 HudElement* todel = player->removeHud(id);
3295 SendHUDRemove(player->getPeerId(), id);
3299 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3304 SendHUDChange(player->getPeerId(), id, stat, data);
3308 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3313 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3314 if (new_hud_flags == player->hud_flags) // no change
3317 SendHUDSetFlags(player->getPeerId(), flags, mask);
3318 player->hud_flags = new_hud_flags;
3320 PlayerSAO* playersao = player->getPlayerSAO();
3325 m_script->player_event(playersao, "hud_changed");
3329 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3334 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3337 player->setHotbarItemcount(hotbar_itemcount);
3338 std::ostringstream os(std::ios::binary);
3339 writeS32(os, hotbar_itemcount);
3340 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3344 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3349 player->setHotbarImage(name);
3350 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3353 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3358 player->setHotbarSelectedImage(name);
3359 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3362 Address Server::getPeerAddress(session_t peer_id)
3364 // Note that this is only set after Init was received in Server::handleCommand_Init
3365 return getClient(peer_id, CS_Invalid)->getAddress();
3368 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3369 v2s32 animation_frames[4], f32 frame_speed)
3371 sanity_check(player);
3372 player->setLocalAnimations(animation_frames, frame_speed);
3373 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3376 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3378 sanity_check(player);
3379 player->eye_offset_first = first;
3380 player->eye_offset_third = third;
3381 SendEyeOffset(player->getPeerId(), first, third);
3384 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3386 sanity_check(player);
3387 player->setSky(params);
3388 SendSetSky(player->getPeerId(), params);
3391 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3393 sanity_check(player);
3394 player->setSun(params);
3395 SendSetSun(player->getPeerId(), params);
3398 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3400 sanity_check(player);
3401 player->setMoon(params);
3402 SendSetMoon(player->getPeerId(), params);
3405 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3407 sanity_check(player);
3408 player->setStars(params);
3409 SendSetStars(player->getPeerId(), params);
3412 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3414 sanity_check(player);
3415 player->setCloudParams(params);
3416 SendCloudParams(player->getPeerId(), params);
3419 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3422 sanity_check(player);
3423 player->overrideDayNightRatio(do_override, ratio);
3424 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3427 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3429 sanity_check(player);
3430 player->setLighting(lighting);
3431 SendSetLighting(player->getPeerId(), lighting);
3434 void Server::notifyPlayers(const std::wstring &msg)
3436 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3439 void Server::spawnParticle(const std::string &playername,
3440 const ParticleParameters &p)
3442 // m_env will be NULL if the server is initializing
3446 session_t peer_id = PEER_ID_INEXISTENT;
3448 if (!playername.empty()) {
3449 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3452 peer_id = player->getPeerId();
3453 proto_ver = player->protocol_version;
3456 SendSpawnParticle(peer_id, proto_ver, p);
3459 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3460 ServerActiveObject *attached, const std::string &playername)
3462 // m_env will be NULL if the server is initializing
3466 session_t peer_id = PEER_ID_INEXISTENT;
3468 if (!playername.empty()) {
3469 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3472 peer_id = player->getPeerId();
3473 proto_ver = player->protocol_version;
3476 u16 attached_id = attached ? attached->getId() : 0;
3479 if (attached_id == 0)
3480 id = m_env->addParticleSpawner(p.time);
3482 id = m_env->addParticleSpawner(p.time, attached_id);
3484 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3488 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3490 // m_env will be NULL if the server is initializing
3492 throw ServerError("Can't delete particle spawners during initialisation!");
3494 session_t peer_id = PEER_ID_INEXISTENT;
3495 if (!playername.empty()) {
3496 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3499 peer_id = player->getPeerId();
3502 m_env->deleteParticleSpawner(id);
3503 SendDeleteParticleSpawner(peer_id, id);
3506 bool Server::dynamicAddMedia(std::string filepath,
3507 const u32 token, const std::string &to_player, bool ephemeral)
3509 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3510 auto it = m_media.find(filename);
3511 if (it != m_media.end()) {
3512 // Allow the same path to be "added" again in certain conditions
3513 if (ephemeral || it->second.path != filepath) {
3514 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3515 << "\" already exists in media cache" << std::endl;
3520 // Load the file and add it to our media cache
3521 std::string filedata, raw_hash;
3522 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3527 // Create a copy of the file and swap out the path, this removes the
3528 // requirement that mods keep the file accessible at the original path.
3529 filepath = fs::CreateTempFile();
3530 bool ok = ([&] () -> bool {
3531 if (filepath.empty())
3533 std::ofstream os(filepath.c_str(), std::ios::binary);
3541 errorstream << "Server: failed to create a copy of media file "
3542 << "\"" << filename << "\"" << std::endl;
3543 m_media.erase(filename);
3546 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3547 << filepath << std::endl;
3549 m_media[filename].path = filepath;
3550 m_media[filename].no_announce = true;
3551 // stepPendingDynMediaCallbacks will clean this up later.
3552 } else if (!to_player.empty()) {
3553 m_media[filename].no_announce = true;
3556 // Push file to existing clients
3557 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3558 pkt << raw_hash << filename << (bool)ephemeral;
3560 NetworkPacket legacy_pkt = pkt;
3562 // Newer clients get asked to fetch the file (asynchronous)
3564 // Older clients have an awful hack that just throws the data at them
3565 legacy_pkt.putLongString(filedata);
3567 std::unordered_set<session_t> delivered, waiting;
3569 ClientInterface::AutoLock clientlock(m_clients);
3570 for (auto &pair : m_clients.getClientList()) {
3571 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3573 If a client is in the DefinitionsSent state it is too late to
3574 transfer the file via sendMediaAnnouncement() but at the same
3575 time the client cannot accept a media push yet.
3576 Short of artificially delaying the joining process there is no
3577 way for the server to resolve this so we (currently) opt not to.
3579 warningstream << "The media \"" << filename << "\" (dynamic) could "
3580 "not be delivered to " << pair.second->getName()
3581 << " due to a race condition." << std::endl;
3584 if (pair.second->getState() < CS_Active)
3587 const auto proto_ver = pair.second->net_proto_version;
3591 const session_t peer_id = pair.second->peer_id;
3592 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3595 if (proto_ver < 40) {
3596 delivered.emplace(peer_id);
3598 The network layer only guarantees ordered delivery inside a channel.
3599 Since the very next packet could be one that uses the media, we have
3600 to push the media over ALL channels to ensure it is processed before
3601 it is used. In practice this means channels 1 and 0.
3603 m_clients.send(peer_id, 1, &legacy_pkt, true);
3604 m_clients.send(peer_id, 0, &legacy_pkt, true);
3606 waiting.emplace(peer_id);
3607 Send(peer_id, &pkt);
3612 // Run callback for players that already had the file delivered (legacy-only)
3613 for (session_t peer_id : delivered) {
3614 if (auto player = m_env->getPlayer(peer_id))
3615 getScriptIface()->on_dynamic_media_added(token, player->getName());
3618 // Save all others in our pending state
3619 auto &state = m_pending_dyn_media[token];
3620 state.waiting_players = std::move(waiting);
3621 // regardless of success throw away the callback after a while
3622 state.expiry_timer = 60.0f;
3624 state.filename = filename;
3629 // actions: time-reversed list
3630 // Return value: success/failure
3631 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3632 std::list<std::string> *log)
3634 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3635 ServerMap *map = (ServerMap*)(&m_env->getMap());
3637 // Fail if no actions to handle
3638 if (actions.empty()) {
3640 log->push_back("Nothing to do.");
3647 for (const RollbackAction &action : actions) {
3649 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3652 std::ostringstream os;
3653 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3654 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3656 log->push_back(os.str());
3658 std::ostringstream os;
3659 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3660 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3662 log->push_back(os.str());
3666 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3667 <<" failed"<<std::endl;
3669 // Call it done if less than half failed
3670 return num_failed <= num_tried/2;
3673 // IGameDef interface
3675 IItemDefManager *Server::getItemDefManager()
3680 const NodeDefManager *Server::getNodeDefManager()
3685 ICraftDefManager *Server::getCraftDefManager()
3690 u16 Server::allocateUnknownNodeId(const std::string &name)
3692 return m_nodedef->allocateDummy(name);
3695 IWritableItemDefManager *Server::getWritableItemDefManager()
3700 NodeDefManager *Server::getWritableNodeDefManager()
3705 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3710 const std::vector<ModSpec> & Server::getMods() const
3712 return m_modmgr->getMods();
3715 const ModSpec *Server::getModSpec(const std::string &modname) const
3717 return m_modmgr->getModSpec(modname);
3720 std::string Server::getBuiltinLuaPath()
3722 return porting::path_share + DIR_DELIM + "builtin";
3725 v3f Server::findSpawnPos()
3727 ServerMap &map = m_env->getServerMap();
3729 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3730 return nodeposf * BS;
3732 bool is_good = false;
3733 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3734 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3736 // Try to find a good place a few times
3737 for (s32 i = 0; i < 4000 && !is_good; i++) {
3738 s32 range = MYMIN(1 + i, range_max);
3739 // We're going to try to throw the player to this position
3740 v2s16 nodepos2d = v2s16(
3741 -range + myrand_range(0, range*2),
3742 -range + myrand_range(0, range*2));
3743 // Get spawn level at point
3744 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3745 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3746 // signify an unsuitable spawn position, or if outside limits.
3747 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3748 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3751 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3752 // Consecutive empty nodes
3755 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3756 // avoid obstructions in already-generated mapblocks.
3757 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3758 // no obstructions, but mapgen decorations are generated after spawn so
3759 // the player may end up inside one.
3760 for (s32 i = 0; i < 8; i++) {
3761 v3s16 blockpos = getNodeBlockPos(nodepos);
3762 map.emergeBlock(blockpos, true);
3763 content_t c = map.getNode(nodepos).getContent();
3765 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3766 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3767 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3769 if (air_count >= 2) {
3770 // Spawn in lower empty node
3772 nodeposf = intToFloat(nodepos, BS);
3773 // Don't spawn the player outside map boundaries
3774 if (objectpos_over_limit(nodeposf))
3775 // Exit this loop, positions above are probably over limit
3778 // Good position found, cause an exit from main loop
3792 // No suitable spawn point found, return fallback 0,0,0
3793 return v3f(0.0f, 0.0f, 0.0f);
3796 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3798 if (delay == 0.0f) {
3799 // No delay, shutdown immediately
3800 m_shutdown_state.is_requested = true;
3801 // only print to the infostream, a chat message saying
3802 // "Server Shutting Down" is sent when the server destructs.
3803 infostream << "*** Immediate Server shutdown requested." << std::endl;
3804 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3805 // Negative delay, cancel shutdown if requested
3806 m_shutdown_state.reset();
3807 std::wstringstream ws;
3809 ws << L"*** Server shutdown canceled.";
3811 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3812 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3813 // m_shutdown_* are already handled, skip.
3815 } else if (delay > 0.0f) {
3816 // Positive delay, tell the clients when the server will shut down
3817 std::wstringstream ws;
3819 ws << L"*** Server shutting down in "
3820 << duration_to_string(myround(delay)).c_str()
3823 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3824 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3827 m_shutdown_state.trigger(delay, msg, reconnect);
3830 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3833 Try to get an existing player
3835 RemotePlayer *player = m_env->getPlayer(name);
3837 // If player is already connected, cancel
3838 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3839 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3844 If player with the wanted peer_id already exists, cancel.
3846 if (m_env->getPlayer(peer_id)) {
3847 infostream<<"emergePlayer(): Player with wrong name but same"
3848 " peer_id already exists"<<std::endl;
3853 player = new RemotePlayer(name, idef());
3856 bool newplayer = false;
3859 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3861 // Complete init with server parts
3862 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3863 player->protocol_version = proto_version;
3867 m_script->on_newplayer(playersao);
3873 void dedicated_server_loop(Server &server, bool &kill)
3875 verbosestream<<"dedicated_server_loop()"<<std::endl;
3877 IntervalLimiter m_profiler_interval;
3879 static thread_local const float steplen =
3880 g_settings->getFloat("dedicated_server_step");
3881 static thread_local const float profiler_print_interval =
3882 g_settings->getFloat("profiler_print_interval");
3885 * The dedicated server loop only does time-keeping (in Server::step) and
3886 * provides a way to main.cpp to kill the server externally (bool &kill).
3890 // This is kind of a hack but can be done like this
3891 // because server.step() is very light
3892 sleep_ms((int)(steplen*1000.0));
3893 server.step(steplen);
3895 if (server.isShutdownRequested() || kill)
3901 if (profiler_print_interval != 0) {
3902 if(m_profiler_interval.step(steplen, profiler_print_interval))
3904 infostream<<"Profiler:"<<std::endl;
3905 g_profiler->print(infostream);
3906 g_profiler->clear();
3911 infostream << "Dedicated server quitting" << std::endl;
3913 if (g_settings->getBool("server_announce"))
3914 ServerList::sendAnnounce(ServerList::AA_DELETE,
3915 server.m_bind_addr.getPort());
3924 bool Server::joinModChannel(const std::string &channel)
3926 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3927 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3930 bool Server::leaveModChannel(const std::string &channel)
3932 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3935 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3937 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3940 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3944 ModChannel* Server::getModChannel(const std::string &channel)
3946 return m_modchannel_mgr->getModChannel(channel);
3949 void Server::broadcastModChannelMessage(const std::string &channel,
3950 const std::string &message, session_t from_peer)
3952 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3956 if (message.size() > STRING_MAX_LEN) {
3957 warningstream << "ModChannel message too long, dropping before sending "
3958 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3959 << channel << ")" << std::endl;
3964 if (from_peer != PEER_ID_SERVER) {
3965 sender = getPlayerName(from_peer);
3968 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3969 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3970 resp_pkt << channel << sender << message;
3971 for (session_t peer_id : peers) {
3973 if (peer_id == from_peer)
3976 Send(peer_id, &resp_pkt);
3979 if (from_peer != PEER_ID_SERVER) {
3980 m_script->on_modchannel_message(channel, sender, message);
3984 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3986 if (lang_code.empty())
3989 auto it = server_translations.find(lang_code);
3990 if (it != server_translations.end())
3991 return &it->second; // Already loaded
3993 // [] will create an entry
3994 auto *translations = &server_translations[lang_code];
3996 std::string suffix = "." + lang_code + ".tr";
3997 for (const auto &i : m_media) {
3998 if (str_ends_with(i.first, suffix)) {
4000 if (fs::ReadFile(i.second.path, data)) {
4001 translations->loadTranslation(data);
4006 return translations;
4009 ModStorageDatabase *Server::openModStorageDatabase(const std::string &world_path)
4011 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4013 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4014 throw BaseException("Cannot read world.mt!");
4016 std::string backend = world_mt.exists("mod_storage_backend") ?
4017 world_mt.get("mod_storage_backend") : "files";
4018 if (backend == "files")
4019 warningstream << "/!\\ You are using the old mod storage files backend. "
4020 << "This backend is deprecated and may be removed in a future release /!\\"
4021 << std::endl << "Switching to SQLite3 is advised, "
4022 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4024 return openModStorageDatabase(backend, world_path, world_mt);
4027 ModStorageDatabase *Server::openModStorageDatabase(const std::string &backend,
4028 const std::string &world_path, const Settings &world_mt)
4030 if (backend == "sqlite3")
4031 return new ModStorageDatabaseSQLite3(world_path);
4034 if (backend == "postgresql") {
4035 std::string connect_string;
4036 world_mt.getNoEx("pgsql_mod_storage_connection", connect_string);
4037 return new ModStorageDatabasePostgreSQL(connect_string);
4039 #endif // USE_POSTGRESQL
4041 if (backend == "files")
4042 return new ModStorageDatabaseFiles(world_path);
4044 if (backend == "dummy")
4045 return new Database_Dummy();
4047 throw BaseException("Mod storage database backend " + backend + " not supported");
4050 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4052 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4054 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4055 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4056 errorstream << "Cannot read world.mt!" << std::endl;
4060 std::string backend = world_mt.exists("mod_storage_backend") ?
4061 world_mt.get("mod_storage_backend") : "files";
4062 if (backend == migrate_to) {
4063 errorstream << "Cannot migrate: new backend is same"
4064 << " as the old one" << std::endl;
4068 ModStorageDatabase *srcdb = nullptr;
4069 ModStorageDatabase *dstdb = nullptr;
4071 bool succeeded = false;
4074 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4075 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4079 std::vector<std::string> mod_list;
4080 srcdb->listMods(&mod_list);
4081 for (const std::string &modname : mod_list) {
4083 srcdb->getModEntries(modname, &meta);
4084 for (const auto &pair : meta) {
4085 dstdb->setModEntry(modname, pair.first, pair.second);
4093 actionstream << "Successfully migrated the metadata of "
4094 << mod_list.size() << " mods" << std::endl;
4095 world_mt.set("mod_storage_backend", migrate_to);
4096 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4097 errorstream << "Failed to update world.mt!" << std::endl;
4099 actionstream << "world.mt updated" << std::endl;
4101 } catch (BaseException &e) {
4102 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4108 if (succeeded && backend == "files") {
4110 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4111 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4112 if (!fs::Rename(storage_path, backup_path))
4113 warningstream << "After migration, " << storage_path
4114 << " could not be renamed to " << backup_path << std::endl;