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 const ClientDynamicInfo *Server::getClientDynamicInfo(session_t peer_id)
1308 ClientInterface::AutoLock clientlock(m_clients);
1309 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1314 return &client->getDynamicInfo();
1317 void Server::handlePeerChanges()
1319 while(!m_peer_change_queue.empty())
1321 con::PeerChange c = m_peer_change_queue.front();
1322 m_peer_change_queue.pop();
1324 verbosestream<<"Server: Handling peer change: "
1325 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1330 case con::PEER_ADDED:
1331 m_clients.CreateClient(c.peer_id);
1334 case con::PEER_REMOVED:
1335 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1339 FATAL_ERROR("Invalid peer change event received!");
1345 void Server::printToConsoleOnly(const std::string &text)
1348 m_admin_chat->outgoing_queue.push_back(
1349 new ChatEventChat("", utf8_to_wide(text)));
1351 std::cout << text << std::endl;
1355 void Server::Send(NetworkPacket *pkt)
1357 Send(pkt->getPeerId(), pkt);
1360 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1362 m_clients.send(peer_id,
1363 clientCommandFactoryTable[pkt->getCommand()].channel,
1365 clientCommandFactoryTable[pkt->getCommand()].reliable);
1368 void Server::SendMovement(session_t peer_id)
1370 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1372 pkt << g_settings->getFloat("movement_acceleration_default");
1373 pkt << g_settings->getFloat("movement_acceleration_air");
1374 pkt << g_settings->getFloat("movement_acceleration_fast");
1375 pkt << g_settings->getFloat("movement_speed_walk");
1376 pkt << g_settings->getFloat("movement_speed_crouch");
1377 pkt << g_settings->getFloat("movement_speed_fast");
1378 pkt << g_settings->getFloat("movement_speed_climb");
1379 pkt << g_settings->getFloat("movement_speed_jump");
1380 pkt << g_settings->getFloat("movement_liquid_fluidity");
1381 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1382 pkt << g_settings->getFloat("movement_liquid_sink");
1383 pkt << g_settings->getFloat("movement_gravity");
1388 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1390 m_script->player_event(playersao, "health_changed");
1391 SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX);
1393 // Send to other clients
1394 playersao->sendPunchCommand();
1396 if (playersao->isDead())
1397 HandlePlayerDeath(playersao, reason);
1400 void Server::SendPlayerHP(PlayerSAO *playersao, bool effect)
1402 SendHP(playersao->getPeerID(), playersao->getHP(), effect);
1405 void Server::SendHP(session_t peer_id, u16 hp, bool effect)
1407 NetworkPacket pkt(TOCLIENT_HP, 3, peer_id);
1408 pkt << hp << effect;
1412 void Server::SendBreath(session_t peer_id, u16 breath)
1414 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1415 pkt << (u16) breath;
1419 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1420 const std::string &custom_reason, bool reconnect)
1422 assert(reason < SERVER_ACCESSDENIED_MAX);
1424 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1426 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1427 pkt << custom_reason;
1428 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1429 reason == SERVER_ACCESSDENIED_CRASH)
1430 pkt << custom_reason << (u8)reconnect;
1434 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1435 v3f camera_point_target)
1437 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1438 pkt << set_camera_point_target << camera_point_target;
1442 void Server::SendItemDef(session_t peer_id,
1443 IItemDefManager *itemdef, u16 protocol_version)
1445 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1449 u32 length of the next item
1450 zlib-compressed serialized ItemDefManager
1452 std::ostringstream tmp_os(std::ios::binary);
1453 itemdef->serialize(tmp_os, protocol_version);
1454 std::ostringstream tmp_os2(std::ios::binary);
1455 compressZlib(tmp_os.str(), tmp_os2);
1456 pkt.putLongString(tmp_os2.str());
1459 verbosestream << "Server: Sending item definitions to id(" << peer_id
1460 << "): size=" << pkt.getSize() << std::endl;
1465 void Server::SendNodeDef(session_t peer_id,
1466 const NodeDefManager *nodedef, u16 protocol_version)
1468 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1472 u32 length of the next item
1473 zlib-compressed serialized NodeDefManager
1475 std::ostringstream tmp_os(std::ios::binary);
1476 nodedef->serialize(tmp_os, protocol_version);
1477 std::ostringstream tmp_os2(std::ios::binary);
1478 compressZlib(tmp_os.str(), tmp_os2);
1480 pkt.putLongString(tmp_os2.str());
1483 verbosestream << "Server: Sending node definitions to id(" << peer_id
1484 << "): size=" << pkt.getSize() << std::endl;
1490 Non-static send methods
1493 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1495 RemotePlayer *player = sao->getPlayer();
1497 // Do not send new format to old clients
1498 incremental &= player->protocol_version >= 38;
1500 UpdateCrafting(player);
1506 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1508 std::ostringstream os(std::ios::binary);
1509 sao->getInventory()->serialize(os, incremental);
1510 sao->getInventory()->setModified(false);
1511 player->setModified(true);
1513 const std::string &s = os.str();
1514 pkt.putRawString(s.c_str(), s.size());
1518 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1520 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1522 u8 type = message.type;
1523 pkt << version << type << message.sender << message.message
1524 << static_cast<u64>(message.timestamp);
1526 if (peer_id != PEER_ID_INEXISTENT) {
1527 RemotePlayer *player = m_env->getPlayer(peer_id);
1533 m_clients.sendToAll(&pkt);
1537 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1538 const std::string &formname)
1540 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1541 if (formspec.empty()){
1542 //the client should close the formspec
1543 //but make sure there wasn't another one open in meantime
1544 const auto it = m_formspec_state_data.find(peer_id);
1545 if (it != m_formspec_state_data.end() && it->second == formname) {
1546 m_formspec_state_data.erase(peer_id);
1548 pkt.putLongString("");
1550 m_formspec_state_data[peer_id] = formname;
1551 pkt.putLongString(formspec);
1558 // Spawns a particle on peer with peer_id
1559 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1560 const ParticleParameters &p)
1562 static thread_local const float radius =
1563 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1565 if (peer_id == PEER_ID_INEXISTENT) {
1566 std::vector<session_t> clients = m_clients.getClientIDs();
1567 const v3f pos = p.pos * BS;
1568 const float radius_sq = radius * radius;
1570 for (const session_t client_id : clients) {
1571 RemotePlayer *player = m_env->getPlayer(client_id);
1575 PlayerSAO *sao = player->getPlayerSAO();
1579 // Do not send to distant clients
1580 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1583 SendSpawnParticle(client_id, player->protocol_version, p);
1587 assert(protocol_version != 0);
1589 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1592 // NetworkPacket and iostreams are incompatible...
1593 std::ostringstream oss(std::ios_base::binary);
1594 p.serialize(oss, protocol_version);
1595 pkt.putRawString(oss.str());
1601 // Adds a ParticleSpawner on peer with peer_id
1602 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1603 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1605 static thread_local const float radius =
1606 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1608 if (peer_id == PEER_ID_INEXISTENT) {
1609 std::vector<session_t> clients = m_clients.getClientIDs();
1611 p.pos.start.min.val +
1612 p.pos.start.max.val +
1616 const float radius_sq = radius * radius;
1617 /* Don't send short-lived spawners to distant players.
1618 * This could be replaced with proper tracking at some point. */
1619 const bool distance_check = !attached_id && p.time <= 1.0f;
1621 for (const session_t client_id : clients) {
1622 RemotePlayer *player = m_env->getPlayer(client_id);
1626 if (distance_check) {
1627 PlayerSAO *sao = player->getPlayerSAO();
1630 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1634 SendAddParticleSpawner(client_id, player->protocol_version,
1635 p, attached_id, id);
1639 assert(protocol_version != 0);
1641 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1643 pkt << p.amount << p.time;
1644 { // serialize legacy fields
1645 std::ostringstream os(std::ios_base::binary);
1646 p.pos.start.legacySerialize(os);
1647 p.vel.start.legacySerialize(os);
1648 p.acc.start.legacySerialize(os);
1649 p.exptime.start.legacySerialize(os);
1650 p.size.start.legacySerialize(os);
1651 pkt.putRawString(os.str());
1653 pkt << p.collisiondetection;
1655 pkt.putLongString(p.texture.string);
1657 pkt << id << p.vertical << p.collision_removal << attached_id;
1659 std::ostringstream os(std::ios_base::binary);
1660 p.animation.serialize(os, protocol_version);
1661 pkt.putRawString(os.str());
1663 pkt << p.glow << p.object_collision;
1664 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1666 { // serialize new fields
1667 // initial bias for older properties
1668 pkt << p.pos.start.bias
1671 << p.exptime.start.bias
1672 << p.size.start.bias;
1674 std::ostringstream os(std::ios_base::binary);
1676 // final tween frames of older properties
1677 p.pos.end.serialize(os);
1678 p.vel.end.serialize(os);
1679 p.acc.end.serialize(os);
1680 p.exptime.end.serialize(os);
1681 p.size.end.serialize(os);
1683 // properties for legacy texture field
1684 p.texture.serialize(os, protocol_version, true);
1687 p.drag.serialize(os);
1688 p.jitter.serialize(os);
1689 p.bounce.serialize(os);
1690 ParticleParamTypes::serializeParameterValue(os, p.attractor_kind);
1691 if (p.attractor_kind != ParticleParamTypes::AttractorKind::none) {
1692 p.attract.serialize(os);
1693 p.attractor_origin.serialize(os);
1694 writeU16(os, p.attractor_attachment); /* object ID */
1695 writeU8(os, p.attractor_kill);
1696 if (p.attractor_kind != ParticleParamTypes::AttractorKind::point) {
1697 p.attractor_direction.serialize(os);
1698 writeU16(os, p.attractor_direction_attachment);
1701 p.radius.serialize(os);
1703 ParticleParamTypes::serializeParameterValue(os, (u16)p.texpool.size());
1704 for (const auto& tex : p.texpool) {
1705 tex.serialize(os, protocol_version);
1708 pkt.putRawString(os.str());
1714 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1716 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1720 if (peer_id != PEER_ID_INEXISTENT)
1723 m_clients.sendToAll(&pkt);
1727 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1729 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1731 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1732 << form->text << form->number << form->item << form->dir
1733 << form->align << form->offset << form->world_pos << form->size
1734 << form->z_index << form->text2 << form->style;
1739 void Server::SendHUDRemove(session_t peer_id, u32 id)
1741 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1746 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1748 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1749 pkt << id << (u8) stat;
1753 case HUD_STAT_SCALE:
1754 case HUD_STAT_ALIGN:
1755 case HUD_STAT_OFFSET:
1756 pkt << *(v2f *) value;
1760 case HUD_STAT_TEXT2:
1761 pkt << *(std::string *) value;
1763 case HUD_STAT_WORLD_POS:
1764 pkt << *(v3f *) value;
1767 pkt << *(v2s32 *) value;
1769 default: // all other types
1770 pkt << *(u32 *) value;
1777 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1779 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1781 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1783 pkt << flags << mask;
1788 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1790 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1791 pkt << param << value;
1795 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1797 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1799 // Handle prior clients here
1800 if (m_clients.getProtocolVersion(peer_id) < 39) {
1801 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1803 for (const std::string& texture : params.textures)
1806 pkt << params.clouds;
1807 } else { // Handle current clients and future clients
1808 pkt << params.bgcolor << params.type
1809 << params.clouds << params.fog_sun_tint
1810 << params.fog_moon_tint << params.fog_tint_type;
1812 if (params.type == "skybox") {
1813 pkt << (u16) params.textures.size();
1814 for (const std::string &texture : params.textures)
1816 } else if (params.type == "regular") {
1817 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1818 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1819 << params.sky_color.night_sky << params.sky_color.night_horizon
1820 << params.sky_color.indoors;
1823 pkt << params.body_orbit_tilt;
1829 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1831 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1832 pkt << params.visible << params.texture
1833 << params.tonemap << params.sunrise
1834 << params.sunrise_visible << params.scale;
1838 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1840 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1842 pkt << params.visible << params.texture
1843 << params.tonemap << params.scale;
1847 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1849 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1851 pkt << params.visible << params.count
1852 << params.starcolor << params.scale
1853 << params.day_opacity;
1858 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1860 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1861 pkt << params.density << params.color_bright << params.color_ambient
1862 << params.height << params.thickness << params.speed;
1866 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1869 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1872 pkt << do_override << (u16) (ratio * 65535);
1877 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1879 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1882 pkt << lighting.shadow_intensity;
1883 pkt << lighting.saturation;
1885 pkt << lighting.exposure.luminance_min
1886 << lighting.exposure.luminance_max
1887 << lighting.exposure.exposure_correction
1888 << lighting.exposure.speed_dark_bright
1889 << lighting.exposure.speed_bright_dark
1890 << lighting.exposure.center_weight_power;
1895 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1897 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1898 pkt << time << time_speed;
1900 if (peer_id == PEER_ID_INEXISTENT) {
1901 m_clients.sendToAll(&pkt);
1908 void Server::SendPlayerBreath(PlayerSAO *sao)
1912 m_script->player_event(sao, "breath_changed");
1913 SendBreath(sao->getPeerID(), sao->getBreath());
1916 void Server::SendMovePlayer(session_t peer_id)
1918 RemotePlayer *player = m_env->getPlayer(peer_id);
1920 PlayerSAO *sao = player->getPlayerSAO();
1923 // Send attachment updates instantly to the client prior updating position
1924 sao->sendOutdatedData();
1926 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1927 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1930 v3f pos = sao->getBasePosition();
1931 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1932 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1933 << " pitch=" << sao->getLookPitch()
1934 << " yaw=" << sao->getRotation().Y
1941 void Server::SendPlayerFov(session_t peer_id)
1943 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1945 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1946 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1951 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1952 f32 animation_speed)
1954 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1957 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1958 << animation_frames[3] << animation_speed;
1963 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1965 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1966 pkt << first << third;
1970 void Server::SendPlayerPrivileges(session_t peer_id)
1972 RemotePlayer *player = m_env->getPlayer(peer_id);
1974 if(player->getPeerId() == PEER_ID_INEXISTENT)
1977 std::set<std::string> privs;
1978 m_script->getAuth(player->getName(), NULL, &privs);
1980 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1981 pkt << (u16) privs.size();
1983 for (const std::string &priv : privs) {
1990 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1992 RemotePlayer *player = m_env->getPlayer(peer_id);
1994 if (player->getPeerId() == PEER_ID_INEXISTENT)
1997 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1998 pkt.putLongString(player->inventory_formspec);
2003 void Server::SendPlayerFormspecPrepend(session_t peer_id)
2005 RemotePlayer *player = m_env->getPlayer(peer_id);
2007 if (player->getPeerId() == PEER_ID_INEXISTENT)
2010 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
2011 pkt << player->formspec_prepend;
2015 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
2017 // Radius inside which objects are active
2018 static thread_local const s16 radius =
2019 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
2021 // Radius inside which players are active
2022 static thread_local const bool is_transfer_limited =
2023 g_settings->exists("unlimited_player_transfer_distance") &&
2024 !g_settings->getBool("unlimited_player_transfer_distance");
2026 static thread_local const s16 player_transfer_dist =
2027 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
2029 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
2030 radius : player_transfer_dist;
2032 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
2036 std::queue<u16> removed_objects, added_objects;
2037 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
2038 client->m_known_objects, removed_objects);
2039 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
2040 client->m_known_objects, added_objects);
2042 int removed_count = removed_objects.size();
2043 int added_count = added_objects.size();
2045 if (removed_objects.empty() && added_objects.empty())
2051 // Handle removed objects
2052 writeU16((u8*)buf, removed_objects.size());
2053 data.append(buf, 2);
2054 while (!removed_objects.empty()) {
2056 u16 id = removed_objects.front();
2057 ServerActiveObject* obj = m_env->getActiveObject(id);
2059 // Add to data buffer for sending
2060 writeU16((u8*)buf, id);
2061 data.append(buf, 2);
2063 // Remove from known objects
2064 client->m_known_objects.erase(id);
2066 if (obj && obj->m_known_by_count > 0)
2067 obj->m_known_by_count--;
2069 removed_objects.pop();
2072 // Handle added objects
2073 writeU16((u8*)buf, added_objects.size());
2074 data.append(buf, 2);
2075 while (!added_objects.empty()) {
2077 u16 id = added_objects.front();
2078 ServerActiveObject *obj = m_env->getActiveObject(id);
2079 added_objects.pop();
2082 warningstream << FUNCTION_NAME << ": NULL object id="
2083 << (int)id << std::endl;
2088 u8 type = obj->getSendType();
2090 // Add to data buffer for sending
2091 writeU16((u8*)buf, id);
2092 data.append(buf, 2);
2093 writeU8((u8*)buf, type);
2094 data.append(buf, 1);
2096 data.append(serializeString32(
2097 obj->getClientInitializationData(client->net_proto_version)));
2099 // Add to known objects
2100 client->m_known_objects.insert(id);
2102 obj->m_known_by_count++;
2105 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2106 pkt.putRawString(data.c_str(), data.size());
2109 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2110 << removed_count << " removed, " << added_count << " added, "
2111 << "packet size is " << pkt.getSize() << std::endl;
2114 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2117 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2118 datas.size(), peer_id);
2120 pkt.putRawString(datas.c_str(), datas.size());
2122 m_clients.send(pkt.getPeerId(),
2123 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2127 void Server::SendCSMRestrictionFlags(session_t peer_id)
2129 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2130 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2131 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2135 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2137 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2142 inline s32 Server::nextSoundId()
2144 s32 ret = m_next_sound_id;
2145 if (m_next_sound_id == INT32_MAX)
2146 m_next_sound_id = 0; // signed overflow is undefined
2152 s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
2154 // Find out initial position of sound
2155 bool pos_exists = false;
2156 const v3f pos = params.getPos(m_env, &pos_exists);
2157 // If position is not found while it should be, cancel sound
2158 if(pos_exists != (params.type != SoundLocation::Local))
2161 // Filter destination clients
2162 std::vector<session_t> dst_clients;
2163 if (!params.to_player.empty()) {
2164 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2166 infostream<<"Server::playSound: Player \""<<params.to_player
2167 <<"\" not found"<<std::endl;
2170 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2171 infostream<<"Server::playSound: Player \""<<params.to_player
2172 <<"\" not connected"<<std::endl;
2175 dst_clients.push_back(player->getPeerId());
2177 std::vector<session_t> clients = m_clients.getClientIDs();
2179 for (const session_t client_id : clients) {
2180 RemotePlayer *player = m_env->getPlayer(client_id);
2183 if (!params.exclude_player.empty() &&
2184 params.exclude_player == player->getName())
2187 PlayerSAO *sao = player->getPlayerSAO();
2192 if(sao->getBasePosition().getDistanceFrom(pos) >
2193 params.max_hear_distance)
2196 dst_clients.push_back(client_id);
2200 if(dst_clients.empty())
2203 // old clients will still use this, so pick a reserved ID (-1)
2204 const s32 id = ephemeral ? -1 : nextSoundId();
2206 float gain = params.gain * params.spec.gain;
2207 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2208 pkt << id << params.spec.name << gain
2209 << (u8) params.type << pos << params.object
2210 << params.spec.loop << params.spec.fade << params.spec.pitch
2213 bool as_reliable = !ephemeral;
2215 for (const session_t peer_id : dst_clients) {
2217 params.clients.insert(peer_id);
2218 m_clients.send(peer_id, 0, &pkt, as_reliable);
2222 m_playing_sounds[id] = std::move(params);
2225 void Server::stopSound(s32 handle)
2227 auto it = m_playing_sounds.find(handle);
2228 if (it == m_playing_sounds.end())
2231 ServerPlayingSound &psound = it->second;
2233 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2236 for (session_t peer_id : psound.clients) {
2238 m_clients.send(peer_id, 0, &pkt, true);
2241 // Remove sound reference
2242 m_playing_sounds.erase(it);
2245 void Server::fadeSound(s32 handle, float step, float gain)
2247 auto it = m_playing_sounds.find(handle);
2248 if (it == m_playing_sounds.end())
2251 ServerPlayingSound &psound = it->second;
2252 psound.gain = gain; // destination gain
2254 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2255 pkt << handle << step << gain;
2257 for (session_t peer_id : psound.clients) {
2259 m_clients.send(peer_id, 0, &pkt, true);
2262 // Remove sound reference
2263 if (gain <= 0 || psound.clients.empty())
2264 m_playing_sounds.erase(it);
2267 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2270 v3f p_f = intToFloat(p, BS);
2271 v3s16 block_pos = getNodeBlockPos(p);
2273 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2276 sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2279 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2280 float far_d_nodes, bool remove_metadata)
2282 v3f p_f = intToFloat(p, BS);
2283 v3s16 block_pos = getNodeBlockPos(p);
2285 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2286 pkt << p << n.param0 << n.param1 << n.param2
2287 << (u8) (remove_metadata ? 0 : 1);
2288 sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2291 void Server::sendNodeChangePkt(NetworkPacket &pkt, v3s16 block_pos,
2292 v3f p, float far_d_nodes, std::unordered_set<u16> *far_players)
2294 float maxd = far_d_nodes * BS;
2295 std::vector<session_t> clients = m_clients.getClientIDs();
2296 ClientInterface::AutoLock clientlock(m_clients);
2298 for (session_t client_id : clients) {
2299 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2303 RemotePlayer *player = m_env->getPlayer(client_id);
2304 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2306 // If player is far away, only set modified blocks not sent
2307 if (!client->isBlockSent(block_pos) || (sao &&
2308 sao->getBasePosition().getDistanceFrom(p) > maxd)) {
2310 far_players->emplace(client_id);
2312 client->SetBlockNotSent(block_pos);
2317 m_clients.send(client_id, 0, &pkt, true);
2321 void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
2323 NodeMetadataList meta_updates_list(false);
2324 std::ostringstream os(std::ios::binary);
2326 std::vector<session_t> clients = m_clients.getClientIDs();
2327 ClientInterface::AutoLock clientlock(m_clients);
2329 for (session_t i : clients) {
2330 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2334 ServerActiveObject *player = getPlayerSAO(i);
2337 player_pos = floatToInt(player->getBasePosition(), BS);
2339 for (const v3s16 pos : positions) {
2340 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2345 v3s16 block_pos = getNodeBlockPos(pos);
2346 if (!client->isBlockSent(block_pos) ||
2347 player_pos.getDistanceFrom(pos) > far_d_nodes) {
2348 client->SetBlockNotSent(block_pos);
2352 // Add the change to send list
2353 meta_updates_list.set(pos, meta);
2355 if (meta_updates_list.size() == 0)
2358 // Send the meta changes
2360 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2361 std::string raw = os.str();
2363 compressZlib(raw, os);
2365 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
2366 pkt.putLongString(os.str());
2369 meta_updates_list.clear();
2373 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2374 u16 net_proto_version, SerializedBlockCache *cache)
2376 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2377 std::string s, *sptr = nullptr;
2380 auto it = cache->find({block->getPos(), ver});
2381 if (it != cache->end())
2385 // Serialize the block in the right format
2387 std::ostringstream os(std::ios_base::binary);
2388 block->serialize(os, ver, false, net_compression_level);
2389 block->serializeNetworkSpecific(os);
2394 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2395 pkt << block->getPos();
2396 pkt.putRawString(*sptr);
2399 // Store away in cache
2400 if (cache && sptr == &s)
2401 (*cache)[{block->getPos(), ver}] = std::move(s);
2404 void Server::SendBlocks(float dtime)
2406 MutexAutoLock envlock(m_env_mutex);
2407 //TODO check if one big lock could be faster then multiple small ones
2409 std::vector<PrioritySortedBlockTransfer> queue;
2411 u32 total_sending = 0, unique_clients = 0;
2414 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2416 std::vector<session_t> clients = m_clients.getClientIDs();
2418 ClientInterface::AutoLock clientlock(m_clients);
2419 for (const session_t client_id : clients) {
2420 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2425 total_sending += client->getSendingCount();
2426 const auto old_count = queue.size();
2427 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2428 unique_clients += queue.size() > old_count ? 1 : 0;
2433 // Lowest priority number comes first.
2434 // Lowest is most important.
2435 std::sort(queue.begin(), queue.end());
2437 ClientInterface::AutoLock clientlock(m_clients);
2439 // Maximal total count calculation
2440 // The per-client block sends is halved with the maximal online users
2441 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2442 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2444 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2445 Map &map = m_env->getMap();
2447 SerializedBlockCache cache, *cache_ptr = nullptr;
2448 if (unique_clients > 1) {
2449 // caching is pointless with a single client
2453 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2454 if (total_sending >= max_blocks_to_send)
2457 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2461 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2466 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2467 client->net_proto_version, cache_ptr);
2469 client->SentBlock(block_to_send.pos);
2474 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2476 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2480 ClientInterface::AutoLock clientlock(m_clients);
2481 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2482 if (!client || client->isBlockSent(blockpos))
2484 SendBlockNoLock(peer_id, block, client->serialization_version,
2485 client->net_proto_version);
2490 bool Server::addMediaFile(const std::string &filename,
2491 const std::string &filepath, std::string *filedata_to,
2492 std::string *digest_to)
2494 // If name contains illegal characters, ignore the file
2495 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2496 infostream << "Server: ignoring illegal file name: \""
2497 << filename << "\"" << std::endl;
2500 // If name is not in a supported format, ignore it
2501 const char *supported_ext[] = {
2502 ".png", ".jpg", ".bmp", ".tga",
2504 ".x", ".b3d", ".obj",
2505 // Custom translation file format
2509 if (removeStringEnd(filename, supported_ext).empty()) {
2510 infostream << "Server: ignoring unsupported file extension: \""
2511 << filename << "\"" << std::endl;
2514 // Ok, attempt to load the file and add to cache
2517 std::string filedata;
2518 if (!fs::ReadFile(filepath, filedata)) {
2519 errorstream << "Server::addMediaFile(): Failed to open \""
2520 << filename << "\" for reading" << std::endl;
2524 if (filedata.empty()) {
2525 errorstream << "Server::addMediaFile(): Empty file \""
2526 << filepath << "\"" << std::endl;
2531 sha1.addBytes(filedata.c_str(), filedata.length());
2533 unsigned char *digest = sha1.getDigest();
2534 std::string sha1_base64 = base64_encode(digest, 20);
2535 std::string sha1_hex = hex_encode((char*) digest, 20);
2537 *digest_to = std::string((char*) digest, 20);
2541 m_media[filename] = MediaInfo(filepath, sha1_base64);
2542 verbosestream << "Server: " << sha1_hex << " is " << filename
2546 *filedata_to = std::move(filedata);
2550 void Server::fillMediaCache()
2552 infostream << "Server: Calculating media file checksums" << std::endl;
2554 // Collect all media file paths
2555 std::vector<std::string> paths;
2557 // ordered in descending priority
2558 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2559 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2560 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2561 m_modmgr->getModsMediaPaths(paths);
2563 // Collect media file information from paths into cache
2564 for (const std::string &mediapath : paths) {
2565 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2566 for (const fs::DirListNode &dln : dirlist) {
2567 if (dln.dir) // Ignore dirs (already in paths)
2570 const std::string &filename = dln.name;
2571 if (m_media.find(filename) != m_media.end()) // Do not override
2574 std::string filepath = mediapath;
2575 filepath.append(DIR_DELIM).append(filename);
2576 addMediaFile(filename, filepath);
2580 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2583 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2586 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2589 std::string lang_suffix;
2590 lang_suffix.append(".").append(lang_code).append(".tr");
2591 for (const auto &i : m_media) {
2592 if (i.second.no_announce)
2594 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2601 for (const auto &i : m_media) {
2602 if (i.second.no_announce)
2604 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2606 pkt << i.first << i.second.sha1_digest;
2609 pkt << g_settings->get("remote_media");
2612 verbosestream << "Server: Announcing files to id(" << peer_id
2613 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2616 struct SendableMedia
2622 SendableMedia(const std::string &name, const std::string &path,
2623 std::string &&data):
2624 name(name), path(path), data(std::move(data))
2628 void Server::sendRequestedMedia(session_t peer_id,
2629 const std::vector<std::string> &tosend)
2631 verbosestream<<"Server::sendRequestedMedia(): "
2632 <<"Sending files to client"<<std::endl;
2636 // Put 5kB in one bunch (this is not accurate)
2637 u32 bytes_per_bunch = 5000;
2639 std::vector< std::vector<SendableMedia> > file_bunches;
2640 file_bunches.emplace_back();
2642 u32 file_size_bunch_total = 0;
2644 for (const std::string &name : tosend) {
2645 if (m_media.find(name) == m_media.end()) {
2646 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2647 <<"unknown file \""<<(name)<<"\""<<std::endl;
2651 const auto &m = m_media[name];
2655 if (!fs::ReadFile(m.path, data)) {
2656 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2657 << name << "\"" << std::endl;
2660 file_size_bunch_total += data.size();
2663 file_bunches.back().emplace_back(name, m.path, std::move(data));
2665 // Start next bunch if got enough data
2666 if(file_size_bunch_total >= bytes_per_bunch) {
2667 file_bunches.emplace_back();
2668 file_size_bunch_total = 0;
2673 /* Create and send packets */
2675 u16 num_bunches = file_bunches.size();
2676 for (u16 i = 0; i < num_bunches; i++) {
2679 u16 total number of texture bunches
2680 u16 index of this bunch
2681 u32 number of files in this bunch
2690 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2691 pkt << num_bunches << i << (u32) file_bunches[i].size();
2693 for (const SendableMedia &j : file_bunches[i]) {
2695 pkt.putLongString(j.data);
2698 verbosestream << "Server::sendRequestedMedia(): bunch "
2699 << i << "/" << num_bunches
2700 << " files=" << file_bunches[i].size()
2701 << " size=" << pkt.getSize() << std::endl;
2706 void Server::stepPendingDynMediaCallbacks(float dtime)
2708 MutexAutoLock lock(m_env_mutex);
2710 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2711 it->second.expiry_timer -= dtime;
2712 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2719 const auto &name = it->second.filename;
2720 if (!name.empty()) {
2721 assert(m_media.count(name));
2722 // if no_announce isn't set we're definitely deleting the wrong file!
2723 sanity_check(m_media[name].no_announce);
2725 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2726 m_media.erase(name);
2728 getScriptIface()->freeDynamicMediaCallback(it->first);
2729 it = m_pending_dyn_media.erase(it);
2733 void Server::SendMinimapModes(session_t peer_id,
2734 std::vector<MinimapMode> &modes, size_t wanted_mode)
2736 RemotePlayer *player = m_env->getPlayer(peer_id);
2738 if (player->getPeerId() == PEER_ID_INEXISTENT)
2741 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2742 pkt << (u16)modes.size() << (u16)wanted_mode;
2744 for (auto &mode : modes)
2745 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2750 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2752 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2756 pkt << false; // Remove inventory
2758 pkt << true; // Update inventory
2760 // Serialization & NetworkPacket isn't a love story
2761 std::ostringstream os(std::ios_base::binary);
2762 inventory->serialize(os);
2763 inventory->setModified(false);
2765 const std::string &os_str = os.str();
2766 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2767 pkt.putRawString(os_str);
2770 if (peer_id == PEER_ID_INEXISTENT)
2771 m_clients.sendToAll(&pkt);
2776 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2778 // Lookup player name, to filter detached inventories just after
2779 std::string peer_name;
2780 if (peer_id != PEER_ID_INEXISTENT) {
2781 peer_name = getClient(peer_id, CS_Created)->getName();
2784 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2785 sendDetachedInventory(inv, name, peer_id);
2788 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2795 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2797 infostream << "Server::DiePlayer(): Player "
2798 << playersao->getPlayer()->getName()
2799 << " dies" << std::endl;
2801 playersao->clearParentAttachment();
2803 // Trigger scripted stuff
2804 m_script->on_dieplayer(playersao, reason);
2806 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2809 void Server::RespawnPlayer(session_t peer_id)
2811 PlayerSAO *playersao = getPlayerSAO(peer_id);
2814 infostream << "Server::RespawnPlayer(): Player "
2815 << playersao->getPlayer()->getName()
2816 << " respawns" << std::endl;
2818 const auto *prop = playersao->accessObjectProperties();
2819 playersao->setHP(prop->hp_max,
2820 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2821 playersao->setBreath(prop->breath_max);
2823 bool repositioned = m_script->on_respawnplayer(playersao);
2824 if (!repositioned) {
2825 // setPos will send the new position to client
2826 playersao->setPos(findSpawnPos());
2831 void Server::DenySudoAccess(session_t peer_id)
2833 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2838 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2839 const std::string &custom_reason, bool reconnect)
2841 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2842 m_clients.event(peer_id, CSE_SetDenied);
2843 DisconnectPeer(peer_id);
2846 void Server::DisconnectPeer(session_t peer_id)
2848 m_modchannel_mgr->leaveAllChannels(peer_id);
2849 m_con->DisconnectPeer(peer_id);
2852 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2855 RemoteClient* client = getClient(peer_id, CS_Invalid);
2857 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2859 // Right now, the auth mechs don't change between login and sudo mode.
2860 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2861 client->allowed_sudo_mechs = sudo_auth_mechs;
2863 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2864 << g_settings->getFloat("dedicated_server_step")
2868 m_clients.event(peer_id, CSE_AuthAccept);
2870 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2872 // We only support SRP right now
2873 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2875 resp_pkt << sudo_auth_mechs;
2877 m_clients.event(peer_id, CSE_SudoSuccess);
2881 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2883 std::wstring message;
2886 Clear references to playing sounds
2888 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2889 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2890 ServerPlayingSound &psound = i->second;
2891 psound.clients.erase(peer_id);
2892 if (psound.clients.empty())
2893 m_playing_sounds.erase(i++);
2898 // clear formspec info so the next client can't abuse the current state
2899 m_formspec_state_data.erase(peer_id);
2901 RemotePlayer *player = m_env->getPlayer(peer_id);
2903 /* Run scripts and remove from environment */
2905 PlayerSAO *playersao = player->getPlayerSAO();
2908 playersao->clearChildAttachments();
2909 playersao->clearParentAttachment();
2911 // inform connected clients
2912 const std::string &player_name = player->getName();
2913 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2914 // (u16) 1 + std::string represents a vector serialization representation
2915 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2916 m_clients.sendToAll(¬ice);
2918 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2920 playersao->disconnected();
2927 if (player && reason != CDR_DENY) {
2928 std::ostringstream os(std::ios_base::binary);
2929 std::vector<session_t> clients = m_clients.getClientIDs();
2931 for (const session_t client_id : clients) {
2933 RemotePlayer *player = m_env->getPlayer(client_id);
2937 // Get name of player
2938 os << player->getName() << " ";
2941 std::string name = player->getName();
2942 actionstream << name << " "
2943 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2944 << " List of players: " << os.str() << std::endl;
2946 m_admin_chat->outgoing_queue.push_back(
2947 new ChatEventNick(CET_NICK_REMOVE, name));
2951 MutexAutoLock env_lock(m_env_mutex);
2952 m_clients.DeleteClient(peer_id);
2956 // Send leave chat message to all remaining clients
2957 if (!message.empty()) {
2958 SendChatMessage(PEER_ID_INEXISTENT,
2959 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2963 void Server::UpdateCrafting(RemotePlayer *player)
2965 InventoryList *clist = player->inventory.getList("craft");
2966 if (!clist || clist->getSize() == 0)
2969 if (!clist->checkModified())
2972 // Get a preview for crafting
2974 InventoryLocation loc;
2975 loc.setPlayer(player->getName());
2976 std::vector<ItemStack> output_replacements;
2977 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2978 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2981 InventoryList *plist = player->inventory.getList("craftpreview");
2982 if (plist && plist->getSize() >= 1) {
2983 // Put the new preview in
2984 plist->changeItem(0, preview);
2988 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2990 if (evt->type == CET_NICK_ADD) {
2991 // The terminal informed us of its nick choice
2992 m_admin_nick = ((ChatEventNick *)evt)->nick;
2993 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2994 errorstream << "You haven't set up an account." << std::endl
2995 << "Please log in using the client as '"
2996 << m_admin_nick << "' with a secure password." << std::endl
2997 << "Until then, you can't execute admin tasks via the console," << std::endl
2998 << "and everybody can claim the user account instead of you," << std::endl
2999 << "giving them full control over this server." << std::endl;
3002 assert(evt->type == CET_CHAT);
3003 handleAdminChat((ChatEventChat *)evt);
3007 std::wstring Server::handleChat(const std::string &name,
3008 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
3010 // If something goes wrong, this player is to blame
3011 RollbackScopeActor rollback_scope(m_rollback,
3012 std::string("player:") + name);
3014 if (g_settings->getBool("strip_color_codes"))
3015 wmessage = unescape_enriched(wmessage);
3018 switch (player->canSendChatMessage()) {
3019 case RPLAYER_CHATRESULT_FLOODING: {
3020 std::wstringstream ws;
3021 ws << L"You cannot send more messages. You are limited to "
3022 << g_settings->getFloat("chat_message_limit_per_10sec")
3023 << L" messages per 10 seconds.";
3026 case RPLAYER_CHATRESULT_KICK:
3027 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
3028 "You have been kicked due to message flooding.");
3030 case RPLAYER_CHATRESULT_OK:
3033 FATAL_ERROR("Unhandled chat filtering result found.");
3037 if (m_max_chatmessage_length > 0
3038 && wmessage.length() > m_max_chatmessage_length) {
3039 return L"Your message exceed the maximum chat message limit set on the server. "
3040 L"It was refused. Send a shorter message";
3043 auto message = trim(wide_to_utf8(wmessage));
3044 if (message.empty())
3047 if (message.find_first_of("\n\r") != std::wstring::npos) {
3048 return L"Newlines are not permitted in chat messages";
3051 // Run script hook, exit if script ate the chat message
3052 if (m_script->on_chat_message(name, message))
3057 // Whether to send line to the player that sent the message, or to all players
3058 bool broadcast_line = true;
3060 if (check_shout_priv && !checkPriv(name, "shout")) {
3061 line += L"-!- You don't have permission to shout.";
3062 broadcast_line = false;
3065 Workaround for fixing chat on Android. Lua doesn't handle
3066 the Cyrillic alphabet and some characters on older Android devices
3069 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3071 line += utf8_to_wide(m_script->formatChatMessage(name,
3072 wide_to_utf8(wmessage)));
3077 Tell calling method to send the message to sender
3079 if (!broadcast_line)
3083 Send the message to others
3085 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3087 ChatMessage chatmsg(line);
3089 std::vector<session_t> clients = m_clients.getClientIDs();
3090 for (u16 cid : clients)
3091 SendChatMessage(cid, chatmsg);
3096 void Server::handleAdminChat(const ChatEventChat *evt)
3098 std::string name = evt->nick;
3099 std::wstring wmessage = evt->evt_msg;
3101 std::wstring answer = handleChat(name, wmessage);
3103 // If asked to send answer to sender
3104 if (!answer.empty()) {
3105 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3109 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3111 RemoteClient *client = getClientNoEx(peer_id,state_min);
3113 throw ClientNotFoundException("Client not found");
3117 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3119 return m_clients.getClientNoEx(peer_id, state_min);
3122 std::string Server::getPlayerName(session_t peer_id)
3124 RemotePlayer *player = m_env->getPlayer(peer_id);
3126 return "[id="+itos(peer_id)+"]";
3127 return player->getName();
3130 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3132 RemotePlayer *player = m_env->getPlayer(peer_id);
3135 return player->getPlayerSAO();
3138 std::string Server::getStatusString()
3140 std::ostringstream os(std::ios_base::binary);
3143 os << "version: " << g_version_string;
3145 os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3147 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3149 os << " | max lag: " << std::setprecision(3);
3150 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3152 // Information about clients
3154 os << " | clients: ";
3156 std::vector<session_t> clients = m_clients.getClientIDs();
3157 for (session_t client_id : clients) {
3158 RemotePlayer *player = m_env->getPlayer(client_id);
3160 // Get name of player
3161 const char *name = player ? player->getName() : "<unknown>";
3163 // Add name to information string
3172 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3173 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3175 if (!g_settings->get("motd").empty())
3176 os << std::endl << "# Server: " << g_settings->get("motd");
3181 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3183 std::set<std::string> privs;
3184 m_script->getAuth(name, NULL, &privs);
3188 bool Server::checkPriv(const std::string &name, const std::string &priv)
3190 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3191 return (privs.count(priv) != 0);
3194 void Server::reportPrivsModified(const std::string &name)
3197 std::vector<session_t> clients = m_clients.getClientIDs();
3198 for (const session_t client_id : clients) {
3199 RemotePlayer *player = m_env->getPlayer(client_id);
3200 reportPrivsModified(player->getName());
3203 RemotePlayer *player = m_env->getPlayer(name.c_str());
3206 SendPlayerPrivileges(player->getPeerId());
3207 PlayerSAO *sao = player->getPlayerSAO();
3210 sao->updatePrivileges(
3211 getPlayerEffectivePrivs(name),
3216 void Server::reportInventoryFormspecModified(const std::string &name)
3218 RemotePlayer *player = m_env->getPlayer(name.c_str());
3221 SendPlayerInventoryFormspec(player->getPeerId());
3224 void Server::reportFormspecPrependModified(const std::string &name)
3226 RemotePlayer *player = m_env->getPlayer(name.c_str());
3229 SendPlayerFormspecPrepend(player->getPeerId());
3232 void Server::setIpBanned(const std::string &ip, const std::string &name)
3234 m_banmanager->add(ip, name);
3237 void Server::unsetIpBanned(const std::string &ip_or_name)
3239 m_banmanager->remove(ip_or_name);
3242 std::string Server::getBanDescription(const std::string &ip_or_name)
3244 return m_banmanager->getBanDescription(ip_or_name);
3247 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3249 // m_env will be NULL if the server is initializing
3253 if (m_admin_nick == name && !m_admin_nick.empty()) {
3254 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3257 RemotePlayer *player = m_env->getPlayer(name);
3262 if (player->getPeerId() == PEER_ID_INEXISTENT)
3265 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3268 bool Server::showFormspec(const char *playername, const std::string &formspec,
3269 const std::string &formname)
3271 // m_env will be NULL if the server is initializing
3275 RemotePlayer *player = m_env->getPlayer(playername);
3279 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3283 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3288 u32 id = player->addHud(form);
3290 SendHUDAdd(player->getPeerId(), id, form);
3295 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3299 HudElement* todel = player->removeHud(id);
3306 SendHUDRemove(player->getPeerId(), id);
3310 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3315 SendHUDChange(player->getPeerId(), id, stat, data);
3319 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3324 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3325 if (new_hud_flags == player->hud_flags) // no change
3328 SendHUDSetFlags(player->getPeerId(), flags, mask);
3329 player->hud_flags = new_hud_flags;
3331 PlayerSAO* playersao = player->getPlayerSAO();
3336 m_script->player_event(playersao, "hud_changed");
3340 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3345 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3348 player->setHotbarItemcount(hotbar_itemcount);
3349 std::ostringstream os(std::ios::binary);
3350 writeS32(os, hotbar_itemcount);
3351 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3355 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3360 player->setHotbarImage(name);
3361 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3364 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3369 player->setHotbarSelectedImage(name);
3370 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3373 Address Server::getPeerAddress(session_t peer_id)
3375 // Note that this is only set after Init was received in Server::handleCommand_Init
3376 return getClient(peer_id, CS_Invalid)->getAddress();
3379 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3380 v2s32 animation_frames[4], f32 frame_speed)
3382 sanity_check(player);
3383 player->setLocalAnimations(animation_frames, frame_speed);
3384 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3387 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3389 sanity_check(player);
3390 player->eye_offset_first = first;
3391 player->eye_offset_third = third;
3392 SendEyeOffset(player->getPeerId(), first, third);
3395 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3397 sanity_check(player);
3398 player->setSky(params);
3399 SendSetSky(player->getPeerId(), params);
3402 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3404 sanity_check(player);
3405 player->setSun(params);
3406 SendSetSun(player->getPeerId(), params);
3409 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3411 sanity_check(player);
3412 player->setMoon(params);
3413 SendSetMoon(player->getPeerId(), params);
3416 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3418 sanity_check(player);
3419 player->setStars(params);
3420 SendSetStars(player->getPeerId(), params);
3423 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3425 sanity_check(player);
3426 player->setCloudParams(params);
3427 SendCloudParams(player->getPeerId(), params);
3430 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3433 sanity_check(player);
3434 player->overrideDayNightRatio(do_override, ratio);
3435 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3438 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3440 sanity_check(player);
3441 player->setLighting(lighting);
3442 SendSetLighting(player->getPeerId(), lighting);
3445 void Server::notifyPlayers(const std::wstring &msg)
3447 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3450 void Server::spawnParticle(const std::string &playername,
3451 const ParticleParameters &p)
3453 // m_env will be NULL if the server is initializing
3457 session_t peer_id = PEER_ID_INEXISTENT;
3459 if (!playername.empty()) {
3460 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3463 peer_id = player->getPeerId();
3464 proto_ver = player->protocol_version;
3467 SendSpawnParticle(peer_id, proto_ver, p);
3470 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3471 ServerActiveObject *attached, const std::string &playername)
3473 // m_env will be NULL if the server is initializing
3477 session_t peer_id = PEER_ID_INEXISTENT;
3479 if (!playername.empty()) {
3480 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3483 peer_id = player->getPeerId();
3484 proto_ver = player->protocol_version;
3487 u16 attached_id = attached ? attached->getId() : 0;
3490 if (attached_id == 0)
3491 id = m_env->addParticleSpawner(p.time);
3493 id = m_env->addParticleSpawner(p.time, attached_id);
3495 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3499 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3501 // m_env will be NULL if the server is initializing
3503 throw ServerError("Can't delete particle spawners during initialisation!");
3505 session_t peer_id = PEER_ID_INEXISTENT;
3506 if (!playername.empty()) {
3507 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3510 peer_id = player->getPeerId();
3513 m_env->deleteParticleSpawner(id);
3514 SendDeleteParticleSpawner(peer_id, id);
3517 bool Server::dynamicAddMedia(std::string filepath,
3518 const u32 token, const std::string &to_player, bool ephemeral)
3520 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3521 auto it = m_media.find(filename);
3522 if (it != m_media.end()) {
3523 // Allow the same path to be "added" again in certain conditions
3524 if (ephemeral || it->second.path != filepath) {
3525 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3526 << "\" already exists in media cache" << std::endl;
3531 // Load the file and add it to our media cache
3532 std::string filedata, raw_hash;
3533 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3538 // Create a copy of the file and swap out the path, this removes the
3539 // requirement that mods keep the file accessible at the original path.
3540 filepath = fs::CreateTempFile();
3541 bool ok = ([&] () -> bool {
3542 if (filepath.empty())
3544 std::ofstream os(filepath.c_str(), std::ios::binary);
3552 errorstream << "Server: failed to create a copy of media file "
3553 << "\"" << filename << "\"" << std::endl;
3554 m_media.erase(filename);
3557 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3558 << filepath << std::endl;
3560 m_media[filename].path = filepath;
3561 m_media[filename].no_announce = true;
3562 // stepPendingDynMediaCallbacks will clean this up later.
3563 } else if (!to_player.empty()) {
3564 m_media[filename].no_announce = true;
3567 // Push file to existing clients
3568 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3569 pkt << raw_hash << filename << (bool)ephemeral;
3571 NetworkPacket legacy_pkt = pkt;
3573 // Newer clients get asked to fetch the file (asynchronous)
3575 // Older clients have an awful hack that just throws the data at them
3576 legacy_pkt.putLongString(filedata);
3578 std::unordered_set<session_t> delivered, waiting;
3580 ClientInterface::AutoLock clientlock(m_clients);
3581 for (auto &pair : m_clients.getClientList()) {
3582 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3584 If a client is in the DefinitionsSent state it is too late to
3585 transfer the file via sendMediaAnnouncement() but at the same
3586 time the client cannot accept a media push yet.
3587 Short of artificially delaying the joining process there is no
3588 way for the server to resolve this so we (currently) opt not to.
3590 warningstream << "The media \"" << filename << "\" (dynamic) could "
3591 "not be delivered to " << pair.second->getName()
3592 << " due to a race condition." << std::endl;
3595 if (pair.second->getState() < CS_Active)
3598 const auto proto_ver = pair.second->net_proto_version;
3602 const session_t peer_id = pair.second->peer_id;
3603 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3606 if (proto_ver < 40) {
3607 delivered.emplace(peer_id);
3609 The network layer only guarantees ordered delivery inside a channel.
3610 Since the very next packet could be one that uses the media, we have
3611 to push the media over ALL channels to ensure it is processed before
3612 it is used. In practice this means channels 1 and 0.
3614 m_clients.send(peer_id, 1, &legacy_pkt, true);
3615 m_clients.send(peer_id, 0, &legacy_pkt, true);
3617 waiting.emplace(peer_id);
3618 Send(peer_id, &pkt);
3623 // Run callback for players that already had the file delivered (legacy-only)
3624 for (session_t peer_id : delivered) {
3625 if (auto player = m_env->getPlayer(peer_id))
3626 getScriptIface()->on_dynamic_media_added(token, player->getName());
3629 // Save all others in our pending state
3630 auto &state = m_pending_dyn_media[token];
3631 state.waiting_players = std::move(waiting);
3632 // regardless of success throw away the callback after a while
3633 state.expiry_timer = 60.0f;
3635 state.filename = filename;
3640 // actions: time-reversed list
3641 // Return value: success/failure
3642 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3643 std::list<std::string> *log)
3645 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3646 ServerMap *map = (ServerMap*)(&m_env->getMap());
3648 // Fail if no actions to handle
3649 if (actions.empty()) {
3651 log->push_back("Nothing to do.");
3658 for (const RollbackAction &action : actions) {
3660 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3663 std::ostringstream os;
3664 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3665 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3667 log->push_back(os.str());
3669 std::ostringstream os;
3670 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3671 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3673 log->push_back(os.str());
3677 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3678 <<" failed"<<std::endl;
3680 // Call it done if less than half failed
3681 return num_failed <= num_tried/2;
3684 // IGameDef interface
3686 IItemDefManager *Server::getItemDefManager()
3691 const NodeDefManager *Server::getNodeDefManager()
3696 ICraftDefManager *Server::getCraftDefManager()
3701 u16 Server::allocateUnknownNodeId(const std::string &name)
3703 return m_nodedef->allocateDummy(name);
3706 IWritableItemDefManager *Server::getWritableItemDefManager()
3711 NodeDefManager *Server::getWritableNodeDefManager()
3716 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3721 const std::vector<ModSpec> & Server::getMods() const
3723 return m_modmgr->getMods();
3726 const ModSpec *Server::getModSpec(const std::string &modname) const
3728 return m_modmgr->getModSpec(modname);
3731 std::string Server::getBuiltinLuaPath()
3733 return porting::path_share + DIR_DELIM + "builtin";
3736 v3f Server::findSpawnPos()
3738 ServerMap &map = m_env->getServerMap();
3740 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3741 return nodeposf * BS;
3743 bool is_good = false;
3744 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3745 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3747 // Try to find a good place a few times
3748 for (s32 i = 0; i < 4000 && !is_good; i++) {
3749 s32 range = MYMIN(1 + i, range_max);
3750 // We're going to try to throw the player to this position
3751 v2s16 nodepos2d = v2s16(
3752 -range + myrand_range(0, range*2),
3753 -range + myrand_range(0, range*2));
3754 // Get spawn level at point
3755 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3756 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3757 // signify an unsuitable spawn position, or if outside limits.
3758 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3759 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3762 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3763 // Consecutive empty nodes
3766 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3767 // avoid obstructions in already-generated mapblocks.
3768 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3769 // no obstructions, but mapgen decorations are generated after spawn so
3770 // the player may end up inside one.
3771 for (s32 i = 0; i < 8; i++) {
3772 v3s16 blockpos = getNodeBlockPos(nodepos);
3773 map.emergeBlock(blockpos, true);
3774 content_t c = map.getNode(nodepos).getContent();
3776 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3777 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3778 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3780 if (air_count >= 2) {
3781 // Spawn in lower empty node
3783 nodeposf = intToFloat(nodepos, BS);
3784 // Don't spawn the player outside map boundaries
3785 if (objectpos_over_limit(nodeposf))
3786 // Exit this loop, positions above are probably over limit
3789 // Good position found, cause an exit from main loop
3803 // No suitable spawn point found, return fallback 0,0,0
3804 return v3f(0.0f, 0.0f, 0.0f);
3807 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3809 if (delay == 0.0f) {
3810 // No delay, shutdown immediately
3811 m_shutdown_state.is_requested = true;
3812 // only print to the infostream, a chat message saying
3813 // "Server Shutting Down" is sent when the server destructs.
3814 infostream << "*** Immediate Server shutdown requested." << std::endl;
3815 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3816 // Negative delay, cancel shutdown if requested
3817 m_shutdown_state.reset();
3818 std::wstringstream ws;
3820 ws << L"*** Server shutdown canceled.";
3822 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3823 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3824 // m_shutdown_* are already handled, skip.
3826 } else if (delay > 0.0f) {
3827 // Positive delay, tell the clients when the server will shut down
3828 std::wstringstream ws;
3830 ws << L"*** Server shutting down in "
3831 << duration_to_string(myround(delay)).c_str()
3834 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3835 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3838 m_shutdown_state.trigger(delay, msg, reconnect);
3841 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3844 Try to get an existing player
3846 RemotePlayer *player = m_env->getPlayer(name);
3848 // If player is already connected, cancel
3849 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3850 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3855 If player with the wanted peer_id already exists, cancel.
3857 if (m_env->getPlayer(peer_id)) {
3858 infostream<<"emergePlayer(): Player with wrong name but same"
3859 " peer_id already exists"<<std::endl;
3864 player = new RemotePlayer(name, idef());
3867 bool newplayer = false;
3870 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3872 // Complete init with server parts
3873 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3874 player->protocol_version = proto_version;
3878 m_script->on_newplayer(playersao);
3884 void dedicated_server_loop(Server &server, bool &kill)
3886 verbosestream<<"dedicated_server_loop()"<<std::endl;
3888 IntervalLimiter m_profiler_interval;
3890 static thread_local const float steplen =
3891 g_settings->getFloat("dedicated_server_step");
3892 static thread_local const float profiler_print_interval =
3893 g_settings->getFloat("profiler_print_interval");
3896 * The dedicated server loop only does time-keeping (in Server::step) and
3897 * provides a way to main.cpp to kill the server externally (bool &kill).
3901 // This is kind of a hack but can be done like this
3902 // because server.step() is very light
3903 sleep_ms((int)(steplen*1000.0));
3904 server.step(steplen);
3906 if (server.isShutdownRequested() || kill)
3912 if (profiler_print_interval != 0) {
3913 if(m_profiler_interval.step(steplen, profiler_print_interval))
3915 infostream<<"Profiler:"<<std::endl;
3916 g_profiler->print(infostream);
3917 g_profiler->clear();
3922 infostream << "Dedicated server quitting" << std::endl;
3924 if (g_settings->getBool("server_announce"))
3925 ServerList::sendAnnounce(ServerList::AA_DELETE,
3926 server.m_bind_addr.getPort());
3935 bool Server::joinModChannel(const std::string &channel)
3937 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3938 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3941 bool Server::leaveModChannel(const std::string &channel)
3943 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3946 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3948 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3951 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3955 ModChannel* Server::getModChannel(const std::string &channel)
3957 return m_modchannel_mgr->getModChannel(channel);
3960 void Server::broadcastModChannelMessage(const std::string &channel,
3961 const std::string &message, session_t from_peer)
3963 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3967 if (message.size() > STRING_MAX_LEN) {
3968 warningstream << "ModChannel message too long, dropping before sending "
3969 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3970 << channel << ")" << std::endl;
3975 if (from_peer != PEER_ID_SERVER) {
3976 sender = getPlayerName(from_peer);
3979 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3980 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3981 resp_pkt << channel << sender << message;
3982 for (session_t peer_id : peers) {
3984 if (peer_id == from_peer)
3987 Send(peer_id, &resp_pkt);
3990 if (from_peer != PEER_ID_SERVER) {
3991 m_script->on_modchannel_message(channel, sender, message);
3995 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3997 if (lang_code.empty())
4000 auto it = server_translations.find(lang_code);
4001 if (it != server_translations.end())
4002 return &it->second; // Already loaded
4004 // [] will create an entry
4005 auto *translations = &server_translations[lang_code];
4007 std::string suffix = "." + lang_code + ".tr";
4008 for (const auto &i : m_media) {
4009 if (str_ends_with(i.first, suffix)) {
4011 if (fs::ReadFile(i.second.path, data)) {
4012 translations->loadTranslation(data);
4017 return translations;
4020 ModStorageDatabase *Server::openModStorageDatabase(const std::string &world_path)
4022 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4024 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4025 throw BaseException("Cannot read world.mt!");
4027 std::string backend = world_mt.exists("mod_storage_backend") ?
4028 world_mt.get("mod_storage_backend") : "files";
4029 if (backend == "files")
4030 warningstream << "/!\\ You are using the old mod storage files backend. "
4031 << "This backend is deprecated and may be removed in a future release /!\\"
4032 << std::endl << "Switching to SQLite3 is advised, "
4033 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4035 return openModStorageDatabase(backend, world_path, world_mt);
4038 ModStorageDatabase *Server::openModStorageDatabase(const std::string &backend,
4039 const std::string &world_path, const Settings &world_mt)
4041 if (backend == "sqlite3")
4042 return new ModStorageDatabaseSQLite3(world_path);
4045 if (backend == "postgresql") {
4046 std::string connect_string;
4047 world_mt.getNoEx("pgsql_mod_storage_connection", connect_string);
4048 return new ModStorageDatabasePostgreSQL(connect_string);
4050 #endif // USE_POSTGRESQL
4052 if (backend == "files")
4053 return new ModStorageDatabaseFiles(world_path);
4055 if (backend == "dummy")
4056 return new Database_Dummy();
4058 throw BaseException("Mod storage database backend " + backend + " not supported");
4061 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4063 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4065 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4066 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4067 errorstream << "Cannot read world.mt!" << std::endl;
4071 std::string backend = world_mt.exists("mod_storage_backend") ?
4072 world_mt.get("mod_storage_backend") : "files";
4073 if (backend == migrate_to) {
4074 errorstream << "Cannot migrate: new backend is same"
4075 << " as the old one" << std::endl;
4079 ModStorageDatabase *srcdb = nullptr;
4080 ModStorageDatabase *dstdb = nullptr;
4082 bool succeeded = false;
4085 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4086 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4090 std::vector<std::string> mod_list;
4091 srcdb->listMods(&mod_list);
4092 for (const std::string &modname : mod_list) {
4094 srcdb->getModEntries(modname, &meta);
4095 for (const auto &pair : meta) {
4096 dstdb->setModEntry(modname, pair.first, pair.second);
4104 actionstream << "Successfully migrated the metadata of "
4105 << mod_list.size() << " mods" << std::endl;
4106 world_mt.set("mod_storage_backend", migrate_to);
4107 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4108 errorstream << "Failed to update world.mt!" << std::endl;
4110 actionstream << "world.mt updated" << std::endl;
4112 } catch (BaseException &e) {
4113 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4119 if (succeeded && backend == "files") {
4121 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4122 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4123 if (!fs::Rename(storage_path, backup_path))
4124 warningstream << "After migration, " << storage_path
4125 << " could not be renamed to " << backup_path << std::endl;