3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "network/connection.h"
25 #include "network/networkprotocol.h"
26 #include "network/serveropcodes.h"
28 #include "environment.h"
30 #include "threading/mutex_auto_lock.h"
31 #include "constants.h"
37 #include "server/serveractiveobject.h"
41 #include "scripting_server.h"
46 #include "mapgen/mapgen.h"
47 #include "mapgen/mg_biome.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content/mods.h"
51 #include "modchannels.h"
52 #include "serverlist.h"
53 #include "util/string.h"
55 #include "util/serialize.h"
56 #include "util/thread.h"
57 #include "defaultsettings.h"
58 #include "server/mods.h"
59 #include "util/base64.h"
60 #include "util/sha1.h"
62 #include "database/database.h"
63 #include "chatmessage.h"
64 #include "chat_interface.h"
65 #include "remoteplayer.h"
66 #include "server/player_sao.h"
67 #include "server/serverinventorymgr.h"
68 #include "translation.h"
69 #include "database/database-sqlite3.h"
71 #include "database/database-postgresql.h"
73 #include "database/database-files.h"
74 #include "database/database-dummy.h"
75 #include "gameparams.h"
77 class ClientNotFoundException : public BaseException
80 ClientNotFoundException(const char *s):
85 class ServerThread : public Thread
89 ServerThread(Server *server):
100 void *ServerThread::run()
102 BEGIN_DEBUG_EXCEPTION_HANDLER
105 * The real business of the server happens on the ServerThread.
107 * AsyncRunStep() runs an actual server step as soon as enough time has
108 * passed (dedicated_server_loop keeps track of that).
109 * Receive() blocks at least(!) 30ms waiting for a packet (so this loop
110 * doesn't busy wait) and will process any remaining packets.
114 m_server->AsyncRunStep(true);
115 } catch (con::ConnectionBindFailed &e) {
116 m_server->setAsyncFatalError(e.what());
117 } catch (LuaError &e) {
118 m_server->setAsyncFatalError(e);
121 while (!stopRequested()) {
123 m_server->AsyncRunStep();
127 } catch (con::PeerNotFoundException &e) {
128 infostream<<"Server: PeerNotFoundException"<<std::endl;
129 } catch (ClientNotFoundException &e) {
130 } catch (con::ConnectionBindFailed &e) {
131 m_server->setAsyncFatalError(e.what());
132 } catch (LuaError &e) {
133 m_server->setAsyncFatalError(e);
137 END_DEBUG_EXCEPTION_HANDLER
142 v3f ServerPlayingSound::getPos(ServerEnvironment *env, bool *pos_exists) const
148 case SoundLocation::Local:
150 case SoundLocation::Position:
154 case SoundLocation::Object:
158 ServerActiveObject *sao = env->getActiveObject(object);
163 return sao->getBasePosition();
170 void Server::ShutdownState::reset()
174 should_reconnect = false;
175 is_requested = false;
178 void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
182 should_reconnect = reconnect;
185 void Server::ShutdownState::tick(float dtime, Server *server)
191 static const float shutdown_msg_times[] =
193 1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
196 // Automated messages
197 if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
198 for (float t : shutdown_msg_times) {
199 // If shutdown timer matches an automessage, shot it
200 if (m_timer > t && m_timer - dtime < t) {
201 std::wstring periodicMsg = getShutdownTimerMessage();
203 infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
204 server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
211 if (m_timer < 0.0f) {
217 std::wstring Server::ShutdownState::getShutdownTimerMessage() const
219 std::wstringstream ws;
220 ws << L"*** Server shutting down in "
221 << duration_to_string(myround(m_timer)).c_str() << ".";
230 const std::string &path_world,
231 const SubgameSpec &gamespec,
232 bool simple_singleplayer_mode,
235 ChatInterface *iface,
236 std::string *on_shutdown_errmsg
238 m_bind_addr(bind_addr),
239 m_path_world(path_world),
240 m_gamespec(gamespec),
241 m_simple_singleplayer_mode(simple_singleplayer_mode),
242 m_dedicated(dedicated),
243 m_async_fatal_error(""),
244 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
247 m_bind_addr.isIPv6(),
249 m_itemdef(createItemDefManager()),
250 m_nodedef(createNodeDefManager()),
251 m_craftdef(createCraftDefManager()),
252 m_thread(new ServerThread(this)),
255 m_on_shutdown_errmsg(on_shutdown_errmsg),
256 m_modchannel_mgr(new ModChannelMgr())
258 if (m_path_world.empty())
259 throw ServerError("Supplied empty world path");
261 if (!gamespec.isValid())
262 throw ServerError("Supplied invalid gamespec");
265 m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
267 m_metrics_backend = std::make_unique<MetricsBackend>();
270 m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
271 m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
273 m_timeofday_gauge = m_metrics_backend->addGauge(
274 "minetest_core_timeofday",
275 "Time of day value");
277 m_lag_gauge = m_metrics_backend->addGauge(
278 "minetest_core_latency",
279 "Latency value (in seconds)");
282 const std::string aom_types[] = {"reliable", "unreliable"};
283 for (u32 i = 0; i < ARRLEN(aom_types); i++) {
284 std::string help_str("Number of active object messages generated (");
285 help_str.append(aom_types[i]).append(")");
286 m_aom_buffer_counter[i] = m_metrics_backend->addCounter(
287 "minetest_core_aom_generated_count", help_str,
288 {{"type", aom_types[i]}});
291 m_packet_recv_counter = m_metrics_backend->addCounter(
292 "minetest_core_server_packet_recv",
293 "Processable packets received");
295 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
296 "minetest_core_server_packet_recv_processed",
297 "Valid received packets processed");
299 m_map_edit_event_counter = m_metrics_backend->addCounter(
300 "minetest_core_map_edit_events",
301 "Number of map edit events");
303 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
309 // Send shutdown message
310 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
311 L"*** Server shutting down"));
314 MutexAutoLock envlock(m_env_mutex);
316 infostream << "Server: Saving players" << std::endl;
317 m_env->saveLoadedPlayers();
319 infostream << "Server: Kicking players" << std::endl;
320 std::string kick_msg;
321 bool reconnect = false;
322 if (isShutdownRequested()) {
323 reconnect = m_shutdown_state.should_reconnect;
324 kick_msg = m_shutdown_state.message;
326 if (kick_msg.empty()) {
327 kick_msg = g_settings->get("kick_msg_shutdown");
329 m_env->saveLoadedPlayers(true);
330 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
331 kick_msg, reconnect);
334 actionstream << "Server: Shutting down" << std::endl;
336 // Do this before stopping the server in case mapgen callbacks need to access
337 // server-controlled resources (like ModStorages). Also do them before
338 // shutdown callbacks since they may modify state that is finalized in a
341 m_emerge->stopThreads();
344 MutexAutoLock envlock(m_env_mutex);
346 // Execute script shutdown hooks
347 infostream << "Executing shutdown hooks" << std::endl;
349 m_script->on_shutdown();
350 } catch (ModError &e) {
351 errorstream << "ModError: " << e.what() << std::endl;
352 if (m_on_shutdown_errmsg) {
353 if (m_on_shutdown_errmsg->empty()) {
354 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
356 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
361 infostream << "Server: Saving environment metadata" << std::endl;
371 // Write any changes before deletion.
372 if (m_mod_storage_database)
373 m_mod_storage_database->endSave();
375 // Delete things in the reverse order of creation
379 delete m_mod_storage_database;
385 // Deinitialize scripting
386 infostream << "Server: Deinitializing scripting" << std::endl;
388 delete m_startup_server_map; // if available
389 delete m_game_settings;
391 while (!m_unsent_map_edit_queue.empty()) {
392 delete m_unsent_map_edit_queue.front();
393 m_unsent_map_edit_queue.pop();
399 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
400 if (m_simple_singleplayer_mode)
401 infostream << " in simple singleplayer mode" << std::endl;
403 infostream << std::endl;
404 infostream << "- world: " << m_path_world << std::endl;
405 infostream << "- game: " << m_gamespec.path << std::endl;
407 m_game_settings = Settings::createLayer(SL_GAME);
409 // Create world if it doesn't exist
411 loadGameConfAndInitWorld(m_path_world,
412 fs::GetFilenameFromPath(m_path_world.c_str()),
414 } catch (const BaseException &e) {
415 throw ServerError(std::string("Failed to initialize world: ") + e.what());
418 // Create emerge manager
419 m_emerge = new EmergeManager(this, m_metrics_backend.get());
421 // Create ban manager
422 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
423 m_banmanager = new BanManager(ban_path);
425 // Create mod storage database and begin a save for later
426 m_mod_storage_database = openModStorageDatabase(m_path_world);
427 m_mod_storage_database->beginSave();
429 m_modmgr = std::make_unique<ServerModManager>(m_path_world);
431 // complain about mods with unsatisfied dependencies
432 if (!m_modmgr->isConsistent()) {
433 std::string error = m_modmgr->getUnsatisfiedModsError();
434 throw ServerError(error);
438 MutexAutoLock envlock(m_env_mutex);
440 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
441 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
442 m_startup_server_map = servermap;
444 // Initialize scripting
445 infostream << "Server: Initializing Lua" << std::endl;
447 m_script = new ServerScripting(this);
449 // Must be created before mod loading because we have some inventory creation
450 m_inventory_mgr = std::make_unique<ServerInventoryManager>();
452 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
453 m_script->checkSetByBuiltin();
455 m_gamespec.checkAndLog();
456 m_modmgr->loadMods(m_script);
458 // Read Textures and calculate sha1 sums
461 // Apply item aliases in the node definition manager
462 m_nodedef->updateAliases(m_itemdef);
464 // Apply texture overrides from texturepack/override.txt
465 std::vector<std::string> paths;
466 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
467 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
468 for (const std::string &path : paths) {
469 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
470 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
471 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
474 m_nodedef->setNodeRegistrationStatus(true);
476 // Perform pending node name resolutions
477 m_nodedef->runNodeResolveCallbacks();
479 // unmap node names in cross-references
480 m_nodedef->resolveCrossrefs();
482 // init the recipe hashes to speed up crafting
483 m_craftdef->initHashes(this);
485 // Initialize Environment
486 m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
487 m_env = new ServerEnvironment(servermap, m_script, this,
488 m_path_world, m_metrics_backend.get());
491 m_inventory_mgr->setEnv(m_env);
492 m_clients.setEnv(m_env);
494 if (!servermap->settings_mgr.makeMapgenParams())
495 FATAL_ERROR("Couldn't create any mapgen type");
497 // Initialize mapgens
498 m_emerge->initMapgens(servermap->getMapgenParams());
500 if (g_settings->getBool("enable_rollback_recording")) {
501 // Create rollback manager
502 m_rollback = new RollbackManager(m_path_world, this);
505 // Give environment reference to scripting api
506 m_script->initializeEnvironment(m_env);
508 // Do this after regular script init is done
509 m_script->initAsync();
511 // Register us to receive map edit events
512 servermap->addEventReceiver(this);
516 // Those settings can be overwritten in world.mt, they are
517 // intended to be cached after environment loading.
518 m_liquid_transform_every = g_settings->getFloat("liquid_update");
519 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
520 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
521 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
528 infostream << "Starting server on " << m_bind_addr.serializeString()
529 << "..." << std::endl;
531 // Stop thread if already running
534 // Initialize connection
535 m_con->SetTimeoutMs(30);
536 m_con->Serve(m_bind_addr);
541 // ASCII art for the win!
543 << " __. __. __. " << std::endl
544 << " _____ |__| ____ _____ / |_ _____ _____ / |_ " << std::endl
545 << " / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\" << std::endl
546 << "| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | " << std::endl
547 << "|__|_| / |___| /\\______> | \\______>_____/| | " << std::endl
548 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
549 actionstream << "World at [" << m_path_world << "]" << std::endl;
550 actionstream << "Server for gameid=\"" << m_gamespec.id
551 << "\" listening on ";
552 m_bind_addr.print(actionstream);
553 actionstream << "." << std::endl;
558 infostream<<"Server: Stopping and waiting threads"<<std::endl;
560 // Stop threads (set run=false first so both start stopping)
564 infostream<<"Server: Threads stopped"<<std::endl;
567 void Server::step(float dtime)
573 MutexAutoLock lock(m_step_dtime_mutex);
574 m_step_dtime += dtime;
576 // Throw if fatal error occurred in thread
577 std::string async_err = m_async_fatal_error.get();
578 if (!async_err.empty()) {
579 if (!m_simple_singleplayer_mode) {
580 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
581 g_settings->get("kick_msg_crash"),
582 g_settings->getBool("ask_reconnect_on_crash"));
584 throw ServerError("AsyncErr: " + async_err);
588 void Server::AsyncRunStep(bool initial_step)
593 MutexAutoLock lock1(m_step_dtime_mutex);
594 dtime = m_step_dtime;
598 // Send blocks to clients
602 if((dtime < 0.001) && !initial_step)
605 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
608 MutexAutoLock lock1(m_step_dtime_mutex);
609 m_step_dtime -= dtime;
615 m_uptime_counter->increment(dtime);
620 Update time of day and overall game time
622 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
625 Send to clients at constant intervals
628 m_time_of_day_send_timer -= dtime;
629 if (m_time_of_day_send_timer < 0.0) {
630 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
631 u16 time = m_env->getTimeOfDay();
632 float time_speed = g_settings->getFloat("time_speed");
633 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
635 m_timeofday_gauge->set(time);
639 MutexAutoLock lock(m_env_mutex);
640 // Figure out and report maximum lag to environment
641 float max_lag = m_env->getMaxLagEstimate();
642 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
644 if(dtime > 0.1 && dtime > max_lag * 2.0)
645 infostream<<"Server: Maximum lag peaked to "<<dtime
649 m_env->reportMaxLagEstimate(max_lag);
655 static const float map_timer_and_unload_dtime = 2.92;
656 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
658 MutexAutoLock lock(m_env_mutex);
659 // Run Map's timers and unload unused data
660 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
661 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
662 std::max(g_settings->getFloat("server_unload_unused_data_timeout"), 0.0f),
667 Listen to the admin chat, if available
670 if (!m_admin_chat->command_queue.empty()) {
671 MutexAutoLock lock(m_env_mutex);
672 while (!m_admin_chat->command_queue.empty()) {
673 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
674 handleChatInterfaceEvent(evt);
678 m_admin_chat->outgoing_queue.push_back(
679 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
686 /* Transform liquids */
687 m_liquid_transform_timer += dtime;
688 if(m_liquid_transform_timer >= m_liquid_transform_every)
690 m_liquid_transform_timer -= m_liquid_transform_every;
692 MutexAutoLock lock(m_env_mutex);
694 ScopeProfiler sp(g_profiler, "Server: liquid transform");
696 std::map<v3s16, MapBlock*> modified_blocks;
697 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
699 if (!modified_blocks.empty()) {
701 event.type = MEET_OTHER;
702 for (const auto &pair : modified_blocks) {
703 event.modified_blocks.insert(pair.first);
705 m_env->getMap().dispatchEvent(event);
708 m_clients.step(dtime);
710 // increase/decrease lag gauge gradually
711 if (m_lag_gauge->get() > dtime) {
712 m_lag_gauge->decrement(dtime/100);
714 m_lag_gauge->increment(dtime/100);
718 float &counter = m_step_pending_dyn_media_timer;
720 if (counter >= 5.0f) {
721 stepPendingDynMediaCallbacks(counter);
728 // send masterserver announce
730 float &counter = m_masterserver_timer;
731 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
732 g_settings->getBool("server_announce")) {
733 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
734 ServerList::AA_START,
735 m_bind_addr.getPort(),
736 m_clients.getPlayerNames(),
737 m_uptime_counter->get(),
738 m_env->getGameTime(),
741 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
751 Check added and deleted active objects
754 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
755 MutexAutoLock envlock(m_env_mutex);
758 ClientInterface::AutoLock clientlock(m_clients);
759 const RemoteClientMap &clients = m_clients.getClientList();
760 ScopeProfiler sp(g_profiler, "Server: update objects within range");
762 m_player_gauge->set(clients.size());
763 for (const auto &client_it : clients) {
764 RemoteClient *client = client_it.second;
766 if (client->getState() < CS_DefinitionsSent)
769 // This can happen if the client times out somehow
770 if (!m_env->getPlayer(client->peer_id))
773 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
777 SendActiveObjectRemoveAdd(client, playersao);
781 // Write changes to the mod storage
782 m_mod_storage_save_timer -= dtime;
783 if (m_mod_storage_save_timer <= 0.0f) {
784 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
785 m_mod_storage_database->endSave();
786 m_mod_storage_database->beginSave();
794 MutexAutoLock envlock(m_env_mutex);
795 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
798 // Value = data sent by object
799 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
801 // Get active object messages from environment
802 ActiveObjectMessage aom(0);
803 u32 count_reliable = 0, count_unreliable = 0;
805 if (!m_env->getActiveObjectMessage(&aom))
812 std::vector<ActiveObjectMessage>* message_list = nullptr;
813 auto n = buffered_messages.find(aom.id);
814 if (n == buffered_messages.end()) {
815 message_list = new std::vector<ActiveObjectMessage>;
816 buffered_messages[aom.id] = message_list;
818 message_list = n->second;
820 message_list->push_back(std::move(aom));
823 m_aom_buffer_counter[0]->increment(count_reliable);
824 m_aom_buffer_counter[1]->increment(count_unreliable);
827 ClientInterface::AutoLock clientlock(m_clients);
828 const RemoteClientMap &clients = m_clients.getClientList();
829 // Route data to every client
830 std::string reliable_data, unreliable_data;
831 for (const auto &client_it : clients) {
832 reliable_data.clear();
833 unreliable_data.clear();
834 RemoteClient *client = client_it.second;
835 PlayerSAO *player = getPlayerSAO(client->peer_id);
836 // Go through all objects in message buffer
837 for (const auto &buffered_message : buffered_messages) {
838 // If object does not exist or is not known by client, skip it
839 u16 id = buffered_message.first;
840 ServerActiveObject *sao = m_env->getActiveObject(id);
841 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
844 // Get message list of object
845 std::vector<ActiveObjectMessage>* list = buffered_message.second;
846 // Go through every message
847 for (const ActiveObjectMessage &aom : *list) {
848 // Send position updates to players who do not see the attachment
849 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
850 if (sao->getId() == player->getId())
853 // Do not send position updates for attached players
854 // as long the parent is known to the client
855 ServerActiveObject *parent = sao->getParent();
856 if (parent && client->m_known_objects.find(parent->getId()) !=
857 client->m_known_objects.end())
861 // Add full new data to appropriate buffer
862 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
864 writeU16((u8*) idbuf, aom.id);
867 buffer.append(idbuf, sizeof(idbuf));
868 buffer.append(serializeString16(aom.datastring));
872 reliable_data and unreliable_data are now ready.
875 if (!reliable_data.empty()) {
876 SendActiveObjectMessages(client->peer_id, reliable_data);
879 if (!unreliable_data.empty()) {
880 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
885 // Clear buffered_messages
886 for (auto &buffered_message : buffered_messages) {
887 delete buffered_message.second;
892 Send queued-for-sending map edit events.
895 // We will be accessing the environment
896 MutexAutoLock lock(m_env_mutex);
898 // Single change sending is disabled if queue size is big
899 bool disable_single_change_sending = false;
900 if(m_unsent_map_edit_queue.size() >= 4)
901 disable_single_change_sending = true;
903 const auto event_count = m_unsent_map_edit_queue.size();
904 m_map_edit_event_counter->increment(event_count);
906 // We'll log the amount of each
909 std::unordered_set<v3s16> node_meta_updates;
911 while (!m_unsent_map_edit_queue.empty()) {
912 MapEditEvent* event = m_unsent_map_edit_queue.front();
913 m_unsent_map_edit_queue.pop();
915 // Players far away from the change are stored here.
916 // Instead of sending the changes, MapBlocks are set not sent
918 std::unordered_set<u16> far_players;
920 switch (event->type) {
923 prof.add("MEET_ADDNODE", 1);
924 sendAddNode(event->p, event->n, &far_players,
925 disable_single_change_sending ? 5 : 30,
926 event->type == MEET_ADDNODE);
928 case MEET_REMOVENODE:
929 prof.add("MEET_REMOVENODE", 1);
930 sendRemoveNode(event->p, &far_players,
931 disable_single_change_sending ? 5 : 30);
933 case MEET_BLOCK_NODE_METADATA_CHANGED: {
934 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
935 if (!event->is_private_change) {
936 node_meta_updates.emplace(event->p);
939 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
940 getNodeBlockPos(event->p))) {
941 block->raiseModified(MOD_STATE_WRITE_NEEDED,
942 MOD_REASON_REPORT_META_CHANGE);
947 prof.add("MEET_OTHER", 1);
948 for (const v3s16 &modified_block : event->modified_blocks) {
949 m_clients.markBlockposAsNotSent(modified_block);
953 prof.add("unknown", 1);
954 warningstream << "Server: Unknown MapEditEvent "
955 << ((u32)event->type) << std::endl;
960 Set blocks not sent to far players
962 if (!far_players.empty()) {
963 // Convert list format to that wanted by SetBlocksNotSent
964 std::map<v3s16, MapBlock*> modified_blocks2;
965 for (const v3s16 &modified_block : event->modified_blocks) {
966 modified_blocks2[modified_block] =
967 m_env->getMap().getBlockNoCreateNoEx(modified_block);
970 // Set blocks not sent
971 for (const u16 far_player : far_players) {
972 if (RemoteClient *client = getClient(far_player))
973 client->SetBlocksNotSent(modified_blocks2);
980 if (event_count >= 5) {
981 infostream << "Server: MapEditEvents:" << std::endl;
982 prof.print(infostream);
983 } else if (event_count != 0) {
984 verbosestream << "Server: MapEditEvents:" << std::endl;
985 prof.print(verbosestream);
988 // Send all metadata updates
989 if (!node_meta_updates.empty())
990 sendMetadataChanged(node_meta_updates);
994 Trigger emerge thread
995 Doing this every 2s is left over from old code, unclear if this is still needed.
998 float &counter = m_emergethread_trigger_timer;
1000 if (counter <= 0.0f) {
1003 m_emerge->startThreads();
1007 // Save map, players and auth stuff
1009 float &counter = m_savemap_timer;
1011 static thread_local const float save_interval =
1012 g_settings->getFloat("server_map_save_interval");
1013 if (counter >= save_interval) {
1015 MutexAutoLock lock(m_env_mutex);
1017 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
1020 if (m_banmanager->isModified()) {
1021 m_banmanager->save();
1024 // Save changed parts of map
1025 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1028 m_env->saveLoadedPlayers();
1030 // Save environment metadata
1035 m_shutdown_state.tick(dtime, this);
1038 void Server::Receive()
1048 In the first iteration *wait* for a packet, afterwards process
1049 all packets that are immediately available (no waiting).
1052 m_con->Receive(&pkt);
1055 if (!m_con->TryReceive(&pkt))
1059 peer_id = pkt.getPeerId();
1060 m_packet_recv_counter->increment();
1062 m_packet_recv_processed_counter->increment();
1063 } catch (const con::InvalidIncomingDataException &e) {
1064 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1065 << e.what() << std::endl;
1066 } catch (const SerializationError &e) {
1067 infostream << "Server::Receive(): SerializationError: what()="
1068 << e.what() << std::endl;
1069 } catch (const ClientStateError &e) {
1070 errorstream << "ProcessData: peer=" << peer_id << " what()="
1071 << e.what() << std::endl;
1072 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1073 } catch (const con::PeerNotFoundException &e) {
1075 } catch (const con::NoIncomingDataException &e) {
1081 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1083 std::string playername;
1084 PlayerSAO *playersao = NULL;
1086 ClientInterface::AutoLock clientlock(m_clients);
1087 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1089 playername = client->getName();
1090 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1094 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1096 // If failed, cancel
1097 if (!playersao || !player) {
1098 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1099 actionstream << "Server: Failed to emerge player \"" << playername
1100 << "\" (player allocated to another client)" << std::endl;
1101 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1103 errorstream << "Server: " << playername << ": Failed to emerge player"
1105 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1111 Send complete position information
1113 SendMovePlayer(peer_id);
1116 SendPlayerPrivileges(peer_id);
1118 // Send inventory formspec
1119 SendPlayerInventoryFormspec(peer_id);
1122 SendInventory(playersao, false);
1125 SendPlayerHP(playersao, false);
1127 // Send death screen
1128 if (playersao->isDead())
1129 SendDeathscreen(peer_id, false, v3f(0,0,0));
1132 SendPlayerBreath(playersao);
1135 Update player list and print action
1138 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1139 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1140 m_clients.sendToAll(¬ice_pkt);
1143 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1144 const auto &names = m_clients.getPlayerNames();
1146 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1147 for (const std::string &name : names)
1148 actionstream << name << " ";
1149 actionstream << player->getName() << std::endl;
1154 inline void Server::handleCommand(NetworkPacket *pkt)
1156 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1157 (this->*opHandle.handler)(pkt);
1160 void Server::ProcessData(NetworkPacket *pkt)
1162 // Environment is locked first.
1163 MutexAutoLock envlock(m_env_mutex);
1165 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1166 u32 peer_id = pkt->getPeerId();
1169 Address address = getPeerAddress(peer_id);
1170 std::string addr_s = address.serializeString();
1172 // FIXME: Isn't it a bit excessive to check this for every packet?
1173 if (m_banmanager->isIpBanned(addr_s)) {
1174 std::string ban_name = m_banmanager->getBanName(addr_s);
1175 infostream << "Server: A banned client tried to connect from "
1176 << addr_s << "; banned name was " << ban_name << std::endl;
1177 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1178 "Your IP is banned. Banned name was " + ban_name);
1181 } catch (con::PeerNotFoundException &e) {
1183 * no peer for this packet found
1184 * most common reason is peer timeout, e.g. peer didn't
1185 * respond for some time, your server was overloaded or
1188 infostream << "Server::ProcessData(): Canceling: peer "
1189 << peer_id << " not found" << std::endl;
1194 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1196 // Command must be handled into ToServerCommandHandler
1197 if (command >= TOSERVER_NUM_MSG_TYPES) {
1198 infostream << "Server: Ignoring unknown command "
1199 << command << std::endl;
1203 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1208 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1210 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1211 errorstream << "Server::ProcessData(): Cancelling: Peer"
1212 " serialization format invalid or not initialized."
1213 " Skipping incoming command=" << command << std::endl;
1217 /* Handle commands related to client startup */
1218 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1223 if (m_clients.getClientState(peer_id) < CS_Active) {
1224 if (command == TOSERVER_PLAYERPOS) return;
1226 errorstream << "Got packet command: " << command << " for peer id "
1227 << peer_id << " but client isn't active yet. Dropping packet "
1233 } catch (SendFailedException &e) {
1234 errorstream << "Server::ProcessData(): SendFailedException: "
1235 << "what=" << e.what()
1237 } catch (PacketError &e) {
1238 actionstream << "Server::ProcessData(): PacketError: "
1239 << "what=" << e.what()
1244 void Server::setTimeOfDay(u32 time)
1246 m_env->setTimeOfDay(time);
1247 m_time_of_day_send_timer = 0;
1250 void Server::onMapEditEvent(const MapEditEvent &event)
1252 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1255 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1258 void Server::peerAdded(con::Peer *peer)
1260 verbosestream<<"Server::peerAdded(): peer->id="
1261 <<peer->id<<std::endl;
1263 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1266 void Server::deletingPeer(con::Peer *peer, bool timeout)
1268 verbosestream<<"Server::deletingPeer(): peer->id="
1269 <<peer->id<<", timeout="<<timeout<<std::endl;
1271 m_clients.event(peer->id, CSE_Disconnect);
1272 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1275 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1277 *retval = m_con->getPeerStat(peer_id,type);
1278 return *retval != -1;
1281 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1283 ClientInterface::AutoLock clientlock(m_clients);
1284 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1289 ret.state = client->getState();
1290 ret.addr = client->getAddress();
1291 ret.uptime = client->uptime();
1292 ret.ser_vers = client->serialization_version;
1293 ret.prot_vers = client->net_proto_version;
1295 ret.major = client->getMajor();
1296 ret.minor = client->getMinor();
1297 ret.patch = client->getPatch();
1298 ret.vers_string = client->getFullVer();
1300 ret.lang_code = client->getLangCode();
1305 void Server::handlePeerChanges()
1307 while(!m_peer_change_queue.empty())
1309 con::PeerChange c = m_peer_change_queue.front();
1310 m_peer_change_queue.pop();
1312 verbosestream<<"Server: Handling peer change: "
1313 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1318 case con::PEER_ADDED:
1319 m_clients.CreateClient(c.peer_id);
1322 case con::PEER_REMOVED:
1323 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1327 FATAL_ERROR("Invalid peer change event received!");
1333 void Server::printToConsoleOnly(const std::string &text)
1336 m_admin_chat->outgoing_queue.push_back(
1337 new ChatEventChat("", utf8_to_wide(text)));
1339 std::cout << text << std::endl;
1343 void Server::Send(NetworkPacket *pkt)
1345 Send(pkt->getPeerId(), pkt);
1348 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1350 m_clients.send(peer_id,
1351 clientCommandFactoryTable[pkt->getCommand()].channel,
1353 clientCommandFactoryTable[pkt->getCommand()].reliable);
1356 void Server::SendMovement(session_t peer_id)
1358 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1360 pkt << g_settings->getFloat("movement_acceleration_default");
1361 pkt << g_settings->getFloat("movement_acceleration_air");
1362 pkt << g_settings->getFloat("movement_acceleration_fast");
1363 pkt << g_settings->getFloat("movement_speed_walk");
1364 pkt << g_settings->getFloat("movement_speed_crouch");
1365 pkt << g_settings->getFloat("movement_speed_fast");
1366 pkt << g_settings->getFloat("movement_speed_climb");
1367 pkt << g_settings->getFloat("movement_speed_jump");
1368 pkt << g_settings->getFloat("movement_liquid_fluidity");
1369 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1370 pkt << g_settings->getFloat("movement_liquid_sink");
1371 pkt << g_settings->getFloat("movement_gravity");
1376 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1378 m_script->player_event(playersao, "health_changed");
1379 SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX);
1381 // Send to other clients
1382 playersao->sendPunchCommand();
1384 if (playersao->isDead())
1385 HandlePlayerDeath(playersao, reason);
1388 void Server::SendPlayerHP(PlayerSAO *playersao, bool effect)
1390 SendHP(playersao->getPeerID(), playersao->getHP(), effect);
1393 void Server::SendHP(session_t peer_id, u16 hp, bool effect)
1395 NetworkPacket pkt(TOCLIENT_HP, 3, peer_id);
1396 pkt << hp << effect;
1400 void Server::SendBreath(session_t peer_id, u16 breath)
1402 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1403 pkt << (u16) breath;
1407 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1408 const std::string &custom_reason, bool reconnect)
1410 assert(reason < SERVER_ACCESSDENIED_MAX);
1412 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1414 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1415 pkt << custom_reason;
1416 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1417 reason == SERVER_ACCESSDENIED_CRASH)
1418 pkt << custom_reason << (u8)reconnect;
1422 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1423 v3f camera_point_target)
1425 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1426 pkt << set_camera_point_target << camera_point_target;
1430 void Server::SendItemDef(session_t peer_id,
1431 IItemDefManager *itemdef, u16 protocol_version)
1433 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1437 u32 length of the next item
1438 zlib-compressed serialized ItemDefManager
1440 std::ostringstream tmp_os(std::ios::binary);
1441 itemdef->serialize(tmp_os, protocol_version);
1442 std::ostringstream tmp_os2(std::ios::binary);
1443 compressZlib(tmp_os.str(), tmp_os2);
1444 pkt.putLongString(tmp_os2.str());
1447 verbosestream << "Server: Sending item definitions to id(" << peer_id
1448 << "): size=" << pkt.getSize() << std::endl;
1453 void Server::SendNodeDef(session_t peer_id,
1454 const NodeDefManager *nodedef, u16 protocol_version)
1456 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1460 u32 length of the next item
1461 zlib-compressed serialized NodeDefManager
1463 std::ostringstream tmp_os(std::ios::binary);
1464 nodedef->serialize(tmp_os, protocol_version);
1465 std::ostringstream tmp_os2(std::ios::binary);
1466 compressZlib(tmp_os.str(), tmp_os2);
1468 pkt.putLongString(tmp_os2.str());
1471 verbosestream << "Server: Sending node definitions to id(" << peer_id
1472 << "): size=" << pkt.getSize() << std::endl;
1478 Non-static send methods
1481 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1483 RemotePlayer *player = sao->getPlayer();
1485 // Do not send new format to old clients
1486 incremental &= player->protocol_version >= 38;
1488 UpdateCrafting(player);
1494 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1496 std::ostringstream os(std::ios::binary);
1497 sao->getInventory()->serialize(os, incremental);
1498 sao->getInventory()->setModified(false);
1499 player->setModified(true);
1501 const std::string &s = os.str();
1502 pkt.putRawString(s.c_str(), s.size());
1506 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1508 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1510 u8 type = message.type;
1511 pkt << version << type << message.sender << message.message
1512 << static_cast<u64>(message.timestamp);
1514 if (peer_id != PEER_ID_INEXISTENT) {
1515 RemotePlayer *player = m_env->getPlayer(peer_id);
1521 m_clients.sendToAll(&pkt);
1525 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1526 const std::string &formname)
1528 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1529 if (formspec.empty()){
1530 //the client should close the formspec
1531 //but make sure there wasn't another one open in meantime
1532 const auto it = m_formspec_state_data.find(peer_id);
1533 if (it != m_formspec_state_data.end() && it->second == formname) {
1534 m_formspec_state_data.erase(peer_id);
1536 pkt.putLongString("");
1538 m_formspec_state_data[peer_id] = formname;
1539 pkt.putLongString(formspec);
1546 // Spawns a particle on peer with peer_id
1547 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1548 const ParticleParameters &p)
1550 static thread_local const float radius =
1551 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1553 if (peer_id == PEER_ID_INEXISTENT) {
1554 std::vector<session_t> clients = m_clients.getClientIDs();
1555 const v3f pos = p.pos * BS;
1556 const float radius_sq = radius * radius;
1558 for (const session_t client_id : clients) {
1559 RemotePlayer *player = m_env->getPlayer(client_id);
1563 PlayerSAO *sao = player->getPlayerSAO();
1567 // Do not send to distant clients
1568 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1571 SendSpawnParticle(client_id, player->protocol_version, p);
1575 assert(protocol_version != 0);
1577 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1580 // NetworkPacket and iostreams are incompatible...
1581 std::ostringstream oss(std::ios_base::binary);
1582 p.serialize(oss, protocol_version);
1583 pkt.putRawString(oss.str());
1589 // Adds a ParticleSpawner on peer with peer_id
1590 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1591 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1593 static thread_local const float radius =
1594 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1596 if (peer_id == PEER_ID_INEXISTENT) {
1597 std::vector<session_t> clients = m_clients.getClientIDs();
1599 p.pos.start.min.val +
1600 p.pos.start.max.val +
1604 const float radius_sq = radius * radius;
1605 /* Don't send short-lived spawners to distant players.
1606 * This could be replaced with proper tracking at some point. */
1607 const bool distance_check = !attached_id && p.time <= 1.0f;
1609 for (const session_t client_id : clients) {
1610 RemotePlayer *player = m_env->getPlayer(client_id);
1614 if (distance_check) {
1615 PlayerSAO *sao = player->getPlayerSAO();
1618 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1622 SendAddParticleSpawner(client_id, player->protocol_version,
1623 p, attached_id, id);
1627 assert(protocol_version != 0);
1629 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1631 pkt << p.amount << p.time;
1632 { // serialize legacy fields
1633 std::ostringstream os(std::ios_base::binary);
1634 p.pos.start.legacySerialize(os);
1635 p.vel.start.legacySerialize(os);
1636 p.acc.start.legacySerialize(os);
1637 p.exptime.start.legacySerialize(os);
1638 p.size.start.legacySerialize(os);
1639 pkt.putRawString(os.str());
1641 pkt << p.collisiondetection;
1643 pkt.putLongString(p.texture.string);
1645 pkt << id << p.vertical << p.collision_removal << attached_id;
1647 std::ostringstream os(std::ios_base::binary);
1648 p.animation.serialize(os, protocol_version);
1649 pkt.putRawString(os.str());
1651 pkt << p.glow << p.object_collision;
1652 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1654 { // serialize new fields
1655 // initial bias for older properties
1656 pkt << p.pos.start.bias
1659 << p.exptime.start.bias
1660 << p.size.start.bias;
1662 std::ostringstream os(std::ios_base::binary);
1664 // final tween frames of older properties
1665 p.pos.end.serialize(os);
1666 p.vel.end.serialize(os);
1667 p.acc.end.serialize(os);
1668 p.exptime.end.serialize(os);
1669 p.size.end.serialize(os);
1671 // properties for legacy texture field
1672 p.texture.serialize(os, protocol_version, true);
1675 p.drag.serialize(os);
1676 p.jitter.serialize(os);
1677 p.bounce.serialize(os);
1678 ParticleParamTypes::serializeParameterValue(os, p.attractor_kind);
1679 if (p.attractor_kind != ParticleParamTypes::AttractorKind::none) {
1680 p.attract.serialize(os);
1681 p.attractor_origin.serialize(os);
1682 writeU16(os, p.attractor_attachment); /* object ID */
1683 writeU8(os, p.attractor_kill);
1684 if (p.attractor_kind != ParticleParamTypes::AttractorKind::point) {
1685 p.attractor_direction.serialize(os);
1686 writeU16(os, p.attractor_direction_attachment);
1689 p.radius.serialize(os);
1691 ParticleParamTypes::serializeParameterValue(os, (u16)p.texpool.size());
1692 for (const auto& tex : p.texpool) {
1693 tex.serialize(os, protocol_version);
1696 pkt.putRawString(os.str());
1702 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1704 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1708 if (peer_id != PEER_ID_INEXISTENT)
1711 m_clients.sendToAll(&pkt);
1715 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1717 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1719 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1720 << form->text << form->number << form->item << form->dir
1721 << form->align << form->offset << form->world_pos << form->size
1722 << form->z_index << form->text2 << form->style;
1727 void Server::SendHUDRemove(session_t peer_id, u32 id)
1729 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1734 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1736 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1737 pkt << id << (u8) stat;
1741 case HUD_STAT_SCALE:
1742 case HUD_STAT_ALIGN:
1743 case HUD_STAT_OFFSET:
1744 pkt << *(v2f *) value;
1748 case HUD_STAT_TEXT2:
1749 pkt << *(std::string *) value;
1751 case HUD_STAT_WORLD_POS:
1752 pkt << *(v3f *) value;
1755 pkt << *(v2s32 *) value;
1757 default: // all other types
1758 pkt << *(u32 *) value;
1765 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1767 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1769 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1771 pkt << flags << mask;
1776 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1778 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1779 pkt << param << value;
1783 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1785 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1787 // Handle prior clients here
1788 if (m_clients.getProtocolVersion(peer_id) < 39) {
1789 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1791 for (const std::string& texture : params.textures)
1794 pkt << params.clouds;
1795 } else { // Handle current clients and future clients
1796 pkt << params.bgcolor << params.type
1797 << params.clouds << params.fog_sun_tint
1798 << params.fog_moon_tint << params.fog_tint_type;
1800 if (params.type == "skybox") {
1801 pkt << (u16) params.textures.size();
1802 for (const std::string &texture : params.textures)
1804 } else if (params.type == "regular") {
1805 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1806 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1807 << params.sky_color.night_sky << params.sky_color.night_horizon
1808 << params.sky_color.indoors;
1815 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1817 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1818 pkt << params.visible << params.texture
1819 << params.tonemap << params.sunrise
1820 << params.sunrise_visible << params.scale;
1824 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1826 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1828 pkt << params.visible << params.texture
1829 << params.tonemap << params.scale;
1833 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1835 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1837 pkt << params.visible << params.count
1838 << params.starcolor << params.scale
1839 << params.day_opacity;
1844 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1846 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1847 pkt << params.density << params.color_bright << params.color_ambient
1848 << params.height << params.thickness << params.speed;
1852 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1855 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1858 pkt << do_override << (u16) (ratio * 65535);
1863 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1865 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1868 pkt << lighting.shadow_intensity;
1873 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1875 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1876 pkt << time << time_speed;
1878 if (peer_id == PEER_ID_INEXISTENT) {
1879 m_clients.sendToAll(&pkt);
1886 void Server::SendPlayerBreath(PlayerSAO *sao)
1890 m_script->player_event(sao, "breath_changed");
1891 SendBreath(sao->getPeerID(), sao->getBreath());
1894 void Server::SendMovePlayer(session_t peer_id)
1896 RemotePlayer *player = m_env->getPlayer(peer_id);
1898 PlayerSAO *sao = player->getPlayerSAO();
1901 // Send attachment updates instantly to the client prior updating position
1902 sao->sendOutdatedData();
1904 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1905 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1908 v3f pos = sao->getBasePosition();
1909 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1910 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1911 << " pitch=" << sao->getLookPitch()
1912 << " yaw=" << sao->getRotation().Y
1919 void Server::SendPlayerFov(session_t peer_id)
1921 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1923 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1924 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1929 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1930 f32 animation_speed)
1932 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1935 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1936 << animation_frames[3] << animation_speed;
1941 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1943 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1944 pkt << first << third;
1948 void Server::SendPlayerPrivileges(session_t peer_id)
1950 RemotePlayer *player = m_env->getPlayer(peer_id);
1952 if(player->getPeerId() == PEER_ID_INEXISTENT)
1955 std::set<std::string> privs;
1956 m_script->getAuth(player->getName(), NULL, &privs);
1958 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1959 pkt << (u16) privs.size();
1961 for (const std::string &priv : privs) {
1968 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1970 RemotePlayer *player = m_env->getPlayer(peer_id);
1972 if (player->getPeerId() == PEER_ID_INEXISTENT)
1975 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1976 pkt.putLongString(player->inventory_formspec);
1981 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1983 RemotePlayer *player = m_env->getPlayer(peer_id);
1985 if (player->getPeerId() == PEER_ID_INEXISTENT)
1988 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1989 pkt << player->formspec_prepend;
1993 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1995 // Radius inside which objects are active
1996 static thread_local const s16 radius =
1997 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1999 // Radius inside which players are active
2000 static thread_local const bool is_transfer_limited =
2001 g_settings->exists("unlimited_player_transfer_distance") &&
2002 !g_settings->getBool("unlimited_player_transfer_distance");
2004 static thread_local const s16 player_transfer_dist =
2005 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
2007 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
2008 radius : player_transfer_dist;
2010 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
2014 std::queue<u16> removed_objects, added_objects;
2015 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
2016 client->m_known_objects, removed_objects);
2017 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
2018 client->m_known_objects, added_objects);
2020 int removed_count = removed_objects.size();
2021 int added_count = added_objects.size();
2023 if (removed_objects.empty() && added_objects.empty())
2029 // Handle removed objects
2030 writeU16((u8*)buf, removed_objects.size());
2031 data.append(buf, 2);
2032 while (!removed_objects.empty()) {
2034 u16 id = removed_objects.front();
2035 ServerActiveObject* obj = m_env->getActiveObject(id);
2037 // Add to data buffer for sending
2038 writeU16((u8*)buf, id);
2039 data.append(buf, 2);
2041 // Remove from known objects
2042 client->m_known_objects.erase(id);
2044 if (obj && obj->m_known_by_count > 0)
2045 obj->m_known_by_count--;
2047 removed_objects.pop();
2050 // Handle added objects
2051 writeU16((u8*)buf, added_objects.size());
2052 data.append(buf, 2);
2053 while (!added_objects.empty()) {
2055 u16 id = added_objects.front();
2056 ServerActiveObject *obj = m_env->getActiveObject(id);
2057 added_objects.pop();
2060 warningstream << FUNCTION_NAME << ": NULL object id="
2061 << (int)id << std::endl;
2066 u8 type = obj->getSendType();
2068 // Add to data buffer for sending
2069 writeU16((u8*)buf, id);
2070 data.append(buf, 2);
2071 writeU8((u8*)buf, type);
2072 data.append(buf, 1);
2074 data.append(serializeString32(
2075 obj->getClientInitializationData(client->net_proto_version)));
2077 // Add to known objects
2078 client->m_known_objects.insert(id);
2080 obj->m_known_by_count++;
2083 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2084 pkt.putRawString(data.c_str(), data.size());
2087 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2088 << removed_count << " removed, " << added_count << " added, "
2089 << "packet size is " << pkt.getSize() << std::endl;
2092 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2095 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2096 datas.size(), peer_id);
2098 pkt.putRawString(datas.c_str(), datas.size());
2100 m_clients.send(pkt.getPeerId(),
2101 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2105 void Server::SendCSMRestrictionFlags(session_t peer_id)
2107 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2108 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2109 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2113 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2115 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2120 inline s32 Server::nextSoundId()
2122 s32 ret = m_next_sound_id;
2123 if (m_next_sound_id == INT32_MAX)
2124 m_next_sound_id = 0; // signed overflow is undefined
2130 s32 Server::playSound(ServerPlayingSound ¶ms, bool ephemeral)
2132 // Find out initial position of sound
2133 bool pos_exists = false;
2134 const v3f pos = params.getPos(m_env, &pos_exists);
2135 // If position is not found while it should be, cancel sound
2136 if(pos_exists != (params.type != SoundLocation::Local))
2139 // Filter destination clients
2140 std::vector<session_t> dst_clients;
2141 if (!params.to_player.empty()) {
2142 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2144 infostream<<"Server::playSound: Player \""<<params.to_player
2145 <<"\" not found"<<std::endl;
2148 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2149 infostream<<"Server::playSound: Player \""<<params.to_player
2150 <<"\" not connected"<<std::endl;
2153 dst_clients.push_back(player->getPeerId());
2155 std::vector<session_t> clients = m_clients.getClientIDs();
2157 for (const session_t client_id : clients) {
2158 RemotePlayer *player = m_env->getPlayer(client_id);
2161 if (!params.exclude_player.empty() &&
2162 params.exclude_player == player->getName())
2165 PlayerSAO *sao = player->getPlayerSAO();
2170 if(sao->getBasePosition().getDistanceFrom(pos) >
2171 params.max_hear_distance)
2174 dst_clients.push_back(client_id);
2178 if(dst_clients.empty())
2181 // old clients will still use this, so pick a reserved ID (-1)
2182 const s32 id = ephemeral ? -1 : nextSoundId();
2184 float gain = params.gain * params.spec.gain;
2185 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2186 pkt << id << params.spec.name << gain
2187 << (u8) params.type << pos << params.object
2188 << params.spec.loop << params.spec.fade << params.spec.pitch
2191 bool as_reliable = !ephemeral;
2193 for (const session_t peer_id : dst_clients) {
2195 params.clients.insert(peer_id);
2196 m_clients.send(peer_id, 0, &pkt, as_reliable);
2200 m_playing_sounds[id] = std::move(params);
2203 void Server::stopSound(s32 handle)
2205 auto it = m_playing_sounds.find(handle);
2206 if (it == m_playing_sounds.end())
2209 ServerPlayingSound &psound = it->second;
2211 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2214 for (session_t peer_id : psound.clients) {
2216 m_clients.send(peer_id, 0, &pkt, true);
2219 // Remove sound reference
2220 m_playing_sounds.erase(it);
2223 void Server::fadeSound(s32 handle, float step, float gain)
2225 auto it = m_playing_sounds.find(handle);
2226 if (it == m_playing_sounds.end())
2229 ServerPlayingSound &psound = it->second;
2230 psound.gain = gain; // destination gain
2232 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2233 pkt << handle << step << gain;
2235 for (session_t peer_id : psound.clients) {
2237 m_clients.send(peer_id, 0, &pkt, true);
2240 // Remove sound reference
2241 if (gain <= 0 || psound.clients.empty())
2242 m_playing_sounds.erase(it);
2245 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2248 v3f p_f = intToFloat(p, BS);
2249 v3s16 block_pos = getNodeBlockPos(p);
2251 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2254 sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2257 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2258 float far_d_nodes, bool remove_metadata)
2260 v3f p_f = intToFloat(p, BS);
2261 v3s16 block_pos = getNodeBlockPos(p);
2263 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2264 pkt << p << n.param0 << n.param1 << n.param2
2265 << (u8) (remove_metadata ? 0 : 1);
2266 sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2269 void Server::sendNodeChangePkt(NetworkPacket &pkt, v3s16 block_pos,
2270 v3f p, float far_d_nodes, std::unordered_set<u16> *far_players)
2272 float maxd = far_d_nodes * BS;
2273 std::vector<session_t> clients = m_clients.getClientIDs();
2274 ClientInterface::AutoLock clientlock(m_clients);
2276 for (session_t client_id : clients) {
2277 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2281 RemotePlayer *player = m_env->getPlayer(client_id);
2282 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2284 // If player is far away, only set modified blocks not sent
2285 if (!client->isBlockSent(block_pos) || (sao &&
2286 sao->getBasePosition().getDistanceFrom(p) > maxd)) {
2288 far_players->emplace(client_id);
2290 client->SetBlockNotSent(block_pos);
2295 m_clients.send(client_id, 0, &pkt, true);
2299 void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
2301 NodeMetadataList meta_updates_list(false);
2302 std::ostringstream os(std::ios::binary);
2304 std::vector<session_t> clients = m_clients.getClientIDs();
2305 ClientInterface::AutoLock clientlock(m_clients);
2307 for (session_t i : clients) {
2308 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2312 ServerActiveObject *player = getPlayerSAO(i);
2315 player_pos = floatToInt(player->getBasePosition(), BS);
2317 for (const v3s16 pos : positions) {
2318 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2323 v3s16 block_pos = getNodeBlockPos(pos);
2324 if (!client->isBlockSent(block_pos) ||
2325 player_pos.getDistanceFrom(pos) > far_d_nodes) {
2326 client->SetBlockNotSent(block_pos);
2330 // Add the change to send list
2331 meta_updates_list.set(pos, meta);
2333 if (meta_updates_list.size() == 0)
2336 // Send the meta changes
2338 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2339 std::string raw = os.str();
2341 compressZlib(raw, os);
2343 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
2344 pkt.putLongString(os.str());
2347 meta_updates_list.clear();
2351 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2352 u16 net_proto_version, SerializedBlockCache *cache)
2354 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2355 std::string s, *sptr = nullptr;
2358 auto it = cache->find({block->getPos(), ver});
2359 if (it != cache->end())
2363 // Serialize the block in the right format
2365 std::ostringstream os(std::ios_base::binary);
2366 block->serialize(os, ver, false, net_compression_level);
2367 block->serializeNetworkSpecific(os);
2372 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2373 pkt << block->getPos();
2374 pkt.putRawString(*sptr);
2377 // Store away in cache
2378 if (cache && sptr == &s)
2379 (*cache)[{block->getPos(), ver}] = std::move(s);
2382 void Server::SendBlocks(float dtime)
2384 MutexAutoLock envlock(m_env_mutex);
2385 //TODO check if one big lock could be faster then multiple small ones
2387 std::vector<PrioritySortedBlockTransfer> queue;
2389 u32 total_sending = 0, unique_clients = 0;
2392 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2394 std::vector<session_t> clients = m_clients.getClientIDs();
2396 ClientInterface::AutoLock clientlock(m_clients);
2397 for (const session_t client_id : clients) {
2398 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2403 total_sending += client->getSendingCount();
2404 const auto old_count = queue.size();
2405 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2406 unique_clients += queue.size() > old_count ? 1 : 0;
2411 // Lowest priority number comes first.
2412 // Lowest is most important.
2413 std::sort(queue.begin(), queue.end());
2415 ClientInterface::AutoLock clientlock(m_clients);
2417 // Maximal total count calculation
2418 // The per-client block sends is halved with the maximal online users
2419 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2420 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2422 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2423 Map &map = m_env->getMap();
2425 SerializedBlockCache cache, *cache_ptr = nullptr;
2426 if (unique_clients > 1) {
2427 // caching is pointless with a single client
2431 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2432 if (total_sending >= max_blocks_to_send)
2435 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2439 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2444 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2445 client->net_proto_version, cache_ptr);
2447 client->SentBlock(block_to_send.pos);
2452 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2454 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2458 ClientInterface::AutoLock clientlock(m_clients);
2459 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2460 if (!client || client->isBlockSent(blockpos))
2462 SendBlockNoLock(peer_id, block, client->serialization_version,
2463 client->net_proto_version);
2468 bool Server::addMediaFile(const std::string &filename,
2469 const std::string &filepath, std::string *filedata_to,
2470 std::string *digest_to)
2472 // If name contains illegal characters, ignore the file
2473 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2474 infostream << "Server: ignoring illegal file name: \""
2475 << filename << "\"" << std::endl;
2478 // If name is not in a supported format, ignore it
2479 const char *supported_ext[] = {
2480 ".png", ".jpg", ".bmp", ".tga",
2482 ".x", ".b3d", ".obj",
2483 // Custom translation file format
2487 if (removeStringEnd(filename, supported_ext).empty()) {
2488 infostream << "Server: ignoring unsupported file extension: \""
2489 << filename << "\"" << std::endl;
2492 // Ok, attempt to load the file and add to cache
2495 std::string filedata;
2496 if (!fs::ReadFile(filepath, filedata)) {
2497 errorstream << "Server::addMediaFile(): Failed to open \""
2498 << filename << "\" for reading" << std::endl;
2502 if (filedata.empty()) {
2503 errorstream << "Server::addMediaFile(): Empty file \""
2504 << filepath << "\"" << std::endl;
2509 sha1.addBytes(filedata.c_str(), filedata.length());
2511 unsigned char *digest = sha1.getDigest();
2512 std::string sha1_base64 = base64_encode(digest, 20);
2513 std::string sha1_hex = hex_encode((char*) digest, 20);
2515 *digest_to = std::string((char*) digest, 20);
2519 m_media[filename] = MediaInfo(filepath, sha1_base64);
2520 verbosestream << "Server: " << sha1_hex << " is " << filename
2524 *filedata_to = std::move(filedata);
2528 void Server::fillMediaCache()
2530 infostream << "Server: Calculating media file checksums" << std::endl;
2532 // Collect all media file paths
2533 std::vector<std::string> paths;
2535 // ordered in descending priority
2536 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2537 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2538 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2539 m_modmgr->getModsMediaPaths(paths);
2541 // Collect media file information from paths into cache
2542 for (const std::string &mediapath : paths) {
2543 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2544 for (const fs::DirListNode &dln : dirlist) {
2545 if (dln.dir) // Ignore dirs (already in paths)
2548 const std::string &filename = dln.name;
2549 if (m_media.find(filename) != m_media.end()) // Do not override
2552 std::string filepath = mediapath;
2553 filepath.append(DIR_DELIM).append(filename);
2554 addMediaFile(filename, filepath);
2558 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2561 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2564 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2567 std::string lang_suffix;
2568 lang_suffix.append(".").append(lang_code).append(".tr");
2569 for (const auto &i : m_media) {
2570 if (i.second.no_announce)
2572 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2579 for (const auto &i : m_media) {
2580 if (i.second.no_announce)
2582 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2584 pkt << i.first << i.second.sha1_digest;
2587 pkt << g_settings->get("remote_media");
2590 verbosestream << "Server: Announcing files to id(" << peer_id
2591 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2594 struct SendableMedia
2600 SendableMedia(const std::string &name, const std::string &path,
2601 std::string &&data):
2602 name(name), path(path), data(std::move(data))
2606 void Server::sendRequestedMedia(session_t peer_id,
2607 const std::vector<std::string> &tosend)
2609 verbosestream<<"Server::sendRequestedMedia(): "
2610 <<"Sending files to client"<<std::endl;
2614 // Put 5kB in one bunch (this is not accurate)
2615 u32 bytes_per_bunch = 5000;
2617 std::vector< std::vector<SendableMedia> > file_bunches;
2618 file_bunches.emplace_back();
2620 u32 file_size_bunch_total = 0;
2622 for (const std::string &name : tosend) {
2623 if (m_media.find(name) == m_media.end()) {
2624 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2625 <<"unknown file \""<<(name)<<"\""<<std::endl;
2629 const auto &m = m_media[name];
2633 if (!fs::ReadFile(m.path, data)) {
2634 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2635 << name << "\"" << std::endl;
2638 file_size_bunch_total += data.size();
2641 file_bunches.back().emplace_back(name, m.path, std::move(data));
2643 // Start next bunch if got enough data
2644 if(file_size_bunch_total >= bytes_per_bunch) {
2645 file_bunches.emplace_back();
2646 file_size_bunch_total = 0;
2651 /* Create and send packets */
2653 u16 num_bunches = file_bunches.size();
2654 for (u16 i = 0; i < num_bunches; i++) {
2657 u16 total number of texture bunches
2658 u16 index of this bunch
2659 u32 number of files in this bunch
2668 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2669 pkt << num_bunches << i << (u32) file_bunches[i].size();
2671 for (const SendableMedia &j : file_bunches[i]) {
2673 pkt.putLongString(j.data);
2676 verbosestream << "Server::sendRequestedMedia(): bunch "
2677 << i << "/" << num_bunches
2678 << " files=" << file_bunches[i].size()
2679 << " size=" << pkt.getSize() << std::endl;
2684 void Server::stepPendingDynMediaCallbacks(float dtime)
2686 MutexAutoLock lock(m_env_mutex);
2688 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2689 it->second.expiry_timer -= dtime;
2690 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2697 const auto &name = it->second.filename;
2698 if (!name.empty()) {
2699 assert(m_media.count(name));
2700 // if no_announce isn't set we're definitely deleting the wrong file!
2701 sanity_check(m_media[name].no_announce);
2703 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2704 m_media.erase(name);
2706 getScriptIface()->freeDynamicMediaCallback(it->first);
2707 it = m_pending_dyn_media.erase(it);
2711 void Server::SendMinimapModes(session_t peer_id,
2712 std::vector<MinimapMode> &modes, size_t wanted_mode)
2714 RemotePlayer *player = m_env->getPlayer(peer_id);
2716 if (player->getPeerId() == PEER_ID_INEXISTENT)
2719 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2720 pkt << (u16)modes.size() << (u16)wanted_mode;
2722 for (auto &mode : modes)
2723 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2728 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2730 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2734 pkt << false; // Remove inventory
2736 pkt << true; // Update inventory
2738 // Serialization & NetworkPacket isn't a love story
2739 std::ostringstream os(std::ios_base::binary);
2740 inventory->serialize(os);
2741 inventory->setModified(false);
2743 const std::string &os_str = os.str();
2744 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2745 pkt.putRawString(os_str);
2748 if (peer_id == PEER_ID_INEXISTENT)
2749 m_clients.sendToAll(&pkt);
2754 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2756 // Lookup player name, to filter detached inventories just after
2757 std::string peer_name;
2758 if (peer_id != PEER_ID_INEXISTENT) {
2759 peer_name = getClient(peer_id, CS_Created)->getName();
2762 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2763 sendDetachedInventory(inv, name, peer_id);
2766 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2773 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2775 infostream << "Server::DiePlayer(): Player "
2776 << playersao->getPlayer()->getName()
2777 << " dies" << std::endl;
2779 playersao->clearParentAttachment();
2781 // Trigger scripted stuff
2782 m_script->on_dieplayer(playersao, reason);
2784 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2787 void Server::RespawnPlayer(session_t peer_id)
2789 PlayerSAO *playersao = getPlayerSAO(peer_id);
2792 infostream << "Server::RespawnPlayer(): Player "
2793 << playersao->getPlayer()->getName()
2794 << " respawns" << std::endl;
2796 const auto *prop = playersao->accessObjectProperties();
2797 playersao->setHP(prop->hp_max,
2798 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2799 playersao->setBreath(prop->breath_max);
2801 bool repositioned = m_script->on_respawnplayer(playersao);
2802 if (!repositioned) {
2803 // setPos will send the new position to client
2804 playersao->setPos(findSpawnPos());
2809 void Server::DenySudoAccess(session_t peer_id)
2811 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2816 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2817 const std::string &custom_reason, bool reconnect)
2819 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2820 m_clients.event(peer_id, CSE_SetDenied);
2821 DisconnectPeer(peer_id);
2824 void Server::DisconnectPeer(session_t peer_id)
2826 m_modchannel_mgr->leaveAllChannels(peer_id);
2827 m_con->DisconnectPeer(peer_id);
2830 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2833 RemoteClient* client = getClient(peer_id, CS_Invalid);
2835 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2837 // Right now, the auth mechs don't change between login and sudo mode.
2838 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2839 client->allowed_sudo_mechs = sudo_auth_mechs;
2841 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2842 << g_settings->getFloat("dedicated_server_step")
2846 m_clients.event(peer_id, CSE_AuthAccept);
2848 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2850 // We only support SRP right now
2851 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2853 resp_pkt << sudo_auth_mechs;
2855 m_clients.event(peer_id, CSE_SudoSuccess);
2859 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2861 std::wstring message;
2864 Clear references to playing sounds
2866 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2867 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2868 ServerPlayingSound &psound = i->second;
2869 psound.clients.erase(peer_id);
2870 if (psound.clients.empty())
2871 m_playing_sounds.erase(i++);
2876 // clear formspec info so the next client can't abuse the current state
2877 m_formspec_state_data.erase(peer_id);
2879 RemotePlayer *player = m_env->getPlayer(peer_id);
2881 /* Run scripts and remove from environment */
2883 PlayerSAO *playersao = player->getPlayerSAO();
2886 playersao->clearChildAttachments();
2887 playersao->clearParentAttachment();
2889 // inform connected clients
2890 const std::string &player_name = player->getName();
2891 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2892 // (u16) 1 + std::string represents a vector serialization representation
2893 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2894 m_clients.sendToAll(¬ice);
2896 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2898 playersao->disconnected();
2905 if (player && reason != CDR_DENY) {
2906 std::ostringstream os(std::ios_base::binary);
2907 std::vector<session_t> clients = m_clients.getClientIDs();
2909 for (const session_t client_id : clients) {
2911 RemotePlayer *player = m_env->getPlayer(client_id);
2915 // Get name of player
2916 os << player->getName() << " ";
2919 std::string name = player->getName();
2920 actionstream << name << " "
2921 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2922 << " List of players: " << os.str() << std::endl;
2924 m_admin_chat->outgoing_queue.push_back(
2925 new ChatEventNick(CET_NICK_REMOVE, name));
2929 MutexAutoLock env_lock(m_env_mutex);
2930 m_clients.DeleteClient(peer_id);
2934 // Send leave chat message to all remaining clients
2935 if (!message.empty()) {
2936 SendChatMessage(PEER_ID_INEXISTENT,
2937 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2941 void Server::UpdateCrafting(RemotePlayer *player)
2943 InventoryList *clist = player->inventory.getList("craft");
2944 if (!clist || clist->getSize() == 0)
2947 if (!clist->checkModified())
2950 // Get a preview for crafting
2952 InventoryLocation loc;
2953 loc.setPlayer(player->getName());
2954 std::vector<ItemStack> output_replacements;
2955 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2956 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2959 InventoryList *plist = player->inventory.getList("craftpreview");
2960 if (plist && plist->getSize() >= 1) {
2961 // Put the new preview in
2962 plist->changeItem(0, preview);
2966 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2968 if (evt->type == CET_NICK_ADD) {
2969 // The terminal informed us of its nick choice
2970 m_admin_nick = ((ChatEventNick *)evt)->nick;
2971 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2972 errorstream << "You haven't set up an account." << std::endl
2973 << "Please log in using the client as '"
2974 << m_admin_nick << "' with a secure password." << std::endl
2975 << "Until then, you can't execute admin tasks via the console," << std::endl
2976 << "and everybody can claim the user account instead of you," << std::endl
2977 << "giving them full control over this server." << std::endl;
2980 assert(evt->type == CET_CHAT);
2981 handleAdminChat((ChatEventChat *)evt);
2985 std::wstring Server::handleChat(const std::string &name,
2986 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2988 // If something goes wrong, this player is to blame
2989 RollbackScopeActor rollback_scope(m_rollback,
2990 std::string("player:") + name);
2992 if (g_settings->getBool("strip_color_codes"))
2993 wmessage = unescape_enriched(wmessage);
2996 switch (player->canSendChatMessage()) {
2997 case RPLAYER_CHATRESULT_FLOODING: {
2998 std::wstringstream ws;
2999 ws << L"You cannot send more messages. You are limited to "
3000 << g_settings->getFloat("chat_message_limit_per_10sec")
3001 << L" messages per 10 seconds.";
3004 case RPLAYER_CHATRESULT_KICK:
3005 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
3006 "You have been kicked due to message flooding.");
3008 case RPLAYER_CHATRESULT_OK:
3011 FATAL_ERROR("Unhandled chat filtering result found.");
3015 if (m_max_chatmessage_length > 0
3016 && wmessage.length() > m_max_chatmessage_length) {
3017 return L"Your message exceed the maximum chat message limit set on the server. "
3018 L"It was refused. Send a shorter message";
3021 auto message = trim(wide_to_utf8(wmessage));
3022 if (message.empty())
3025 if (message.find_first_of("\n\r") != std::wstring::npos) {
3026 return L"Newlines are not permitted in chat messages";
3029 // Run script hook, exit if script ate the chat message
3030 if (m_script->on_chat_message(name, message))
3035 // Whether to send line to the player that sent the message, or to all players
3036 bool broadcast_line = true;
3038 if (check_shout_priv && !checkPriv(name, "shout")) {
3039 line += L"-!- You don't have permission to shout.";
3040 broadcast_line = false;
3043 Workaround for fixing chat on Android. Lua doesn't handle
3044 the Cyrillic alphabet and some characters on older Android devices
3047 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3049 line += utf8_to_wide(m_script->formatChatMessage(name,
3050 wide_to_utf8(wmessage)));
3055 Tell calling method to send the message to sender
3057 if (!broadcast_line)
3061 Send the message to others
3063 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3065 ChatMessage chatmsg(line);
3067 std::vector<session_t> clients = m_clients.getClientIDs();
3068 for (u16 cid : clients)
3069 SendChatMessage(cid, chatmsg);
3074 void Server::handleAdminChat(const ChatEventChat *evt)
3076 std::string name = evt->nick;
3077 std::wstring wmessage = evt->evt_msg;
3079 std::wstring answer = handleChat(name, wmessage);
3081 // If asked to send answer to sender
3082 if (!answer.empty()) {
3083 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3087 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3089 RemoteClient *client = getClientNoEx(peer_id,state_min);
3091 throw ClientNotFoundException("Client not found");
3095 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3097 return m_clients.getClientNoEx(peer_id, state_min);
3100 std::string Server::getPlayerName(session_t peer_id)
3102 RemotePlayer *player = m_env->getPlayer(peer_id);
3104 return "[id="+itos(peer_id)+"]";
3105 return player->getName();
3108 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3110 RemotePlayer *player = m_env->getPlayer(peer_id);
3113 return player->getPlayerSAO();
3116 std::string Server::getStatusString()
3118 std::ostringstream os(std::ios_base::binary);
3121 os << "version: " << g_version_string;
3123 os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3125 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3127 os << " | max lag: " << std::setprecision(3);
3128 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3130 // Information about clients
3132 os << " | clients: ";
3134 std::vector<session_t> clients = m_clients.getClientIDs();
3135 for (session_t client_id : clients) {
3136 RemotePlayer *player = m_env->getPlayer(client_id);
3138 // Get name of player
3139 const char *name = player ? player->getName() : "<unknown>";
3141 // Add name to information string
3150 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3151 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3153 if (!g_settings->get("motd").empty())
3154 os << std::endl << "# Server: " << g_settings->get("motd");
3159 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3161 std::set<std::string> privs;
3162 m_script->getAuth(name, NULL, &privs);
3166 bool Server::checkPriv(const std::string &name, const std::string &priv)
3168 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3169 return (privs.count(priv) != 0);
3172 void Server::reportPrivsModified(const std::string &name)
3175 std::vector<session_t> clients = m_clients.getClientIDs();
3176 for (const session_t client_id : clients) {
3177 RemotePlayer *player = m_env->getPlayer(client_id);
3178 reportPrivsModified(player->getName());
3181 RemotePlayer *player = m_env->getPlayer(name.c_str());
3184 SendPlayerPrivileges(player->getPeerId());
3185 PlayerSAO *sao = player->getPlayerSAO();
3188 sao->updatePrivileges(
3189 getPlayerEffectivePrivs(name),
3194 void Server::reportInventoryFormspecModified(const std::string &name)
3196 RemotePlayer *player = m_env->getPlayer(name.c_str());
3199 SendPlayerInventoryFormspec(player->getPeerId());
3202 void Server::reportFormspecPrependModified(const std::string &name)
3204 RemotePlayer *player = m_env->getPlayer(name.c_str());
3207 SendPlayerFormspecPrepend(player->getPeerId());
3210 void Server::setIpBanned(const std::string &ip, const std::string &name)
3212 m_banmanager->add(ip, name);
3215 void Server::unsetIpBanned(const std::string &ip_or_name)
3217 m_banmanager->remove(ip_or_name);
3220 std::string Server::getBanDescription(const std::string &ip_or_name)
3222 return m_banmanager->getBanDescription(ip_or_name);
3225 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3227 // m_env will be NULL if the server is initializing
3231 if (m_admin_nick == name && !m_admin_nick.empty()) {
3232 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3235 RemotePlayer *player = m_env->getPlayer(name);
3240 if (player->getPeerId() == PEER_ID_INEXISTENT)
3243 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3246 bool Server::showFormspec(const char *playername, const std::string &formspec,
3247 const std::string &formname)
3249 // m_env will be NULL if the server is initializing
3253 RemotePlayer *player = m_env->getPlayer(playername);
3257 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3261 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3266 u32 id = player->addHud(form);
3268 SendHUDAdd(player->getPeerId(), id, form);
3273 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3277 HudElement* todel = player->removeHud(id);
3284 SendHUDRemove(player->getPeerId(), id);
3288 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3293 SendHUDChange(player->getPeerId(), id, stat, data);
3297 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3302 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3303 if (new_hud_flags == player->hud_flags) // no change
3306 SendHUDSetFlags(player->getPeerId(), flags, mask);
3307 player->hud_flags = new_hud_flags;
3309 PlayerSAO* playersao = player->getPlayerSAO();
3314 m_script->player_event(playersao, "hud_changed");
3318 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3323 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3326 player->setHotbarItemcount(hotbar_itemcount);
3327 std::ostringstream os(std::ios::binary);
3328 writeS32(os, hotbar_itemcount);
3329 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3333 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3338 player->setHotbarImage(name);
3339 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3342 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3347 player->setHotbarSelectedImage(name);
3348 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3351 Address Server::getPeerAddress(session_t peer_id)
3353 // Note that this is only set after Init was received in Server::handleCommand_Init
3354 return getClient(peer_id, CS_Invalid)->getAddress();
3357 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3358 v2s32 animation_frames[4], f32 frame_speed)
3360 sanity_check(player);
3361 player->setLocalAnimations(animation_frames, frame_speed);
3362 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3365 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3367 sanity_check(player);
3368 player->eye_offset_first = first;
3369 player->eye_offset_third = third;
3370 SendEyeOffset(player->getPeerId(), first, third);
3373 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3375 sanity_check(player);
3376 player->setSky(params);
3377 SendSetSky(player->getPeerId(), params);
3380 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3382 sanity_check(player);
3383 player->setSun(params);
3384 SendSetSun(player->getPeerId(), params);
3387 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3389 sanity_check(player);
3390 player->setMoon(params);
3391 SendSetMoon(player->getPeerId(), params);
3394 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3396 sanity_check(player);
3397 player->setStars(params);
3398 SendSetStars(player->getPeerId(), params);
3401 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3403 sanity_check(player);
3404 player->setCloudParams(params);
3405 SendCloudParams(player->getPeerId(), params);
3408 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3411 sanity_check(player);
3412 player->overrideDayNightRatio(do_override, ratio);
3413 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3416 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3418 sanity_check(player);
3419 player->setLighting(lighting);
3420 SendSetLighting(player->getPeerId(), lighting);
3423 void Server::notifyPlayers(const std::wstring &msg)
3425 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3428 void Server::spawnParticle(const std::string &playername,
3429 const ParticleParameters &p)
3431 // m_env will be NULL if the server is initializing
3435 session_t peer_id = PEER_ID_INEXISTENT;
3437 if (!playername.empty()) {
3438 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3441 peer_id = player->getPeerId();
3442 proto_ver = player->protocol_version;
3445 SendSpawnParticle(peer_id, proto_ver, p);
3448 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3449 ServerActiveObject *attached, const std::string &playername)
3451 // m_env will be NULL if the server is initializing
3455 session_t peer_id = PEER_ID_INEXISTENT;
3457 if (!playername.empty()) {
3458 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3461 peer_id = player->getPeerId();
3462 proto_ver = player->protocol_version;
3465 u16 attached_id = attached ? attached->getId() : 0;
3468 if (attached_id == 0)
3469 id = m_env->addParticleSpawner(p.time);
3471 id = m_env->addParticleSpawner(p.time, attached_id);
3473 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3477 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3479 // m_env will be NULL if the server is initializing
3481 throw ServerError("Can't delete particle spawners during initialisation!");
3483 session_t peer_id = PEER_ID_INEXISTENT;
3484 if (!playername.empty()) {
3485 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3488 peer_id = player->getPeerId();
3491 m_env->deleteParticleSpawner(id);
3492 SendDeleteParticleSpawner(peer_id, id);
3495 bool Server::dynamicAddMedia(std::string filepath,
3496 const u32 token, const std::string &to_player, bool ephemeral)
3498 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3499 auto it = m_media.find(filename);
3500 if (it != m_media.end()) {
3501 // Allow the same path to be "added" again in certain conditions
3502 if (ephemeral || it->second.path != filepath) {
3503 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3504 << "\" already exists in media cache" << std::endl;
3509 // Load the file and add it to our media cache
3510 std::string filedata, raw_hash;
3511 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3516 // Create a copy of the file and swap out the path, this removes the
3517 // requirement that mods keep the file accessible at the original path.
3518 filepath = fs::CreateTempFile();
3519 bool ok = ([&] () -> bool {
3520 if (filepath.empty())
3522 std::ofstream os(filepath.c_str(), std::ios::binary);
3530 errorstream << "Server: failed to create a copy of media file "
3531 << "\"" << filename << "\"" << std::endl;
3532 m_media.erase(filename);
3535 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3536 << filepath << std::endl;
3538 m_media[filename].path = filepath;
3539 m_media[filename].no_announce = true;
3540 // stepPendingDynMediaCallbacks will clean this up later.
3541 } else if (!to_player.empty()) {
3542 m_media[filename].no_announce = true;
3545 // Push file to existing clients
3546 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3547 pkt << raw_hash << filename << (bool)ephemeral;
3549 NetworkPacket legacy_pkt = pkt;
3551 // Newer clients get asked to fetch the file (asynchronous)
3553 // Older clients have an awful hack that just throws the data at them
3554 legacy_pkt.putLongString(filedata);
3556 std::unordered_set<session_t> delivered, waiting;
3558 ClientInterface::AutoLock clientlock(m_clients);
3559 for (auto &pair : m_clients.getClientList()) {
3560 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3562 If a client is in the DefinitionsSent state it is too late to
3563 transfer the file via sendMediaAnnouncement() but at the same
3564 time the client cannot accept a media push yet.
3565 Short of artificially delaying the joining process there is no
3566 way for the server to resolve this so we (currently) opt not to.
3568 warningstream << "The media \"" << filename << "\" (dynamic) could "
3569 "not be delivered to " << pair.second->getName()
3570 << " due to a race condition." << std::endl;
3573 if (pair.second->getState() < CS_Active)
3576 const auto proto_ver = pair.second->net_proto_version;
3580 const session_t peer_id = pair.second->peer_id;
3581 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3584 if (proto_ver < 40) {
3585 delivered.emplace(peer_id);
3587 The network layer only guarantees ordered delivery inside a channel.
3588 Since the very next packet could be one that uses the media, we have
3589 to push the media over ALL channels to ensure it is processed before
3590 it is used. In practice this means channels 1 and 0.
3592 m_clients.send(peer_id, 1, &legacy_pkt, true);
3593 m_clients.send(peer_id, 0, &legacy_pkt, true);
3595 waiting.emplace(peer_id);
3596 Send(peer_id, &pkt);
3601 // Run callback for players that already had the file delivered (legacy-only)
3602 for (session_t peer_id : delivered) {
3603 if (auto player = m_env->getPlayer(peer_id))
3604 getScriptIface()->on_dynamic_media_added(token, player->getName());
3607 // Save all others in our pending state
3608 auto &state = m_pending_dyn_media[token];
3609 state.waiting_players = std::move(waiting);
3610 // regardless of success throw away the callback after a while
3611 state.expiry_timer = 60.0f;
3613 state.filename = filename;
3618 // actions: time-reversed list
3619 // Return value: success/failure
3620 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3621 std::list<std::string> *log)
3623 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3624 ServerMap *map = (ServerMap*)(&m_env->getMap());
3626 // Fail if no actions to handle
3627 if (actions.empty()) {
3629 log->push_back("Nothing to do.");
3636 for (const RollbackAction &action : actions) {
3638 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3641 std::ostringstream os;
3642 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3643 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3645 log->push_back(os.str());
3647 std::ostringstream os;
3648 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3649 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3651 log->push_back(os.str());
3655 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3656 <<" failed"<<std::endl;
3658 // Call it done if less than half failed
3659 return num_failed <= num_tried/2;
3662 // IGameDef interface
3664 IItemDefManager *Server::getItemDefManager()
3669 const NodeDefManager *Server::getNodeDefManager()
3674 ICraftDefManager *Server::getCraftDefManager()
3679 u16 Server::allocateUnknownNodeId(const std::string &name)
3681 return m_nodedef->allocateDummy(name);
3684 IWritableItemDefManager *Server::getWritableItemDefManager()
3689 NodeDefManager *Server::getWritableNodeDefManager()
3694 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3699 const std::vector<ModSpec> & Server::getMods() const
3701 return m_modmgr->getMods();
3704 const ModSpec *Server::getModSpec(const std::string &modname) const
3706 return m_modmgr->getModSpec(modname);
3709 std::string Server::getBuiltinLuaPath()
3711 return porting::path_share + DIR_DELIM + "builtin";
3714 v3f Server::findSpawnPos()
3716 ServerMap &map = m_env->getServerMap();
3718 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3719 return nodeposf * BS;
3721 bool is_good = false;
3722 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3723 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3725 // Try to find a good place a few times
3726 for (s32 i = 0; i < 4000 && !is_good; i++) {
3727 s32 range = MYMIN(1 + i, range_max);
3728 // We're going to try to throw the player to this position
3729 v2s16 nodepos2d = v2s16(
3730 -range + myrand_range(0, range*2),
3731 -range + myrand_range(0, range*2));
3732 // Get spawn level at point
3733 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3734 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3735 // signify an unsuitable spawn position, or if outside limits.
3736 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3737 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3740 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3741 // Consecutive empty nodes
3744 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3745 // avoid obstructions in already-generated mapblocks.
3746 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3747 // no obstructions, but mapgen decorations are generated after spawn so
3748 // the player may end up inside one.
3749 for (s32 i = 0; i < 8; i++) {
3750 v3s16 blockpos = getNodeBlockPos(nodepos);
3751 map.emergeBlock(blockpos, true);
3752 content_t c = map.getNode(nodepos).getContent();
3754 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3755 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3756 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3758 if (air_count >= 2) {
3759 // Spawn in lower empty node
3761 nodeposf = intToFloat(nodepos, BS);
3762 // Don't spawn the player outside map boundaries
3763 if (objectpos_over_limit(nodeposf))
3764 // Exit this loop, positions above are probably over limit
3767 // Good position found, cause an exit from main loop
3781 // No suitable spawn point found, return fallback 0,0,0
3782 return v3f(0.0f, 0.0f, 0.0f);
3785 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3787 if (delay == 0.0f) {
3788 // No delay, shutdown immediately
3789 m_shutdown_state.is_requested = true;
3790 // only print to the infostream, a chat message saying
3791 // "Server Shutting Down" is sent when the server destructs.
3792 infostream << "*** Immediate Server shutdown requested." << std::endl;
3793 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3794 // Negative delay, cancel shutdown if requested
3795 m_shutdown_state.reset();
3796 std::wstringstream ws;
3798 ws << L"*** Server shutdown canceled.";
3800 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3801 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3802 // m_shutdown_* are already handled, skip.
3804 } else if (delay > 0.0f) {
3805 // Positive delay, tell the clients when the server will shut down
3806 std::wstringstream ws;
3808 ws << L"*** Server shutting down in "
3809 << duration_to_string(myround(delay)).c_str()
3812 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3813 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3816 m_shutdown_state.trigger(delay, msg, reconnect);
3819 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3822 Try to get an existing player
3824 RemotePlayer *player = m_env->getPlayer(name);
3826 // If player is already connected, cancel
3827 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3828 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3833 If player with the wanted peer_id already exists, cancel.
3835 if (m_env->getPlayer(peer_id)) {
3836 infostream<<"emergePlayer(): Player with wrong name but same"
3837 " peer_id already exists"<<std::endl;
3842 player = new RemotePlayer(name, idef());
3845 bool newplayer = false;
3848 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3850 // Complete init with server parts
3851 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3852 player->protocol_version = proto_version;
3856 m_script->on_newplayer(playersao);
3862 void dedicated_server_loop(Server &server, bool &kill)
3864 verbosestream<<"dedicated_server_loop()"<<std::endl;
3866 IntervalLimiter m_profiler_interval;
3868 static thread_local const float steplen =
3869 g_settings->getFloat("dedicated_server_step");
3870 static thread_local const float profiler_print_interval =
3871 g_settings->getFloat("profiler_print_interval");
3874 * The dedicated server loop only does time-keeping (in Server::step) and
3875 * provides a way to main.cpp to kill the server externally (bool &kill).
3879 // This is kind of a hack but can be done like this
3880 // because server.step() is very light
3881 sleep_ms((int)(steplen*1000.0));
3882 server.step(steplen);
3884 if (server.isShutdownRequested() || kill)
3890 if (profiler_print_interval != 0) {
3891 if(m_profiler_interval.step(steplen, profiler_print_interval))
3893 infostream<<"Profiler:"<<std::endl;
3894 g_profiler->print(infostream);
3895 g_profiler->clear();
3900 infostream << "Dedicated server quitting" << std::endl;
3902 if (g_settings->getBool("server_announce"))
3903 ServerList::sendAnnounce(ServerList::AA_DELETE,
3904 server.m_bind_addr.getPort());
3913 bool Server::joinModChannel(const std::string &channel)
3915 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3916 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3919 bool Server::leaveModChannel(const std::string &channel)
3921 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3924 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3926 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3929 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3933 ModChannel* Server::getModChannel(const std::string &channel)
3935 return m_modchannel_mgr->getModChannel(channel);
3938 void Server::broadcastModChannelMessage(const std::string &channel,
3939 const std::string &message, session_t from_peer)
3941 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3945 if (message.size() > STRING_MAX_LEN) {
3946 warningstream << "ModChannel message too long, dropping before sending "
3947 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3948 << channel << ")" << std::endl;
3953 if (from_peer != PEER_ID_SERVER) {
3954 sender = getPlayerName(from_peer);
3957 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3958 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3959 resp_pkt << channel << sender << message;
3960 for (session_t peer_id : peers) {
3962 if (peer_id == from_peer)
3965 Send(peer_id, &resp_pkt);
3968 if (from_peer != PEER_ID_SERVER) {
3969 m_script->on_modchannel_message(channel, sender, message);
3973 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3975 if (lang_code.empty())
3978 auto it = server_translations.find(lang_code);
3979 if (it != server_translations.end())
3980 return &it->second; // Already loaded
3982 // [] will create an entry
3983 auto *translations = &server_translations[lang_code];
3985 std::string suffix = "." + lang_code + ".tr";
3986 for (const auto &i : m_media) {
3987 if (str_ends_with(i.first, suffix)) {
3989 if (fs::ReadFile(i.second.path, data)) {
3990 translations->loadTranslation(data);
3995 return translations;
3998 ModStorageDatabase *Server::openModStorageDatabase(const std::string &world_path)
4000 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4002 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4003 throw BaseException("Cannot read world.mt!");
4005 std::string backend = world_mt.exists("mod_storage_backend") ?
4006 world_mt.get("mod_storage_backend") : "files";
4007 if (backend == "files")
4008 warningstream << "/!\\ You are using the old mod storage files backend. "
4009 << "This backend is deprecated and may be removed in a future release /!\\"
4010 << std::endl << "Switching to SQLite3 is advised, "
4011 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4013 return openModStorageDatabase(backend, world_path, world_mt);
4016 ModStorageDatabase *Server::openModStorageDatabase(const std::string &backend,
4017 const std::string &world_path, const Settings &world_mt)
4019 if (backend == "sqlite3")
4020 return new ModStorageDatabaseSQLite3(world_path);
4023 if (backend == "postgresql") {
4024 std::string connect_string;
4025 world_mt.getNoEx("pgsql_mod_storage_connection", connect_string);
4026 return new ModStorageDatabasePostgreSQL(connect_string);
4028 #endif // USE_POSTGRESQL
4030 if (backend == "files")
4031 return new ModStorageDatabaseFiles(world_path);
4033 if (backend == "dummy")
4034 return new Database_Dummy();
4036 throw BaseException("Mod storage database backend " + backend + " not supported");
4039 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4041 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4043 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4044 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4045 errorstream << "Cannot read world.mt!" << std::endl;
4049 std::string backend = world_mt.exists("mod_storage_backend") ?
4050 world_mt.get("mod_storage_backend") : "files";
4051 if (backend == migrate_to) {
4052 errorstream << "Cannot migrate: new backend is same"
4053 << " as the old one" << std::endl;
4057 ModStorageDatabase *srcdb = nullptr;
4058 ModStorageDatabase *dstdb = nullptr;
4060 bool succeeded = false;
4063 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4064 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4068 std::vector<std::string> mod_list;
4069 srcdb->listMods(&mod_list);
4070 for (const std::string &modname : mod_list) {
4072 srcdb->getModEntries(modname, &meta);
4073 for (const auto &pair : meta) {
4074 dstdb->setModEntry(modname, pair.first, pair.second);
4082 actionstream << "Successfully migrated the metadata of "
4083 << mod_list.size() << " mods" << std::endl;
4084 world_mt.set("mod_storage_backend", migrate_to);
4085 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4086 errorstream << "Failed to update world.mt!" << std::endl;
4088 actionstream << "world.mt updated" << std::endl;
4090 } catch (BaseException &e) {
4091 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4097 if (succeeded && backend == "files") {
4099 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4100 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4101 if (!fs::Rename(storage_path, backup_path))
4102 warningstream << "After migration, " << storage_path
4103 << " could not be renamed to " << backup_path << std::endl;