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::SendSetLighting(session_t peer_id, const Lighting &lighting)
1800 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1803 pkt << lighting.shadow_intensity;
1808 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1810 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1811 pkt << time << time_speed;
1813 if (peer_id == PEER_ID_INEXISTENT) {
1814 m_clients.sendToAll(&pkt);
1821 void Server::SendPlayerBreath(PlayerSAO *sao)
1825 m_script->player_event(sao, "breath_changed");
1826 SendBreath(sao->getPeerID(), sao->getBreath());
1829 void Server::SendMovePlayer(session_t peer_id)
1831 RemotePlayer *player = m_env->getPlayer(peer_id);
1833 PlayerSAO *sao = player->getPlayerSAO();
1836 // Send attachment updates instantly to the client prior updating position
1837 sao->sendOutdatedData();
1839 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1840 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1843 v3f pos = sao->getBasePosition();
1844 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1845 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1846 << " pitch=" << sao->getLookPitch()
1847 << " yaw=" << sao->getRotation().Y
1854 void Server::SendPlayerFov(session_t peer_id)
1856 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1858 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1859 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1864 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1865 f32 animation_speed)
1867 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1870 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1871 << animation_frames[3] << animation_speed;
1876 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1878 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1879 pkt << first << third;
1883 void Server::SendPlayerPrivileges(session_t peer_id)
1885 RemotePlayer *player = m_env->getPlayer(peer_id);
1887 if(player->getPeerId() == PEER_ID_INEXISTENT)
1890 std::set<std::string> privs;
1891 m_script->getAuth(player->getName(), NULL, &privs);
1893 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1894 pkt << (u16) privs.size();
1896 for (const std::string &priv : privs) {
1903 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1905 RemotePlayer *player = m_env->getPlayer(peer_id);
1907 if (player->getPeerId() == PEER_ID_INEXISTENT)
1910 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1911 pkt.putLongString(player->inventory_formspec);
1916 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1918 RemotePlayer *player = m_env->getPlayer(peer_id);
1920 if (player->getPeerId() == PEER_ID_INEXISTENT)
1923 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1924 pkt << player->formspec_prepend;
1928 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1930 // Radius inside which objects are active
1931 static thread_local const s16 radius =
1932 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1934 // Radius inside which players are active
1935 static thread_local const bool is_transfer_limited =
1936 g_settings->exists("unlimited_player_transfer_distance") &&
1937 !g_settings->getBool("unlimited_player_transfer_distance");
1939 static thread_local const s16 player_transfer_dist =
1940 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
1942 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
1943 radius : player_transfer_dist;
1945 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
1949 std::queue<u16> removed_objects, added_objects;
1950 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
1951 client->m_known_objects, removed_objects);
1952 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
1953 client->m_known_objects, added_objects);
1955 int removed_count = removed_objects.size();
1956 int added_count = added_objects.size();
1958 if (removed_objects.empty() && added_objects.empty())
1964 // Handle removed objects
1965 writeU16((u8*)buf, removed_objects.size());
1966 data.append(buf, 2);
1967 while (!removed_objects.empty()) {
1969 u16 id = removed_objects.front();
1970 ServerActiveObject* obj = m_env->getActiveObject(id);
1972 // Add to data buffer for sending
1973 writeU16((u8*)buf, id);
1974 data.append(buf, 2);
1976 // Remove from known objects
1977 client->m_known_objects.erase(id);
1979 if (obj && obj->m_known_by_count > 0)
1980 obj->m_known_by_count--;
1982 removed_objects.pop();
1985 // Handle added objects
1986 writeU16((u8*)buf, added_objects.size());
1987 data.append(buf, 2);
1988 while (!added_objects.empty()) {
1990 u16 id = added_objects.front();
1991 ServerActiveObject *obj = m_env->getActiveObject(id);
1992 added_objects.pop();
1995 warningstream << FUNCTION_NAME << ": NULL object id="
1996 << (int)id << std::endl;
2001 u8 type = obj->getSendType();
2003 // Add to data buffer for sending
2004 writeU16((u8*)buf, id);
2005 data.append(buf, 2);
2006 writeU8((u8*)buf, type);
2007 data.append(buf, 1);
2009 data.append(serializeString32(
2010 obj->getClientInitializationData(client->net_proto_version)));
2012 // Add to known objects
2013 client->m_known_objects.insert(id);
2015 obj->m_known_by_count++;
2018 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2019 pkt.putRawString(data.c_str(), data.size());
2022 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2023 << removed_count << " removed, " << added_count << " added, "
2024 << "packet size is " << pkt.getSize() << std::endl;
2027 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2030 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2031 datas.size(), peer_id);
2033 pkt.putRawString(datas.c_str(), datas.size());
2035 m_clients.send(pkt.getPeerId(),
2036 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2040 void Server::SendCSMRestrictionFlags(session_t peer_id)
2042 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2043 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2044 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2048 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2050 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2055 inline s32 Server::nextSoundId()
2057 s32 ret = m_next_sound_id;
2058 if (m_next_sound_id == INT32_MAX)
2059 m_next_sound_id = 0; // signed overflow is undefined
2065 s32 Server::playSound(const SimpleSoundSpec &spec,
2066 const ServerSoundParams ¶ms, bool ephemeral)
2068 // Find out initial position of sound
2069 bool pos_exists = false;
2070 v3f pos = params.getPos(m_env, &pos_exists);
2071 // If position is not found while it should be, cancel sound
2072 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2075 // Filter destination clients
2076 std::vector<session_t> dst_clients;
2077 if (!params.to_player.empty()) {
2078 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2080 infostream<<"Server::playSound: Player \""<<params.to_player
2081 <<"\" not found"<<std::endl;
2084 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2085 infostream<<"Server::playSound: Player \""<<params.to_player
2086 <<"\" not connected"<<std::endl;
2089 dst_clients.push_back(player->getPeerId());
2091 std::vector<session_t> clients = m_clients.getClientIDs();
2093 for (const session_t client_id : clients) {
2094 RemotePlayer *player = m_env->getPlayer(client_id);
2097 if (!params.exclude_player.empty() &&
2098 params.exclude_player == player->getName())
2101 PlayerSAO *sao = player->getPlayerSAO();
2106 if(sao->getBasePosition().getDistanceFrom(pos) >
2107 params.max_hear_distance)
2110 dst_clients.push_back(client_id);
2114 if(dst_clients.empty())
2119 ServerPlayingSound *psound = nullptr;
2121 id = -1; // old clients will still use this, so pick a reserved ID
2124 // The sound will exist as a reference in m_playing_sounds
2125 m_playing_sounds[id] = ServerPlayingSound();
2126 psound = &m_playing_sounds[id];
2127 psound->params = params;
2128 psound->spec = spec;
2131 float gain = params.gain * spec.gain;
2132 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2133 pkt << id << spec.name << gain
2134 << (u8) params.type << pos << params.object
2135 << params.loop << params.fade << params.pitch
2138 bool as_reliable = !ephemeral;
2140 for (const u16 dst_client : dst_clients) {
2142 psound->clients.insert(dst_client);
2143 m_clients.send(dst_client, 0, &pkt, as_reliable);
2147 void Server::stopSound(s32 handle)
2149 // Get sound reference
2150 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2151 m_playing_sounds.find(handle);
2152 if (i == m_playing_sounds.end())
2154 ServerPlayingSound &psound = i->second;
2156 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2159 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2160 si != psound.clients.end(); ++si) {
2162 m_clients.send(*si, 0, &pkt, true);
2164 // Remove sound reference
2165 m_playing_sounds.erase(i);
2168 void Server::fadeSound(s32 handle, float step, float gain)
2170 // Get sound reference
2171 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2172 m_playing_sounds.find(handle);
2173 if (i == m_playing_sounds.end())
2176 ServerPlayingSound &psound = i->second;
2177 psound.params.gain = gain;
2179 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2180 pkt << handle << step << gain;
2182 // Backwards compability
2183 bool play_sound = gain > 0;
2184 ServerPlayingSound compat_psound = psound;
2185 compat_psound.clients.clear();
2187 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2188 compat_pkt << handle;
2190 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2191 it != psound.clients.end();) {
2192 if (m_clients.getProtocolVersion(*it) >= 32) {
2194 m_clients.send(*it, 0, &pkt, true);
2197 compat_psound.clients.insert(*it);
2199 m_clients.send(*it, 0, &compat_pkt, true);
2200 psound.clients.erase(it++);
2204 // Remove sound reference
2205 if (!play_sound || psound.clients.empty())
2206 m_playing_sounds.erase(i);
2208 if (play_sound && !compat_psound.clients.empty()) {
2209 // Play new sound volume on older clients
2210 playSound(compat_psound.spec, compat_psound.params);
2214 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2217 float maxd = far_d_nodes * BS;
2218 v3f p_f = intToFloat(p, BS);
2219 v3s16 block_pos = getNodeBlockPos(p);
2221 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2224 std::vector<session_t> clients = m_clients.getClientIDs();
2225 ClientInterface::AutoLock clientlock(m_clients);
2227 for (session_t client_id : clients) {
2228 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2232 RemotePlayer *player = m_env->getPlayer(client_id);
2233 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2235 // If player is far away, only set modified blocks not sent
2236 if (!client->isBlockSent(block_pos) || (sao &&
2237 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2239 far_players->emplace(client_id);
2241 client->SetBlockNotSent(block_pos);
2246 m_clients.send(client_id, 0, &pkt, true);
2250 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2251 float far_d_nodes, bool remove_metadata)
2253 float maxd = far_d_nodes * BS;
2254 v3f p_f = intToFloat(p, BS);
2255 v3s16 block_pos = getNodeBlockPos(p);
2257 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2258 pkt << p << n.param0 << n.param1 << n.param2
2259 << (u8) (remove_metadata ? 0 : 1);
2261 std::vector<session_t> clients = m_clients.getClientIDs();
2262 ClientInterface::AutoLock clientlock(m_clients);
2264 for (session_t client_id : clients) {
2265 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2269 RemotePlayer *player = m_env->getPlayer(client_id);
2270 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2272 // If player is far away, only set modified blocks not sent
2273 if (!client->isBlockSent(block_pos) || (sao &&
2274 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2276 far_players->emplace(client_id);
2278 client->SetBlockNotSent(block_pos);
2283 m_clients.send(client_id, 0, &pkt, true);
2287 void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes)
2289 float maxd = far_d_nodes * BS;
2290 NodeMetadataList meta_updates_list(false);
2291 std::vector<session_t> clients = m_clients.getClientIDs();
2293 ClientInterface::AutoLock clientlock(m_clients);
2295 for (session_t i : clients) {
2296 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2300 ServerActiveObject *player = m_env->getActiveObject(i);
2301 v3f player_pos = player ? player->getBasePosition() : v3f();
2303 for (const v3s16 &pos : meta_updates) {
2304 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2309 v3s16 block_pos = getNodeBlockPos(pos);
2310 if (!client->isBlockSent(block_pos) || (player &&
2311 player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) {
2312 client->SetBlockNotSent(block_pos);
2316 // Add the change to send list
2317 meta_updates_list.set(pos, meta);
2319 if (meta_updates_list.size() == 0)
2322 // Send the meta changes
2323 std::ostringstream os(std::ios::binary);
2324 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2325 std::ostringstream oss(std::ios::binary);
2326 compressZlib(os.str(), oss);
2328 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0);
2329 pkt.putLongString(oss.str());
2330 m_clients.send(i, 0, &pkt, true);
2332 meta_updates_list.clear();
2336 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2337 u16 net_proto_version)
2340 Create a packet with the block in the right format
2342 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2343 std::ostringstream os(std::ios_base::binary);
2344 block->serialize(os, ver, false, net_compression_level);
2345 block->serializeNetworkSpecific(os);
2346 std::string s = os.str();
2348 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + s.size(), peer_id);
2350 pkt << block->getPos();
2351 pkt.putRawString(s.c_str(), s.size());
2355 void Server::SendBlocks(float dtime)
2357 MutexAutoLock envlock(m_env_mutex);
2358 //TODO check if one big lock could be faster then multiple small ones
2360 std::vector<PrioritySortedBlockTransfer> queue;
2362 u32 total_sending = 0;
2365 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2367 std::vector<session_t> clients = m_clients.getClientIDs();
2369 ClientInterface::AutoLock clientlock(m_clients);
2370 for (const session_t client_id : clients) {
2371 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2376 total_sending += client->getSendingCount();
2377 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2382 // Lowest priority number comes first.
2383 // Lowest is most important.
2384 std::sort(queue.begin(), queue.end());
2386 ClientInterface::AutoLock clientlock(m_clients);
2388 // Maximal total count calculation
2389 // The per-client block sends is halved with the maximal online users
2390 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2391 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2393 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2394 Map &map = m_env->getMap();
2396 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2397 if (total_sending >= max_blocks_to_send)
2400 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2404 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2409 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2410 client->net_proto_version);
2412 client->SentBlock(block_to_send.pos);
2417 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2419 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2423 ClientInterface::AutoLock clientlock(m_clients);
2424 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2425 if (!client || client->isBlockSent(blockpos))
2427 SendBlockNoLock(peer_id, block, client->serialization_version,
2428 client->net_proto_version);
2433 bool Server::addMediaFile(const std::string &filename,
2434 const std::string &filepath, std::string *filedata_to,
2435 std::string *digest_to)
2437 // If name contains illegal characters, ignore the file
2438 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2439 infostream << "Server: ignoring illegal file name: \""
2440 << filename << "\"" << std::endl;
2443 // If name is not in a supported format, ignore it
2444 const char *supported_ext[] = {
2445 ".png", ".jpg", ".bmp", ".tga",
2447 ".x", ".b3d", ".obj",
2448 // Custom translation file format
2452 if (removeStringEnd(filename, supported_ext).empty()) {
2453 infostream << "Server: ignoring unsupported file extension: \""
2454 << filename << "\"" << std::endl;
2457 // Ok, attempt to load the file and add to cache
2460 std::string filedata;
2461 if (!fs::ReadFile(filepath, filedata)) {
2462 errorstream << "Server::addMediaFile(): Failed to open \""
2463 << filename << "\" for reading" << std::endl;
2467 if (filedata.empty()) {
2468 errorstream << "Server::addMediaFile(): Empty file \""
2469 << filepath << "\"" << std::endl;
2474 sha1.addBytes(filedata.c_str(), filedata.length());
2476 unsigned char *digest = sha1.getDigest();
2477 std::string sha1_base64 = base64_encode(digest, 20);
2478 std::string sha1_hex = hex_encode((char*) digest, 20);
2480 *digest_to = std::string((char*) digest, 20);
2484 m_media[filename] = MediaInfo(filepath, sha1_base64);
2485 verbosestream << "Server: " << sha1_hex << " is " << filename
2489 *filedata_to = std::move(filedata);
2493 void Server::fillMediaCache()
2495 infostream << "Server: Calculating media file checksums" << std::endl;
2497 // Collect all media file paths
2498 std::vector<std::string> paths;
2500 // ordered in descending priority
2501 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2502 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2503 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2504 m_modmgr->getModsMediaPaths(paths);
2506 // Collect media file information from paths into cache
2507 for (const std::string &mediapath : paths) {
2508 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2509 for (const fs::DirListNode &dln : dirlist) {
2510 if (dln.dir) // Ignore dirs (already in paths)
2513 const std::string &filename = dln.name;
2514 if (m_media.find(filename) != m_media.end()) // Do not override
2517 std::string filepath = mediapath;
2518 filepath.append(DIR_DELIM).append(filename);
2519 addMediaFile(filename, filepath);
2523 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2526 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2529 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2532 std::string lang_suffix;
2533 lang_suffix.append(".").append(lang_code).append(".tr");
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))
2544 for (const auto &i : m_media) {
2545 if (i.second.no_announce)
2547 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2549 pkt << i.first << i.second.sha1_digest;
2552 pkt << g_settings->get("remote_media");
2555 verbosestream << "Server: Announcing files to id(" << peer_id
2556 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2559 struct SendableMedia
2565 SendableMedia(const std::string &name, const std::string &path,
2566 std::string &&data):
2567 name(name), path(path), data(std::move(data))
2571 void Server::sendRequestedMedia(session_t peer_id,
2572 const std::vector<std::string> &tosend)
2574 verbosestream<<"Server::sendRequestedMedia(): "
2575 <<"Sending files to client"<<std::endl;
2579 // Put 5kB in one bunch (this is not accurate)
2580 u32 bytes_per_bunch = 5000;
2582 std::vector< std::vector<SendableMedia> > file_bunches;
2583 file_bunches.emplace_back();
2585 u32 file_size_bunch_total = 0;
2587 for (const std::string &name : tosend) {
2588 if (m_media.find(name) == m_media.end()) {
2589 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2590 <<"unknown file \""<<(name)<<"\""<<std::endl;
2594 const auto &m = m_media[name];
2598 if (!fs::ReadFile(m.path, data)) {
2599 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2600 << name << "\"" << std::endl;
2603 file_size_bunch_total += data.size();
2606 file_bunches.back().emplace_back(name, m.path, std::move(data));
2608 // Start next bunch if got enough data
2609 if(file_size_bunch_total >= bytes_per_bunch) {
2610 file_bunches.emplace_back();
2611 file_size_bunch_total = 0;
2616 /* Create and send packets */
2618 u16 num_bunches = file_bunches.size();
2619 for (u16 i = 0; i < num_bunches; i++) {
2622 u16 total number of texture bunches
2623 u16 index of this bunch
2624 u32 number of files in this bunch
2633 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2634 pkt << num_bunches << i << (u32) file_bunches[i].size();
2636 for (const SendableMedia &j : file_bunches[i]) {
2638 pkt.putLongString(j.data);
2641 verbosestream << "Server::sendRequestedMedia(): bunch "
2642 << i << "/" << num_bunches
2643 << " files=" << file_bunches[i].size()
2644 << " size=" << pkt.getSize() << std::endl;
2649 void Server::stepPendingDynMediaCallbacks(float dtime)
2651 MutexAutoLock lock(m_env_mutex);
2653 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2654 it->second.expiry_timer -= dtime;
2655 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2662 const auto &name = it->second.filename;
2663 if (!name.empty()) {
2664 assert(m_media.count(name));
2665 // if no_announce isn't set we're definitely deleting the wrong file!
2666 sanity_check(m_media[name].no_announce);
2668 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2669 m_media.erase(name);
2671 getScriptIface()->freeDynamicMediaCallback(it->first);
2672 it = m_pending_dyn_media.erase(it);
2676 void Server::SendMinimapModes(session_t peer_id,
2677 std::vector<MinimapMode> &modes, size_t wanted_mode)
2679 RemotePlayer *player = m_env->getPlayer(peer_id);
2681 if (player->getPeerId() == PEER_ID_INEXISTENT)
2684 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2685 pkt << (u16)modes.size() << (u16)wanted_mode;
2687 for (auto &mode : modes)
2688 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2693 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2695 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2699 pkt << false; // Remove inventory
2701 pkt << true; // Update inventory
2703 // Serialization & NetworkPacket isn't a love story
2704 std::ostringstream os(std::ios_base::binary);
2705 inventory->serialize(os);
2706 inventory->setModified(false);
2708 const std::string &os_str = os.str();
2709 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2710 pkt.putRawString(os_str);
2713 if (peer_id == PEER_ID_INEXISTENT)
2714 m_clients.sendToAll(&pkt);
2719 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2721 // Lookup player name, to filter detached inventories just after
2722 std::string peer_name;
2723 if (peer_id != PEER_ID_INEXISTENT) {
2724 peer_name = getClient(peer_id, CS_Created)->getName();
2727 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2728 sendDetachedInventory(inv, name, peer_id);
2731 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2738 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2740 infostream << "Server::DiePlayer(): Player "
2741 << playersao->getPlayer()->getName()
2742 << " dies" << std::endl;
2744 playersao->clearParentAttachment();
2746 // Trigger scripted stuff
2747 m_script->on_dieplayer(playersao, reason);
2749 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2752 void Server::RespawnPlayer(session_t peer_id)
2754 PlayerSAO *playersao = getPlayerSAO(peer_id);
2757 infostream << "Server::RespawnPlayer(): Player "
2758 << playersao->getPlayer()->getName()
2759 << " respawns" << std::endl;
2761 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2762 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2763 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2765 bool repositioned = m_script->on_respawnplayer(playersao);
2766 if (!repositioned) {
2767 // setPos will send the new position to client
2768 playersao->setPos(findSpawnPos());
2773 void Server::DenySudoAccess(session_t peer_id)
2775 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2780 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2781 const std::string &str_reason, bool reconnect)
2783 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2785 m_clients.event(peer_id, CSE_SetDenied);
2786 DisconnectPeer(peer_id);
2790 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2791 const std::string &custom_reason)
2793 SendAccessDenied(peer_id, reason, custom_reason);
2794 m_clients.event(peer_id, CSE_SetDenied);
2795 DisconnectPeer(peer_id);
2798 // 13/03/15: remove this function when protocol version 25 will become
2799 // the minimum version for MT users, maybe in 1 year
2800 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2802 SendAccessDenied_Legacy(peer_id, reason);
2803 m_clients.event(peer_id, CSE_SetDenied);
2804 DisconnectPeer(peer_id);
2807 void Server::DisconnectPeer(session_t peer_id)
2809 m_modchannel_mgr->leaveAllChannels(peer_id);
2810 m_con->DisconnectPeer(peer_id);
2813 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2816 RemoteClient* client = getClient(peer_id, CS_Invalid);
2818 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2820 // Right now, the auth mechs don't change between login and sudo mode.
2821 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2822 client->allowed_sudo_mechs = sudo_auth_mechs;
2824 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2825 << g_settings->getFloat("dedicated_server_step")
2829 m_clients.event(peer_id, CSE_AuthAccept);
2831 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2833 // We only support SRP right now
2834 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2836 resp_pkt << sudo_auth_mechs;
2838 m_clients.event(peer_id, CSE_SudoSuccess);
2842 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2844 std::wstring message;
2847 Clear references to playing sounds
2849 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2850 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2851 ServerPlayingSound &psound = i->second;
2852 psound.clients.erase(peer_id);
2853 if (psound.clients.empty())
2854 m_playing_sounds.erase(i++);
2859 // clear formspec info so the next client can't abuse the current state
2860 m_formspec_state_data.erase(peer_id);
2862 RemotePlayer *player = m_env->getPlayer(peer_id);
2864 /* Run scripts and remove from environment */
2866 PlayerSAO *playersao = player->getPlayerSAO();
2869 playersao->clearChildAttachments();
2870 playersao->clearParentAttachment();
2872 // inform connected clients
2873 const std::string &player_name = player->getName();
2874 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2875 // (u16) 1 + std::string represents a vector serialization representation
2876 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2877 m_clients.sendToAll(¬ice);
2879 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2881 playersao->disconnected();
2888 if (player && reason != CDR_DENY) {
2889 std::ostringstream os(std::ios_base::binary);
2890 std::vector<session_t> clients = m_clients.getClientIDs();
2892 for (const session_t client_id : clients) {
2894 RemotePlayer *player = m_env->getPlayer(client_id);
2898 // Get name of player
2899 os << player->getName() << " ";
2902 std::string name = player->getName();
2903 actionstream << name << " "
2904 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2905 << " List of players: " << os.str() << std::endl;
2907 m_admin_chat->outgoing_queue.push_back(
2908 new ChatEventNick(CET_NICK_REMOVE, name));
2912 MutexAutoLock env_lock(m_env_mutex);
2913 m_clients.DeleteClient(peer_id);
2917 // Send leave chat message to all remaining clients
2918 if (!message.empty()) {
2919 SendChatMessage(PEER_ID_INEXISTENT,
2920 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2924 void Server::UpdateCrafting(RemotePlayer *player)
2926 InventoryList *clist = player->inventory.getList("craft");
2927 if (!clist || clist->getSize() == 0)
2930 if (!clist->checkModified())
2933 // Get a preview for crafting
2935 InventoryLocation loc;
2936 loc.setPlayer(player->getName());
2937 std::vector<ItemStack> output_replacements;
2938 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2939 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2942 InventoryList *plist = player->inventory.getList("craftpreview");
2943 if (plist && plist->getSize() >= 1) {
2944 // Put the new preview in
2945 plist->changeItem(0, preview);
2949 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2951 if (evt->type == CET_NICK_ADD) {
2952 // The terminal informed us of its nick choice
2953 m_admin_nick = ((ChatEventNick *)evt)->nick;
2954 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2955 errorstream << "You haven't set up an account." << std::endl
2956 << "Please log in using the client as '"
2957 << m_admin_nick << "' with a secure password." << std::endl
2958 << "Until then, you can't execute admin tasks via the console," << std::endl
2959 << "and everybody can claim the user account instead of you," << std::endl
2960 << "giving them full control over this server." << std::endl;
2963 assert(evt->type == CET_CHAT);
2964 handleAdminChat((ChatEventChat *)evt);
2968 std::wstring Server::handleChat(const std::string &name,
2969 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2971 // If something goes wrong, this player is to blame
2972 RollbackScopeActor rollback_scope(m_rollback,
2973 std::string("player:") + name);
2975 if (g_settings->getBool("strip_color_codes"))
2976 wmessage = unescape_enriched(wmessage);
2979 switch (player->canSendChatMessage()) {
2980 case RPLAYER_CHATRESULT_FLOODING: {
2981 std::wstringstream ws;
2982 ws << L"You cannot send more messages. You are limited to "
2983 << g_settings->getFloat("chat_message_limit_per_10sec")
2984 << L" messages per 10 seconds.";
2987 case RPLAYER_CHATRESULT_KICK:
2988 DenyAccess_Legacy(player->getPeerId(),
2989 L"You have been kicked due to message flooding.");
2991 case RPLAYER_CHATRESULT_OK:
2994 FATAL_ERROR("Unhandled chat filtering result found.");
2998 if (m_max_chatmessage_length > 0
2999 && wmessage.length() > m_max_chatmessage_length) {
3000 return L"Your message exceed the maximum chat message limit set on the server. "
3001 L"It was refused. Send a shorter message";
3004 auto message = trim(wide_to_utf8(wmessage));
3005 if (message.empty())
3008 if (message.find_first_of("\n\r") != std::wstring::npos) {
3009 return L"Newlines are not permitted in chat messages";
3012 // Run script hook, exit if script ate the chat message
3013 if (m_script->on_chat_message(name, message))
3018 // Whether to send line to the player that sent the message, or to all players
3019 bool broadcast_line = true;
3021 if (check_shout_priv && !checkPriv(name, "shout")) {
3022 line += L"-!- You don't have permission to shout.";
3023 broadcast_line = false;
3026 Workaround for fixing chat on Android. Lua doesn't handle
3027 the Cyrillic alphabet and some characters on older Android devices
3030 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3032 line += utf8_to_wide(m_script->formatChatMessage(name,
3033 wide_to_utf8(wmessage)));
3038 Tell calling method to send the message to sender
3040 if (!broadcast_line)
3044 Send the message to others
3046 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3048 ChatMessage chatmsg(line);
3050 std::vector<session_t> clients = m_clients.getClientIDs();
3051 for (u16 cid : clients)
3052 SendChatMessage(cid, chatmsg);
3057 void Server::handleAdminChat(const ChatEventChat *evt)
3059 std::string name = evt->nick;
3060 std::wstring wmessage = evt->evt_msg;
3062 std::wstring answer = handleChat(name, wmessage);
3064 // If asked to send answer to sender
3065 if (!answer.empty()) {
3066 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3070 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3072 RemoteClient *client = getClientNoEx(peer_id,state_min);
3074 throw ClientNotFoundException("Client not found");
3078 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3080 return m_clients.getClientNoEx(peer_id, state_min);
3083 std::string Server::getPlayerName(session_t peer_id)
3085 RemotePlayer *player = m_env->getPlayer(peer_id);
3087 return "[id="+itos(peer_id)+"]";
3088 return player->getName();
3091 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3093 RemotePlayer *player = m_env->getPlayer(peer_id);
3096 return player->getPlayerSAO();
3099 std::string Server::getStatusString()
3101 std::ostringstream os(std::ios_base::binary);
3104 os << "version: " << g_version_string;
3106 os << " | game: " << (m_gamespec.name.empty() ? m_gamespec.id : m_gamespec.name);
3108 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3110 os << " | max lag: " << std::setprecision(3);
3111 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3113 // Information about clients
3115 os << " | clients: ";
3117 std::vector<session_t> clients = m_clients.getClientIDs();
3118 for (session_t client_id : clients) {
3119 RemotePlayer *player = m_env->getPlayer(client_id);
3121 // Get name of player
3122 const char *name = player ? player->getName() : "<unknown>";
3124 // Add name to information string
3133 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3134 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3136 if (!g_settings->get("motd").empty())
3137 os << std::endl << "# Server: " << g_settings->get("motd");
3142 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3144 std::set<std::string> privs;
3145 m_script->getAuth(name, NULL, &privs);
3149 bool Server::checkPriv(const std::string &name, const std::string &priv)
3151 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3152 return (privs.count(priv) != 0);
3155 void Server::reportPrivsModified(const std::string &name)
3158 std::vector<session_t> clients = m_clients.getClientIDs();
3159 for (const session_t client_id : clients) {
3160 RemotePlayer *player = m_env->getPlayer(client_id);
3161 reportPrivsModified(player->getName());
3164 RemotePlayer *player = m_env->getPlayer(name.c_str());
3167 SendPlayerPrivileges(player->getPeerId());
3168 PlayerSAO *sao = player->getPlayerSAO();
3171 sao->updatePrivileges(
3172 getPlayerEffectivePrivs(name),
3177 void Server::reportInventoryFormspecModified(const std::string &name)
3179 RemotePlayer *player = m_env->getPlayer(name.c_str());
3182 SendPlayerInventoryFormspec(player->getPeerId());
3185 void Server::reportFormspecPrependModified(const std::string &name)
3187 RemotePlayer *player = m_env->getPlayer(name.c_str());
3190 SendPlayerFormspecPrepend(player->getPeerId());
3193 void Server::setIpBanned(const std::string &ip, const std::string &name)
3195 m_banmanager->add(ip, name);
3198 void Server::unsetIpBanned(const std::string &ip_or_name)
3200 m_banmanager->remove(ip_or_name);
3203 std::string Server::getBanDescription(const std::string &ip_or_name)
3205 return m_banmanager->getBanDescription(ip_or_name);
3208 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3210 // m_env will be NULL if the server is initializing
3214 if (m_admin_nick == name && !m_admin_nick.empty()) {
3215 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3218 RemotePlayer *player = m_env->getPlayer(name);
3223 if (player->getPeerId() == PEER_ID_INEXISTENT)
3226 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3229 bool Server::showFormspec(const char *playername, const std::string &formspec,
3230 const std::string &formname)
3232 // m_env will be NULL if the server is initializing
3236 RemotePlayer *player = m_env->getPlayer(playername);
3240 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3244 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3249 u32 id = player->addHud(form);
3251 SendHUDAdd(player->getPeerId(), id, form);
3256 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3260 HudElement* todel = player->removeHud(id);
3267 SendHUDRemove(player->getPeerId(), id);
3271 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3276 SendHUDChange(player->getPeerId(), id, stat, data);
3280 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3285 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3286 if (new_hud_flags == player->hud_flags) // no change
3289 SendHUDSetFlags(player->getPeerId(), flags, mask);
3290 player->hud_flags = new_hud_flags;
3292 PlayerSAO* playersao = player->getPlayerSAO();
3297 m_script->player_event(playersao, "hud_changed");
3301 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3306 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3309 player->setHotbarItemcount(hotbar_itemcount);
3310 std::ostringstream os(std::ios::binary);
3311 writeS32(os, hotbar_itemcount);
3312 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3316 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3321 player->setHotbarImage(name);
3322 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3325 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3330 player->setHotbarSelectedImage(name);
3331 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3334 Address Server::getPeerAddress(session_t peer_id)
3336 // Note that this is only set after Init was received in Server::handleCommand_Init
3337 return getClient(peer_id, CS_Invalid)->getAddress();
3340 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3341 v2s32 animation_frames[4], f32 frame_speed)
3343 sanity_check(player);
3344 player->setLocalAnimations(animation_frames, frame_speed);
3345 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3348 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3350 sanity_check(player);
3351 player->eye_offset_first = first;
3352 player->eye_offset_third = third;
3353 SendEyeOffset(player->getPeerId(), first, third);
3356 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3358 sanity_check(player);
3359 player->setSky(params);
3360 SendSetSky(player->getPeerId(), params);
3363 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3365 sanity_check(player);
3366 player->setSun(params);
3367 SendSetSun(player->getPeerId(), params);
3370 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3372 sanity_check(player);
3373 player->setMoon(params);
3374 SendSetMoon(player->getPeerId(), params);
3377 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3379 sanity_check(player);
3380 player->setStars(params);
3381 SendSetStars(player->getPeerId(), params);
3384 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3386 sanity_check(player);
3387 player->setCloudParams(params);
3388 SendCloudParams(player->getPeerId(), params);
3391 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3394 sanity_check(player);
3395 player->overrideDayNightRatio(do_override, ratio);
3396 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3399 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3401 sanity_check(player);
3402 player->setLighting(lighting);
3403 SendSetLighting(player->getPeerId(), lighting);
3406 void Server::notifyPlayers(const std::wstring &msg)
3408 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3411 void Server::spawnParticle(const std::string &playername,
3412 const ParticleParameters &p)
3414 // m_env will be NULL if the server is initializing
3418 session_t peer_id = PEER_ID_INEXISTENT;
3420 if (!playername.empty()) {
3421 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3424 peer_id = player->getPeerId();
3425 proto_ver = player->protocol_version;
3428 SendSpawnParticle(peer_id, proto_ver, p);
3431 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3432 ServerActiveObject *attached, const std::string &playername)
3434 // m_env will be NULL if the server is initializing
3438 session_t peer_id = PEER_ID_INEXISTENT;
3440 if (!playername.empty()) {
3441 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3444 peer_id = player->getPeerId();
3445 proto_ver = player->protocol_version;
3448 u16 attached_id = attached ? attached->getId() : 0;
3451 if (attached_id == 0)
3452 id = m_env->addParticleSpawner(p.time);
3454 id = m_env->addParticleSpawner(p.time, attached_id);
3456 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3460 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3462 // m_env will be NULL if the server is initializing
3464 throw ServerError("Can't delete particle spawners during initialisation!");
3466 session_t peer_id = PEER_ID_INEXISTENT;
3467 if (!playername.empty()) {
3468 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3471 peer_id = player->getPeerId();
3474 m_env->deleteParticleSpawner(id);
3475 SendDeleteParticleSpawner(peer_id, id);
3478 bool Server::dynamicAddMedia(std::string filepath,
3479 const u32 token, const std::string &to_player, bool ephemeral)
3481 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3482 auto it = m_media.find(filename);
3483 if (it != m_media.end()) {
3484 // Allow the same path to be "added" again in certain conditions
3485 if (ephemeral || it->second.path != filepath) {
3486 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3487 << "\" already exists in media cache" << std::endl;
3492 // Load the file and add it to our media cache
3493 std::string filedata, raw_hash;
3494 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3499 // Create a copy of the file and swap out the path, this removes the
3500 // requirement that mods keep the file accessible at the original path.
3501 filepath = fs::CreateTempFile();
3502 bool ok = ([&] () -> bool {
3503 if (filepath.empty())
3505 std::ofstream os(filepath.c_str(), std::ios::binary);
3513 errorstream << "Server: failed to create a copy of media file "
3514 << "\"" << filename << "\"" << std::endl;
3515 m_media.erase(filename);
3518 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3519 << filepath << std::endl;
3521 m_media[filename].path = filepath;
3522 m_media[filename].no_announce = true;
3523 // stepPendingDynMediaCallbacks will clean this up later.
3524 } else if (!to_player.empty()) {
3525 m_media[filename].no_announce = true;
3528 // Push file to existing clients
3529 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3530 pkt << raw_hash << filename << (bool)ephemeral;
3532 NetworkPacket legacy_pkt = pkt;
3534 // Newer clients get asked to fetch the file (asynchronous)
3536 // Older clients have an awful hack that just throws the data at them
3537 legacy_pkt.putLongString(filedata);
3539 std::unordered_set<session_t> delivered, waiting;
3541 ClientInterface::AutoLock clientlock(m_clients);
3542 for (auto &pair : m_clients.getClientList()) {
3543 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3545 If a client is in the DefinitionsSent state it is too late to
3546 transfer the file via sendMediaAnnouncement() but at the same
3547 time the client cannot accept a media push yet.
3548 Short of artificially delaying the joining process there is no
3549 way for the server to resolve this so we (currently) opt not to.
3551 warningstream << "The media \"" << filename << "\" (dynamic) could "
3552 "not be delivered to " << pair.second->getName()
3553 << " due to a race condition." << std::endl;
3556 if (pair.second->getState() < CS_Active)
3559 const auto proto_ver = pair.second->net_proto_version;
3563 const session_t peer_id = pair.second->peer_id;
3564 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3567 if (proto_ver < 40) {
3568 delivered.emplace(peer_id);
3570 The network layer only guarantees ordered delivery inside a channel.
3571 Since the very next packet could be one that uses the media, we have
3572 to push the media over ALL channels to ensure it is processed before
3573 it is used. In practice this means channels 1 and 0.
3575 m_clients.send(peer_id, 1, &legacy_pkt, true);
3576 m_clients.send(peer_id, 0, &legacy_pkt, true);
3578 waiting.emplace(peer_id);
3579 Send(peer_id, &pkt);
3584 // Run callback for players that already had the file delivered (legacy-only)
3585 for (session_t peer_id : delivered) {
3586 if (auto player = m_env->getPlayer(peer_id))
3587 getScriptIface()->on_dynamic_media_added(token, player->getName());
3590 // Save all others in our pending state
3591 auto &state = m_pending_dyn_media[token];
3592 state.waiting_players = std::move(waiting);
3593 // regardless of success throw away the callback after a while
3594 state.expiry_timer = 60.0f;
3596 state.filename = filename;
3601 // actions: time-reversed list
3602 // Return value: success/failure
3603 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3604 std::list<std::string> *log)
3606 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3607 ServerMap *map = (ServerMap*)(&m_env->getMap());
3609 // Fail if no actions to handle
3610 if (actions.empty()) {
3612 log->push_back("Nothing to do.");
3619 for (const RollbackAction &action : actions) {
3621 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3624 std::ostringstream os;
3625 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3626 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3628 log->push_back(os.str());
3630 std::ostringstream os;
3631 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3632 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3634 log->push_back(os.str());
3638 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3639 <<" failed"<<std::endl;
3641 // Call it done if less than half failed
3642 return num_failed <= num_tried/2;
3645 // IGameDef interface
3647 IItemDefManager *Server::getItemDefManager()
3652 const NodeDefManager *Server::getNodeDefManager()
3657 ICraftDefManager *Server::getCraftDefManager()
3662 u16 Server::allocateUnknownNodeId(const std::string &name)
3664 return m_nodedef->allocateDummy(name);
3667 IWritableItemDefManager *Server::getWritableItemDefManager()
3672 NodeDefManager *Server::getWritableNodeDefManager()
3677 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3682 const std::vector<ModSpec> & Server::getMods() const
3684 return m_modmgr->getMods();
3687 const ModSpec *Server::getModSpec(const std::string &modname) const
3689 return m_modmgr->getModSpec(modname);
3692 void Server::getModNames(std::vector<std::string> &modlist)
3694 m_modmgr->getModNames(modlist);
3697 std::string Server::getBuiltinLuaPath()
3699 return porting::path_share + DIR_DELIM + "builtin";
3702 v3f Server::findSpawnPos()
3704 ServerMap &map = m_env->getServerMap();
3706 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3707 return nodeposf * BS;
3709 bool is_good = false;
3710 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3711 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3713 // Try to find a good place a few times
3714 for (s32 i = 0; i < 4000 && !is_good; i++) {
3715 s32 range = MYMIN(1 + i, range_max);
3716 // We're going to try to throw the player to this position
3717 v2s16 nodepos2d = v2s16(
3718 -range + (myrand() % (range * 2)),
3719 -range + (myrand() % (range * 2)));
3720 // Get spawn level at point
3721 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3722 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3723 // signify an unsuitable spawn position, or if outside limits.
3724 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3725 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3728 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3729 // Consecutive empty nodes
3732 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3733 // avoid obstructions in already-generated mapblocks.
3734 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3735 // no obstructions, but mapgen decorations are generated after spawn so
3736 // the player may end up inside one.
3737 for (s32 i = 0; i < 8; i++) {
3738 v3s16 blockpos = getNodeBlockPos(nodepos);
3739 map.emergeBlock(blockpos, true);
3740 content_t c = map.getNode(nodepos).getContent();
3742 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3743 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3744 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3746 if (air_count >= 2) {
3747 // Spawn in lower empty node
3749 nodeposf = intToFloat(nodepos, BS);
3750 // Don't spawn the player outside map boundaries
3751 if (objectpos_over_limit(nodeposf))
3752 // Exit this loop, positions above are probably over limit
3755 // Good position found, cause an exit from main loop
3769 // No suitable spawn point found, return fallback 0,0,0
3770 return v3f(0.0f, 0.0f, 0.0f);
3773 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3775 if (delay == 0.0f) {
3776 // No delay, shutdown immediately
3777 m_shutdown_state.is_requested = true;
3778 // only print to the infostream, a chat message saying
3779 // "Server Shutting Down" is sent when the server destructs.
3780 infostream << "*** Immediate Server shutdown requested." << std::endl;
3781 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3782 // Negative delay, cancel shutdown if requested
3783 m_shutdown_state.reset();
3784 std::wstringstream ws;
3786 ws << L"*** Server shutdown canceled.";
3788 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3789 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3790 // m_shutdown_* are already handled, skip.
3792 } else if (delay > 0.0f) {
3793 // Positive delay, tell the clients when the server will shut down
3794 std::wstringstream ws;
3796 ws << L"*** Server shutting down in "
3797 << duration_to_string(myround(delay)).c_str()
3800 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3801 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3804 m_shutdown_state.trigger(delay, msg, reconnect);
3807 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3810 Try to get an existing player
3812 RemotePlayer *player = m_env->getPlayer(name);
3814 // If player is already connected, cancel
3815 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3816 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3821 If player with the wanted peer_id already exists, cancel.
3823 if (m_env->getPlayer(peer_id)) {
3824 infostream<<"emergePlayer(): Player with wrong name but same"
3825 " peer_id already exists"<<std::endl;
3830 player = new RemotePlayer(name, idef());
3833 bool newplayer = false;
3836 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3838 // Complete init with server parts
3839 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3840 player->protocol_version = proto_version;
3844 m_script->on_newplayer(playersao);
3850 bool Server::registerModStorage(ModMetadata *storage)
3852 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3853 errorstream << "Unable to register same mod storage twice. Storage name: "
3854 << storage->getModName() << std::endl;
3858 m_mod_storages[storage->getModName()] = storage;
3862 void Server::unregisterModStorage(const std::string &name)
3864 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3865 if (it != m_mod_storages.end())
3866 m_mod_storages.erase(name);
3869 void dedicated_server_loop(Server &server, bool &kill)
3871 verbosestream<<"dedicated_server_loop()"<<std::endl;
3873 IntervalLimiter m_profiler_interval;
3875 static thread_local const float steplen =
3876 g_settings->getFloat("dedicated_server_step");
3877 static thread_local const float profiler_print_interval =
3878 g_settings->getFloat("profiler_print_interval");
3881 * The dedicated server loop only does time-keeping (in Server::step) and
3882 * provides a way to main.cpp to kill the server externally (bool &kill).
3886 // This is kind of a hack but can be done like this
3887 // because server.step() is very light
3888 sleep_ms((int)(steplen*1000.0));
3889 server.step(steplen);
3891 if (server.isShutdownRequested() || kill)
3897 if (profiler_print_interval != 0) {
3898 if(m_profiler_interval.step(steplen, profiler_print_interval))
3900 infostream<<"Profiler:"<<std::endl;
3901 g_profiler->print(infostream);
3902 g_profiler->clear();
3907 infostream << "Dedicated server quitting" << std::endl;
3909 if (g_settings->getBool("server_announce"))
3910 ServerList::sendAnnounce(ServerList::AA_DELETE,
3911 server.m_bind_addr.getPort());
3920 bool Server::joinModChannel(const std::string &channel)
3922 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3923 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3926 bool Server::leaveModChannel(const std::string &channel)
3928 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3931 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3933 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3936 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3940 ModChannel* Server::getModChannel(const std::string &channel)
3942 return m_modchannel_mgr->getModChannel(channel);
3945 void Server::broadcastModChannelMessage(const std::string &channel,
3946 const std::string &message, session_t from_peer)
3948 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3952 if (message.size() > STRING_MAX_LEN) {
3953 warningstream << "ModChannel message too long, dropping before sending "
3954 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3955 << channel << ")" << std::endl;
3960 if (from_peer != PEER_ID_SERVER) {
3961 sender = getPlayerName(from_peer);
3964 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3965 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3966 resp_pkt << channel << sender << message;
3967 for (session_t peer_id : peers) {
3969 if (peer_id == from_peer)
3972 Send(peer_id, &resp_pkt);
3975 if (from_peer != PEER_ID_SERVER) {
3976 m_script->on_modchannel_message(channel, sender, message);
3980 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3982 if (lang_code.empty())
3985 auto it = server_translations.find(lang_code);
3986 if (it != server_translations.end())
3987 return &it->second; // Already loaded
3989 // [] will create an entry
3990 auto *translations = &server_translations[lang_code];
3992 std::string suffix = "." + lang_code + ".tr";
3993 for (const auto &i : m_media) {
3994 if (str_ends_with(i.first, suffix)) {
3996 if (fs::ReadFile(i.second.path, data)) {
3997 translations->loadTranslation(data);
4002 return translations;
4005 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
4007 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4009 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4010 throw BaseException("Cannot read world.mt!");
4012 std::string backend = world_mt.exists("mod_storage_backend") ?
4013 world_mt.get("mod_storage_backend") : "files";
4014 if (backend == "files")
4015 warningstream << "/!\\ You are using the old mod storage files backend. "
4016 << "This backend is deprecated and may be removed in a future release /!\\"
4017 << std::endl << "Switching to SQLite3 is advised, "
4018 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4020 return openModStorageDatabase(backend, world_path, world_mt);
4023 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
4024 const std::string &world_path, const Settings &world_mt)
4026 if (backend == "sqlite3")
4027 return new ModMetadataDatabaseSQLite3(world_path);
4029 if (backend == "files")
4030 return new ModMetadataDatabaseFiles(world_path);
4032 if (backend == "dummy")
4033 return new Database_Dummy();
4035 throw BaseException("Mod storage database backend " + backend + " not supported");
4038 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4040 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4042 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4043 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4044 errorstream << "Cannot read world.mt!" << std::endl;
4048 std::string backend = world_mt.exists("mod_storage_backend") ?
4049 world_mt.get("mod_storage_backend") : "files";
4050 if (backend == migrate_to) {
4051 errorstream << "Cannot migrate: new backend is same"
4052 << " as the old one" << std::endl;
4056 ModMetadataDatabase *srcdb = nullptr;
4057 ModMetadataDatabase *dstdb = nullptr;
4059 bool succeeded = false;
4062 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4063 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4067 std::vector<std::string> mod_list;
4068 srcdb->listMods(&mod_list);
4069 for (const std::string &modname : mod_list) {
4071 srcdb->getModEntries(modname, &meta);
4072 for (const auto &pair : meta) {
4073 dstdb->setModEntry(modname, pair.first, pair.second);
4081 actionstream << "Successfully migrated the metadata of "
4082 << mod_list.size() << " mods" << std::endl;
4083 world_mt.set("mod_storage_backend", migrate_to);
4084 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4085 errorstream << "Failed to update world.mt!" << std::endl;
4087 actionstream << "world.mt updated" << std::endl;
4089 } catch (BaseException &e) {
4090 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4096 if (succeeded && backend == "files") {
4098 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4099 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4100 if (!fs::Rename(storage_path, backup_path))
4101 warningstream << "After migration, " << storage_path
4102 << " could not be renamed to " << backup_path << std::endl;