3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "network/connection.h"
25 #include "network/networkprotocol.h"
26 #include "network/serveropcodes.h"
28 #include "environment.h"
30 #include "threading/mutex_auto_lock.h"
31 #include "constants.h"
37 #include "server/serveractiveobject.h"
41 #include "scripting_server.h"
46 #include "mapgen/mapgen.h"
47 #include "mapgen/mg_biome.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content/mods.h"
51 #include "modchannels.h"
52 #include "serverlist.h"
53 #include "util/string.h"
55 #include "util/serialize.h"
56 #include "util/thread.h"
57 #include "defaultsettings.h"
58 #include "server/mods.h"
59 #include "util/base64.h"
60 #include "util/sha1.h"
62 #include "database/database.h"
63 #include "chatmessage.h"
64 #include "chat_interface.h"
65 #include "remoteplayer.h"
66 #include "server/player_sao.h"
67 #include "server/serverinventorymgr.h"
68 #include "translation.h"
69 #include "database/database-sqlite3.h"
70 #include "database/database-files.h"
71 #include "database/database-dummy.h"
72 #include "gameparams.h"
74 class ClientNotFoundException : public BaseException
77 ClientNotFoundException(const char *s):
82 class ServerThread : public Thread
86 ServerThread(Server *server):
97 void *ServerThread::run()
99 BEGIN_DEBUG_EXCEPTION_HANDLER
102 * The real business of the server happens on the ServerThread.
104 * AsyncRunStep() runs an actual server step as soon as enough time has
105 * passed (dedicated_server_loop keeps track of that).
106 * Receive() blocks at least(!) 30ms waiting for a packet (so this loop
107 * doesn't busy wait) and will process any remaining packets.
111 m_server->AsyncRunStep(true);
112 } catch (con::ConnectionBindFailed &e) {
113 m_server->setAsyncFatalError(e.what());
114 } catch (LuaError &e) {
115 m_server->setAsyncFatalError(e);
118 while (!stopRequested()) {
120 m_server->AsyncRunStep();
124 } catch (con::PeerNotFoundException &e) {
125 infostream<<"Server: PeerNotFoundException"<<std::endl;
126 } catch (ClientNotFoundException &e) {
127 } catch (con::ConnectionBindFailed &e) {
128 m_server->setAsyncFatalError(e.what());
129 } catch (LuaError &e) {
130 m_server->setAsyncFatalError(e);
134 END_DEBUG_EXCEPTION_HANDLER
139 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
141 if(pos_exists) *pos_exists = false;
146 if(pos_exists) *pos_exists = true;
151 ServerActiveObject *sao = env->getActiveObject(object);
154 if(pos_exists) *pos_exists = true;
155 return sao->getBasePosition(); }
160 void Server::ShutdownState::reset()
164 should_reconnect = false;
165 is_requested = false;
168 void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
172 should_reconnect = reconnect;
175 void Server::ShutdownState::tick(float dtime, Server *server)
181 static const float shutdown_msg_times[] =
183 1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
186 // Automated messages
187 if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
188 for (float t : shutdown_msg_times) {
189 // If shutdown timer matches an automessage, shot it
190 if (m_timer > t && m_timer - dtime < t) {
191 std::wstring periodicMsg = getShutdownTimerMessage();
193 infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
194 server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
201 if (m_timer < 0.0f) {
207 std::wstring Server::ShutdownState::getShutdownTimerMessage() const
209 std::wstringstream ws;
210 ws << L"*** Server shutting down in "
211 << duration_to_string(myround(m_timer)).c_str() << ".";
220 const std::string &path_world,
221 const SubgameSpec &gamespec,
222 bool simple_singleplayer_mode,
225 ChatInterface *iface,
226 std::string *on_shutdown_errmsg
228 m_bind_addr(bind_addr),
229 m_path_world(path_world),
230 m_gamespec(gamespec),
231 m_simple_singleplayer_mode(simple_singleplayer_mode),
232 m_dedicated(dedicated),
233 m_async_fatal_error(""),
234 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
237 m_bind_addr.isIPv6(),
239 m_itemdef(createItemDefManager()),
240 m_nodedef(createNodeDefManager()),
241 m_craftdef(createCraftDefManager()),
242 m_thread(new ServerThread(this)),
245 m_on_shutdown_errmsg(on_shutdown_errmsg),
246 m_modchannel_mgr(new ModChannelMgr())
248 if (m_path_world.empty())
249 throw ServerError("Supplied empty world path");
251 if (!gamespec.isValid())
252 throw ServerError("Supplied invalid gamespec");
255 m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
257 m_metrics_backend = std::make_unique<MetricsBackend>();
260 m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
261 m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
263 m_timeofday_gauge = m_metrics_backend->addGauge(
264 "minetest_core_timeofday",
265 "Time of day value");
267 m_lag_gauge = m_metrics_backend->addGauge(
268 "minetest_core_latency",
269 "Latency value (in seconds)");
271 m_aom_buffer_counter = m_metrics_backend->addCounter(
272 "minetest_core_aom_generated_count",
273 "Number of active object messages generated");
275 m_packet_recv_counter = m_metrics_backend->addCounter(
276 "minetest_core_server_packet_recv",
277 "Processable packets received");
279 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
280 "minetest_core_server_packet_recv_processed",
281 "Valid received packets processed");
283 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
289 // Send shutdown message
290 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
291 L"*** Server shutting down"));
294 MutexAutoLock envlock(m_env_mutex);
296 infostream << "Server: Saving players" << std::endl;
297 m_env->saveLoadedPlayers();
299 infostream << "Server: Kicking players" << std::endl;
300 std::string kick_msg;
301 bool reconnect = false;
302 if (isShutdownRequested()) {
303 reconnect = m_shutdown_state.should_reconnect;
304 kick_msg = m_shutdown_state.message;
306 if (kick_msg.empty()) {
307 kick_msg = g_settings->get("kick_msg_shutdown");
309 m_env->saveLoadedPlayers(true);
310 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
311 kick_msg, reconnect);
314 actionstream << "Server: Shutting down" << std::endl;
316 // Do this before stopping the server in case mapgen callbacks need to access
317 // server-controlled resources (like ModStorages). Also do them before
318 // shutdown callbacks since they may modify state that is finalized in a
321 m_emerge->stopThreads();
324 MutexAutoLock envlock(m_env_mutex);
326 // Execute script shutdown hooks
327 infostream << "Executing shutdown hooks" << std::endl;
329 m_script->on_shutdown();
330 } catch (ModError &e) {
331 errorstream << "ModError: " << e.what() << std::endl;
332 if (m_on_shutdown_errmsg) {
333 if (m_on_shutdown_errmsg->empty()) {
334 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
336 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
341 infostream << "Server: Saving environment metadata" << std::endl;
351 // Write any changes before deletion.
352 if (m_mod_storage_database)
353 m_mod_storage_database->endSave();
355 // Delete things in the reverse order of creation
359 delete m_mod_storage_database;
365 // Deinitialize scripting
366 infostream << "Server: Deinitializing scripting" << std::endl;
368 delete m_startup_server_map; // if available
369 delete m_game_settings;
371 while (!m_unsent_map_edit_queue.empty()) {
372 delete m_unsent_map_edit_queue.front();
373 m_unsent_map_edit_queue.pop();
379 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
380 if (m_simple_singleplayer_mode)
381 infostream << " in simple singleplayer mode" << std::endl;
383 infostream << std::endl;
384 infostream << "- world: " << m_path_world << std::endl;
385 infostream << "- game: " << m_gamespec.path << std::endl;
387 m_game_settings = Settings::createLayer(SL_GAME);
389 // Create world if it doesn't exist
391 loadGameConfAndInitWorld(m_path_world,
392 fs::GetFilenameFromPath(m_path_world.c_str()),
394 } catch (const BaseException &e) {
395 throw ServerError(std::string("Failed to initialize world: ") + e.what());
398 // Create emerge manager
399 m_emerge = new EmergeManager(this);
401 // Create ban manager
402 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
403 m_banmanager = new BanManager(ban_path);
405 // Create mod storage database and begin a save for later
406 m_mod_storage_database = openModStorageDatabase(m_path_world);
407 m_mod_storage_database->beginSave();
409 m_modmgr = std::make_unique<ServerModManager>(m_path_world);
410 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
411 // complain about mods with unsatisfied dependencies
412 if (!m_modmgr->isConsistent()) {
413 m_modmgr->printUnsatisfiedModsError();
417 MutexAutoLock envlock(m_env_mutex);
419 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
420 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
421 m_startup_server_map = servermap;
423 // Initialize scripting
424 infostream << "Server: Initializing Lua" << std::endl;
426 m_script = new ServerScripting(this);
428 // Must be created before mod loading because we have some inventory creation
429 m_inventory_mgr = std::make_unique<ServerInventoryManager>();
431 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
433 m_modmgr->loadMods(m_script);
435 // Read Textures and calculate sha1 sums
438 // Apply item aliases in the node definition manager
439 m_nodedef->updateAliases(m_itemdef);
441 // Apply texture overrides from texturepack/override.txt
442 std::vector<std::string> paths;
443 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
444 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
445 for (const std::string &path : paths) {
446 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
447 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
448 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
451 m_nodedef->setNodeRegistrationStatus(true);
453 // Perform pending node name resolutions
454 m_nodedef->runNodeResolveCallbacks();
456 // unmap node names in cross-references
457 m_nodedef->resolveCrossrefs();
459 // init the recipe hashes to speed up crafting
460 m_craftdef->initHashes(this);
462 // Initialize Environment
463 m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
464 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
466 m_inventory_mgr->setEnv(m_env);
467 m_clients.setEnv(m_env);
469 if (!servermap->settings_mgr.makeMapgenParams())
470 FATAL_ERROR("Couldn't create any mapgen type");
472 // Initialize mapgens
473 m_emerge->initMapgens(servermap->getMapgenParams());
475 if (g_settings->getBool("enable_rollback_recording")) {
476 // Create rollback manager
477 m_rollback = new RollbackManager(m_path_world, this);
480 // Give environment reference to scripting api
481 m_script->initializeEnvironment(m_env);
483 // Register us to receive map edit events
484 servermap->addEventReceiver(this);
488 // Those settings can be overwritten in world.mt, they are
489 // intended to be cached after environment loading.
490 m_liquid_transform_every = g_settings->getFloat("liquid_update");
491 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
492 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
493 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
500 infostream << "Starting server on " << m_bind_addr.serializeString()
501 << "..." << std::endl;
503 // Stop thread if already running
506 // Initialize connection
507 m_con->SetTimeoutMs(30);
508 m_con->Serve(m_bind_addr);
513 // ASCII art for the win!
515 << " __. __. __. " << std::endl
516 << " _____ |__| ____ _____ / |_ _____ _____ / |_ " << std::endl
517 << " / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\" << std::endl
518 << "| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | " << std::endl
519 << "|__|_| / |___| /\\______> | \\______>_____/| | " << std::endl
520 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
521 actionstream << "World at [" << m_path_world << "]" << std::endl;
522 actionstream << "Server for gameid=\"" << m_gamespec.id
523 << "\" listening on ";
524 m_bind_addr.print(&actionstream);
525 actionstream << "." << std::endl;
530 infostream<<"Server: Stopping and waiting threads"<<std::endl;
532 // Stop threads (set run=false first so both start stopping)
536 infostream<<"Server: Threads stopped"<<std::endl;
539 void Server::step(float dtime)
545 MutexAutoLock lock(m_step_dtime_mutex);
546 m_step_dtime += dtime;
548 // Throw if fatal error occurred in thread
549 std::string async_err = m_async_fatal_error.get();
550 if (!async_err.empty()) {
551 if (!m_simple_singleplayer_mode) {
552 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
553 g_settings->get("kick_msg_crash"),
554 g_settings->getBool("ask_reconnect_on_crash"));
556 throw ServerError("AsyncErr: " + async_err);
560 void Server::AsyncRunStep(bool initial_step)
565 MutexAutoLock lock1(m_step_dtime_mutex);
566 dtime = m_step_dtime;
570 // Send blocks to clients
574 if((dtime < 0.001) && !initial_step)
577 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
580 MutexAutoLock lock1(m_step_dtime_mutex);
581 m_step_dtime -= dtime;
587 m_uptime_counter->increment(dtime);
592 Update time of day and overall game time
594 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
597 Send to clients at constant intervals
600 m_time_of_day_send_timer -= dtime;
601 if (m_time_of_day_send_timer < 0.0) {
602 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
603 u16 time = m_env->getTimeOfDay();
604 float time_speed = g_settings->getFloat("time_speed");
605 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
607 m_timeofday_gauge->set(time);
611 MutexAutoLock lock(m_env_mutex);
612 // Figure out and report maximum lag to environment
613 float max_lag = m_env->getMaxLagEstimate();
614 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
616 if(dtime > 0.1 && dtime > max_lag * 2.0)
617 infostream<<"Server: Maximum lag peaked to "<<dtime
621 m_env->reportMaxLagEstimate(max_lag);
626 static const float map_timer_and_unload_dtime = 2.92;
627 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
629 MutexAutoLock lock(m_env_mutex);
630 // Run Map's timers and unload unused data
631 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
632 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
633 g_settings->getFloat("server_unload_unused_data_timeout"),
638 Listen to the admin chat, if available
641 if (!m_admin_chat->command_queue.empty()) {
642 MutexAutoLock lock(m_env_mutex);
643 while (!m_admin_chat->command_queue.empty()) {
644 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
645 handleChatInterfaceEvent(evt);
649 m_admin_chat->outgoing_queue.push_back(
650 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
657 /* Transform liquids */
658 m_liquid_transform_timer += dtime;
659 if(m_liquid_transform_timer >= m_liquid_transform_every)
661 m_liquid_transform_timer -= m_liquid_transform_every;
663 MutexAutoLock lock(m_env_mutex);
665 ScopeProfiler sp(g_profiler, "Server: liquid transform");
667 std::map<v3s16, MapBlock*> modified_blocks;
668 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
671 Set the modified blocks unsent for all the clients
673 if (!modified_blocks.empty()) {
674 SetBlocksNotSent(modified_blocks);
677 m_clients.step(dtime);
679 // increase/decrease lag gauge gradually
680 if (m_lag_gauge->get() > dtime) {
681 m_lag_gauge->decrement(dtime/100);
683 m_lag_gauge->increment(dtime/100);
687 float &counter = m_step_pending_dyn_media_timer;
689 if (counter >= 5.0f) {
690 stepPendingDynMediaCallbacks(counter);
697 // send masterserver announce
699 float &counter = m_masterserver_timer;
700 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
701 g_settings->getBool("server_announce")) {
702 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
703 ServerList::AA_START,
704 m_bind_addr.getPort(),
705 m_clients.getPlayerNames(),
706 m_uptime_counter->get(),
707 m_env->getGameTime(),
710 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
720 Check added and deleted active objects
723 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
724 MutexAutoLock envlock(m_env_mutex);
727 ClientInterface::AutoLock clientlock(m_clients);
728 const RemoteClientMap &clients = m_clients.getClientList();
729 ScopeProfiler sp(g_profiler, "Server: update objects within range");
731 m_player_gauge->set(clients.size());
732 for (const auto &client_it : clients) {
733 RemoteClient *client = client_it.second;
735 if (client->getState() < CS_DefinitionsSent)
738 // This can happen if the client times out somehow
739 if (!m_env->getPlayer(client->peer_id))
742 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
746 SendActiveObjectRemoveAdd(client, playersao);
750 // Write changes to the mod storage
751 m_mod_storage_save_timer -= dtime;
752 if (m_mod_storage_save_timer <= 0.0f) {
753 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
754 m_mod_storage_database->endSave();
755 m_mod_storage_database->beginSave();
763 MutexAutoLock envlock(m_env_mutex);
764 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
767 // Value = data sent by object
768 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
770 // Get active object messages from environment
771 ActiveObjectMessage aom(0);
774 if (!m_env->getActiveObjectMessage(&aom))
777 std::vector<ActiveObjectMessage>* message_list = nullptr;
778 auto n = buffered_messages.find(aom.id);
779 if (n == buffered_messages.end()) {
780 message_list = new std::vector<ActiveObjectMessage>;
781 buffered_messages[aom.id] = message_list;
783 message_list = n->second;
785 message_list->push_back(std::move(aom));
789 m_aom_buffer_counter->increment(aom_count);
792 ClientInterface::AutoLock clientlock(m_clients);
793 const RemoteClientMap &clients = m_clients.getClientList();
794 // Route data to every client
795 std::string reliable_data, unreliable_data;
796 for (const auto &client_it : clients) {
797 reliable_data.clear();
798 unreliable_data.clear();
799 RemoteClient *client = client_it.second;
800 PlayerSAO *player = getPlayerSAO(client->peer_id);
801 // Go through all objects in message buffer
802 for (const auto &buffered_message : buffered_messages) {
803 // If object does not exist or is not known by client, skip it
804 u16 id = buffered_message.first;
805 ServerActiveObject *sao = m_env->getActiveObject(id);
806 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
809 // Get message list of object
810 std::vector<ActiveObjectMessage>* list = buffered_message.second;
811 // Go through every message
812 for (const ActiveObjectMessage &aom : *list) {
813 // Send position updates to players who do not see the attachment
814 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
815 if (sao->getId() == player->getId())
818 // Do not send position updates for attached players
819 // as long the parent is known to the client
820 ServerActiveObject *parent = sao->getParent();
821 if (parent && client->m_known_objects.find(parent->getId()) !=
822 client->m_known_objects.end())
826 // Add full new data to appropriate buffer
827 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
829 writeU16((u8*) idbuf, aom.id);
832 buffer.append(idbuf, sizeof(idbuf));
833 buffer.append(serializeString16(aom.datastring));
837 reliable_data and unreliable_data are now ready.
840 if (!reliable_data.empty()) {
841 SendActiveObjectMessages(client->peer_id, reliable_data);
844 if (!unreliable_data.empty()) {
845 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
850 // Clear buffered_messages
851 for (auto &buffered_message : buffered_messages) {
852 delete buffered_message.second;
857 Send queued-for-sending map edit events.
860 // We will be accessing the environment
861 MutexAutoLock lock(m_env_mutex);
863 // Don't send too many at a time
866 // Single change sending is disabled if queue size is not small
867 bool disable_single_change_sending = false;
868 if(m_unsent_map_edit_queue.size() >= 4)
869 disable_single_change_sending = true;
871 int event_count = m_unsent_map_edit_queue.size();
873 // We'll log the amount of each
876 std::list<v3s16> node_meta_updates;
878 while (!m_unsent_map_edit_queue.empty()) {
879 MapEditEvent* event = m_unsent_map_edit_queue.front();
880 m_unsent_map_edit_queue.pop();
882 // Players far away from the change are stored here.
883 // Instead of sending the changes, MapBlocks are set not sent
885 std::unordered_set<u16> far_players;
887 switch (event->type) {
890 prof.add("MEET_ADDNODE", 1);
891 sendAddNode(event->p, event->n, &far_players,
892 disable_single_change_sending ? 5 : 30,
893 event->type == MEET_ADDNODE);
895 case MEET_REMOVENODE:
896 prof.add("MEET_REMOVENODE", 1);
897 sendRemoveNode(event->p, &far_players,
898 disable_single_change_sending ? 5 : 30);
900 case MEET_BLOCK_NODE_METADATA_CHANGED: {
901 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
902 if (!event->is_private_change) {
903 // Don't send the change yet. Collect them to eliminate dupes.
904 node_meta_updates.remove(event->p);
905 node_meta_updates.push_back(event->p);
908 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
909 getNodeBlockPos(event->p))) {
910 block->raiseModified(MOD_STATE_WRITE_NEEDED,
911 MOD_REASON_REPORT_META_CHANGE);
916 prof.add("MEET_OTHER", 1);
917 for (const v3s16 &modified_block : event->modified_blocks) {
918 m_clients.markBlockposAsNotSent(modified_block);
922 prof.add("unknown", 1);
923 warningstream << "Server: Unknown MapEditEvent "
924 << ((u32)event->type) << std::endl;
929 Set blocks not sent to far players
931 if (!far_players.empty()) {
932 // Convert list format to that wanted by SetBlocksNotSent
933 std::map<v3s16, MapBlock*> modified_blocks2;
934 for (const v3s16 &modified_block : event->modified_blocks) {
935 modified_blocks2[modified_block] =
936 m_env->getMap().getBlockNoCreateNoEx(modified_block);
939 // Set blocks not sent
940 for (const u16 far_player : far_players) {
941 if (RemoteClient *client = getClient(far_player))
942 client->SetBlocksNotSent(modified_blocks2);
949 if (event_count >= 5) {
950 infostream << "Server: MapEditEvents:" << std::endl;
951 prof.print(infostream);
952 } else if (event_count != 0) {
953 verbosestream << "Server: MapEditEvents:" << std::endl;
954 prof.print(verbosestream);
957 // Send all metadata updates
958 if (node_meta_updates.size())
959 sendMetadataChanged(node_meta_updates);
963 Trigger emerge thread
964 Doing this every 2s is left over from old code, unclear if this is still needed.
967 float &counter = m_emergethread_trigger_timer;
969 if (counter <= 0.0f) {
972 m_emerge->startThreads();
976 // Save map, players and auth stuff
978 float &counter = m_savemap_timer;
980 static thread_local const float save_interval =
981 g_settings->getFloat("server_map_save_interval");
982 if (counter >= save_interval) {
984 MutexAutoLock lock(m_env_mutex);
986 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
989 if (m_banmanager->isModified()) {
990 m_banmanager->save();
993 // Save changed parts of map
994 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
997 m_env->saveLoadedPlayers();
999 // Save environment metadata
1004 m_shutdown_state.tick(dtime, this);
1007 void Server::Receive()
1017 In the first iteration *wait* for a packet, afterwards process
1018 all packets that are immediately available (no waiting).
1021 m_con->Receive(&pkt);
1024 if (!m_con->TryReceive(&pkt))
1028 peer_id = pkt.getPeerId();
1029 m_packet_recv_counter->increment();
1031 m_packet_recv_processed_counter->increment();
1032 } catch (const con::InvalidIncomingDataException &e) {
1033 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1034 << e.what() << std::endl;
1035 } catch (const SerializationError &e) {
1036 infostream << "Server::Receive(): SerializationError: what()="
1037 << e.what() << std::endl;
1038 } catch (const ClientStateError &e) {
1039 errorstream << "ProcessData: peer=" << peer_id << " what()="
1040 << e.what() << std::endl;
1041 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1042 } catch (const con::PeerNotFoundException &e) {
1044 } catch (const con::NoIncomingDataException &e) {
1050 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1052 std::string playername;
1053 PlayerSAO *playersao = NULL;
1055 ClientInterface::AutoLock clientlock(m_clients);
1056 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1058 playername = client->getName();
1059 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1063 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1065 // If failed, cancel
1066 if (!playersao || !player) {
1067 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1068 actionstream << "Server: Failed to emerge player \"" << playername
1069 << "\" (player allocated to an another client)" << std::endl;
1070 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1072 errorstream << "Server: " << playername << ": Failed to emerge player"
1074 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1080 Send complete position information
1082 SendMovePlayer(peer_id);
1085 SendPlayerPrivileges(peer_id);
1087 // Send inventory formspec
1088 SendPlayerInventoryFormspec(peer_id);
1091 SendInventory(playersao, false);
1094 SendPlayerHP(playersao);
1096 // Send death screen
1097 if (playersao->isDead())
1098 SendDeathscreen(peer_id, false, v3f(0,0,0));
1101 SendPlayerBreath(playersao);
1104 Update player list and print action
1107 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1108 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1109 m_clients.sendToAll(¬ice_pkt);
1112 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1113 const auto &names = m_clients.getPlayerNames();
1115 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1116 for (const std::string &name : names)
1117 actionstream << name << " ";
1118 actionstream << player->getName() << std::endl;
1123 inline void Server::handleCommand(NetworkPacket *pkt)
1125 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1126 (this->*opHandle.handler)(pkt);
1129 void Server::ProcessData(NetworkPacket *pkt)
1131 // Environment is locked first.
1132 MutexAutoLock envlock(m_env_mutex);
1134 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1135 u32 peer_id = pkt->getPeerId();
1138 Address address = getPeerAddress(peer_id);
1139 std::string addr_s = address.serializeString();
1141 // FIXME: Isn't it a bit excessive to check this for every packet?
1142 if (m_banmanager->isIpBanned(addr_s)) {
1143 std::string ban_name = m_banmanager->getBanName(addr_s);
1144 infostream << "Server: A banned client tried to connect from "
1145 << addr_s << "; banned name was " << ban_name << std::endl;
1146 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1147 "Your IP is banned. Banned name was " + ban_name);
1150 } catch (con::PeerNotFoundException &e) {
1152 * no peer for this packet found
1153 * most common reason is peer timeout, e.g. peer didn't
1154 * respond for some time, your server was overloaded or
1157 infostream << "Server::ProcessData(): Canceling: peer "
1158 << peer_id << " not found" << std::endl;
1163 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1165 // Command must be handled into ToServerCommandHandler
1166 if (command >= TOSERVER_NUM_MSG_TYPES) {
1167 infostream << "Server: Ignoring unknown command "
1168 << command << std::endl;
1172 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1177 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1179 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1180 errorstream << "Server::ProcessData(): Cancelling: Peer"
1181 " serialization format invalid or not initialized."
1182 " Skipping incoming command=" << command << std::endl;
1186 /* Handle commands related to client startup */
1187 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1192 if (m_clients.getClientState(peer_id) < CS_Active) {
1193 if (command == TOSERVER_PLAYERPOS) return;
1195 errorstream << "Got packet command: " << command << " for peer id "
1196 << peer_id << " but client isn't active yet. Dropping packet "
1202 } catch (SendFailedException &e) {
1203 errorstream << "Server::ProcessData(): SendFailedException: "
1204 << "what=" << e.what()
1206 } catch (PacketError &e) {
1207 actionstream << "Server::ProcessData(): PacketError: "
1208 << "what=" << e.what()
1213 void Server::setTimeOfDay(u32 time)
1215 m_env->setTimeOfDay(time);
1216 m_time_of_day_send_timer = 0;
1219 void Server::onMapEditEvent(const MapEditEvent &event)
1221 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1224 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1227 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1229 std::vector<session_t> clients = m_clients.getClientIDs();
1230 ClientInterface::AutoLock clientlock(m_clients);
1231 // Set the modified blocks unsent for all the clients
1232 for (const session_t client_id : clients) {
1233 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1234 client->SetBlocksNotSent(block);
1238 void Server::peerAdded(con::Peer *peer)
1240 verbosestream<<"Server::peerAdded(): peer->id="
1241 <<peer->id<<std::endl;
1243 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1246 void Server::deletingPeer(con::Peer *peer, bool timeout)
1248 verbosestream<<"Server::deletingPeer(): peer->id="
1249 <<peer->id<<", timeout="<<timeout<<std::endl;
1251 m_clients.event(peer->id, CSE_Disconnect);
1252 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1255 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1257 *retval = m_con->getPeerStat(peer_id,type);
1258 return *retval != -1;
1261 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1263 ClientInterface::AutoLock clientlock(m_clients);
1264 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1269 ret.state = client->getState();
1270 ret.addr = client->getAddress();
1271 ret.uptime = client->uptime();
1272 ret.ser_vers = client->serialization_version;
1273 ret.prot_vers = client->net_proto_version;
1275 ret.major = client->getMajor();
1276 ret.minor = client->getMinor();
1277 ret.patch = client->getPatch();
1278 ret.vers_string = client->getFullVer();
1280 ret.lang_code = client->getLangCode();
1285 void Server::handlePeerChanges()
1287 while(!m_peer_change_queue.empty())
1289 con::PeerChange c = m_peer_change_queue.front();
1290 m_peer_change_queue.pop();
1292 verbosestream<<"Server: Handling peer change: "
1293 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1298 case con::PEER_ADDED:
1299 m_clients.CreateClient(c.peer_id);
1302 case con::PEER_REMOVED:
1303 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1307 FATAL_ERROR("Invalid peer change event received!");
1313 void Server::printToConsoleOnly(const std::string &text)
1316 m_admin_chat->outgoing_queue.push_back(
1317 new ChatEventChat("", utf8_to_wide(text)));
1319 std::cout << text << std::endl;
1323 void Server::Send(NetworkPacket *pkt)
1325 Send(pkt->getPeerId(), pkt);
1328 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1330 m_clients.send(peer_id,
1331 clientCommandFactoryTable[pkt->getCommand()].channel,
1333 clientCommandFactoryTable[pkt->getCommand()].reliable);
1336 void Server::SendMovement(session_t peer_id)
1338 std::ostringstream os(std::ios_base::binary);
1340 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1342 pkt << g_settings->getFloat("movement_acceleration_default");
1343 pkt << g_settings->getFloat("movement_acceleration_air");
1344 pkt << g_settings->getFloat("movement_acceleration_fast");
1345 pkt << g_settings->getFloat("movement_speed_walk");
1346 pkt << g_settings->getFloat("movement_speed_crouch");
1347 pkt << g_settings->getFloat("movement_speed_fast");
1348 pkt << g_settings->getFloat("movement_speed_climb");
1349 pkt << g_settings->getFloat("movement_speed_jump");
1350 pkt << g_settings->getFloat("movement_liquid_fluidity");
1351 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1352 pkt << g_settings->getFloat("movement_liquid_sink");
1353 pkt << g_settings->getFloat("movement_gravity");
1358 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1360 m_script->player_event(playersao, "health_changed");
1361 SendPlayerHP(playersao);
1363 // Send to other clients
1364 playersao->sendPunchCommand();
1366 if (playersao->isDead())
1367 HandlePlayerDeath(playersao, reason);
1370 void Server::SendPlayerHP(PlayerSAO *playersao)
1372 SendHP(playersao->getPeerID(), playersao->getHP());
1375 void Server::SendHP(session_t peer_id, u16 hp)
1377 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1382 void Server::SendBreath(session_t peer_id, u16 breath)
1384 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1385 pkt << (u16) breath;
1389 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1390 const std::string &custom_reason, bool reconnect)
1392 assert(reason < SERVER_ACCESSDENIED_MAX);
1394 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1396 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1397 pkt << custom_reason;
1398 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1399 reason == SERVER_ACCESSDENIED_CRASH)
1400 pkt << custom_reason << (u8)reconnect;
1404 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1405 v3f camera_point_target)
1407 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1408 pkt << set_camera_point_target << camera_point_target;
1412 void Server::SendItemDef(session_t peer_id,
1413 IItemDefManager *itemdef, u16 protocol_version)
1415 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1419 u32 length of the next item
1420 zlib-compressed serialized ItemDefManager
1422 std::ostringstream tmp_os(std::ios::binary);
1423 itemdef->serialize(tmp_os, protocol_version);
1424 std::ostringstream tmp_os2(std::ios::binary);
1425 compressZlib(tmp_os.str(), tmp_os2);
1426 pkt.putLongString(tmp_os2.str());
1429 verbosestream << "Server: Sending item definitions to id(" << peer_id
1430 << "): size=" << pkt.getSize() << std::endl;
1435 void Server::SendNodeDef(session_t peer_id,
1436 const NodeDefManager *nodedef, u16 protocol_version)
1438 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1442 u32 length of the next item
1443 zlib-compressed serialized NodeDefManager
1445 std::ostringstream tmp_os(std::ios::binary);
1446 nodedef->serialize(tmp_os, protocol_version);
1447 std::ostringstream tmp_os2(std::ios::binary);
1448 compressZlib(tmp_os.str(), tmp_os2);
1450 pkt.putLongString(tmp_os2.str());
1453 verbosestream << "Server: Sending node definitions to id(" << peer_id
1454 << "): size=" << pkt.getSize() << std::endl;
1460 Non-static send methods
1463 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1465 RemotePlayer *player = sao->getPlayer();
1467 // Do not send new format to old clients
1468 incremental &= player->protocol_version >= 38;
1470 UpdateCrafting(player);
1476 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1478 std::ostringstream os(std::ios::binary);
1479 sao->getInventory()->serialize(os, incremental);
1480 sao->getInventory()->setModified(false);
1481 player->setModified(true);
1483 const std::string &s = os.str();
1484 pkt.putRawString(s.c_str(), s.size());
1488 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1490 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1492 u8 type = message.type;
1493 pkt << version << type << message.sender << message.message
1494 << static_cast<u64>(message.timestamp);
1496 if (peer_id != PEER_ID_INEXISTENT) {
1497 RemotePlayer *player = m_env->getPlayer(peer_id);
1503 m_clients.sendToAll(&pkt);
1507 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1508 const std::string &formname)
1510 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1511 if (formspec.empty()){
1512 //the client should close the formspec
1513 //but make sure there wasn't another one open in meantime
1514 const auto it = m_formspec_state_data.find(peer_id);
1515 if (it != m_formspec_state_data.end() && it->second == formname) {
1516 m_formspec_state_data.erase(peer_id);
1518 pkt.putLongString("");
1520 m_formspec_state_data[peer_id] = formname;
1521 pkt.putLongString(formspec);
1528 // Spawns a particle on peer with peer_id
1529 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1530 const ParticleParameters &p)
1532 static thread_local const float radius =
1533 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1535 if (peer_id == PEER_ID_INEXISTENT) {
1536 std::vector<session_t> clients = m_clients.getClientIDs();
1537 const v3f pos = p.pos * BS;
1538 const float radius_sq = radius * radius;
1540 for (const session_t client_id : clients) {
1541 RemotePlayer *player = m_env->getPlayer(client_id);
1545 PlayerSAO *sao = player->getPlayerSAO();
1549 // Do not send to distant clients
1550 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1553 SendSpawnParticle(client_id, player->protocol_version, p);
1557 assert(protocol_version != 0);
1559 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1562 // NetworkPacket and iostreams are incompatible...
1563 std::ostringstream oss(std::ios_base::binary);
1564 p.serialize(oss, protocol_version);
1565 pkt.putRawString(oss.str());
1571 // Adds a ParticleSpawner on peer with peer_id
1572 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1573 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1575 static thread_local const float radius =
1576 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1578 if (peer_id == PEER_ID_INEXISTENT) {
1579 std::vector<session_t> clients = m_clients.getClientIDs();
1580 const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS;
1581 const float radius_sq = radius * radius;
1582 /* Don't send short-lived spawners to distant players.
1583 * This could be replaced with proper tracking at some point. */
1584 const bool distance_check = !attached_id && p.time <= 1.0f;
1586 for (const session_t client_id : clients) {
1587 RemotePlayer *player = m_env->getPlayer(client_id);
1591 if (distance_check) {
1592 PlayerSAO *sao = player->getPlayerSAO();
1595 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1599 SendAddParticleSpawner(client_id, player->protocol_version,
1600 p, attached_id, id);
1604 assert(protocol_version != 0);
1606 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1608 pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel
1609 << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime
1610 << p.minsize << p.maxsize << p.collisiondetection;
1612 pkt.putLongString(p.texture);
1614 pkt << id << p.vertical << p.collision_removal << attached_id;
1616 std::ostringstream os(std::ios_base::binary);
1617 p.animation.serialize(os, protocol_version);
1618 pkt.putRawString(os.str());
1620 pkt << p.glow << p.object_collision;
1621 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1626 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1628 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1632 if (peer_id != PEER_ID_INEXISTENT)
1635 m_clients.sendToAll(&pkt);
1639 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1641 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1643 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1644 << form->text << form->number << form->item << form->dir
1645 << form->align << form->offset << form->world_pos << form->size
1646 << form->z_index << form->text2 << form->style;
1651 void Server::SendHUDRemove(session_t peer_id, u32 id)
1653 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1658 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1660 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1661 pkt << id << (u8) stat;
1665 case HUD_STAT_SCALE:
1666 case HUD_STAT_ALIGN:
1667 case HUD_STAT_OFFSET:
1668 pkt << *(v2f *) value;
1672 case HUD_STAT_TEXT2:
1673 pkt << *(std::string *) value;
1675 case HUD_STAT_WORLD_POS:
1676 pkt << *(v3f *) value;
1679 pkt << *(v2s32 *) value;
1681 default: // all other types
1682 pkt << *(u32 *) value;
1689 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1691 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1693 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1695 pkt << flags << mask;
1700 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1702 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1703 pkt << param << value;
1707 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1709 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1711 // Handle prior clients here
1712 if (m_clients.getProtocolVersion(peer_id) < 39) {
1713 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1715 for (const std::string& texture : params.textures)
1718 pkt << params.clouds;
1719 } else { // Handle current clients and future clients
1720 pkt << params.bgcolor << params.type
1721 << params.clouds << params.fog_sun_tint
1722 << params.fog_moon_tint << params.fog_tint_type;
1724 if (params.type == "skybox") {
1725 pkt << (u16) params.textures.size();
1726 for (const std::string &texture : params.textures)
1728 } else if (params.type == "regular") {
1729 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1730 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1731 << params.sky_color.night_sky << params.sky_color.night_horizon
1732 << params.sky_color.indoors;
1739 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1741 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1742 pkt << params.visible << params.texture
1743 << params.tonemap << params.sunrise
1744 << params.sunrise_visible << params.scale;
1748 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1750 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1752 pkt << params.visible << params.texture
1753 << params.tonemap << params.scale;
1757 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1759 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1761 pkt << params.visible << params.count
1762 << params.starcolor << params.scale;
1767 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1769 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1770 pkt << params.density << params.color_bright << params.color_ambient
1771 << params.height << params.thickness << params.speed;
1775 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1778 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1781 pkt << do_override << (u16) (ratio * 65535);
1786 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1788 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1791 pkt << lighting.shadow_intensity;
1796 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1798 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1799 pkt << time << time_speed;
1801 if (peer_id == PEER_ID_INEXISTENT) {
1802 m_clients.sendToAll(&pkt);
1809 void Server::SendPlayerBreath(PlayerSAO *sao)
1813 m_script->player_event(sao, "breath_changed");
1814 SendBreath(sao->getPeerID(), sao->getBreath());
1817 void Server::SendMovePlayer(session_t peer_id)
1819 RemotePlayer *player = m_env->getPlayer(peer_id);
1821 PlayerSAO *sao = player->getPlayerSAO();
1824 // Send attachment updates instantly to the client prior updating position
1825 sao->sendOutdatedData();
1827 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1828 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1831 v3f pos = sao->getBasePosition();
1832 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1833 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1834 << " pitch=" << sao->getLookPitch()
1835 << " yaw=" << sao->getRotation().Y
1842 void Server::SendPlayerFov(session_t peer_id)
1844 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1846 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1847 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1852 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1853 f32 animation_speed)
1855 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1858 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1859 << animation_frames[3] << animation_speed;
1864 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1866 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1867 pkt << first << third;
1871 void Server::SendPlayerPrivileges(session_t peer_id)
1873 RemotePlayer *player = m_env->getPlayer(peer_id);
1875 if(player->getPeerId() == PEER_ID_INEXISTENT)
1878 std::set<std::string> privs;
1879 m_script->getAuth(player->getName(), NULL, &privs);
1881 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1882 pkt << (u16) privs.size();
1884 for (const std::string &priv : privs) {
1891 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1893 RemotePlayer *player = m_env->getPlayer(peer_id);
1895 if (player->getPeerId() == PEER_ID_INEXISTENT)
1898 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1899 pkt.putLongString(player->inventory_formspec);
1904 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1906 RemotePlayer *player = m_env->getPlayer(peer_id);
1908 if (player->getPeerId() == PEER_ID_INEXISTENT)
1911 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1912 pkt << player->formspec_prepend;
1916 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1918 // Radius inside which objects are active
1919 static thread_local const s16 radius =
1920 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1922 // Radius inside which players are active
1923 static thread_local const bool is_transfer_limited =
1924 g_settings->exists("unlimited_player_transfer_distance") &&
1925 !g_settings->getBool("unlimited_player_transfer_distance");
1927 static thread_local const s16 player_transfer_dist =
1928 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
1930 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
1931 radius : player_transfer_dist;
1933 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
1937 std::queue<u16> removed_objects, added_objects;
1938 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
1939 client->m_known_objects, removed_objects);
1940 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
1941 client->m_known_objects, added_objects);
1943 int removed_count = removed_objects.size();
1944 int added_count = added_objects.size();
1946 if (removed_objects.empty() && added_objects.empty())
1952 // Handle removed objects
1953 writeU16((u8*)buf, removed_objects.size());
1954 data.append(buf, 2);
1955 while (!removed_objects.empty()) {
1957 u16 id = removed_objects.front();
1958 ServerActiveObject* obj = m_env->getActiveObject(id);
1960 // Add to data buffer for sending
1961 writeU16((u8*)buf, id);
1962 data.append(buf, 2);
1964 // Remove from known objects
1965 client->m_known_objects.erase(id);
1967 if (obj && obj->m_known_by_count > 0)
1968 obj->m_known_by_count--;
1970 removed_objects.pop();
1973 // Handle added objects
1974 writeU16((u8*)buf, added_objects.size());
1975 data.append(buf, 2);
1976 while (!added_objects.empty()) {
1978 u16 id = added_objects.front();
1979 ServerActiveObject *obj = m_env->getActiveObject(id);
1980 added_objects.pop();
1983 warningstream << FUNCTION_NAME << ": NULL object id="
1984 << (int)id << std::endl;
1989 u8 type = obj->getSendType();
1991 // Add to data buffer for sending
1992 writeU16((u8*)buf, id);
1993 data.append(buf, 2);
1994 writeU8((u8*)buf, type);
1995 data.append(buf, 1);
1997 data.append(serializeString32(
1998 obj->getClientInitializationData(client->net_proto_version)));
2000 // Add to known objects
2001 client->m_known_objects.insert(id);
2003 obj->m_known_by_count++;
2006 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2007 pkt.putRawString(data.c_str(), data.size());
2010 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2011 << removed_count << " removed, " << added_count << " added, "
2012 << "packet size is " << pkt.getSize() << std::endl;
2015 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2018 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2019 datas.size(), peer_id);
2021 pkt.putRawString(datas.c_str(), datas.size());
2023 m_clients.send(pkt.getPeerId(),
2024 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2028 void Server::SendCSMRestrictionFlags(session_t peer_id)
2030 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2031 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2032 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2036 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2038 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2043 inline s32 Server::nextSoundId()
2045 s32 ret = m_next_sound_id;
2046 if (m_next_sound_id == INT32_MAX)
2047 m_next_sound_id = 0; // signed overflow is undefined
2053 s32 Server::playSound(const SimpleSoundSpec &spec,
2054 const ServerSoundParams ¶ms, bool ephemeral)
2056 // Find out initial position of sound
2057 bool pos_exists = false;
2058 v3f pos = params.getPos(m_env, &pos_exists);
2059 // If position is not found while it should be, cancel sound
2060 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2063 // Filter destination clients
2064 std::vector<session_t> dst_clients;
2065 if (!params.to_player.empty()) {
2066 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2068 infostream<<"Server::playSound: Player \""<<params.to_player
2069 <<"\" not found"<<std::endl;
2072 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2073 infostream<<"Server::playSound: Player \""<<params.to_player
2074 <<"\" not connected"<<std::endl;
2077 dst_clients.push_back(player->getPeerId());
2079 std::vector<session_t> clients = m_clients.getClientIDs();
2081 for (const session_t client_id : clients) {
2082 RemotePlayer *player = m_env->getPlayer(client_id);
2085 if (!params.exclude_player.empty() &&
2086 params.exclude_player == player->getName())
2089 PlayerSAO *sao = player->getPlayerSAO();
2094 if(sao->getBasePosition().getDistanceFrom(pos) >
2095 params.max_hear_distance)
2098 dst_clients.push_back(client_id);
2102 if(dst_clients.empty())
2107 ServerPlayingSound *psound = nullptr;
2109 id = -1; // old clients will still use this, so pick a reserved ID
2112 // The sound will exist as a reference in m_playing_sounds
2113 m_playing_sounds[id] = ServerPlayingSound();
2114 psound = &m_playing_sounds[id];
2115 psound->params = params;
2116 psound->spec = spec;
2119 float gain = params.gain * spec.gain;
2120 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2121 pkt << id << spec.name << gain
2122 << (u8) params.type << pos << params.object
2123 << params.loop << params.fade << params.pitch
2126 bool as_reliable = !ephemeral;
2128 for (const u16 dst_client : dst_clients) {
2130 psound->clients.insert(dst_client);
2131 m_clients.send(dst_client, 0, &pkt, as_reliable);
2135 void Server::stopSound(s32 handle)
2137 // Get sound reference
2138 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2139 m_playing_sounds.find(handle);
2140 if (i == m_playing_sounds.end())
2142 ServerPlayingSound &psound = i->second;
2144 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2147 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2148 si != psound.clients.end(); ++si) {
2150 m_clients.send(*si, 0, &pkt, true);
2152 // Remove sound reference
2153 m_playing_sounds.erase(i);
2156 void Server::fadeSound(s32 handle, float step, float gain)
2158 // Get sound reference
2159 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2160 m_playing_sounds.find(handle);
2161 if (i == m_playing_sounds.end())
2164 ServerPlayingSound &psound = i->second;
2165 psound.params.gain = gain;
2167 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2168 pkt << handle << step << gain;
2170 // Backwards compability
2171 bool play_sound = gain > 0;
2172 ServerPlayingSound compat_psound = psound;
2173 compat_psound.clients.clear();
2175 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2176 compat_pkt << handle;
2178 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2179 it != psound.clients.end();) {
2180 if (m_clients.getProtocolVersion(*it) >= 32) {
2182 m_clients.send(*it, 0, &pkt, true);
2185 compat_psound.clients.insert(*it);
2187 m_clients.send(*it, 0, &compat_pkt, true);
2188 psound.clients.erase(it++);
2192 // Remove sound reference
2193 if (!play_sound || psound.clients.empty())
2194 m_playing_sounds.erase(i);
2196 if (play_sound && !compat_psound.clients.empty()) {
2197 // Play new sound volume on older clients
2198 playSound(compat_psound.spec, compat_psound.params);
2202 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2205 float maxd = far_d_nodes * BS;
2206 v3f p_f = intToFloat(p, BS);
2207 v3s16 block_pos = getNodeBlockPos(p);
2209 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2212 std::vector<session_t> clients = m_clients.getClientIDs();
2213 ClientInterface::AutoLock clientlock(m_clients);
2215 for (session_t client_id : clients) {
2216 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2220 RemotePlayer *player = m_env->getPlayer(client_id);
2221 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2223 // If player is far away, only set modified blocks not sent
2224 if (!client->isBlockSent(block_pos) || (sao &&
2225 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2227 far_players->emplace(client_id);
2229 client->SetBlockNotSent(block_pos);
2234 m_clients.send(client_id, 0, &pkt, true);
2238 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2239 float far_d_nodes, bool remove_metadata)
2241 float maxd = far_d_nodes * BS;
2242 v3f p_f = intToFloat(p, BS);
2243 v3s16 block_pos = getNodeBlockPos(p);
2245 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2246 pkt << p << n.param0 << n.param1 << n.param2
2247 << (u8) (remove_metadata ? 0 : 1);
2249 std::vector<session_t> clients = m_clients.getClientIDs();
2250 ClientInterface::AutoLock clientlock(m_clients);
2252 for (session_t client_id : clients) {
2253 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2257 RemotePlayer *player = m_env->getPlayer(client_id);
2258 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2260 // If player is far away, only set modified blocks not sent
2261 if (!client->isBlockSent(block_pos) || (sao &&
2262 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2264 far_players->emplace(client_id);
2266 client->SetBlockNotSent(block_pos);
2271 m_clients.send(client_id, 0, &pkt, true);
2275 void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes)
2277 float maxd = far_d_nodes * BS;
2278 NodeMetadataList meta_updates_list(false);
2279 std::vector<session_t> clients = m_clients.getClientIDs();
2281 ClientInterface::AutoLock clientlock(m_clients);
2283 for (session_t i : clients) {
2284 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2288 ServerActiveObject *player = m_env->getActiveObject(i);
2289 v3f player_pos = player ? player->getBasePosition() : v3f();
2291 for (const v3s16 &pos : meta_updates) {
2292 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2297 v3s16 block_pos = getNodeBlockPos(pos);
2298 if (!client->isBlockSent(block_pos) || (player &&
2299 player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) {
2300 client->SetBlockNotSent(block_pos);
2304 // Add the change to send list
2305 meta_updates_list.set(pos, meta);
2307 if (meta_updates_list.size() == 0)
2310 // Send the meta changes
2311 std::ostringstream os(std::ios::binary);
2312 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2313 std::ostringstream oss(std::ios::binary);
2314 compressZlib(os.str(), oss);
2316 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0);
2317 pkt.putLongString(oss.str());
2318 m_clients.send(i, 0, &pkt, true);
2320 meta_updates_list.clear();
2324 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2325 u16 net_proto_version)
2328 Create a packet with the block in the right format
2330 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2331 std::ostringstream os(std::ios_base::binary);
2332 block->serialize(os, ver, false, net_compression_level);
2333 block->serializeNetworkSpecific(os);
2334 std::string s = os.str();
2336 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + s.size(), peer_id);
2338 pkt << block->getPos();
2339 pkt.putRawString(s.c_str(), s.size());
2343 void Server::SendBlocks(float dtime)
2345 MutexAutoLock envlock(m_env_mutex);
2346 //TODO check if one big lock could be faster then multiple small ones
2348 std::vector<PrioritySortedBlockTransfer> queue;
2350 u32 total_sending = 0;
2353 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2355 std::vector<session_t> clients = m_clients.getClientIDs();
2357 ClientInterface::AutoLock clientlock(m_clients);
2358 for (const session_t client_id : clients) {
2359 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2364 total_sending += client->getSendingCount();
2365 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2370 // Lowest priority number comes first.
2371 // Lowest is most important.
2372 std::sort(queue.begin(), queue.end());
2374 ClientInterface::AutoLock clientlock(m_clients);
2376 // Maximal total count calculation
2377 // The per-client block sends is halved with the maximal online users
2378 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2379 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2381 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2382 Map &map = m_env->getMap();
2384 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2385 if (total_sending >= max_blocks_to_send)
2388 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2392 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2397 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2398 client->net_proto_version);
2400 client->SentBlock(block_to_send.pos);
2405 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2407 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2411 ClientInterface::AutoLock clientlock(m_clients);
2412 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2413 if (!client || client->isBlockSent(blockpos))
2415 SendBlockNoLock(peer_id, block, client->serialization_version,
2416 client->net_proto_version);
2421 bool Server::addMediaFile(const std::string &filename,
2422 const std::string &filepath, std::string *filedata_to,
2423 std::string *digest_to)
2425 // If name contains illegal characters, ignore the file
2426 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2427 infostream << "Server: ignoring illegal file name: \""
2428 << filename << "\"" << std::endl;
2431 // If name is not in a supported format, ignore it
2432 const char *supported_ext[] = {
2433 ".png", ".jpg", ".bmp", ".tga",
2435 ".x", ".b3d", ".obj",
2436 // Custom translation file format
2440 if (removeStringEnd(filename, supported_ext).empty()) {
2441 infostream << "Server: ignoring unsupported file extension: \""
2442 << filename << "\"" << std::endl;
2445 // Ok, attempt to load the file and add to cache
2448 std::string filedata;
2449 if (!fs::ReadFile(filepath, filedata)) {
2450 errorstream << "Server::addMediaFile(): Failed to open \""
2451 << filename << "\" for reading" << std::endl;
2455 if (filedata.empty()) {
2456 errorstream << "Server::addMediaFile(): Empty file \""
2457 << filepath << "\"" << std::endl;
2462 sha1.addBytes(filedata.c_str(), filedata.length());
2464 unsigned char *digest = sha1.getDigest();
2465 std::string sha1_base64 = base64_encode(digest, 20);
2466 std::string sha1_hex = hex_encode((char*) digest, 20);
2468 *digest_to = std::string((char*) digest, 20);
2472 m_media[filename] = MediaInfo(filepath, sha1_base64);
2473 verbosestream << "Server: " << sha1_hex << " is " << filename
2477 *filedata_to = std::move(filedata);
2481 void Server::fillMediaCache()
2483 infostream << "Server: Calculating media file checksums" << std::endl;
2485 // Collect all media file paths
2486 std::vector<std::string> paths;
2488 // ordered in descending priority
2489 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2490 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2491 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2492 m_modmgr->getModsMediaPaths(paths);
2494 // Collect media file information from paths into cache
2495 for (const std::string &mediapath : paths) {
2496 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2497 for (const fs::DirListNode &dln : dirlist) {
2498 if (dln.dir) // Ignore dirs (already in paths)
2501 const std::string &filename = dln.name;
2502 if (m_media.find(filename) != m_media.end()) // Do not override
2505 std::string filepath = mediapath;
2506 filepath.append(DIR_DELIM).append(filename);
2507 addMediaFile(filename, filepath);
2511 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2514 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2517 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2520 std::string lang_suffix;
2521 lang_suffix.append(".").append(lang_code).append(".tr");
2522 for (const auto &i : m_media) {
2523 if (i.second.no_announce)
2525 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2532 for (const auto &i : m_media) {
2533 if (i.second.no_announce)
2535 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2537 pkt << i.first << i.second.sha1_digest;
2540 pkt << g_settings->get("remote_media");
2543 verbosestream << "Server: Announcing files to id(" << peer_id
2544 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2547 struct SendableMedia
2553 SendableMedia(const std::string &name, const std::string &path,
2554 std::string &&data):
2555 name(name), path(path), data(std::move(data))
2559 void Server::sendRequestedMedia(session_t peer_id,
2560 const std::vector<std::string> &tosend)
2562 verbosestream<<"Server::sendRequestedMedia(): "
2563 <<"Sending files to client"<<std::endl;
2567 // Put 5kB in one bunch (this is not accurate)
2568 u32 bytes_per_bunch = 5000;
2570 std::vector< std::vector<SendableMedia> > file_bunches;
2571 file_bunches.emplace_back();
2573 u32 file_size_bunch_total = 0;
2575 for (const std::string &name : tosend) {
2576 if (m_media.find(name) == m_media.end()) {
2577 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2578 <<"unknown file \""<<(name)<<"\""<<std::endl;
2582 const auto &m = m_media[name];
2586 if (!fs::ReadFile(m.path, data)) {
2587 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2588 << name << "\"" << std::endl;
2591 file_size_bunch_total += data.size();
2594 file_bunches.back().emplace_back(name, m.path, std::move(data));
2596 // Start next bunch if got enough data
2597 if(file_size_bunch_total >= bytes_per_bunch) {
2598 file_bunches.emplace_back();
2599 file_size_bunch_total = 0;
2604 /* Create and send packets */
2606 u16 num_bunches = file_bunches.size();
2607 for (u16 i = 0; i < num_bunches; i++) {
2610 u16 total number of texture bunches
2611 u16 index of this bunch
2612 u32 number of files in this bunch
2621 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2622 pkt << num_bunches << i << (u32) file_bunches[i].size();
2624 for (const SendableMedia &j : file_bunches[i]) {
2626 pkt.putLongString(j.data);
2629 verbosestream << "Server::sendRequestedMedia(): bunch "
2630 << i << "/" << num_bunches
2631 << " files=" << file_bunches[i].size()
2632 << " size=" << pkt.getSize() << std::endl;
2637 void Server::stepPendingDynMediaCallbacks(float dtime)
2639 MutexAutoLock lock(m_env_mutex);
2641 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2642 it->second.expiry_timer -= dtime;
2643 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2650 const auto &name = it->second.filename;
2651 if (!name.empty()) {
2652 assert(m_media.count(name));
2653 // if no_announce isn't set we're definitely deleting the wrong file!
2654 sanity_check(m_media[name].no_announce);
2656 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2657 m_media.erase(name);
2659 getScriptIface()->freeDynamicMediaCallback(it->first);
2660 it = m_pending_dyn_media.erase(it);
2664 void Server::SendMinimapModes(session_t peer_id,
2665 std::vector<MinimapMode> &modes, size_t wanted_mode)
2667 RemotePlayer *player = m_env->getPlayer(peer_id);
2669 if (player->getPeerId() == PEER_ID_INEXISTENT)
2672 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2673 pkt << (u16)modes.size() << (u16)wanted_mode;
2675 for (auto &mode : modes)
2676 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2681 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2683 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2687 pkt << false; // Remove inventory
2689 pkt << true; // Update inventory
2691 // Serialization & NetworkPacket isn't a love story
2692 std::ostringstream os(std::ios_base::binary);
2693 inventory->serialize(os);
2694 inventory->setModified(false);
2696 const std::string &os_str = os.str();
2697 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2698 pkt.putRawString(os_str);
2701 if (peer_id == PEER_ID_INEXISTENT)
2702 m_clients.sendToAll(&pkt);
2707 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2709 // Lookup player name, to filter detached inventories just after
2710 std::string peer_name;
2711 if (peer_id != PEER_ID_INEXISTENT) {
2712 peer_name = getClient(peer_id, CS_Created)->getName();
2715 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2716 sendDetachedInventory(inv, name, peer_id);
2719 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2726 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2728 infostream << "Server::DiePlayer(): Player "
2729 << playersao->getPlayer()->getName()
2730 << " dies" << std::endl;
2732 playersao->clearParentAttachment();
2734 // Trigger scripted stuff
2735 m_script->on_dieplayer(playersao, reason);
2737 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2740 void Server::RespawnPlayer(session_t peer_id)
2742 PlayerSAO *playersao = getPlayerSAO(peer_id);
2745 infostream << "Server::RespawnPlayer(): Player "
2746 << playersao->getPlayer()->getName()
2747 << " respawns" << std::endl;
2749 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2750 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2751 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2753 bool repositioned = m_script->on_respawnplayer(playersao);
2754 if (!repositioned) {
2755 // setPos will send the new position to client
2756 playersao->setPos(findSpawnPos());
2761 void Server::DenySudoAccess(session_t peer_id)
2763 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2768 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2769 const std::string &custom_reason, bool reconnect)
2771 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2772 m_clients.event(peer_id, CSE_SetDenied);
2773 DisconnectPeer(peer_id);
2776 void Server::DisconnectPeer(session_t peer_id)
2778 m_modchannel_mgr->leaveAllChannels(peer_id);
2779 m_con->DisconnectPeer(peer_id);
2782 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2785 RemoteClient* client = getClient(peer_id, CS_Invalid);
2787 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2789 // Right now, the auth mechs don't change between login and sudo mode.
2790 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2791 client->allowed_sudo_mechs = sudo_auth_mechs;
2793 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2794 << g_settings->getFloat("dedicated_server_step")
2798 m_clients.event(peer_id, CSE_AuthAccept);
2800 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2802 // We only support SRP right now
2803 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2805 resp_pkt << sudo_auth_mechs;
2807 m_clients.event(peer_id, CSE_SudoSuccess);
2811 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2813 std::wstring message;
2816 Clear references to playing sounds
2818 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2819 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2820 ServerPlayingSound &psound = i->second;
2821 psound.clients.erase(peer_id);
2822 if (psound.clients.empty())
2823 m_playing_sounds.erase(i++);
2828 // clear formspec info so the next client can't abuse the current state
2829 m_formspec_state_data.erase(peer_id);
2831 RemotePlayer *player = m_env->getPlayer(peer_id);
2833 /* Run scripts and remove from environment */
2835 PlayerSAO *playersao = player->getPlayerSAO();
2838 playersao->clearChildAttachments();
2839 playersao->clearParentAttachment();
2841 // inform connected clients
2842 const std::string &player_name = player->getName();
2843 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2844 // (u16) 1 + std::string represents a vector serialization representation
2845 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2846 m_clients.sendToAll(¬ice);
2848 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2850 playersao->disconnected();
2857 if (player && reason != CDR_DENY) {
2858 std::ostringstream os(std::ios_base::binary);
2859 std::vector<session_t> clients = m_clients.getClientIDs();
2861 for (const session_t client_id : clients) {
2863 RemotePlayer *player = m_env->getPlayer(client_id);
2867 // Get name of player
2868 os << player->getName() << " ";
2871 std::string name = player->getName();
2872 actionstream << name << " "
2873 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2874 << " List of players: " << os.str() << std::endl;
2876 m_admin_chat->outgoing_queue.push_back(
2877 new ChatEventNick(CET_NICK_REMOVE, name));
2881 MutexAutoLock env_lock(m_env_mutex);
2882 m_clients.DeleteClient(peer_id);
2886 // Send leave chat message to all remaining clients
2887 if (!message.empty()) {
2888 SendChatMessage(PEER_ID_INEXISTENT,
2889 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2893 void Server::UpdateCrafting(RemotePlayer *player)
2895 InventoryList *clist = player->inventory.getList("craft");
2896 if (!clist || clist->getSize() == 0)
2899 if (!clist->checkModified())
2902 // Get a preview for crafting
2904 InventoryLocation loc;
2905 loc.setPlayer(player->getName());
2906 std::vector<ItemStack> output_replacements;
2907 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2908 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2911 InventoryList *plist = player->inventory.getList("craftpreview");
2912 if (plist && plist->getSize() >= 1) {
2913 // Put the new preview in
2914 plist->changeItem(0, preview);
2918 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2920 if (evt->type == CET_NICK_ADD) {
2921 // The terminal informed us of its nick choice
2922 m_admin_nick = ((ChatEventNick *)evt)->nick;
2923 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2924 errorstream << "You haven't set up an account." << std::endl
2925 << "Please log in using the client as '"
2926 << m_admin_nick << "' with a secure password." << std::endl
2927 << "Until then, you can't execute admin tasks via the console," << std::endl
2928 << "and everybody can claim the user account instead of you," << std::endl
2929 << "giving them full control over this server." << std::endl;
2932 assert(evt->type == CET_CHAT);
2933 handleAdminChat((ChatEventChat *)evt);
2937 std::wstring Server::handleChat(const std::string &name,
2938 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2940 // If something goes wrong, this player is to blame
2941 RollbackScopeActor rollback_scope(m_rollback,
2942 std::string("player:") + name);
2944 if (g_settings->getBool("strip_color_codes"))
2945 wmessage = unescape_enriched(wmessage);
2948 switch (player->canSendChatMessage()) {
2949 case RPLAYER_CHATRESULT_FLOODING: {
2950 std::wstringstream ws;
2951 ws << L"You cannot send more messages. You are limited to "
2952 << g_settings->getFloat("chat_message_limit_per_10sec")
2953 << L" messages per 10 seconds.";
2956 case RPLAYER_CHATRESULT_KICK:
2957 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
2958 "You have been kicked due to message flooding.");
2960 case RPLAYER_CHATRESULT_OK:
2963 FATAL_ERROR("Unhandled chat filtering result found.");
2967 if (m_max_chatmessage_length > 0
2968 && wmessage.length() > m_max_chatmessage_length) {
2969 return L"Your message exceed the maximum chat message limit set on the server. "
2970 L"It was refused. Send a shorter message";
2973 auto message = trim(wide_to_utf8(wmessage));
2974 if (message.empty())
2977 if (message.find_first_of("\n\r") != std::wstring::npos) {
2978 return L"Newlines are not permitted in chat messages";
2981 // Run script hook, exit if script ate the chat message
2982 if (m_script->on_chat_message(name, message))
2987 // Whether to send line to the player that sent the message, or to all players
2988 bool broadcast_line = true;
2990 if (check_shout_priv && !checkPriv(name, "shout")) {
2991 line += L"-!- You don't have permission to shout.";
2992 broadcast_line = false;
2995 Workaround for fixing chat on Android. Lua doesn't handle
2996 the Cyrillic alphabet and some characters on older Android devices
2999 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3001 line += utf8_to_wide(m_script->formatChatMessage(name,
3002 wide_to_utf8(wmessage)));
3007 Tell calling method to send the message to sender
3009 if (!broadcast_line)
3013 Send the message to others
3015 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3017 ChatMessage chatmsg(line);
3019 std::vector<session_t> clients = m_clients.getClientIDs();
3020 for (u16 cid : clients)
3021 SendChatMessage(cid, chatmsg);
3026 void Server::handleAdminChat(const ChatEventChat *evt)
3028 std::string name = evt->nick;
3029 std::wstring wmessage = evt->evt_msg;
3031 std::wstring answer = handleChat(name, wmessage);
3033 // If asked to send answer to sender
3034 if (!answer.empty()) {
3035 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3039 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3041 RemoteClient *client = getClientNoEx(peer_id,state_min);
3043 throw ClientNotFoundException("Client not found");
3047 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3049 return m_clients.getClientNoEx(peer_id, state_min);
3052 std::string Server::getPlayerName(session_t peer_id)
3054 RemotePlayer *player = m_env->getPlayer(peer_id);
3056 return "[id="+itos(peer_id)+"]";
3057 return player->getName();
3060 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3062 RemotePlayer *player = m_env->getPlayer(peer_id);
3065 return player->getPlayerSAO();
3068 std::string Server::getStatusString()
3070 std::ostringstream os(std::ios_base::binary);
3073 os << "version: " << g_version_string;
3075 os << " | game: " << (m_gamespec.name.empty() ? m_gamespec.id : m_gamespec.name);
3077 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3079 os << " | max lag: " << std::setprecision(3);
3080 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3082 // Information about clients
3084 os << " | clients: ";
3086 std::vector<session_t> clients = m_clients.getClientIDs();
3087 for (session_t client_id : clients) {
3088 RemotePlayer *player = m_env->getPlayer(client_id);
3090 // Get name of player
3091 const char *name = player ? player->getName() : "<unknown>";
3093 // Add name to information string
3102 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3103 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3105 if (!g_settings->get("motd").empty())
3106 os << std::endl << "# Server: " << g_settings->get("motd");
3111 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3113 std::set<std::string> privs;
3114 m_script->getAuth(name, NULL, &privs);
3118 bool Server::checkPriv(const std::string &name, const std::string &priv)
3120 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3121 return (privs.count(priv) != 0);
3124 void Server::reportPrivsModified(const std::string &name)
3127 std::vector<session_t> clients = m_clients.getClientIDs();
3128 for (const session_t client_id : clients) {
3129 RemotePlayer *player = m_env->getPlayer(client_id);
3130 reportPrivsModified(player->getName());
3133 RemotePlayer *player = m_env->getPlayer(name.c_str());
3136 SendPlayerPrivileges(player->getPeerId());
3137 PlayerSAO *sao = player->getPlayerSAO();
3140 sao->updatePrivileges(
3141 getPlayerEffectivePrivs(name),
3146 void Server::reportInventoryFormspecModified(const std::string &name)
3148 RemotePlayer *player = m_env->getPlayer(name.c_str());
3151 SendPlayerInventoryFormspec(player->getPeerId());
3154 void Server::reportFormspecPrependModified(const std::string &name)
3156 RemotePlayer *player = m_env->getPlayer(name.c_str());
3159 SendPlayerFormspecPrepend(player->getPeerId());
3162 void Server::setIpBanned(const std::string &ip, const std::string &name)
3164 m_banmanager->add(ip, name);
3167 void Server::unsetIpBanned(const std::string &ip_or_name)
3169 m_banmanager->remove(ip_or_name);
3172 std::string Server::getBanDescription(const std::string &ip_or_name)
3174 return m_banmanager->getBanDescription(ip_or_name);
3177 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3179 // m_env will be NULL if the server is initializing
3183 if (m_admin_nick == name && !m_admin_nick.empty()) {
3184 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3187 RemotePlayer *player = m_env->getPlayer(name);
3192 if (player->getPeerId() == PEER_ID_INEXISTENT)
3195 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3198 bool Server::showFormspec(const char *playername, const std::string &formspec,
3199 const std::string &formname)
3201 // m_env will be NULL if the server is initializing
3205 RemotePlayer *player = m_env->getPlayer(playername);
3209 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3213 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3218 u32 id = player->addHud(form);
3220 SendHUDAdd(player->getPeerId(), id, form);
3225 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3229 HudElement* todel = player->removeHud(id);
3236 SendHUDRemove(player->getPeerId(), id);
3240 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3245 SendHUDChange(player->getPeerId(), id, stat, data);
3249 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3254 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3255 if (new_hud_flags == player->hud_flags) // no change
3258 SendHUDSetFlags(player->getPeerId(), flags, mask);
3259 player->hud_flags = new_hud_flags;
3261 PlayerSAO* playersao = player->getPlayerSAO();
3266 m_script->player_event(playersao, "hud_changed");
3270 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3275 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3278 player->setHotbarItemcount(hotbar_itemcount);
3279 std::ostringstream os(std::ios::binary);
3280 writeS32(os, hotbar_itemcount);
3281 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3285 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3290 player->setHotbarImage(name);
3291 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3294 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3299 player->setHotbarSelectedImage(name);
3300 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3303 Address Server::getPeerAddress(session_t peer_id)
3305 // Note that this is only set after Init was received in Server::handleCommand_Init
3306 return getClient(peer_id, CS_Invalid)->getAddress();
3309 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3310 v2s32 animation_frames[4], f32 frame_speed)
3312 sanity_check(player);
3313 player->setLocalAnimations(animation_frames, frame_speed);
3314 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3317 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3319 sanity_check(player);
3320 player->eye_offset_first = first;
3321 player->eye_offset_third = third;
3322 SendEyeOffset(player->getPeerId(), first, third);
3325 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3327 sanity_check(player);
3328 player->setSky(params);
3329 SendSetSky(player->getPeerId(), params);
3332 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3334 sanity_check(player);
3335 player->setSun(params);
3336 SendSetSun(player->getPeerId(), params);
3339 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3341 sanity_check(player);
3342 player->setMoon(params);
3343 SendSetMoon(player->getPeerId(), params);
3346 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3348 sanity_check(player);
3349 player->setStars(params);
3350 SendSetStars(player->getPeerId(), params);
3353 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3355 sanity_check(player);
3356 player->setCloudParams(params);
3357 SendCloudParams(player->getPeerId(), params);
3360 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3363 sanity_check(player);
3364 player->overrideDayNightRatio(do_override, ratio);
3365 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3368 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3370 sanity_check(player);
3371 player->setLighting(lighting);
3372 SendSetLighting(player->getPeerId(), lighting);
3375 void Server::notifyPlayers(const std::wstring &msg)
3377 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3380 void Server::spawnParticle(const std::string &playername,
3381 const ParticleParameters &p)
3383 // m_env will be NULL if the server is initializing
3387 session_t peer_id = PEER_ID_INEXISTENT;
3389 if (!playername.empty()) {
3390 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3393 peer_id = player->getPeerId();
3394 proto_ver = player->protocol_version;
3397 SendSpawnParticle(peer_id, proto_ver, p);
3400 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3401 ServerActiveObject *attached, const std::string &playername)
3403 // m_env will be NULL if the server is initializing
3407 session_t peer_id = PEER_ID_INEXISTENT;
3409 if (!playername.empty()) {
3410 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3413 peer_id = player->getPeerId();
3414 proto_ver = player->protocol_version;
3417 u16 attached_id = attached ? attached->getId() : 0;
3420 if (attached_id == 0)
3421 id = m_env->addParticleSpawner(p.time);
3423 id = m_env->addParticleSpawner(p.time, attached_id);
3425 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3429 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3431 // m_env will be NULL if the server is initializing
3433 throw ServerError("Can't delete particle spawners during initialisation!");
3435 session_t peer_id = PEER_ID_INEXISTENT;
3436 if (!playername.empty()) {
3437 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3440 peer_id = player->getPeerId();
3443 m_env->deleteParticleSpawner(id);
3444 SendDeleteParticleSpawner(peer_id, id);
3447 bool Server::dynamicAddMedia(std::string filepath,
3448 const u32 token, const std::string &to_player, bool ephemeral)
3450 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3451 auto it = m_media.find(filename);
3452 if (it != m_media.end()) {
3453 // Allow the same path to be "added" again in certain conditions
3454 if (ephemeral || it->second.path != filepath) {
3455 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3456 << "\" already exists in media cache" << std::endl;
3461 // Load the file and add it to our media cache
3462 std::string filedata, raw_hash;
3463 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3468 // Create a copy of the file and swap out the path, this removes the
3469 // requirement that mods keep the file accessible at the original path.
3470 filepath = fs::CreateTempFile();
3471 bool ok = ([&] () -> bool {
3472 if (filepath.empty())
3474 std::ofstream os(filepath.c_str(), std::ios::binary);
3482 errorstream << "Server: failed to create a copy of media file "
3483 << "\"" << filename << "\"" << std::endl;
3484 m_media.erase(filename);
3487 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3488 << filepath << std::endl;
3490 m_media[filename].path = filepath;
3491 m_media[filename].no_announce = true;
3492 // stepPendingDynMediaCallbacks will clean this up later.
3493 } else if (!to_player.empty()) {
3494 m_media[filename].no_announce = true;
3497 // Push file to existing clients
3498 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3499 pkt << raw_hash << filename << (bool)ephemeral;
3501 NetworkPacket legacy_pkt = pkt;
3503 // Newer clients get asked to fetch the file (asynchronous)
3505 // Older clients have an awful hack that just throws the data at them
3506 legacy_pkt.putLongString(filedata);
3508 std::unordered_set<session_t> delivered, waiting;
3510 ClientInterface::AutoLock clientlock(m_clients);
3511 for (auto &pair : m_clients.getClientList()) {
3512 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3514 If a client is in the DefinitionsSent state it is too late to
3515 transfer the file via sendMediaAnnouncement() but at the same
3516 time the client cannot accept a media push yet.
3517 Short of artificially delaying the joining process there is no
3518 way for the server to resolve this so we (currently) opt not to.
3520 warningstream << "The media \"" << filename << "\" (dynamic) could "
3521 "not be delivered to " << pair.second->getName()
3522 << " due to a race condition." << std::endl;
3525 if (pair.second->getState() < CS_Active)
3528 const auto proto_ver = pair.second->net_proto_version;
3532 const session_t peer_id = pair.second->peer_id;
3533 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3536 if (proto_ver < 40) {
3537 delivered.emplace(peer_id);
3539 The network layer only guarantees ordered delivery inside a channel.
3540 Since the very next packet could be one that uses the media, we have
3541 to push the media over ALL channels to ensure it is processed before
3542 it is used. In practice this means channels 1 and 0.
3544 m_clients.send(peer_id, 1, &legacy_pkt, true);
3545 m_clients.send(peer_id, 0, &legacy_pkt, true);
3547 waiting.emplace(peer_id);
3548 Send(peer_id, &pkt);
3553 // Run callback for players that already had the file delivered (legacy-only)
3554 for (session_t peer_id : delivered) {
3555 if (auto player = m_env->getPlayer(peer_id))
3556 getScriptIface()->on_dynamic_media_added(token, player->getName());
3559 // Save all others in our pending state
3560 auto &state = m_pending_dyn_media[token];
3561 state.waiting_players = std::move(waiting);
3562 // regardless of success throw away the callback after a while
3563 state.expiry_timer = 60.0f;
3565 state.filename = filename;
3570 // actions: time-reversed list
3571 // Return value: success/failure
3572 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3573 std::list<std::string> *log)
3575 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3576 ServerMap *map = (ServerMap*)(&m_env->getMap());
3578 // Fail if no actions to handle
3579 if (actions.empty()) {
3581 log->push_back("Nothing to do.");
3588 for (const RollbackAction &action : actions) {
3590 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3593 std::ostringstream os;
3594 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3595 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3597 log->push_back(os.str());
3599 std::ostringstream os;
3600 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3601 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3603 log->push_back(os.str());
3607 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3608 <<" failed"<<std::endl;
3610 // Call it done if less than half failed
3611 return num_failed <= num_tried/2;
3614 // IGameDef interface
3616 IItemDefManager *Server::getItemDefManager()
3621 const NodeDefManager *Server::getNodeDefManager()
3626 ICraftDefManager *Server::getCraftDefManager()
3631 u16 Server::allocateUnknownNodeId(const std::string &name)
3633 return m_nodedef->allocateDummy(name);
3636 IWritableItemDefManager *Server::getWritableItemDefManager()
3641 NodeDefManager *Server::getWritableNodeDefManager()
3646 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3651 const std::vector<ModSpec> & Server::getMods() const
3653 return m_modmgr->getMods();
3656 const ModSpec *Server::getModSpec(const std::string &modname) const
3658 return m_modmgr->getModSpec(modname);
3661 std::string Server::getBuiltinLuaPath()
3663 return porting::path_share + DIR_DELIM + "builtin";
3666 v3f Server::findSpawnPos()
3668 ServerMap &map = m_env->getServerMap();
3670 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3671 return nodeposf * BS;
3673 bool is_good = false;
3674 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3675 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3677 // Try to find a good place a few times
3678 for (s32 i = 0; i < 4000 && !is_good; i++) {
3679 s32 range = MYMIN(1 + i, range_max);
3680 // We're going to try to throw the player to this position
3681 v2s16 nodepos2d = v2s16(
3682 -range + (myrand() % (range * 2)),
3683 -range + (myrand() % (range * 2)));
3684 // Get spawn level at point
3685 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3686 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3687 // signify an unsuitable spawn position, or if outside limits.
3688 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3689 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3692 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3693 // Consecutive empty nodes
3696 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3697 // avoid obstructions in already-generated mapblocks.
3698 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3699 // no obstructions, but mapgen decorations are generated after spawn so
3700 // the player may end up inside one.
3701 for (s32 i = 0; i < 8; i++) {
3702 v3s16 blockpos = getNodeBlockPos(nodepos);
3703 map.emergeBlock(blockpos, true);
3704 content_t c = map.getNode(nodepos).getContent();
3706 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3707 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3708 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3710 if (air_count >= 2) {
3711 // Spawn in lower empty node
3713 nodeposf = intToFloat(nodepos, BS);
3714 // Don't spawn the player outside map boundaries
3715 if (objectpos_over_limit(nodeposf))
3716 // Exit this loop, positions above are probably over limit
3719 // Good position found, cause an exit from main loop
3733 // No suitable spawn point found, return fallback 0,0,0
3734 return v3f(0.0f, 0.0f, 0.0f);
3737 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3739 if (delay == 0.0f) {
3740 // No delay, shutdown immediately
3741 m_shutdown_state.is_requested = true;
3742 // only print to the infostream, a chat message saying
3743 // "Server Shutting Down" is sent when the server destructs.
3744 infostream << "*** Immediate Server shutdown requested." << std::endl;
3745 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3746 // Negative delay, cancel shutdown if requested
3747 m_shutdown_state.reset();
3748 std::wstringstream ws;
3750 ws << L"*** Server shutdown canceled.";
3752 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3753 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3754 // m_shutdown_* are already handled, skip.
3756 } else if (delay > 0.0f) {
3757 // Positive delay, tell the clients when the server will shut down
3758 std::wstringstream ws;
3760 ws << L"*** Server shutting down in "
3761 << duration_to_string(myround(delay)).c_str()
3764 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3765 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3768 m_shutdown_state.trigger(delay, msg, reconnect);
3771 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3774 Try to get an existing player
3776 RemotePlayer *player = m_env->getPlayer(name);
3778 // If player is already connected, cancel
3779 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3780 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3785 If player with the wanted peer_id already exists, cancel.
3787 if (m_env->getPlayer(peer_id)) {
3788 infostream<<"emergePlayer(): Player with wrong name but same"
3789 " peer_id already exists"<<std::endl;
3794 player = new RemotePlayer(name, idef());
3797 bool newplayer = false;
3800 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3802 // Complete init with server parts
3803 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3804 player->protocol_version = proto_version;
3808 m_script->on_newplayer(playersao);
3814 bool Server::registerModStorage(ModMetadata *storage)
3816 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3817 errorstream << "Unable to register same mod storage twice. Storage name: "
3818 << storage->getModName() << std::endl;
3822 m_mod_storages[storage->getModName()] = storage;
3826 void Server::unregisterModStorage(const std::string &name)
3828 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3829 if (it != m_mod_storages.end())
3830 m_mod_storages.erase(name);
3833 void dedicated_server_loop(Server &server, bool &kill)
3835 verbosestream<<"dedicated_server_loop()"<<std::endl;
3837 IntervalLimiter m_profiler_interval;
3839 static thread_local const float steplen =
3840 g_settings->getFloat("dedicated_server_step");
3841 static thread_local const float profiler_print_interval =
3842 g_settings->getFloat("profiler_print_interval");
3845 * The dedicated server loop only does time-keeping (in Server::step) and
3846 * provides a way to main.cpp to kill the server externally (bool &kill).
3850 // This is kind of a hack but can be done like this
3851 // because server.step() is very light
3852 sleep_ms((int)(steplen*1000.0));
3853 server.step(steplen);
3855 if (server.isShutdownRequested() || kill)
3861 if (profiler_print_interval != 0) {
3862 if(m_profiler_interval.step(steplen, profiler_print_interval))
3864 infostream<<"Profiler:"<<std::endl;
3865 g_profiler->print(infostream);
3866 g_profiler->clear();
3871 infostream << "Dedicated server quitting" << std::endl;
3873 if (g_settings->getBool("server_announce"))
3874 ServerList::sendAnnounce(ServerList::AA_DELETE,
3875 server.m_bind_addr.getPort());
3884 bool Server::joinModChannel(const std::string &channel)
3886 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3887 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3890 bool Server::leaveModChannel(const std::string &channel)
3892 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3895 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3897 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3900 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3904 ModChannel* Server::getModChannel(const std::string &channel)
3906 return m_modchannel_mgr->getModChannel(channel);
3909 void Server::broadcastModChannelMessage(const std::string &channel,
3910 const std::string &message, session_t from_peer)
3912 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3916 if (message.size() > STRING_MAX_LEN) {
3917 warningstream << "ModChannel message too long, dropping before sending "
3918 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3919 << channel << ")" << std::endl;
3924 if (from_peer != PEER_ID_SERVER) {
3925 sender = getPlayerName(from_peer);
3928 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3929 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3930 resp_pkt << channel << sender << message;
3931 for (session_t peer_id : peers) {
3933 if (peer_id == from_peer)
3936 Send(peer_id, &resp_pkt);
3939 if (from_peer != PEER_ID_SERVER) {
3940 m_script->on_modchannel_message(channel, sender, message);
3944 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3946 if (lang_code.empty())
3949 auto it = server_translations.find(lang_code);
3950 if (it != server_translations.end())
3951 return &it->second; // Already loaded
3953 // [] will create an entry
3954 auto *translations = &server_translations[lang_code];
3956 std::string suffix = "." + lang_code + ".tr";
3957 for (const auto &i : m_media) {
3958 if (str_ends_with(i.first, suffix)) {
3960 if (fs::ReadFile(i.second.path, data)) {
3961 translations->loadTranslation(data);
3966 return translations;
3969 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
3971 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
3973 if (!world_mt.readConfigFile(world_mt_path.c_str()))
3974 throw BaseException("Cannot read world.mt!");
3976 std::string backend = world_mt.exists("mod_storage_backend") ?
3977 world_mt.get("mod_storage_backend") : "files";
3978 if (backend == "files")
3979 warningstream << "/!\\ You are using the old mod storage files backend. "
3980 << "This backend is deprecated and may be removed in a future release /!\\"
3981 << std::endl << "Switching to SQLite3 is advised, "
3982 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
3984 return openModStorageDatabase(backend, world_path, world_mt);
3987 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
3988 const std::string &world_path, const Settings &world_mt)
3990 if (backend == "sqlite3")
3991 return new ModMetadataDatabaseSQLite3(world_path);
3993 if (backend == "files")
3994 return new ModMetadataDatabaseFiles(world_path);
3996 if (backend == "dummy")
3997 return new Database_Dummy();
3999 throw BaseException("Mod storage database backend " + backend + " not supported");
4002 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4004 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4006 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4007 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4008 errorstream << "Cannot read world.mt!" << std::endl;
4012 std::string backend = world_mt.exists("mod_storage_backend") ?
4013 world_mt.get("mod_storage_backend") : "files";
4014 if (backend == migrate_to) {
4015 errorstream << "Cannot migrate: new backend is same"
4016 << " as the old one" << std::endl;
4020 ModMetadataDatabase *srcdb = nullptr;
4021 ModMetadataDatabase *dstdb = nullptr;
4023 bool succeeded = false;
4026 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4027 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4031 std::vector<std::string> mod_list;
4032 srcdb->listMods(&mod_list);
4033 for (const std::string &modname : mod_list) {
4035 srcdb->getModEntries(modname, &meta);
4036 for (const auto &pair : meta) {
4037 dstdb->setModEntry(modname, pair.first, pair.second);
4045 actionstream << "Successfully migrated the metadata of "
4046 << mod_list.size() << " mods" << std::endl;
4047 world_mt.set("mod_storage_backend", migrate_to);
4048 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4049 errorstream << "Failed to update world.mt!" << std::endl;
4051 actionstream << "world.mt updated" << std::endl;
4053 } catch (BaseException &e) {
4054 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4060 if (succeeded && backend == "files") {
4062 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4063 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4064 if (!fs::Rename(storage_path, backup_path))
4065 warningstream << "After migration, " << storage_path
4066 << " could not be renamed to " << backup_path << std::endl;