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->getMap().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_Legacy(peer_id, L"Your client sent something server didn't expect."
1042 L"Try reconnecting or updating your client");
1043 } catch (const con::PeerNotFoundException &e) {
1045 } catch (const con::NoIncomingDataException &e) {
1051 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1053 std::string playername;
1054 PlayerSAO *playersao = NULL;
1056 ClientInterface::AutoLock clientlock(m_clients);
1057 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1059 playername = client->getName();
1060 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1064 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1066 // If failed, cancel
1067 if (!playersao || !player) {
1068 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1069 actionstream << "Server: Failed to emerge player \"" << playername
1070 << "\" (player allocated to an another client)" << std::endl;
1071 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1072 L"name. If your client closed unexpectedly, try again in "
1075 errorstream << "Server: " << playername << ": Failed to emerge player"
1077 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1083 Send complete position information
1085 SendMovePlayer(peer_id);
1088 SendPlayerPrivileges(peer_id);
1090 // Send inventory formspec
1091 SendPlayerInventoryFormspec(peer_id);
1094 SendInventory(playersao, false);
1097 SendPlayerHP(playersao);
1099 // Send death screen
1100 if (playersao->isDead())
1101 SendDeathscreen(peer_id, false, v3f(0,0,0));
1104 SendPlayerBreath(playersao);
1107 Update player list and print action
1110 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1111 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1112 m_clients.sendToAll(¬ice_pkt);
1115 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1116 const auto &names = m_clients.getPlayerNames();
1118 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1119 for (const std::string &name : names)
1120 actionstream << name << " ";
1121 actionstream << player->getName() << std::endl;
1126 inline void Server::handleCommand(NetworkPacket *pkt)
1128 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1129 (this->*opHandle.handler)(pkt);
1132 void Server::ProcessData(NetworkPacket *pkt)
1134 // Environment is locked first.
1135 MutexAutoLock envlock(m_env_mutex);
1137 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1138 u32 peer_id = pkt->getPeerId();
1141 Address address = getPeerAddress(peer_id);
1142 std::string addr_s = address.serializeString();
1144 if(m_banmanager->isIpBanned(addr_s)) {
1145 std::string ban_name = m_banmanager->getBanName(addr_s);
1146 infostream << "Server: A banned client tried to connect from "
1147 << addr_s << "; banned name was "
1148 << ban_name << std::endl;
1149 // This actually doesn't seem to transfer to the client
1150 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1151 + utf8_to_wide(ban_name));
1155 catch(con::PeerNotFoundException &e) {
1157 * no peer for this packet found
1158 * most common reason is peer timeout, e.g. peer didn't
1159 * respond for some time, your server was overloaded or
1162 infostream << "Server::ProcessData(): Canceling: peer "
1163 << peer_id << " not found" << std::endl;
1168 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1170 // Command must be handled into ToServerCommandHandler
1171 if (command >= TOSERVER_NUM_MSG_TYPES) {
1172 infostream << "Server: Ignoring unknown command "
1173 << command << std::endl;
1177 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1182 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1184 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1185 errorstream << "Server::ProcessData(): Cancelling: Peer"
1186 " serialization format invalid or not initialized."
1187 " Skipping incoming command=" << command << std::endl;
1191 /* Handle commands related to client startup */
1192 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1197 if (m_clients.getClientState(peer_id) < CS_Active) {
1198 if (command == TOSERVER_PLAYERPOS) return;
1200 errorstream << "Got packet command: " << command << " for peer id "
1201 << peer_id << " but client isn't active yet. Dropping packet "
1207 } catch (SendFailedException &e) {
1208 errorstream << "Server::ProcessData(): SendFailedException: "
1209 << "what=" << e.what()
1211 } catch (PacketError &e) {
1212 actionstream << "Server::ProcessData(): PacketError: "
1213 << "what=" << e.what()
1218 void Server::setTimeOfDay(u32 time)
1220 m_env->setTimeOfDay(time);
1221 m_time_of_day_send_timer = 0;
1224 void Server::onMapEditEvent(const MapEditEvent &event)
1226 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1229 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1232 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1234 std::vector<session_t> clients = m_clients.getClientIDs();
1235 ClientInterface::AutoLock clientlock(m_clients);
1236 // Set the modified blocks unsent for all the clients
1237 for (const session_t client_id : clients) {
1238 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1239 client->SetBlocksNotSent(block);
1243 void Server::peerAdded(con::Peer *peer)
1245 verbosestream<<"Server::peerAdded(): peer->id="
1246 <<peer->id<<std::endl;
1248 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1251 void Server::deletingPeer(con::Peer *peer, bool timeout)
1253 verbosestream<<"Server::deletingPeer(): peer->id="
1254 <<peer->id<<", timeout="<<timeout<<std::endl;
1256 m_clients.event(peer->id, CSE_Disconnect);
1257 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1260 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1262 *retval = m_con->getPeerStat(peer_id,type);
1263 return *retval != -1;
1266 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1268 ClientInterface::AutoLock clientlock(m_clients);
1269 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1274 ret.state = client->getState();
1275 ret.addr = client->getAddress();
1276 ret.uptime = client->uptime();
1277 ret.ser_vers = client->serialization_version;
1278 ret.prot_vers = client->net_proto_version;
1280 ret.major = client->getMajor();
1281 ret.minor = client->getMinor();
1282 ret.patch = client->getPatch();
1283 ret.vers_string = client->getFullVer();
1285 ret.lang_code = client->getLangCode();
1290 void Server::handlePeerChanges()
1292 while(!m_peer_change_queue.empty())
1294 con::PeerChange c = m_peer_change_queue.front();
1295 m_peer_change_queue.pop();
1297 verbosestream<<"Server: Handling peer change: "
1298 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1303 case con::PEER_ADDED:
1304 m_clients.CreateClient(c.peer_id);
1307 case con::PEER_REMOVED:
1308 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1312 FATAL_ERROR("Invalid peer change event received!");
1318 void Server::printToConsoleOnly(const std::string &text)
1321 m_admin_chat->outgoing_queue.push_back(
1322 new ChatEventChat("", utf8_to_wide(text)));
1324 std::cout << text << std::endl;
1328 void Server::Send(NetworkPacket *pkt)
1330 Send(pkt->getPeerId(), pkt);
1333 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1335 m_clients.send(peer_id,
1336 clientCommandFactoryTable[pkt->getCommand()].channel,
1338 clientCommandFactoryTable[pkt->getCommand()].reliable);
1341 void Server::SendMovement(session_t peer_id)
1343 std::ostringstream os(std::ios_base::binary);
1345 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1347 pkt << g_settings->getFloat("movement_acceleration_default");
1348 pkt << g_settings->getFloat("movement_acceleration_air");
1349 pkt << g_settings->getFloat("movement_acceleration_fast");
1350 pkt << g_settings->getFloat("movement_speed_walk");
1351 pkt << g_settings->getFloat("movement_speed_crouch");
1352 pkt << g_settings->getFloat("movement_speed_fast");
1353 pkt << g_settings->getFloat("movement_speed_climb");
1354 pkt << g_settings->getFloat("movement_speed_jump");
1355 pkt << g_settings->getFloat("movement_liquid_fluidity");
1356 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1357 pkt << g_settings->getFloat("movement_liquid_sink");
1358 pkt << g_settings->getFloat("movement_gravity");
1363 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1365 m_script->player_event(playersao, "health_changed");
1366 SendPlayerHP(playersao);
1368 // Send to other clients
1369 playersao->sendPunchCommand();
1371 if (playersao->isDead())
1372 HandlePlayerDeath(playersao, reason);
1375 void Server::SendPlayerHP(PlayerSAO *playersao)
1377 SendHP(playersao->getPeerID(), playersao->getHP());
1380 void Server::SendHP(session_t peer_id, u16 hp)
1382 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1387 void Server::SendBreath(session_t peer_id, u16 breath)
1389 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1390 pkt << (u16) breath;
1394 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1395 const std::string &custom_reason, bool reconnect)
1397 assert(reason < SERVER_ACCESSDENIED_MAX);
1399 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1401 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1402 pkt << custom_reason;
1403 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1404 reason == SERVER_ACCESSDENIED_CRASH)
1405 pkt << custom_reason << (u8)reconnect;
1409 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1411 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1416 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1417 v3f camera_point_target)
1419 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1420 pkt << set_camera_point_target << camera_point_target;
1424 void Server::SendItemDef(session_t peer_id,
1425 IItemDefManager *itemdef, u16 protocol_version)
1427 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1431 u32 length of the next item
1432 zlib-compressed serialized ItemDefManager
1434 std::ostringstream tmp_os(std::ios::binary);
1435 itemdef->serialize(tmp_os, protocol_version);
1436 std::ostringstream tmp_os2(std::ios::binary);
1437 compressZlib(tmp_os.str(), tmp_os2);
1438 pkt.putLongString(tmp_os2.str());
1441 verbosestream << "Server: Sending item definitions to id(" << peer_id
1442 << "): size=" << pkt.getSize() << std::endl;
1447 void Server::SendNodeDef(session_t peer_id,
1448 const NodeDefManager *nodedef, u16 protocol_version)
1450 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1454 u32 length of the next item
1455 zlib-compressed serialized NodeDefManager
1457 std::ostringstream tmp_os(std::ios::binary);
1458 nodedef->serialize(tmp_os, protocol_version);
1459 std::ostringstream tmp_os2(std::ios::binary);
1460 compressZlib(tmp_os.str(), tmp_os2);
1462 pkt.putLongString(tmp_os2.str());
1465 verbosestream << "Server: Sending node definitions to id(" << peer_id
1466 << "): size=" << pkt.getSize() << std::endl;
1472 Non-static send methods
1475 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1477 RemotePlayer *player = sao->getPlayer();
1479 // Do not send new format to old clients
1480 incremental &= player->protocol_version >= 38;
1482 UpdateCrafting(player);
1488 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1490 std::ostringstream os(std::ios::binary);
1491 sao->getInventory()->serialize(os, incremental);
1492 sao->getInventory()->setModified(false);
1493 player->setModified(true);
1495 const std::string &s = os.str();
1496 pkt.putRawString(s.c_str(), s.size());
1500 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1502 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1504 u8 type = message.type;
1505 pkt << version << type << message.sender << message.message
1506 << static_cast<u64>(message.timestamp);
1508 if (peer_id != PEER_ID_INEXISTENT) {
1509 RemotePlayer *player = m_env->getPlayer(peer_id);
1515 m_clients.sendToAll(&pkt);
1519 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1520 const std::string &formname)
1522 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1523 if (formspec.empty()){
1524 //the client should close the formspec
1525 //but make sure there wasn't another one open in meantime
1526 const auto it = m_formspec_state_data.find(peer_id);
1527 if (it != m_formspec_state_data.end() && it->second == formname) {
1528 m_formspec_state_data.erase(peer_id);
1530 pkt.putLongString("");
1532 m_formspec_state_data[peer_id] = formname;
1533 pkt.putLongString(formspec);
1540 // Spawns a particle on peer with peer_id
1541 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1542 const ParticleParameters &p)
1544 static thread_local const float radius =
1545 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1547 if (peer_id == PEER_ID_INEXISTENT) {
1548 std::vector<session_t> clients = m_clients.getClientIDs();
1549 const v3f pos = p.pos * BS;
1550 const float radius_sq = radius * radius;
1552 for (const session_t client_id : clients) {
1553 RemotePlayer *player = m_env->getPlayer(client_id);
1557 PlayerSAO *sao = player->getPlayerSAO();
1561 // Do not send to distant clients
1562 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1565 SendSpawnParticle(client_id, player->protocol_version, p);
1569 assert(protocol_version != 0);
1571 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1574 // NetworkPacket and iostreams are incompatible...
1575 std::ostringstream oss(std::ios_base::binary);
1576 p.serialize(oss, protocol_version);
1577 pkt.putRawString(oss.str());
1583 // Adds a ParticleSpawner on peer with peer_id
1584 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1585 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1587 static thread_local const float radius =
1588 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1590 if (peer_id == PEER_ID_INEXISTENT) {
1591 std::vector<session_t> clients = m_clients.getClientIDs();
1592 const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS;
1593 const float radius_sq = radius * radius;
1594 /* Don't send short-lived spawners to distant players.
1595 * This could be replaced with proper tracking at some point. */
1596 const bool distance_check = !attached_id && p.time <= 1.0f;
1598 for (const session_t client_id : clients) {
1599 RemotePlayer *player = m_env->getPlayer(client_id);
1603 if (distance_check) {
1604 PlayerSAO *sao = player->getPlayerSAO();
1607 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1611 SendAddParticleSpawner(client_id, player->protocol_version,
1612 p, attached_id, id);
1616 assert(protocol_version != 0);
1618 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1620 pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel
1621 << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime
1622 << p.minsize << p.maxsize << p.collisiondetection;
1624 pkt.putLongString(p.texture);
1626 pkt << id << p.vertical << p.collision_removal << attached_id;
1628 std::ostringstream os(std::ios_base::binary);
1629 p.animation.serialize(os, protocol_version);
1630 pkt.putRawString(os.str());
1632 pkt << p.glow << p.object_collision;
1633 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1638 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1640 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1644 if (peer_id != PEER_ID_INEXISTENT)
1647 m_clients.sendToAll(&pkt);
1651 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1653 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1655 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1656 << form->text << form->number << form->item << form->dir
1657 << form->align << form->offset << form->world_pos << form->size
1658 << form->z_index << form->text2 << form->style;
1663 void Server::SendHUDRemove(session_t peer_id, u32 id)
1665 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1670 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1672 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1673 pkt << id << (u8) stat;
1677 case HUD_STAT_SCALE:
1678 case HUD_STAT_ALIGN:
1679 case HUD_STAT_OFFSET:
1680 pkt << *(v2f *) value;
1684 case HUD_STAT_TEXT2:
1685 pkt << *(std::string *) value;
1687 case HUD_STAT_WORLD_POS:
1688 pkt << *(v3f *) value;
1691 pkt << *(v2s32 *) value;
1693 default: // all other types
1694 pkt << *(u32 *) value;
1701 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1703 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1705 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1707 pkt << flags << mask;
1712 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1714 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1715 pkt << param << value;
1719 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1721 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1723 // Handle prior clients here
1724 if (m_clients.getProtocolVersion(peer_id) < 39) {
1725 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1727 for (const std::string& texture : params.textures)
1730 pkt << params.clouds;
1731 } else { // Handle current clients and future clients
1732 pkt << params.bgcolor << params.type
1733 << params.clouds << params.fog_sun_tint
1734 << params.fog_moon_tint << params.fog_tint_type;
1736 if (params.type == "skybox") {
1737 pkt << (u16) params.textures.size();
1738 for (const std::string &texture : params.textures)
1740 } else if (params.type == "regular") {
1741 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1742 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1743 << params.sky_color.night_sky << params.sky_color.night_horizon
1744 << params.sky_color.indoors;
1751 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1753 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1754 pkt << params.visible << params.texture
1755 << params.tonemap << params.sunrise
1756 << params.sunrise_visible << params.scale;
1760 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1762 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1764 pkt << params.visible << params.texture
1765 << params.tonemap << params.scale;
1769 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1771 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1773 pkt << params.visible << params.count
1774 << params.starcolor << params.scale;
1779 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1781 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1782 pkt << params.density << params.color_bright << params.color_ambient
1783 << params.height << params.thickness << params.speed;
1787 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1790 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1793 pkt << do_override << (u16) (ratio * 65535);
1798 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1800 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1801 pkt << time << time_speed;
1803 if (peer_id == PEER_ID_INEXISTENT) {
1804 m_clients.sendToAll(&pkt);
1811 void Server::SendPlayerBreath(PlayerSAO *sao)
1815 m_script->player_event(sao, "breath_changed");
1816 SendBreath(sao->getPeerID(), sao->getBreath());
1819 void Server::SendMovePlayer(session_t peer_id)
1821 RemotePlayer *player = m_env->getPlayer(peer_id);
1823 PlayerSAO *sao = player->getPlayerSAO();
1826 // Send attachment updates instantly to the client prior updating position
1827 sao->sendOutdatedData();
1829 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1830 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1833 v3f pos = sao->getBasePosition();
1834 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1835 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1836 << " pitch=" << sao->getLookPitch()
1837 << " yaw=" << sao->getRotation().Y
1844 void Server::SendPlayerFov(session_t peer_id)
1846 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1848 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1849 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1854 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1855 f32 animation_speed)
1857 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1860 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1861 << animation_frames[3] << animation_speed;
1866 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1868 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1869 pkt << first << third;
1873 void Server::SendPlayerPrivileges(session_t peer_id)
1875 RemotePlayer *player = m_env->getPlayer(peer_id);
1877 if(player->getPeerId() == PEER_ID_INEXISTENT)
1880 std::set<std::string> privs;
1881 m_script->getAuth(player->getName(), NULL, &privs);
1883 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1884 pkt << (u16) privs.size();
1886 for (const std::string &priv : privs) {
1893 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1895 RemotePlayer *player = m_env->getPlayer(peer_id);
1897 if (player->getPeerId() == PEER_ID_INEXISTENT)
1900 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1901 pkt.putLongString(player->inventory_formspec);
1906 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1908 RemotePlayer *player = m_env->getPlayer(peer_id);
1910 if (player->getPeerId() == PEER_ID_INEXISTENT)
1913 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1914 pkt << player->formspec_prepend;
1918 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1920 // Radius inside which objects are active
1921 static thread_local const s16 radius =
1922 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1924 // Radius inside which players are active
1925 static thread_local const bool is_transfer_limited =
1926 g_settings->exists("unlimited_player_transfer_distance") &&
1927 !g_settings->getBool("unlimited_player_transfer_distance");
1929 static thread_local const s16 player_transfer_dist =
1930 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
1932 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
1933 radius : player_transfer_dist;
1935 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
1939 std::queue<u16> removed_objects, added_objects;
1940 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
1941 client->m_known_objects, removed_objects);
1942 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
1943 client->m_known_objects, added_objects);
1945 int removed_count = removed_objects.size();
1946 int added_count = added_objects.size();
1948 if (removed_objects.empty() && added_objects.empty())
1954 // Handle removed objects
1955 writeU16((u8*)buf, removed_objects.size());
1956 data.append(buf, 2);
1957 while (!removed_objects.empty()) {
1959 u16 id = removed_objects.front();
1960 ServerActiveObject* obj = m_env->getActiveObject(id);
1962 // Add to data buffer for sending
1963 writeU16((u8*)buf, id);
1964 data.append(buf, 2);
1966 // Remove from known objects
1967 client->m_known_objects.erase(id);
1969 if (obj && obj->m_known_by_count > 0)
1970 obj->m_known_by_count--;
1972 removed_objects.pop();
1975 // Handle added objects
1976 writeU16((u8*)buf, added_objects.size());
1977 data.append(buf, 2);
1978 while (!added_objects.empty()) {
1980 u16 id = added_objects.front();
1981 ServerActiveObject *obj = m_env->getActiveObject(id);
1982 added_objects.pop();
1985 warningstream << FUNCTION_NAME << ": NULL object id="
1986 << (int)id << std::endl;
1991 u8 type = obj->getSendType();
1993 // Add to data buffer for sending
1994 writeU16((u8*)buf, id);
1995 data.append(buf, 2);
1996 writeU8((u8*)buf, type);
1997 data.append(buf, 1);
1999 data.append(serializeString32(
2000 obj->getClientInitializationData(client->net_proto_version)));
2002 // Add to known objects
2003 client->m_known_objects.insert(id);
2005 obj->m_known_by_count++;
2008 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2009 pkt.putRawString(data.c_str(), data.size());
2012 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2013 << removed_count << " removed, " << added_count << " added, "
2014 << "packet size is " << pkt.getSize() << std::endl;
2017 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2020 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2021 datas.size(), peer_id);
2023 pkt.putRawString(datas.c_str(), datas.size());
2025 m_clients.send(pkt.getPeerId(),
2026 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2030 void Server::SendCSMRestrictionFlags(session_t peer_id)
2032 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2033 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2034 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2038 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2040 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2045 inline s32 Server::nextSoundId()
2047 s32 ret = m_next_sound_id;
2048 if (m_next_sound_id == INT32_MAX)
2049 m_next_sound_id = 0; // signed overflow is undefined
2055 s32 Server::playSound(const SimpleSoundSpec &spec,
2056 const ServerSoundParams ¶ms, bool ephemeral)
2058 // Find out initial position of sound
2059 bool pos_exists = false;
2060 v3f pos = params.getPos(m_env, &pos_exists);
2061 // If position is not found while it should be, cancel sound
2062 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2065 // Filter destination clients
2066 std::vector<session_t> dst_clients;
2067 if (!params.to_player.empty()) {
2068 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2070 infostream<<"Server::playSound: Player \""<<params.to_player
2071 <<"\" not found"<<std::endl;
2074 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2075 infostream<<"Server::playSound: Player \""<<params.to_player
2076 <<"\" not connected"<<std::endl;
2079 dst_clients.push_back(player->getPeerId());
2081 std::vector<session_t> clients = m_clients.getClientIDs();
2083 for (const session_t client_id : clients) {
2084 RemotePlayer *player = m_env->getPlayer(client_id);
2087 if (!params.exclude_player.empty() &&
2088 params.exclude_player == player->getName())
2091 PlayerSAO *sao = player->getPlayerSAO();
2096 if(sao->getBasePosition().getDistanceFrom(pos) >
2097 params.max_hear_distance)
2100 dst_clients.push_back(client_id);
2104 if(dst_clients.empty())
2109 ServerPlayingSound *psound = nullptr;
2111 id = -1; // old clients will still use this, so pick a reserved ID
2114 // The sound will exist as a reference in m_playing_sounds
2115 m_playing_sounds[id] = ServerPlayingSound();
2116 psound = &m_playing_sounds[id];
2117 psound->params = params;
2118 psound->spec = spec;
2121 float gain = params.gain * spec.gain;
2122 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2123 pkt << id << spec.name << gain
2124 << (u8) params.type << pos << params.object
2125 << params.loop << params.fade << params.pitch
2128 bool as_reliable = !ephemeral;
2130 for (const u16 dst_client : dst_clients) {
2132 psound->clients.insert(dst_client);
2133 m_clients.send(dst_client, 0, &pkt, as_reliable);
2137 void Server::stopSound(s32 handle)
2139 // Get sound reference
2140 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2141 m_playing_sounds.find(handle);
2142 if (i == m_playing_sounds.end())
2144 ServerPlayingSound &psound = i->second;
2146 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2149 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2150 si != psound.clients.end(); ++si) {
2152 m_clients.send(*si, 0, &pkt, true);
2154 // Remove sound reference
2155 m_playing_sounds.erase(i);
2158 void Server::fadeSound(s32 handle, float step, float gain)
2160 // Get sound reference
2161 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2162 m_playing_sounds.find(handle);
2163 if (i == m_playing_sounds.end())
2166 ServerPlayingSound &psound = i->second;
2167 psound.params.gain = gain;
2169 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2170 pkt << handle << step << gain;
2172 // Backwards compability
2173 bool play_sound = gain > 0;
2174 ServerPlayingSound compat_psound = psound;
2175 compat_psound.clients.clear();
2177 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2178 compat_pkt << handle;
2180 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2181 it != psound.clients.end();) {
2182 if (m_clients.getProtocolVersion(*it) >= 32) {
2184 m_clients.send(*it, 0, &pkt, true);
2187 compat_psound.clients.insert(*it);
2189 m_clients.send(*it, 0, &compat_pkt, true);
2190 psound.clients.erase(it++);
2194 // Remove sound reference
2195 if (!play_sound || psound.clients.empty())
2196 m_playing_sounds.erase(i);
2198 if (play_sound && !compat_psound.clients.empty()) {
2199 // Play new sound volume on older clients
2200 playSound(compat_psound.spec, compat_psound.params);
2204 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2207 float maxd = far_d_nodes * BS;
2208 v3f p_f = intToFloat(p, BS);
2209 v3s16 block_pos = getNodeBlockPos(p);
2211 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2214 std::vector<session_t> clients = m_clients.getClientIDs();
2215 ClientInterface::AutoLock clientlock(m_clients);
2217 for (session_t client_id : clients) {
2218 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2222 RemotePlayer *player = m_env->getPlayer(client_id);
2223 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2225 // If player is far away, only set modified blocks not sent
2226 if (!client->isBlockSent(block_pos) || (sao &&
2227 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2229 far_players->emplace(client_id);
2231 client->SetBlockNotSent(block_pos);
2236 m_clients.send(client_id, 0, &pkt, true);
2240 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2241 float far_d_nodes, bool remove_metadata)
2243 float maxd = far_d_nodes * BS;
2244 v3f p_f = intToFloat(p, BS);
2245 v3s16 block_pos = getNodeBlockPos(p);
2247 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2248 pkt << p << n.param0 << n.param1 << n.param2
2249 << (u8) (remove_metadata ? 0 : 1);
2251 std::vector<session_t> clients = m_clients.getClientIDs();
2252 ClientInterface::AutoLock clientlock(m_clients);
2254 for (session_t client_id : clients) {
2255 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2259 RemotePlayer *player = m_env->getPlayer(client_id);
2260 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2262 // If player is far away, only set modified blocks not sent
2263 if (!client->isBlockSent(block_pos) || (sao &&
2264 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2266 far_players->emplace(client_id);
2268 client->SetBlockNotSent(block_pos);
2273 m_clients.send(client_id, 0, &pkt, true);
2277 void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes)
2279 float maxd = far_d_nodes * BS;
2280 NodeMetadataList meta_updates_list(false);
2281 std::vector<session_t> clients = m_clients.getClientIDs();
2283 ClientInterface::AutoLock clientlock(m_clients);
2285 for (session_t i : clients) {
2286 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2290 ServerActiveObject *player = m_env->getActiveObject(i);
2291 v3f player_pos = player ? player->getBasePosition() : v3f();
2293 for (const v3s16 &pos : meta_updates) {
2294 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2299 v3s16 block_pos = getNodeBlockPos(pos);
2300 if (!client->isBlockSent(block_pos) || (player &&
2301 player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) {
2302 client->SetBlockNotSent(block_pos);
2306 // Add the change to send list
2307 meta_updates_list.set(pos, meta);
2309 if (meta_updates_list.size() == 0)
2312 // Send the meta changes
2313 std::ostringstream os(std::ios::binary);
2314 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2315 std::ostringstream oss(std::ios::binary);
2316 compressZlib(os.str(), oss);
2318 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0);
2319 pkt.putLongString(oss.str());
2320 m_clients.send(i, 0, &pkt, true);
2322 meta_updates_list.clear();
2326 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2327 u16 net_proto_version)
2330 Create a packet with the block in the right format
2332 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2333 std::ostringstream os(std::ios_base::binary);
2334 block->serialize(os, ver, false, net_compression_level);
2335 block->serializeNetworkSpecific(os);
2336 std::string s = os.str();
2338 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + s.size(), peer_id);
2340 pkt << block->getPos();
2341 pkt.putRawString(s.c_str(), s.size());
2345 void Server::SendBlocks(float dtime)
2347 MutexAutoLock envlock(m_env_mutex);
2348 //TODO check if one big lock could be faster then multiple small ones
2350 std::vector<PrioritySortedBlockTransfer> queue;
2352 u32 total_sending = 0;
2355 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2357 std::vector<session_t> clients = m_clients.getClientIDs();
2359 ClientInterface::AutoLock clientlock(m_clients);
2360 for (const session_t client_id : clients) {
2361 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2366 total_sending += client->getSendingCount();
2367 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2372 // Lowest priority number comes first.
2373 // Lowest is most important.
2374 std::sort(queue.begin(), queue.end());
2376 ClientInterface::AutoLock clientlock(m_clients);
2378 // Maximal total count calculation
2379 // The per-client block sends is halved with the maximal online users
2380 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2381 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2383 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2384 Map &map = m_env->getMap();
2386 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2387 if (total_sending >= max_blocks_to_send)
2390 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2394 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2399 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2400 client->net_proto_version);
2402 client->SentBlock(block_to_send.pos);
2407 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2409 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2413 ClientInterface::AutoLock clientlock(m_clients);
2414 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2415 if (!client || client->isBlockSent(blockpos))
2417 SendBlockNoLock(peer_id, block, client->serialization_version,
2418 client->net_proto_version);
2423 bool Server::addMediaFile(const std::string &filename,
2424 const std::string &filepath, std::string *filedata_to,
2425 std::string *digest_to)
2427 // If name contains illegal characters, ignore the file
2428 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2429 infostream << "Server: ignoring illegal file name: \""
2430 << filename << "\"" << std::endl;
2433 // If name is not in a supported format, ignore it
2434 const char *supported_ext[] = {
2435 ".png", ".jpg", ".bmp", ".tga",
2437 ".x", ".b3d", ".obj",
2438 // Custom translation file format
2442 if (removeStringEnd(filename, supported_ext).empty()) {
2443 infostream << "Server: ignoring unsupported file extension: \""
2444 << filename << "\"" << std::endl;
2447 // Ok, attempt to load the file and add to cache
2450 std::string filedata;
2451 if (!fs::ReadFile(filepath, filedata)) {
2452 errorstream << "Server::addMediaFile(): Failed to open \""
2453 << filename << "\" for reading" << std::endl;
2457 if (filedata.empty()) {
2458 errorstream << "Server::addMediaFile(): Empty file \""
2459 << filepath << "\"" << std::endl;
2464 sha1.addBytes(filedata.c_str(), filedata.length());
2466 unsigned char *digest = sha1.getDigest();
2467 std::string sha1_base64 = base64_encode(digest, 20);
2468 std::string sha1_hex = hex_encode((char*) digest, 20);
2470 *digest_to = std::string((char*) digest, 20);
2474 m_media[filename] = MediaInfo(filepath, sha1_base64);
2475 verbosestream << "Server: " << sha1_hex << " is " << filename
2479 *filedata_to = std::move(filedata);
2483 void Server::fillMediaCache()
2485 infostream << "Server: Calculating media file checksums" << std::endl;
2487 // Collect all media file paths
2488 std::vector<std::string> paths;
2490 // ordered in descending priority
2491 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2492 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2493 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2494 m_modmgr->getModsMediaPaths(paths);
2496 // Collect media file information from paths into cache
2497 for (const std::string &mediapath : paths) {
2498 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2499 for (const fs::DirListNode &dln : dirlist) {
2500 if (dln.dir) // Ignore dirs (already in paths)
2503 const std::string &filename = dln.name;
2504 if (m_media.find(filename) != m_media.end()) // Do not override
2507 std::string filepath = mediapath;
2508 filepath.append(DIR_DELIM).append(filename);
2509 addMediaFile(filename, filepath);
2513 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2516 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2519 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2522 std::string lang_suffix;
2523 lang_suffix.append(".").append(lang_code).append(".tr");
2524 for (const auto &i : m_media) {
2525 if (i.second.no_announce)
2527 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2534 for (const auto &i : m_media) {
2535 if (i.second.no_announce)
2537 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2539 pkt << i.first << i.second.sha1_digest;
2542 pkt << g_settings->get("remote_media");
2545 verbosestream << "Server: Announcing files to id(" << peer_id
2546 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2549 struct SendableMedia
2555 SendableMedia(const std::string &name, const std::string &path,
2556 std::string &&data):
2557 name(name), path(path), data(std::move(data))
2561 void Server::sendRequestedMedia(session_t peer_id,
2562 const std::vector<std::string> &tosend)
2564 verbosestream<<"Server::sendRequestedMedia(): "
2565 <<"Sending files to client"<<std::endl;
2569 // Put 5kB in one bunch (this is not accurate)
2570 u32 bytes_per_bunch = 5000;
2572 std::vector< std::vector<SendableMedia> > file_bunches;
2573 file_bunches.emplace_back();
2575 u32 file_size_bunch_total = 0;
2577 for (const std::string &name : tosend) {
2578 if (m_media.find(name) == m_media.end()) {
2579 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2580 <<"unknown file \""<<(name)<<"\""<<std::endl;
2584 const auto &m = m_media[name];
2588 if (!fs::ReadFile(m.path, data)) {
2589 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2590 << name << "\"" << std::endl;
2593 file_size_bunch_total += data.size();
2596 file_bunches.back().emplace_back(name, m.path, std::move(data));
2598 // Start next bunch if got enough data
2599 if(file_size_bunch_total >= bytes_per_bunch) {
2600 file_bunches.emplace_back();
2601 file_size_bunch_total = 0;
2606 /* Create and send packets */
2608 u16 num_bunches = file_bunches.size();
2609 for (u16 i = 0; i < num_bunches; i++) {
2612 u16 total number of texture bunches
2613 u16 index of this bunch
2614 u32 number of files in this bunch
2623 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2624 pkt << num_bunches << i << (u32) file_bunches[i].size();
2626 for (const SendableMedia &j : file_bunches[i]) {
2628 pkt.putLongString(j.data);
2631 verbosestream << "Server::sendRequestedMedia(): bunch "
2632 << i << "/" << num_bunches
2633 << " files=" << file_bunches[i].size()
2634 << " size=" << pkt.getSize() << std::endl;
2639 void Server::stepPendingDynMediaCallbacks(float dtime)
2641 MutexAutoLock lock(m_env_mutex);
2643 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2644 it->second.expiry_timer -= dtime;
2645 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2652 const auto &name = it->second.filename;
2653 if (!name.empty()) {
2654 assert(m_media.count(name));
2655 // if no_announce isn't set we're definitely deleting the wrong file!
2656 sanity_check(m_media[name].no_announce);
2658 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2659 m_media.erase(name);
2661 getScriptIface()->freeDynamicMediaCallback(it->first);
2662 it = m_pending_dyn_media.erase(it);
2666 void Server::SendMinimapModes(session_t peer_id,
2667 std::vector<MinimapMode> &modes, size_t wanted_mode)
2669 RemotePlayer *player = m_env->getPlayer(peer_id);
2671 if (player->getPeerId() == PEER_ID_INEXISTENT)
2674 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2675 pkt << (u16)modes.size() << (u16)wanted_mode;
2677 for (auto &mode : modes)
2678 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2683 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2685 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2689 pkt << false; // Remove inventory
2691 pkt << true; // Update inventory
2693 // Serialization & NetworkPacket isn't a love story
2694 std::ostringstream os(std::ios_base::binary);
2695 inventory->serialize(os);
2696 inventory->setModified(false);
2698 const std::string &os_str = os.str();
2699 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2700 pkt.putRawString(os_str);
2703 if (peer_id == PEER_ID_INEXISTENT)
2704 m_clients.sendToAll(&pkt);
2709 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2711 // Lookup player name, to filter detached inventories just after
2712 std::string peer_name;
2713 if (peer_id != PEER_ID_INEXISTENT) {
2714 peer_name = getClient(peer_id, CS_Created)->getName();
2717 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2718 sendDetachedInventory(inv, name, peer_id);
2721 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2728 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2730 infostream << "Server::DiePlayer(): Player "
2731 << playersao->getPlayer()->getName()
2732 << " dies" << std::endl;
2734 playersao->clearParentAttachment();
2736 // Trigger scripted stuff
2737 m_script->on_dieplayer(playersao, reason);
2739 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2742 void Server::RespawnPlayer(session_t peer_id)
2744 PlayerSAO *playersao = getPlayerSAO(peer_id);
2747 infostream << "Server::RespawnPlayer(): Player "
2748 << playersao->getPlayer()->getName()
2749 << " respawns" << std::endl;
2751 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2752 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2753 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2755 bool repositioned = m_script->on_respawnplayer(playersao);
2756 if (!repositioned) {
2757 // setPos will send the new position to client
2758 playersao->setPos(findSpawnPos());
2763 void Server::DenySudoAccess(session_t peer_id)
2765 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2770 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2771 const std::string &str_reason, bool reconnect)
2773 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2775 m_clients.event(peer_id, CSE_SetDenied);
2776 DisconnectPeer(peer_id);
2780 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2781 const std::string &custom_reason)
2783 SendAccessDenied(peer_id, reason, custom_reason);
2784 m_clients.event(peer_id, CSE_SetDenied);
2785 DisconnectPeer(peer_id);
2788 // 13/03/15: remove this function when protocol version 25 will become
2789 // the minimum version for MT users, maybe in 1 year
2790 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2792 SendAccessDenied_Legacy(peer_id, reason);
2793 m_clients.event(peer_id, CSE_SetDenied);
2794 DisconnectPeer(peer_id);
2797 void Server::DisconnectPeer(session_t peer_id)
2799 m_modchannel_mgr->leaveAllChannels(peer_id);
2800 m_con->DisconnectPeer(peer_id);
2803 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2806 RemoteClient* client = getClient(peer_id, CS_Invalid);
2808 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2810 // Right now, the auth mechs don't change between login and sudo mode.
2811 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2812 client->allowed_sudo_mechs = sudo_auth_mechs;
2814 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2815 << g_settings->getFloat("dedicated_server_step")
2819 m_clients.event(peer_id, CSE_AuthAccept);
2821 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2823 // We only support SRP right now
2824 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2826 resp_pkt << sudo_auth_mechs;
2828 m_clients.event(peer_id, CSE_SudoSuccess);
2832 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2834 std::wstring message;
2837 Clear references to playing sounds
2839 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2840 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2841 ServerPlayingSound &psound = i->second;
2842 psound.clients.erase(peer_id);
2843 if (psound.clients.empty())
2844 m_playing_sounds.erase(i++);
2849 // clear formspec info so the next client can't abuse the current state
2850 m_formspec_state_data.erase(peer_id);
2852 RemotePlayer *player = m_env->getPlayer(peer_id);
2854 /* Run scripts and remove from environment */
2856 PlayerSAO *playersao = player->getPlayerSAO();
2859 playersao->clearChildAttachments();
2860 playersao->clearParentAttachment();
2862 // inform connected clients
2863 const std::string &player_name = player->getName();
2864 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2865 // (u16) 1 + std::string represents a vector serialization representation
2866 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2867 m_clients.sendToAll(¬ice);
2869 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2871 playersao->disconnected();
2878 if (player && reason != CDR_DENY) {
2879 std::ostringstream os(std::ios_base::binary);
2880 std::vector<session_t> clients = m_clients.getClientIDs();
2882 for (const session_t client_id : clients) {
2884 RemotePlayer *player = m_env->getPlayer(client_id);
2888 // Get name of player
2889 os << player->getName() << " ";
2892 std::string name = player->getName();
2893 actionstream << name << " "
2894 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2895 << " List of players: " << os.str() << std::endl;
2897 m_admin_chat->outgoing_queue.push_back(
2898 new ChatEventNick(CET_NICK_REMOVE, name));
2902 MutexAutoLock env_lock(m_env_mutex);
2903 m_clients.DeleteClient(peer_id);
2907 // Send leave chat message to all remaining clients
2908 if (!message.empty()) {
2909 SendChatMessage(PEER_ID_INEXISTENT,
2910 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2914 void Server::UpdateCrafting(RemotePlayer *player)
2916 InventoryList *clist = player->inventory.getList("craft");
2917 if (!clist || clist->getSize() == 0)
2920 if (!clist->checkModified())
2923 // Get a preview for crafting
2925 InventoryLocation loc;
2926 loc.setPlayer(player->getName());
2927 std::vector<ItemStack> output_replacements;
2928 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2929 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2932 InventoryList *plist = player->inventory.getList("craftpreview");
2933 if (plist && plist->getSize() >= 1) {
2934 // Put the new preview in
2935 plist->changeItem(0, preview);
2939 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2941 if (evt->type == CET_NICK_ADD) {
2942 // The terminal informed us of its nick choice
2943 m_admin_nick = ((ChatEventNick *)evt)->nick;
2944 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2945 errorstream << "You haven't set up an account." << std::endl
2946 << "Please log in using the client as '"
2947 << m_admin_nick << "' with a secure password." << std::endl
2948 << "Until then, you can't execute admin tasks via the console," << std::endl
2949 << "and everybody can claim the user account instead of you," << std::endl
2950 << "giving them full control over this server." << std::endl;
2953 assert(evt->type == CET_CHAT);
2954 handleAdminChat((ChatEventChat *)evt);
2958 std::wstring Server::handleChat(const std::string &name,
2959 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2961 // If something goes wrong, this player is to blame
2962 RollbackScopeActor rollback_scope(m_rollback,
2963 std::string("player:") + name);
2965 if (g_settings->getBool("strip_color_codes"))
2966 wmessage = unescape_enriched(wmessage);
2969 switch (player->canSendChatMessage()) {
2970 case RPLAYER_CHATRESULT_FLOODING: {
2971 std::wstringstream ws;
2972 ws << L"You cannot send more messages. You are limited to "
2973 << g_settings->getFloat("chat_message_limit_per_10sec")
2974 << L" messages per 10 seconds.";
2977 case RPLAYER_CHATRESULT_KICK:
2978 DenyAccess_Legacy(player->getPeerId(),
2979 L"You have been kicked due to message flooding.");
2981 case RPLAYER_CHATRESULT_OK:
2984 FATAL_ERROR("Unhandled chat filtering result found.");
2988 if (m_max_chatmessage_length > 0
2989 && wmessage.length() > m_max_chatmessage_length) {
2990 return L"Your message exceed the maximum chat message limit set on the server. "
2991 L"It was refused. Send a shorter message";
2994 auto message = trim(wide_to_utf8(wmessage));
2995 if (message.empty())
2998 if (message.find_first_of("\n\r") != std::wstring::npos) {
2999 return L"Newlines are not permitted in chat messages";
3002 // Run script hook, exit if script ate the chat message
3003 if (m_script->on_chat_message(name, message))
3008 // Whether to send line to the player that sent the message, or to all players
3009 bool broadcast_line = true;
3011 if (check_shout_priv && !checkPriv(name, "shout")) {
3012 line += L"-!- You don't have permission to shout.";
3013 broadcast_line = false;
3016 Workaround for fixing chat on Android. Lua doesn't handle
3017 the Cyrillic alphabet and some characters on older Android devices
3020 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3022 line += utf8_to_wide(m_script->formatChatMessage(name,
3023 wide_to_utf8(wmessage)));
3028 Tell calling method to send the message to sender
3030 if (!broadcast_line)
3034 Send the message to others
3036 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3038 ChatMessage chatmsg(line);
3040 std::vector<session_t> clients = m_clients.getClientIDs();
3041 for (u16 cid : clients)
3042 SendChatMessage(cid, chatmsg);
3047 void Server::handleAdminChat(const ChatEventChat *evt)
3049 std::string name = evt->nick;
3050 std::wstring wmessage = evt->evt_msg;
3052 std::wstring answer = handleChat(name, wmessage);
3054 // If asked to send answer to sender
3055 if (!answer.empty()) {
3056 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3060 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3062 RemoteClient *client = getClientNoEx(peer_id,state_min);
3064 throw ClientNotFoundException("Client not found");
3068 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3070 return m_clients.getClientNoEx(peer_id, state_min);
3073 std::string Server::getPlayerName(session_t peer_id)
3075 RemotePlayer *player = m_env->getPlayer(peer_id);
3077 return "[id="+itos(peer_id)+"]";
3078 return player->getName();
3081 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3083 RemotePlayer *player = m_env->getPlayer(peer_id);
3086 return player->getPlayerSAO();
3089 std::string Server::getStatusString()
3091 std::ostringstream os(std::ios_base::binary);
3094 os << "version: " << g_version_string;
3096 os << " | game: " << (m_gamespec.name.empty() ? m_gamespec.id : m_gamespec.name);
3098 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3100 os << " | max lag: " << std::setprecision(3);
3101 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3103 // Information about clients
3105 os << " | clients: ";
3107 std::vector<session_t> clients = m_clients.getClientIDs();
3108 for (session_t client_id : clients) {
3109 RemotePlayer *player = m_env->getPlayer(client_id);
3111 // Get name of player
3112 const char *name = player ? player->getName() : "<unknown>";
3114 // Add name to information string
3123 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3124 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3126 if (!g_settings->get("motd").empty())
3127 os << std::endl << "# Server: " << g_settings->get("motd");
3132 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3134 std::set<std::string> privs;
3135 m_script->getAuth(name, NULL, &privs);
3139 bool Server::checkPriv(const std::string &name, const std::string &priv)
3141 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3142 return (privs.count(priv) != 0);
3145 void Server::reportPrivsModified(const std::string &name)
3148 std::vector<session_t> clients = m_clients.getClientIDs();
3149 for (const session_t client_id : clients) {
3150 RemotePlayer *player = m_env->getPlayer(client_id);
3151 reportPrivsModified(player->getName());
3154 RemotePlayer *player = m_env->getPlayer(name.c_str());
3157 SendPlayerPrivileges(player->getPeerId());
3158 PlayerSAO *sao = player->getPlayerSAO();
3161 sao->updatePrivileges(
3162 getPlayerEffectivePrivs(name),
3167 void Server::reportInventoryFormspecModified(const std::string &name)
3169 RemotePlayer *player = m_env->getPlayer(name.c_str());
3172 SendPlayerInventoryFormspec(player->getPeerId());
3175 void Server::reportFormspecPrependModified(const std::string &name)
3177 RemotePlayer *player = m_env->getPlayer(name.c_str());
3180 SendPlayerFormspecPrepend(player->getPeerId());
3183 void Server::setIpBanned(const std::string &ip, const std::string &name)
3185 m_banmanager->add(ip, name);
3188 void Server::unsetIpBanned(const std::string &ip_or_name)
3190 m_banmanager->remove(ip_or_name);
3193 std::string Server::getBanDescription(const std::string &ip_or_name)
3195 return m_banmanager->getBanDescription(ip_or_name);
3198 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3200 // m_env will be NULL if the server is initializing
3204 if (m_admin_nick == name && !m_admin_nick.empty()) {
3205 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3208 RemotePlayer *player = m_env->getPlayer(name);
3213 if (player->getPeerId() == PEER_ID_INEXISTENT)
3216 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3219 bool Server::showFormspec(const char *playername, const std::string &formspec,
3220 const std::string &formname)
3222 // m_env will be NULL if the server is initializing
3226 RemotePlayer *player = m_env->getPlayer(playername);
3230 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3234 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3239 u32 id = player->addHud(form);
3241 SendHUDAdd(player->getPeerId(), id, form);
3246 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3250 HudElement* todel = player->removeHud(id);
3257 SendHUDRemove(player->getPeerId(), id);
3261 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3266 SendHUDChange(player->getPeerId(), id, stat, data);
3270 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3275 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3276 if (new_hud_flags == player->hud_flags) // no change
3279 SendHUDSetFlags(player->getPeerId(), flags, mask);
3280 player->hud_flags = new_hud_flags;
3282 PlayerSAO* playersao = player->getPlayerSAO();
3287 m_script->player_event(playersao, "hud_changed");
3291 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3296 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3299 player->setHotbarItemcount(hotbar_itemcount);
3300 std::ostringstream os(std::ios::binary);
3301 writeS32(os, hotbar_itemcount);
3302 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3306 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3311 player->setHotbarImage(name);
3312 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3315 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3320 player->setHotbarSelectedImage(name);
3321 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3324 Address Server::getPeerAddress(session_t peer_id)
3326 // Note that this is only set after Init was received in Server::handleCommand_Init
3327 return getClient(peer_id, CS_Invalid)->getAddress();
3330 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3331 v2s32 animation_frames[4], f32 frame_speed)
3333 sanity_check(player);
3334 player->setLocalAnimations(animation_frames, frame_speed);
3335 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3338 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3340 sanity_check(player);
3341 player->eye_offset_first = first;
3342 player->eye_offset_third = third;
3343 SendEyeOffset(player->getPeerId(), first, third);
3346 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3348 sanity_check(player);
3349 player->setSky(params);
3350 SendSetSky(player->getPeerId(), params);
3353 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3355 sanity_check(player);
3356 player->setSun(params);
3357 SendSetSun(player->getPeerId(), params);
3360 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3362 sanity_check(player);
3363 player->setMoon(params);
3364 SendSetMoon(player->getPeerId(), params);
3367 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3369 sanity_check(player);
3370 player->setStars(params);
3371 SendSetStars(player->getPeerId(), params);
3374 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3376 sanity_check(player);
3377 player->setCloudParams(params);
3378 SendCloudParams(player->getPeerId(), params);
3381 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3384 sanity_check(player);
3385 player->overrideDayNightRatio(do_override, ratio);
3386 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3389 void Server::notifyPlayers(const std::wstring &msg)
3391 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3394 void Server::spawnParticle(const std::string &playername,
3395 const ParticleParameters &p)
3397 // m_env will be NULL if the server is initializing
3401 session_t peer_id = PEER_ID_INEXISTENT;
3403 if (!playername.empty()) {
3404 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3407 peer_id = player->getPeerId();
3408 proto_ver = player->protocol_version;
3411 SendSpawnParticle(peer_id, proto_ver, p);
3414 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3415 ServerActiveObject *attached, const std::string &playername)
3417 // m_env will be NULL if the server is initializing
3421 session_t peer_id = PEER_ID_INEXISTENT;
3423 if (!playername.empty()) {
3424 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3427 peer_id = player->getPeerId();
3428 proto_ver = player->protocol_version;
3431 u16 attached_id = attached ? attached->getId() : 0;
3434 if (attached_id == 0)
3435 id = m_env->addParticleSpawner(p.time);
3437 id = m_env->addParticleSpawner(p.time, attached_id);
3439 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3443 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3445 // m_env will be NULL if the server is initializing
3447 throw ServerError("Can't delete particle spawners during initialisation!");
3449 session_t peer_id = PEER_ID_INEXISTENT;
3450 if (!playername.empty()) {
3451 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3454 peer_id = player->getPeerId();
3457 m_env->deleteParticleSpawner(id);
3458 SendDeleteParticleSpawner(peer_id, id);
3461 bool Server::dynamicAddMedia(std::string filepath,
3462 const u32 token, const std::string &to_player, bool ephemeral)
3464 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3465 auto it = m_media.find(filename);
3466 if (it != m_media.end()) {
3467 // Allow the same path to be "added" again in certain conditions
3468 if (ephemeral || it->second.path != filepath) {
3469 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3470 << "\" already exists in media cache" << std::endl;
3475 // Load the file and add it to our media cache
3476 std::string filedata, raw_hash;
3477 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3482 // Create a copy of the file and swap out the path, this removes the
3483 // requirement that mods keep the file accessible at the original path.
3484 filepath = fs::CreateTempFile();
3485 bool ok = ([&] () -> bool {
3486 if (filepath.empty())
3488 std::ofstream os(filepath.c_str(), std::ios::binary);
3496 errorstream << "Server: failed to create a copy of media file "
3497 << "\"" << filename << "\"" << std::endl;
3498 m_media.erase(filename);
3501 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3502 << filepath << std::endl;
3504 m_media[filename].path = filepath;
3505 m_media[filename].no_announce = true;
3506 // stepPendingDynMediaCallbacks will clean this up later.
3507 } else if (!to_player.empty()) {
3508 m_media[filename].no_announce = true;
3511 // Push file to existing clients
3512 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3513 pkt << raw_hash << filename << (bool)ephemeral;
3515 NetworkPacket legacy_pkt = pkt;
3517 // Newer clients get asked to fetch the file (asynchronous)
3519 // Older clients have an awful hack that just throws the data at them
3520 legacy_pkt.putLongString(filedata);
3522 std::unordered_set<session_t> delivered, waiting;
3524 ClientInterface::AutoLock clientlock(m_clients);
3525 for (auto &pair : m_clients.getClientList()) {
3526 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3528 If a client is in the DefinitionsSent state it is too late to
3529 transfer the file via sendMediaAnnouncement() but at the same
3530 time the client cannot accept a media push yet.
3531 Short of artificially delaying the joining process there is no
3532 way for the server to resolve this so we (currently) opt not to.
3534 warningstream << "The media \"" << filename << "\" (dynamic) could "
3535 "not be delivered to " << pair.second->getName()
3536 << " due to a race condition." << std::endl;
3539 if (pair.second->getState() < CS_Active)
3542 const auto proto_ver = pair.second->net_proto_version;
3546 const session_t peer_id = pair.second->peer_id;
3547 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3550 if (proto_ver < 40) {
3551 delivered.emplace(peer_id);
3553 The network layer only guarantees ordered delivery inside a channel.
3554 Since the very next packet could be one that uses the media, we have
3555 to push the media over ALL channels to ensure it is processed before
3556 it is used. In practice this means channels 1 and 0.
3558 m_clients.send(peer_id, 1, &legacy_pkt, true);
3559 m_clients.send(peer_id, 0, &legacy_pkt, true);
3561 waiting.emplace(peer_id);
3562 Send(peer_id, &pkt);
3567 // Run callback for players that already had the file delivered (legacy-only)
3568 for (session_t peer_id : delivered) {
3569 if (auto player = m_env->getPlayer(peer_id))
3570 getScriptIface()->on_dynamic_media_added(token, player->getName());
3573 // Save all others in our pending state
3574 auto &state = m_pending_dyn_media[token];
3575 state.waiting_players = std::move(waiting);
3576 // regardless of success throw away the callback after a while
3577 state.expiry_timer = 60.0f;
3579 state.filename = filename;
3584 // actions: time-reversed list
3585 // Return value: success/failure
3586 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3587 std::list<std::string> *log)
3589 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3590 ServerMap *map = (ServerMap*)(&m_env->getMap());
3592 // Fail if no actions to handle
3593 if (actions.empty()) {
3595 log->push_back("Nothing to do.");
3602 for (const RollbackAction &action : actions) {
3604 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3607 std::ostringstream os;
3608 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3609 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3611 log->push_back(os.str());
3613 std::ostringstream os;
3614 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3615 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3617 log->push_back(os.str());
3621 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3622 <<" failed"<<std::endl;
3624 // Call it done if less than half failed
3625 return num_failed <= num_tried/2;
3628 // IGameDef interface
3630 IItemDefManager *Server::getItemDefManager()
3635 const NodeDefManager *Server::getNodeDefManager()
3640 ICraftDefManager *Server::getCraftDefManager()
3645 u16 Server::allocateUnknownNodeId(const std::string &name)
3647 return m_nodedef->allocateDummy(name);
3650 IWritableItemDefManager *Server::getWritableItemDefManager()
3655 NodeDefManager *Server::getWritableNodeDefManager()
3660 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3665 const std::vector<ModSpec> & Server::getMods() const
3667 return m_modmgr->getMods();
3670 const ModSpec *Server::getModSpec(const std::string &modname) const
3672 return m_modmgr->getModSpec(modname);
3675 void Server::getModNames(std::vector<std::string> &modlist)
3677 m_modmgr->getModNames(modlist);
3680 std::string Server::getBuiltinLuaPath()
3682 return porting::path_share + DIR_DELIM + "builtin";
3685 v3f Server::findSpawnPos()
3687 ServerMap &map = m_env->getServerMap();
3689 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3690 return nodeposf * BS;
3692 bool is_good = false;
3693 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3694 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3696 // Try to find a good place a few times
3697 for (s32 i = 0; i < 4000 && !is_good; i++) {
3698 s32 range = MYMIN(1 + i, range_max);
3699 // We're going to try to throw the player to this position
3700 v2s16 nodepos2d = v2s16(
3701 -range + (myrand() % (range * 2)),
3702 -range + (myrand() % (range * 2)));
3703 // Get spawn level at point
3704 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3705 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3706 // signify an unsuitable spawn position, or if outside limits.
3707 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3708 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3711 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3712 // Consecutive empty nodes
3715 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3716 // avoid obstructions in already-generated mapblocks.
3717 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3718 // no obstructions, but mapgen decorations are generated after spawn so
3719 // the player may end up inside one.
3720 for (s32 i = 0; i < 8; i++) {
3721 v3s16 blockpos = getNodeBlockPos(nodepos);
3722 map.emergeBlock(blockpos, true);
3723 content_t c = map.getNode(nodepos).getContent();
3725 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3726 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3727 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3729 if (air_count >= 2) {
3730 // Spawn in lower empty node
3732 nodeposf = intToFloat(nodepos, BS);
3733 // Don't spawn the player outside map boundaries
3734 if (objectpos_over_limit(nodeposf))
3735 // Exit this loop, positions above are probably over limit
3738 // Good position found, cause an exit from main loop
3752 // No suitable spawn point found, return fallback 0,0,0
3753 return v3f(0.0f, 0.0f, 0.0f);
3756 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3758 if (delay == 0.0f) {
3759 // No delay, shutdown immediately
3760 m_shutdown_state.is_requested = true;
3761 // only print to the infostream, a chat message saying
3762 // "Server Shutting Down" is sent when the server destructs.
3763 infostream << "*** Immediate Server shutdown requested." << std::endl;
3764 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3765 // Negative delay, cancel shutdown if requested
3766 m_shutdown_state.reset();
3767 std::wstringstream ws;
3769 ws << L"*** Server shutdown canceled.";
3771 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3772 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3773 // m_shutdown_* are already handled, skip.
3775 } else if (delay > 0.0f) {
3776 // Positive delay, tell the clients when the server will shut down
3777 std::wstringstream ws;
3779 ws << L"*** Server shutting down in "
3780 << duration_to_string(myround(delay)).c_str()
3783 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3784 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3787 m_shutdown_state.trigger(delay, msg, reconnect);
3790 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3793 Try to get an existing player
3795 RemotePlayer *player = m_env->getPlayer(name);
3797 // If player is already connected, cancel
3798 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3799 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3804 If player with the wanted peer_id already exists, cancel.
3806 if (m_env->getPlayer(peer_id)) {
3807 infostream<<"emergePlayer(): Player with wrong name but same"
3808 " peer_id already exists"<<std::endl;
3813 player = new RemotePlayer(name, idef());
3816 bool newplayer = false;
3819 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3821 // Complete init with server parts
3822 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3823 player->protocol_version = proto_version;
3827 m_script->on_newplayer(playersao);
3833 bool Server::registerModStorage(ModMetadata *storage)
3835 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3836 errorstream << "Unable to register same mod storage twice. Storage name: "
3837 << storage->getModName() << std::endl;
3841 m_mod_storages[storage->getModName()] = storage;
3845 void Server::unregisterModStorage(const std::string &name)
3847 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3848 if (it != m_mod_storages.end())
3849 m_mod_storages.erase(name);
3852 void dedicated_server_loop(Server &server, bool &kill)
3854 verbosestream<<"dedicated_server_loop()"<<std::endl;
3856 IntervalLimiter m_profiler_interval;
3858 static thread_local const float steplen =
3859 g_settings->getFloat("dedicated_server_step");
3860 static thread_local const float profiler_print_interval =
3861 g_settings->getFloat("profiler_print_interval");
3864 * The dedicated server loop only does time-keeping (in Server::step) and
3865 * provides a way to main.cpp to kill the server externally (bool &kill).
3869 // This is kind of a hack but can be done like this
3870 // because server.step() is very light
3871 sleep_ms((int)(steplen*1000.0));
3872 server.step(steplen);
3874 if (server.isShutdownRequested() || kill)
3880 if (profiler_print_interval != 0) {
3881 if(m_profiler_interval.step(steplen, profiler_print_interval))
3883 infostream<<"Profiler:"<<std::endl;
3884 g_profiler->print(infostream);
3885 g_profiler->clear();
3890 infostream << "Dedicated server quitting" << std::endl;
3892 if (g_settings->getBool("server_announce"))
3893 ServerList::sendAnnounce(ServerList::AA_DELETE,
3894 server.m_bind_addr.getPort());
3903 bool Server::joinModChannel(const std::string &channel)
3905 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3906 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3909 bool Server::leaveModChannel(const std::string &channel)
3911 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3914 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3916 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3919 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3923 ModChannel* Server::getModChannel(const std::string &channel)
3925 return m_modchannel_mgr->getModChannel(channel);
3928 void Server::broadcastModChannelMessage(const std::string &channel,
3929 const std::string &message, session_t from_peer)
3931 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3935 if (message.size() > STRING_MAX_LEN) {
3936 warningstream << "ModChannel message too long, dropping before sending "
3937 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3938 << channel << ")" << std::endl;
3943 if (from_peer != PEER_ID_SERVER) {
3944 sender = getPlayerName(from_peer);
3947 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3948 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3949 resp_pkt << channel << sender << message;
3950 for (session_t peer_id : peers) {
3952 if (peer_id == from_peer)
3955 Send(peer_id, &resp_pkt);
3958 if (from_peer != PEER_ID_SERVER) {
3959 m_script->on_modchannel_message(channel, sender, message);
3963 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3965 if (lang_code.empty())
3968 auto it = server_translations.find(lang_code);
3969 if (it != server_translations.end())
3970 return &it->second; // Already loaded
3972 // [] will create an entry
3973 auto *translations = &server_translations[lang_code];
3975 std::string suffix = "." + lang_code + ".tr";
3976 for (const auto &i : m_media) {
3977 if (str_ends_with(i.first, suffix)) {
3979 if (fs::ReadFile(i.second.path, data)) {
3980 translations->loadTranslation(data);
3985 return translations;
3988 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
3990 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
3992 if (!world_mt.readConfigFile(world_mt_path.c_str()))
3993 throw BaseException("Cannot read world.mt!");
3995 std::string backend = world_mt.exists("mod_storage_backend") ?
3996 world_mt.get("mod_storage_backend") : "files";
3997 if (backend == "files")
3998 warningstream << "/!\\ You are using the old mod storage files backend. "
3999 << "This backend is deprecated and may be removed in a future release /!\\"
4000 << std::endl << "Switching to SQLite3 is advised, "
4001 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4003 return openModStorageDatabase(backend, world_path, world_mt);
4006 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
4007 const std::string &world_path, const Settings &world_mt)
4009 if (backend == "sqlite3")
4010 return new ModMetadataDatabaseSQLite3(world_path);
4012 if (backend == "files")
4013 return new ModMetadataDatabaseFiles(world_path);
4015 if (backend == "dummy")
4016 return new Database_Dummy();
4018 throw BaseException("Mod storage database backend " + backend + " not supported");
4021 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4023 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4025 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4026 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4027 errorstream << "Cannot read world.mt!" << std::endl;
4031 std::string backend = world_mt.exists("mod_storage_backend") ?
4032 world_mt.get("mod_storage_backend") : "files";
4033 if (backend == migrate_to) {
4034 errorstream << "Cannot migrate: new backend is same"
4035 << " as the old one" << std::endl;
4039 ModMetadataDatabase *srcdb = nullptr;
4040 ModMetadataDatabase *dstdb = nullptr;
4042 bool succeeded = false;
4045 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4046 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4050 std::vector<std::string> mod_list;
4051 srcdb->listMods(&mod_list);
4052 for (const std::string &modname : mod_list) {
4054 srcdb->getModEntries(modname, &meta);
4055 for (const auto &pair : meta) {
4056 dstdb->setModEntry(modname, pair.first, pair.second);
4064 actionstream << "Successfully migrated the metadata of "
4065 << mod_list.size() << " mods" << std::endl;
4066 world_mt.set("mod_storage_backend", migrate_to);
4067 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4068 errorstream << "Failed to update world.mt!" << std::endl;
4070 actionstream << "world.mt updated" << std::endl;
4072 } catch (BaseException &e) {
4073 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4079 if (succeeded && backend == "files") {
4081 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4082 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4083 if (!fs::Rename(storage_path, backup_path))
4084 warningstream << "After migration, " << storage_path
4085 << " could not be renamed to " << backup_path << std::endl;