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)");
272 const std::string aom_types[] = {"reliable", "unreliable"};
273 for (u32 i = 0; i < ARRLEN(aom_types); i++) {
274 std::string help_str("Number of active object messages generated (");
275 help_str.append(aom_types[i]).append(")");
276 m_aom_buffer_counter[i] = m_metrics_backend->addCounter(
277 "minetest_core_aom_generated_count", help_str,
278 {{"type", aom_types[i]}});
281 m_packet_recv_counter = m_metrics_backend->addCounter(
282 "minetest_core_server_packet_recv",
283 "Processable packets received");
285 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
286 "minetest_core_server_packet_recv_processed",
287 "Valid received packets processed");
289 m_map_edit_event_counter = m_metrics_backend->addCounter(
290 "minetest_core_map_edit_events",
291 "Number of map edit events");
293 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
299 // Send shutdown message
300 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
301 L"*** Server shutting down"));
304 MutexAutoLock envlock(m_env_mutex);
306 infostream << "Server: Saving players" << std::endl;
307 m_env->saveLoadedPlayers();
309 infostream << "Server: Kicking players" << std::endl;
310 std::string kick_msg;
311 bool reconnect = false;
312 if (isShutdownRequested()) {
313 reconnect = m_shutdown_state.should_reconnect;
314 kick_msg = m_shutdown_state.message;
316 if (kick_msg.empty()) {
317 kick_msg = g_settings->get("kick_msg_shutdown");
319 m_env->saveLoadedPlayers(true);
320 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
321 kick_msg, reconnect);
324 actionstream << "Server: Shutting down" << std::endl;
326 // Do this before stopping the server in case mapgen callbacks need to access
327 // server-controlled resources (like ModStorages). Also do them before
328 // shutdown callbacks since they may modify state that is finalized in a
331 m_emerge->stopThreads();
334 MutexAutoLock envlock(m_env_mutex);
336 // Execute script shutdown hooks
337 infostream << "Executing shutdown hooks" << std::endl;
339 m_script->on_shutdown();
340 } catch (ModError &e) {
341 errorstream << "ModError: " << e.what() << std::endl;
342 if (m_on_shutdown_errmsg) {
343 if (m_on_shutdown_errmsg->empty()) {
344 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
346 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
351 infostream << "Server: Saving environment metadata" << std::endl;
361 // Write any changes before deletion.
362 if (m_mod_storage_database)
363 m_mod_storage_database->endSave();
365 // Delete things in the reverse order of creation
369 delete m_mod_storage_database;
375 // Deinitialize scripting
376 infostream << "Server: Deinitializing scripting" << std::endl;
378 delete m_startup_server_map; // if available
379 delete m_game_settings;
381 while (!m_unsent_map_edit_queue.empty()) {
382 delete m_unsent_map_edit_queue.front();
383 m_unsent_map_edit_queue.pop();
389 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
390 if (m_simple_singleplayer_mode)
391 infostream << " in simple singleplayer mode" << std::endl;
393 infostream << std::endl;
394 infostream << "- world: " << m_path_world << std::endl;
395 infostream << "- game: " << m_gamespec.path << std::endl;
397 m_game_settings = Settings::createLayer(SL_GAME);
399 // Create world if it doesn't exist
401 loadGameConfAndInitWorld(m_path_world,
402 fs::GetFilenameFromPath(m_path_world.c_str()),
404 } catch (const BaseException &e) {
405 throw ServerError(std::string("Failed to initialize world: ") + e.what());
408 // Create emerge manager
409 m_emerge = new EmergeManager(this, m_metrics_backend.get());
411 // Create ban manager
412 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
413 m_banmanager = new BanManager(ban_path);
415 // Create mod storage database and begin a save for later
416 m_mod_storage_database = openModStorageDatabase(m_path_world);
417 m_mod_storage_database->beginSave();
419 m_modmgr = std::make_unique<ServerModManager>(m_path_world);
420 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
421 // complain about mods with unsatisfied dependencies
422 if (!m_modmgr->isConsistent()) {
423 m_modmgr->printUnsatisfiedModsError();
427 MutexAutoLock envlock(m_env_mutex);
429 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
430 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
431 m_startup_server_map = servermap;
433 // Initialize scripting
434 infostream << "Server: Initializing Lua" << std::endl;
436 m_script = new ServerScripting(this);
438 // Must be created before mod loading because we have some inventory creation
439 m_inventory_mgr = std::make_unique<ServerInventoryManager>();
441 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
443 m_gamespec.checkAndLog();
444 m_modmgr->loadMods(m_script);
446 // Read Textures and calculate sha1 sums
449 // Apply item aliases in the node definition manager
450 m_nodedef->updateAliases(m_itemdef);
452 // Apply texture overrides from texturepack/override.txt
453 std::vector<std::string> paths;
454 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
455 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
456 for (const std::string &path : paths) {
457 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
458 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
459 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
462 m_nodedef->setNodeRegistrationStatus(true);
464 // Perform pending node name resolutions
465 m_nodedef->runNodeResolveCallbacks();
467 // unmap node names in cross-references
468 m_nodedef->resolveCrossrefs();
470 // init the recipe hashes to speed up crafting
471 m_craftdef->initHashes(this);
473 // Initialize Environment
474 m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
475 m_env = new ServerEnvironment(servermap, m_script, this,
476 m_path_world, m_metrics_backend.get());
478 m_inventory_mgr->setEnv(m_env);
479 m_clients.setEnv(m_env);
481 if (!servermap->settings_mgr.makeMapgenParams())
482 FATAL_ERROR("Couldn't create any mapgen type");
484 // Initialize mapgens
485 m_emerge->initMapgens(servermap->getMapgenParams());
487 if (g_settings->getBool("enable_rollback_recording")) {
488 // Create rollback manager
489 m_rollback = new RollbackManager(m_path_world, this);
492 // Give environment reference to scripting api
493 m_script->initializeEnvironment(m_env);
495 // Do this after regular script init is done
496 m_script->initAsync();
498 // Register us to receive map edit events
499 servermap->addEventReceiver(this);
503 // Those settings can be overwritten in world.mt, they are
504 // intended to be cached after environment loading.
505 m_liquid_transform_every = g_settings->getFloat("liquid_update");
506 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
507 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
508 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
515 infostream << "Starting server on " << m_bind_addr.serializeString()
516 << "..." << std::endl;
518 // Stop thread if already running
521 // Initialize connection
522 m_con->SetTimeoutMs(30);
523 m_con->Serve(m_bind_addr);
528 // ASCII art for the win!
530 << " __. __. __. " << std::endl
531 << " _____ |__| ____ _____ / |_ _____ _____ / |_ " << std::endl
532 << " / \\| |/ \\ / __ \\ _\\/ __ \\/ __> _\\" << std::endl
533 << "| Y Y \\ | | \\ ___/| | | ___/\\___ \\| | " << std::endl
534 << "|__|_| / |___| /\\______> | \\______>_____/| | " << std::endl
535 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
536 actionstream << "World at [" << m_path_world << "]" << std::endl;
537 actionstream << "Server for gameid=\"" << m_gamespec.id
538 << "\" listening on ";
539 m_bind_addr.print(actionstream);
540 actionstream << "." << std::endl;
545 infostream<<"Server: Stopping and waiting threads"<<std::endl;
547 // Stop threads (set run=false first so both start stopping)
551 infostream<<"Server: Threads stopped"<<std::endl;
554 void Server::step(float dtime)
560 MutexAutoLock lock(m_step_dtime_mutex);
561 m_step_dtime += dtime;
563 // Throw if fatal error occurred in thread
564 std::string async_err = m_async_fatal_error.get();
565 if (!async_err.empty()) {
566 if (!m_simple_singleplayer_mode) {
567 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
568 g_settings->get("kick_msg_crash"),
569 g_settings->getBool("ask_reconnect_on_crash"));
571 throw ServerError("AsyncErr: " + async_err);
575 void Server::AsyncRunStep(bool initial_step)
580 MutexAutoLock lock1(m_step_dtime_mutex);
581 dtime = m_step_dtime;
585 // Send blocks to clients
589 if((dtime < 0.001) && !initial_step)
592 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
595 MutexAutoLock lock1(m_step_dtime_mutex);
596 m_step_dtime -= dtime;
602 m_uptime_counter->increment(dtime);
607 Update time of day and overall game time
609 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
612 Send to clients at constant intervals
615 m_time_of_day_send_timer -= dtime;
616 if (m_time_of_day_send_timer < 0.0) {
617 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
618 u16 time = m_env->getTimeOfDay();
619 float time_speed = g_settings->getFloat("time_speed");
620 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
622 m_timeofday_gauge->set(time);
626 MutexAutoLock lock(m_env_mutex);
627 // Figure out and report maximum lag to environment
628 float max_lag = m_env->getMaxLagEstimate();
629 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
631 if(dtime > 0.1 && dtime > max_lag * 2.0)
632 infostream<<"Server: Maximum lag peaked to "<<dtime
636 m_env->reportMaxLagEstimate(max_lag);
642 static const float map_timer_and_unload_dtime = 2.92;
643 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
645 MutexAutoLock lock(m_env_mutex);
646 // Run Map's timers and unload unused data
647 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
648 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
649 g_settings->getFloat("server_unload_unused_data_timeout"),
654 Listen to the admin chat, if available
657 if (!m_admin_chat->command_queue.empty()) {
658 MutexAutoLock lock(m_env_mutex);
659 while (!m_admin_chat->command_queue.empty()) {
660 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
661 handleChatInterfaceEvent(evt);
665 m_admin_chat->outgoing_queue.push_back(
666 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
673 /* Transform liquids */
674 m_liquid_transform_timer += dtime;
675 if(m_liquid_transform_timer >= m_liquid_transform_every)
677 m_liquid_transform_timer -= m_liquid_transform_every;
679 MutexAutoLock lock(m_env_mutex);
681 ScopeProfiler sp(g_profiler, "Server: liquid transform");
683 std::map<v3s16, MapBlock*> modified_blocks;
684 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
687 Set the modified blocks unsent for all the clients
689 if (!modified_blocks.empty()) {
690 SetBlocksNotSent(modified_blocks);
693 m_clients.step(dtime);
695 // increase/decrease lag gauge gradually
696 if (m_lag_gauge->get() > dtime) {
697 m_lag_gauge->decrement(dtime/100);
699 m_lag_gauge->increment(dtime/100);
703 float &counter = m_step_pending_dyn_media_timer;
705 if (counter >= 5.0f) {
706 stepPendingDynMediaCallbacks(counter);
713 // send masterserver announce
715 float &counter = m_masterserver_timer;
716 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
717 g_settings->getBool("server_announce")) {
718 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
719 ServerList::AA_START,
720 m_bind_addr.getPort(),
721 m_clients.getPlayerNames(),
722 m_uptime_counter->get(),
723 m_env->getGameTime(),
726 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
736 Check added and deleted active objects
739 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
740 MutexAutoLock envlock(m_env_mutex);
743 ClientInterface::AutoLock clientlock(m_clients);
744 const RemoteClientMap &clients = m_clients.getClientList();
745 ScopeProfiler sp(g_profiler, "Server: update objects within range");
747 m_player_gauge->set(clients.size());
748 for (const auto &client_it : clients) {
749 RemoteClient *client = client_it.second;
751 if (client->getState() < CS_DefinitionsSent)
754 // This can happen if the client times out somehow
755 if (!m_env->getPlayer(client->peer_id))
758 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
762 SendActiveObjectRemoveAdd(client, playersao);
766 // Write changes to the mod storage
767 m_mod_storage_save_timer -= dtime;
768 if (m_mod_storage_save_timer <= 0.0f) {
769 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
770 m_mod_storage_database->endSave();
771 m_mod_storage_database->beginSave();
779 MutexAutoLock envlock(m_env_mutex);
780 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
783 // Value = data sent by object
784 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
786 // Get active object messages from environment
787 ActiveObjectMessage aom(0);
788 u32 count_reliable = 0, count_unreliable = 0;
790 if (!m_env->getActiveObjectMessage(&aom))
797 std::vector<ActiveObjectMessage>* message_list = nullptr;
798 auto n = buffered_messages.find(aom.id);
799 if (n == buffered_messages.end()) {
800 message_list = new std::vector<ActiveObjectMessage>;
801 buffered_messages[aom.id] = message_list;
803 message_list = n->second;
805 message_list->push_back(std::move(aom));
808 m_aom_buffer_counter[0]->increment(count_reliable);
809 m_aom_buffer_counter[1]->increment(count_unreliable);
812 ClientInterface::AutoLock clientlock(m_clients);
813 const RemoteClientMap &clients = m_clients.getClientList();
814 // Route data to every client
815 std::string reliable_data, unreliable_data;
816 for (const auto &client_it : clients) {
817 reliable_data.clear();
818 unreliable_data.clear();
819 RemoteClient *client = client_it.second;
820 PlayerSAO *player = getPlayerSAO(client->peer_id);
821 // Go through all objects in message buffer
822 for (const auto &buffered_message : buffered_messages) {
823 // If object does not exist or is not known by client, skip it
824 u16 id = buffered_message.first;
825 ServerActiveObject *sao = m_env->getActiveObject(id);
826 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
829 // Get message list of object
830 std::vector<ActiveObjectMessage>* list = buffered_message.second;
831 // Go through every message
832 for (const ActiveObjectMessage &aom : *list) {
833 // Send position updates to players who do not see the attachment
834 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
835 if (sao->getId() == player->getId())
838 // Do not send position updates for attached players
839 // as long the parent is known to the client
840 ServerActiveObject *parent = sao->getParent();
841 if (parent && client->m_known_objects.find(parent->getId()) !=
842 client->m_known_objects.end())
846 // Add full new data to appropriate buffer
847 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
849 writeU16((u8*) idbuf, aom.id);
852 buffer.append(idbuf, sizeof(idbuf));
853 buffer.append(serializeString16(aom.datastring));
857 reliable_data and unreliable_data are now ready.
860 if (!reliable_data.empty()) {
861 SendActiveObjectMessages(client->peer_id, reliable_data);
864 if (!unreliable_data.empty()) {
865 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
870 // Clear buffered_messages
871 for (auto &buffered_message : buffered_messages) {
872 delete buffered_message.second;
877 Send queued-for-sending map edit events.
880 // We will be accessing the environment
881 MutexAutoLock lock(m_env_mutex);
883 // Single change sending is disabled if queue size is big
884 bool disable_single_change_sending = false;
885 if(m_unsent_map_edit_queue.size() >= 4)
886 disable_single_change_sending = true;
888 const auto event_count = m_unsent_map_edit_queue.size();
889 m_map_edit_event_counter->increment(event_count);
891 // We'll log the amount of each
894 std::unordered_set<v3s16> node_meta_updates;
896 while (!m_unsent_map_edit_queue.empty()) {
897 MapEditEvent* event = m_unsent_map_edit_queue.front();
898 m_unsent_map_edit_queue.pop();
900 // Players far away from the change are stored here.
901 // Instead of sending the changes, MapBlocks are set not sent
903 std::unordered_set<u16> far_players;
905 switch (event->type) {
908 prof.add("MEET_ADDNODE", 1);
909 sendAddNode(event->p, event->n, &far_players,
910 disable_single_change_sending ? 5 : 30,
911 event->type == MEET_ADDNODE);
913 case MEET_REMOVENODE:
914 prof.add("MEET_REMOVENODE", 1);
915 sendRemoveNode(event->p, &far_players,
916 disable_single_change_sending ? 5 : 30);
918 case MEET_BLOCK_NODE_METADATA_CHANGED: {
919 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
920 if (!event->is_private_change) {
921 node_meta_updates.emplace(event->p);
924 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
925 getNodeBlockPos(event->p))) {
926 block->raiseModified(MOD_STATE_WRITE_NEEDED,
927 MOD_REASON_REPORT_META_CHANGE);
932 prof.add("MEET_OTHER", 1);
933 for (const v3s16 &modified_block : event->modified_blocks) {
934 m_clients.markBlockposAsNotSent(modified_block);
938 prof.add("unknown", 1);
939 warningstream << "Server: Unknown MapEditEvent "
940 << ((u32)event->type) << std::endl;
945 Set blocks not sent to far players
947 if (!far_players.empty()) {
948 // Convert list format to that wanted by SetBlocksNotSent
949 std::map<v3s16, MapBlock*> modified_blocks2;
950 for (const v3s16 &modified_block : event->modified_blocks) {
951 modified_blocks2[modified_block] =
952 m_env->getMap().getBlockNoCreateNoEx(modified_block);
955 // Set blocks not sent
956 for (const u16 far_player : far_players) {
957 if (RemoteClient *client = getClient(far_player))
958 client->SetBlocksNotSent(modified_blocks2);
965 if (event_count >= 5) {
966 infostream << "Server: MapEditEvents:" << std::endl;
967 prof.print(infostream);
968 } else if (event_count != 0) {
969 verbosestream << "Server: MapEditEvents:" << std::endl;
970 prof.print(verbosestream);
973 // Send all metadata updates
974 if (!node_meta_updates.empty())
975 sendMetadataChanged(node_meta_updates);
979 Trigger emerge thread
980 Doing this every 2s is left over from old code, unclear if this is still needed.
983 float &counter = m_emergethread_trigger_timer;
985 if (counter <= 0.0f) {
988 m_emerge->startThreads();
992 // Save map, players and auth stuff
994 float &counter = m_savemap_timer;
996 static thread_local const float save_interval =
997 g_settings->getFloat("server_map_save_interval");
998 if (counter >= save_interval) {
1000 MutexAutoLock lock(m_env_mutex);
1002 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
1005 if (m_banmanager->isModified()) {
1006 m_banmanager->save();
1009 // Save changed parts of map
1010 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1013 m_env->saveLoadedPlayers();
1015 // Save environment metadata
1020 m_shutdown_state.tick(dtime, this);
1023 void Server::Receive()
1033 In the first iteration *wait* for a packet, afterwards process
1034 all packets that are immediately available (no waiting).
1037 m_con->Receive(&pkt);
1040 if (!m_con->TryReceive(&pkt))
1044 peer_id = pkt.getPeerId();
1045 m_packet_recv_counter->increment();
1047 m_packet_recv_processed_counter->increment();
1048 } catch (const con::InvalidIncomingDataException &e) {
1049 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1050 << e.what() << std::endl;
1051 } catch (const SerializationError &e) {
1052 infostream << "Server::Receive(): SerializationError: what()="
1053 << e.what() << std::endl;
1054 } catch (const ClientStateError &e) {
1055 errorstream << "ProcessData: peer=" << peer_id << " what()="
1056 << e.what() << std::endl;
1057 DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1058 } catch (const con::PeerNotFoundException &e) {
1060 } catch (const con::NoIncomingDataException &e) {
1066 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1068 std::string playername;
1069 PlayerSAO *playersao = NULL;
1071 ClientInterface::AutoLock clientlock(m_clients);
1072 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1074 playername = client->getName();
1075 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1079 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1081 // If failed, cancel
1082 if (!playersao || !player) {
1083 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1084 actionstream << "Server: Failed to emerge player \"" << playername
1085 << "\" (player allocated to an another client)" << std::endl;
1086 DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1088 errorstream << "Server: " << playername << ": Failed to emerge player"
1090 DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1096 Send complete position information
1098 SendMovePlayer(peer_id);
1101 SendPlayerPrivileges(peer_id);
1103 // Send inventory formspec
1104 SendPlayerInventoryFormspec(peer_id);
1107 SendInventory(playersao, false);
1110 SendPlayerHP(playersao);
1112 // Send death screen
1113 if (playersao->isDead())
1114 SendDeathscreen(peer_id, false, v3f(0,0,0));
1117 SendPlayerBreath(playersao);
1120 Update player list and print action
1123 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1124 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1125 m_clients.sendToAll(¬ice_pkt);
1128 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1129 const auto &names = m_clients.getPlayerNames();
1131 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1132 for (const std::string &name : names)
1133 actionstream << name << " ";
1134 actionstream << player->getName() << std::endl;
1139 inline void Server::handleCommand(NetworkPacket *pkt)
1141 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1142 (this->*opHandle.handler)(pkt);
1145 void Server::ProcessData(NetworkPacket *pkt)
1147 // Environment is locked first.
1148 MutexAutoLock envlock(m_env_mutex);
1150 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1151 u32 peer_id = pkt->getPeerId();
1154 Address address = getPeerAddress(peer_id);
1155 std::string addr_s = address.serializeString();
1157 // FIXME: Isn't it a bit excessive to check this for every packet?
1158 if (m_banmanager->isIpBanned(addr_s)) {
1159 std::string ban_name = m_banmanager->getBanName(addr_s);
1160 infostream << "Server: A banned client tried to connect from "
1161 << addr_s << "; banned name was " << ban_name << std::endl;
1162 DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1163 "Your IP is banned. Banned name was " + ban_name);
1166 } catch (con::PeerNotFoundException &e) {
1168 * no peer for this packet found
1169 * most common reason is peer timeout, e.g. peer didn't
1170 * respond for some time, your server was overloaded or
1173 infostream << "Server::ProcessData(): Canceling: peer "
1174 << peer_id << " not found" << std::endl;
1179 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1181 // Command must be handled into ToServerCommandHandler
1182 if (command >= TOSERVER_NUM_MSG_TYPES) {
1183 infostream << "Server: Ignoring unknown command "
1184 << command << std::endl;
1188 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1193 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1195 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1196 errorstream << "Server::ProcessData(): Cancelling: Peer"
1197 " serialization format invalid or not initialized."
1198 " Skipping incoming command=" << command << std::endl;
1202 /* Handle commands related to client startup */
1203 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1208 if (m_clients.getClientState(peer_id) < CS_Active) {
1209 if (command == TOSERVER_PLAYERPOS) return;
1211 errorstream << "Got packet command: " << command << " for peer id "
1212 << peer_id << " but client isn't active yet. Dropping packet "
1218 } catch (SendFailedException &e) {
1219 errorstream << "Server::ProcessData(): SendFailedException: "
1220 << "what=" << e.what()
1222 } catch (PacketError &e) {
1223 actionstream << "Server::ProcessData(): PacketError: "
1224 << "what=" << e.what()
1229 void Server::setTimeOfDay(u32 time)
1231 m_env->setTimeOfDay(time);
1232 m_time_of_day_send_timer = 0;
1235 void Server::onMapEditEvent(const MapEditEvent &event)
1237 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1240 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1243 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1245 std::vector<session_t> clients = m_clients.getClientIDs();
1246 ClientInterface::AutoLock clientlock(m_clients);
1247 // Set the modified blocks unsent for all the clients
1248 for (const session_t client_id : clients) {
1249 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1250 client->SetBlocksNotSent(block);
1254 void Server::peerAdded(con::Peer *peer)
1256 verbosestream<<"Server::peerAdded(): peer->id="
1257 <<peer->id<<std::endl;
1259 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1262 void Server::deletingPeer(con::Peer *peer, bool timeout)
1264 verbosestream<<"Server::deletingPeer(): peer->id="
1265 <<peer->id<<", timeout="<<timeout<<std::endl;
1267 m_clients.event(peer->id, CSE_Disconnect);
1268 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1271 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1273 *retval = m_con->getPeerStat(peer_id,type);
1274 return *retval != -1;
1277 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1279 ClientInterface::AutoLock clientlock(m_clients);
1280 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1285 ret.state = client->getState();
1286 ret.addr = client->getAddress();
1287 ret.uptime = client->uptime();
1288 ret.ser_vers = client->serialization_version;
1289 ret.prot_vers = client->net_proto_version;
1291 ret.major = client->getMajor();
1292 ret.minor = client->getMinor();
1293 ret.patch = client->getPatch();
1294 ret.vers_string = client->getFullVer();
1296 ret.lang_code = client->getLangCode();
1301 void Server::handlePeerChanges()
1303 while(!m_peer_change_queue.empty())
1305 con::PeerChange c = m_peer_change_queue.front();
1306 m_peer_change_queue.pop();
1308 verbosestream<<"Server: Handling peer change: "
1309 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1314 case con::PEER_ADDED:
1315 m_clients.CreateClient(c.peer_id);
1318 case con::PEER_REMOVED:
1319 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1323 FATAL_ERROR("Invalid peer change event received!");
1329 void Server::printToConsoleOnly(const std::string &text)
1332 m_admin_chat->outgoing_queue.push_back(
1333 new ChatEventChat("", utf8_to_wide(text)));
1335 std::cout << text << std::endl;
1339 void Server::Send(NetworkPacket *pkt)
1341 Send(pkt->getPeerId(), pkt);
1344 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1346 m_clients.send(peer_id,
1347 clientCommandFactoryTable[pkt->getCommand()].channel,
1349 clientCommandFactoryTable[pkt->getCommand()].reliable);
1352 void Server::SendMovement(session_t peer_id)
1354 std::ostringstream os(std::ios_base::binary);
1356 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1358 pkt << g_settings->getFloat("movement_acceleration_default");
1359 pkt << g_settings->getFloat("movement_acceleration_air");
1360 pkt << g_settings->getFloat("movement_acceleration_fast");
1361 pkt << g_settings->getFloat("movement_speed_walk");
1362 pkt << g_settings->getFloat("movement_speed_crouch");
1363 pkt << g_settings->getFloat("movement_speed_fast");
1364 pkt << g_settings->getFloat("movement_speed_climb");
1365 pkt << g_settings->getFloat("movement_speed_jump");
1366 pkt << g_settings->getFloat("movement_liquid_fluidity");
1367 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1368 pkt << g_settings->getFloat("movement_liquid_sink");
1369 pkt << g_settings->getFloat("movement_gravity");
1374 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1376 m_script->player_event(playersao, "health_changed");
1377 SendPlayerHP(playersao);
1379 // Send to other clients
1380 playersao->sendPunchCommand();
1382 if (playersao->isDead())
1383 HandlePlayerDeath(playersao, reason);
1386 void Server::SendPlayerHP(PlayerSAO *playersao)
1388 SendHP(playersao->getPeerID(), playersao->getHP());
1391 void Server::SendHP(session_t peer_id, u16 hp)
1393 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1398 void Server::SendBreath(session_t peer_id, u16 breath)
1400 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1401 pkt << (u16) breath;
1405 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1406 const std::string &custom_reason, bool reconnect)
1408 assert(reason < SERVER_ACCESSDENIED_MAX);
1410 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1412 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1413 pkt << custom_reason;
1414 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1415 reason == SERVER_ACCESSDENIED_CRASH)
1416 pkt << custom_reason << (u8)reconnect;
1420 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1421 v3f camera_point_target)
1423 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1424 pkt << set_camera_point_target << camera_point_target;
1428 void Server::SendItemDef(session_t peer_id,
1429 IItemDefManager *itemdef, u16 protocol_version)
1431 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1435 u32 length of the next item
1436 zlib-compressed serialized ItemDefManager
1438 std::ostringstream tmp_os(std::ios::binary);
1439 itemdef->serialize(tmp_os, protocol_version);
1440 std::ostringstream tmp_os2(std::ios::binary);
1441 compressZlib(tmp_os.str(), tmp_os2);
1442 pkt.putLongString(tmp_os2.str());
1445 verbosestream << "Server: Sending item definitions to id(" << peer_id
1446 << "): size=" << pkt.getSize() << std::endl;
1451 void Server::SendNodeDef(session_t peer_id,
1452 const NodeDefManager *nodedef, u16 protocol_version)
1454 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1458 u32 length of the next item
1459 zlib-compressed serialized NodeDefManager
1461 std::ostringstream tmp_os(std::ios::binary);
1462 nodedef->serialize(tmp_os, protocol_version);
1463 std::ostringstream tmp_os2(std::ios::binary);
1464 compressZlib(tmp_os.str(), tmp_os2);
1466 pkt.putLongString(tmp_os2.str());
1469 verbosestream << "Server: Sending node definitions to id(" << peer_id
1470 << "): size=" << pkt.getSize() << std::endl;
1476 Non-static send methods
1479 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1481 RemotePlayer *player = sao->getPlayer();
1483 // Do not send new format to old clients
1484 incremental &= player->protocol_version >= 38;
1486 UpdateCrafting(player);
1492 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1494 std::ostringstream os(std::ios::binary);
1495 sao->getInventory()->serialize(os, incremental);
1496 sao->getInventory()->setModified(false);
1497 player->setModified(true);
1499 const std::string &s = os.str();
1500 pkt.putRawString(s.c_str(), s.size());
1504 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1506 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1508 u8 type = message.type;
1509 pkt << version << type << message.sender << message.message
1510 << static_cast<u64>(message.timestamp);
1512 if (peer_id != PEER_ID_INEXISTENT) {
1513 RemotePlayer *player = m_env->getPlayer(peer_id);
1519 m_clients.sendToAll(&pkt);
1523 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1524 const std::string &formname)
1526 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1527 if (formspec.empty()){
1528 //the client should close the formspec
1529 //but make sure there wasn't another one open in meantime
1530 const auto it = m_formspec_state_data.find(peer_id);
1531 if (it != m_formspec_state_data.end() && it->second == formname) {
1532 m_formspec_state_data.erase(peer_id);
1534 pkt.putLongString("");
1536 m_formspec_state_data[peer_id] = formname;
1537 pkt.putLongString(formspec);
1544 // Spawns a particle on peer with peer_id
1545 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1546 const ParticleParameters &p)
1548 static thread_local const float radius =
1549 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1551 if (peer_id == PEER_ID_INEXISTENT) {
1552 std::vector<session_t> clients = m_clients.getClientIDs();
1553 const v3f pos = p.pos * BS;
1554 const float radius_sq = radius * radius;
1556 for (const session_t client_id : clients) {
1557 RemotePlayer *player = m_env->getPlayer(client_id);
1561 PlayerSAO *sao = player->getPlayerSAO();
1565 // Do not send to distant clients
1566 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1569 SendSpawnParticle(client_id, player->protocol_version, p);
1573 assert(protocol_version != 0);
1575 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1578 // NetworkPacket and iostreams are incompatible...
1579 std::ostringstream oss(std::ios_base::binary);
1580 p.serialize(oss, protocol_version);
1581 pkt.putRawString(oss.str());
1587 // Adds a ParticleSpawner on peer with peer_id
1588 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1589 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1591 static thread_local const float radius =
1592 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1594 if (peer_id == PEER_ID_INEXISTENT) {
1595 std::vector<session_t> clients = m_clients.getClientIDs();
1596 const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS;
1597 const float radius_sq = radius * radius;
1598 /* Don't send short-lived spawners to distant players.
1599 * This could be replaced with proper tracking at some point. */
1600 const bool distance_check = !attached_id && p.time <= 1.0f;
1602 for (const session_t client_id : clients) {
1603 RemotePlayer *player = m_env->getPlayer(client_id);
1607 if (distance_check) {
1608 PlayerSAO *sao = player->getPlayerSAO();
1611 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1615 SendAddParticleSpawner(client_id, player->protocol_version,
1616 p, attached_id, id);
1620 assert(protocol_version != 0);
1622 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1624 pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel
1625 << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime
1626 << p.minsize << p.maxsize << p.collisiondetection;
1628 pkt.putLongString(p.texture);
1630 pkt << id << p.vertical << p.collision_removal << attached_id;
1632 std::ostringstream os(std::ios_base::binary);
1633 p.animation.serialize(os, protocol_version);
1634 pkt.putRawString(os.str());
1636 pkt << p.glow << p.object_collision;
1637 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1642 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1644 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1648 if (peer_id != PEER_ID_INEXISTENT)
1651 m_clients.sendToAll(&pkt);
1655 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1657 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1659 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1660 << form->text << form->number << form->item << form->dir
1661 << form->align << form->offset << form->world_pos << form->size
1662 << form->z_index << form->text2 << form->style;
1667 void Server::SendHUDRemove(session_t peer_id, u32 id)
1669 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1674 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1676 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1677 pkt << id << (u8) stat;
1681 case HUD_STAT_SCALE:
1682 case HUD_STAT_ALIGN:
1683 case HUD_STAT_OFFSET:
1684 pkt << *(v2f *) value;
1688 case HUD_STAT_TEXT2:
1689 pkt << *(std::string *) value;
1691 case HUD_STAT_WORLD_POS:
1692 pkt << *(v3f *) value;
1695 pkt << *(v2s32 *) value;
1697 default: // all other types
1698 pkt << *(u32 *) value;
1705 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1707 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1709 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1711 pkt << flags << mask;
1716 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1718 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1719 pkt << param << value;
1723 void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
1725 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1727 // Handle prior clients here
1728 if (m_clients.getProtocolVersion(peer_id) < 39) {
1729 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1731 for (const std::string& texture : params.textures)
1734 pkt << params.clouds;
1735 } else { // Handle current clients and future clients
1736 pkt << params.bgcolor << params.type
1737 << params.clouds << params.fog_sun_tint
1738 << params.fog_moon_tint << params.fog_tint_type;
1740 if (params.type == "skybox") {
1741 pkt << (u16) params.textures.size();
1742 for (const std::string &texture : params.textures)
1744 } else if (params.type == "regular") {
1745 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1746 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1747 << params.sky_color.night_sky << params.sky_color.night_horizon
1748 << params.sky_color.indoors;
1755 void Server::SendSetSun(session_t peer_id, const SunParams ¶ms)
1757 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1758 pkt << params.visible << params.texture
1759 << params.tonemap << params.sunrise
1760 << params.sunrise_visible << params.scale;
1764 void Server::SendSetMoon(session_t peer_id, const MoonParams ¶ms)
1766 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1768 pkt << params.visible << params.texture
1769 << params.tonemap << params.scale;
1773 void Server::SendSetStars(session_t peer_id, const StarParams ¶ms)
1775 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1777 pkt << params.visible << params.count
1778 << params.starcolor << params.scale;
1783 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1785 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1786 pkt << params.density << params.color_bright << params.color_ambient
1787 << params.height << params.thickness << params.speed;
1791 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1794 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1797 pkt << do_override << (u16) (ratio * 65535);
1802 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1804 NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1807 pkt << lighting.shadow_intensity;
1812 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1814 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1815 pkt << time << time_speed;
1817 if (peer_id == PEER_ID_INEXISTENT) {
1818 m_clients.sendToAll(&pkt);
1825 void Server::SendPlayerBreath(PlayerSAO *sao)
1829 m_script->player_event(sao, "breath_changed");
1830 SendBreath(sao->getPeerID(), sao->getBreath());
1833 void Server::SendMovePlayer(session_t peer_id)
1835 RemotePlayer *player = m_env->getPlayer(peer_id);
1837 PlayerSAO *sao = player->getPlayerSAO();
1840 // Send attachment updates instantly to the client prior updating position
1841 sao->sendOutdatedData();
1843 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1844 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1847 v3f pos = sao->getBasePosition();
1848 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1849 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1850 << " pitch=" << sao->getLookPitch()
1851 << " yaw=" << sao->getRotation().Y
1858 void Server::SendPlayerFov(session_t peer_id)
1860 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1862 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1863 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1868 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1869 f32 animation_speed)
1871 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1874 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1875 << animation_frames[3] << animation_speed;
1880 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1882 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1883 pkt << first << third;
1887 void Server::SendPlayerPrivileges(session_t peer_id)
1889 RemotePlayer *player = m_env->getPlayer(peer_id);
1891 if(player->getPeerId() == PEER_ID_INEXISTENT)
1894 std::set<std::string> privs;
1895 m_script->getAuth(player->getName(), NULL, &privs);
1897 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1898 pkt << (u16) privs.size();
1900 for (const std::string &priv : privs) {
1907 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1909 RemotePlayer *player = m_env->getPlayer(peer_id);
1911 if (player->getPeerId() == PEER_ID_INEXISTENT)
1914 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1915 pkt.putLongString(player->inventory_formspec);
1920 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1922 RemotePlayer *player = m_env->getPlayer(peer_id);
1924 if (player->getPeerId() == PEER_ID_INEXISTENT)
1927 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1928 pkt << player->formspec_prepend;
1932 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1934 // Radius inside which objects are active
1935 static thread_local const s16 radius =
1936 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1938 // Radius inside which players are active
1939 static thread_local const bool is_transfer_limited =
1940 g_settings->exists("unlimited_player_transfer_distance") &&
1941 !g_settings->getBool("unlimited_player_transfer_distance");
1943 static thread_local const s16 player_transfer_dist =
1944 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
1946 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
1947 radius : player_transfer_dist;
1949 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
1953 std::queue<u16> removed_objects, added_objects;
1954 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
1955 client->m_known_objects, removed_objects);
1956 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
1957 client->m_known_objects, added_objects);
1959 int removed_count = removed_objects.size();
1960 int added_count = added_objects.size();
1962 if (removed_objects.empty() && added_objects.empty())
1968 // Handle removed objects
1969 writeU16((u8*)buf, removed_objects.size());
1970 data.append(buf, 2);
1971 while (!removed_objects.empty()) {
1973 u16 id = removed_objects.front();
1974 ServerActiveObject* obj = m_env->getActiveObject(id);
1976 // Add to data buffer for sending
1977 writeU16((u8*)buf, id);
1978 data.append(buf, 2);
1980 // Remove from known objects
1981 client->m_known_objects.erase(id);
1983 if (obj && obj->m_known_by_count > 0)
1984 obj->m_known_by_count--;
1986 removed_objects.pop();
1989 // Handle added objects
1990 writeU16((u8*)buf, added_objects.size());
1991 data.append(buf, 2);
1992 while (!added_objects.empty()) {
1994 u16 id = added_objects.front();
1995 ServerActiveObject *obj = m_env->getActiveObject(id);
1996 added_objects.pop();
1999 warningstream << FUNCTION_NAME << ": NULL object id="
2000 << (int)id << std::endl;
2005 u8 type = obj->getSendType();
2007 // Add to data buffer for sending
2008 writeU16((u8*)buf, id);
2009 data.append(buf, 2);
2010 writeU8((u8*)buf, type);
2011 data.append(buf, 1);
2013 data.append(serializeString32(
2014 obj->getClientInitializationData(client->net_proto_version)));
2016 // Add to known objects
2017 client->m_known_objects.insert(id);
2019 obj->m_known_by_count++;
2022 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2023 pkt.putRawString(data.c_str(), data.size());
2026 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2027 << removed_count << " removed, " << added_count << " added, "
2028 << "packet size is " << pkt.getSize() << std::endl;
2031 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2034 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2035 datas.size(), peer_id);
2037 pkt.putRawString(datas.c_str(), datas.size());
2039 m_clients.send(pkt.getPeerId(),
2040 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2044 void Server::SendCSMRestrictionFlags(session_t peer_id)
2046 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2047 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2048 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2052 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2054 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2059 inline s32 Server::nextSoundId()
2061 s32 ret = m_next_sound_id;
2062 if (m_next_sound_id == INT32_MAX)
2063 m_next_sound_id = 0; // signed overflow is undefined
2069 s32 Server::playSound(const SimpleSoundSpec &spec,
2070 const ServerSoundParams ¶ms, bool ephemeral)
2072 // Find out initial position of sound
2073 bool pos_exists = false;
2074 v3f pos = params.getPos(m_env, &pos_exists);
2075 // If position is not found while it should be, cancel sound
2076 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2079 // Filter destination clients
2080 std::vector<session_t> dst_clients;
2081 if (!params.to_player.empty()) {
2082 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2084 infostream<<"Server::playSound: Player \""<<params.to_player
2085 <<"\" not found"<<std::endl;
2088 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2089 infostream<<"Server::playSound: Player \""<<params.to_player
2090 <<"\" not connected"<<std::endl;
2093 dst_clients.push_back(player->getPeerId());
2095 std::vector<session_t> clients = m_clients.getClientIDs();
2097 for (const session_t client_id : clients) {
2098 RemotePlayer *player = m_env->getPlayer(client_id);
2101 if (!params.exclude_player.empty() &&
2102 params.exclude_player == player->getName())
2105 PlayerSAO *sao = player->getPlayerSAO();
2110 if(sao->getBasePosition().getDistanceFrom(pos) >
2111 params.max_hear_distance)
2114 dst_clients.push_back(client_id);
2118 if(dst_clients.empty())
2123 ServerPlayingSound *psound = nullptr;
2125 id = -1; // old clients will still use this, so pick a reserved ID
2128 // The sound will exist as a reference in m_playing_sounds
2129 m_playing_sounds[id] = ServerPlayingSound();
2130 psound = &m_playing_sounds[id];
2131 psound->params = params;
2132 psound->spec = spec;
2135 float gain = params.gain * spec.gain;
2136 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2137 pkt << id << spec.name << gain
2138 << (u8) params.type << pos << params.object
2139 << params.loop << params.fade << params.pitch
2142 bool as_reliable = !ephemeral;
2144 for (const u16 dst_client : dst_clients) {
2146 psound->clients.insert(dst_client);
2147 m_clients.send(dst_client, 0, &pkt, as_reliable);
2151 void Server::stopSound(s32 handle)
2153 // Get sound reference
2154 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2155 m_playing_sounds.find(handle);
2156 if (i == m_playing_sounds.end())
2158 ServerPlayingSound &psound = i->second;
2160 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2163 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2164 si != psound.clients.end(); ++si) {
2166 m_clients.send(*si, 0, &pkt, true);
2168 // Remove sound reference
2169 m_playing_sounds.erase(i);
2172 void Server::fadeSound(s32 handle, float step, float gain)
2174 // Get sound reference
2175 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2176 m_playing_sounds.find(handle);
2177 if (i == m_playing_sounds.end())
2180 ServerPlayingSound &psound = i->second;
2181 psound.params.gain = gain;
2183 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2184 pkt << handle << step << gain;
2186 // Backwards compability
2187 bool play_sound = gain > 0;
2188 ServerPlayingSound compat_psound = psound;
2189 compat_psound.clients.clear();
2191 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2192 compat_pkt << handle;
2194 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2195 it != psound.clients.end();) {
2196 if (m_clients.getProtocolVersion(*it) >= 32) {
2198 m_clients.send(*it, 0, &pkt, true);
2201 compat_psound.clients.insert(*it);
2203 m_clients.send(*it, 0, &compat_pkt, true);
2204 psound.clients.erase(it++);
2208 // Remove sound reference
2209 if (!play_sound || psound.clients.empty())
2210 m_playing_sounds.erase(i);
2212 if (play_sound && !compat_psound.clients.empty()) {
2213 // Play new sound volume on older clients
2214 playSound(compat_psound.spec, compat_psound.params);
2218 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2221 float maxd = far_d_nodes * BS;
2222 v3f p_f = intToFloat(p, BS);
2223 v3s16 block_pos = getNodeBlockPos(p);
2225 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2228 std::vector<session_t> clients = m_clients.getClientIDs();
2229 ClientInterface::AutoLock clientlock(m_clients);
2231 for (session_t client_id : clients) {
2232 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2236 RemotePlayer *player = m_env->getPlayer(client_id);
2237 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2239 // If player is far away, only set modified blocks not sent
2240 if (!client->isBlockSent(block_pos) || (sao &&
2241 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2243 far_players->emplace(client_id);
2245 client->SetBlockNotSent(block_pos);
2250 m_clients.send(client_id, 0, &pkt, true);
2254 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2255 float far_d_nodes, bool remove_metadata)
2257 float maxd = far_d_nodes * BS;
2258 v3f p_f = intToFloat(p, BS);
2259 v3s16 block_pos = getNodeBlockPos(p);
2261 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2262 pkt << p << n.param0 << n.param1 << n.param2
2263 << (u8) (remove_metadata ? 0 : 1);
2265 std::vector<session_t> clients = m_clients.getClientIDs();
2266 ClientInterface::AutoLock clientlock(m_clients);
2268 for (session_t client_id : clients) {
2269 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2273 RemotePlayer *player = m_env->getPlayer(client_id);
2274 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2276 // If player is far away, only set modified blocks not sent
2277 if (!client->isBlockSent(block_pos) || (sao &&
2278 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2280 far_players->emplace(client_id);
2282 client->SetBlockNotSent(block_pos);
2287 m_clients.send(client_id, 0, &pkt, true);
2291 void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
2293 NodeMetadataList meta_updates_list(false);
2294 std::ostringstream os(std::ios::binary);
2296 std::vector<session_t> clients = m_clients.getClientIDs();
2297 ClientInterface::AutoLock clientlock(m_clients);
2299 for (session_t i : clients) {
2300 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2304 ServerActiveObject *player = getPlayerSAO(i);
2307 player_pos = floatToInt(player->getBasePosition(), BS);
2309 for (const v3s16 pos : positions) {
2310 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2315 v3s16 block_pos = getNodeBlockPos(pos);
2316 if (!client->isBlockSent(block_pos) ||
2317 player_pos.getDistanceFrom(pos) > far_d_nodes) {
2318 client->SetBlockNotSent(block_pos);
2322 // Add the change to send list
2323 meta_updates_list.set(pos, meta);
2325 if (meta_updates_list.size() == 0)
2328 // Send the meta changes
2330 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2331 std::string raw = os.str();
2333 compressZlib(raw, os);
2335 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
2336 pkt.putLongString(os.str());
2339 meta_updates_list.clear();
2343 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2344 u16 net_proto_version, SerializedBlockCache *cache)
2346 thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2347 std::string s, *sptr = nullptr;
2350 auto it = cache->find({block->getPos(), ver});
2351 if (it != cache->end())
2355 // Serialize the block in the right format
2357 std::ostringstream os(std::ios_base::binary);
2358 block->serialize(os, ver, false, net_compression_level);
2359 block->serializeNetworkSpecific(os);
2364 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2365 pkt << block->getPos();
2366 pkt.putRawString(*sptr);
2369 // Store away in cache
2370 if (cache && sptr == &s)
2371 (*cache)[{block->getPos(), ver}] = std::move(s);
2374 void Server::SendBlocks(float dtime)
2376 MutexAutoLock envlock(m_env_mutex);
2377 //TODO check if one big lock could be faster then multiple small ones
2379 std::vector<PrioritySortedBlockTransfer> queue;
2381 u32 total_sending = 0, unique_clients = 0;
2384 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2386 std::vector<session_t> clients = m_clients.getClientIDs();
2388 ClientInterface::AutoLock clientlock(m_clients);
2389 for (const session_t client_id : clients) {
2390 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2395 total_sending += client->getSendingCount();
2396 const auto old_count = queue.size();
2397 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2398 unique_clients += queue.size() > old_count ? 1 : 0;
2403 // Lowest priority number comes first.
2404 // Lowest is most important.
2405 std::sort(queue.begin(), queue.end());
2407 ClientInterface::AutoLock clientlock(m_clients);
2409 // Maximal total count calculation
2410 // The per-client block sends is halved with the maximal online users
2411 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2412 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2414 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2415 Map &map = m_env->getMap();
2417 SerializedBlockCache cache, *cache_ptr = nullptr;
2418 if (unique_clients > 1) {
2419 // caching is pointless with a single client
2423 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2424 if (total_sending >= max_blocks_to_send)
2427 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2431 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2436 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2437 client->net_proto_version, cache_ptr);
2439 client->SentBlock(block_to_send.pos);
2444 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2446 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2450 ClientInterface::AutoLock clientlock(m_clients);
2451 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2452 if (!client || client->isBlockSent(blockpos))
2454 SendBlockNoLock(peer_id, block, client->serialization_version,
2455 client->net_proto_version);
2460 bool Server::addMediaFile(const std::string &filename,
2461 const std::string &filepath, std::string *filedata_to,
2462 std::string *digest_to)
2464 // If name contains illegal characters, ignore the file
2465 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2466 infostream << "Server: ignoring illegal file name: \""
2467 << filename << "\"" << std::endl;
2470 // If name is not in a supported format, ignore it
2471 const char *supported_ext[] = {
2472 ".png", ".jpg", ".bmp", ".tga",
2474 ".x", ".b3d", ".obj",
2475 // Custom translation file format
2479 if (removeStringEnd(filename, supported_ext).empty()) {
2480 infostream << "Server: ignoring unsupported file extension: \""
2481 << filename << "\"" << std::endl;
2484 // Ok, attempt to load the file and add to cache
2487 std::string filedata;
2488 if (!fs::ReadFile(filepath, filedata)) {
2489 errorstream << "Server::addMediaFile(): Failed to open \""
2490 << filename << "\" for reading" << std::endl;
2494 if (filedata.empty()) {
2495 errorstream << "Server::addMediaFile(): Empty file \""
2496 << filepath << "\"" << std::endl;
2501 sha1.addBytes(filedata.c_str(), filedata.length());
2503 unsigned char *digest = sha1.getDigest();
2504 std::string sha1_base64 = base64_encode(digest, 20);
2505 std::string sha1_hex = hex_encode((char*) digest, 20);
2507 *digest_to = std::string((char*) digest, 20);
2511 m_media[filename] = MediaInfo(filepath, sha1_base64);
2512 verbosestream << "Server: " << sha1_hex << " is " << filename
2516 *filedata_to = std::move(filedata);
2520 void Server::fillMediaCache()
2522 infostream << "Server: Calculating media file checksums" << std::endl;
2524 // Collect all media file paths
2525 std::vector<std::string> paths;
2527 // ordered in descending priority
2528 paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2529 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2530 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2531 m_modmgr->getModsMediaPaths(paths);
2533 // Collect media file information from paths into cache
2534 for (const std::string &mediapath : paths) {
2535 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2536 for (const fs::DirListNode &dln : dirlist) {
2537 if (dln.dir) // Ignore dirs (already in paths)
2540 const std::string &filename = dln.name;
2541 if (m_media.find(filename) != m_media.end()) // Do not override
2544 std::string filepath = mediapath;
2545 filepath.append(DIR_DELIM).append(filename);
2546 addMediaFile(filename, filepath);
2550 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2553 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2556 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2559 std::string lang_suffix;
2560 lang_suffix.append(".").append(lang_code).append(".tr");
2561 for (const auto &i : m_media) {
2562 if (i.second.no_announce)
2564 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2571 for (const auto &i : m_media) {
2572 if (i.second.no_announce)
2574 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2576 pkt << i.first << i.second.sha1_digest;
2579 pkt << g_settings->get("remote_media");
2582 verbosestream << "Server: Announcing files to id(" << peer_id
2583 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2586 struct SendableMedia
2592 SendableMedia(const std::string &name, const std::string &path,
2593 std::string &&data):
2594 name(name), path(path), data(std::move(data))
2598 void Server::sendRequestedMedia(session_t peer_id,
2599 const std::vector<std::string> &tosend)
2601 verbosestream<<"Server::sendRequestedMedia(): "
2602 <<"Sending files to client"<<std::endl;
2606 // Put 5kB in one bunch (this is not accurate)
2607 u32 bytes_per_bunch = 5000;
2609 std::vector< std::vector<SendableMedia> > file_bunches;
2610 file_bunches.emplace_back();
2612 u32 file_size_bunch_total = 0;
2614 for (const std::string &name : tosend) {
2615 if (m_media.find(name) == m_media.end()) {
2616 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2617 <<"unknown file \""<<(name)<<"\""<<std::endl;
2621 const auto &m = m_media[name];
2625 if (!fs::ReadFile(m.path, data)) {
2626 errorstream << "Server::sendRequestedMedia(): Failed to read \""
2627 << name << "\"" << std::endl;
2630 file_size_bunch_total += data.size();
2633 file_bunches.back().emplace_back(name, m.path, std::move(data));
2635 // Start next bunch if got enough data
2636 if(file_size_bunch_total >= bytes_per_bunch) {
2637 file_bunches.emplace_back();
2638 file_size_bunch_total = 0;
2643 /* Create and send packets */
2645 u16 num_bunches = file_bunches.size();
2646 for (u16 i = 0; i < num_bunches; i++) {
2649 u16 total number of texture bunches
2650 u16 index of this bunch
2651 u32 number of files in this bunch
2660 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2661 pkt << num_bunches << i << (u32) file_bunches[i].size();
2663 for (const SendableMedia &j : file_bunches[i]) {
2665 pkt.putLongString(j.data);
2668 verbosestream << "Server::sendRequestedMedia(): bunch "
2669 << i << "/" << num_bunches
2670 << " files=" << file_bunches[i].size()
2671 << " size=" << pkt.getSize() << std::endl;
2676 void Server::stepPendingDynMediaCallbacks(float dtime)
2678 MutexAutoLock lock(m_env_mutex);
2680 for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2681 it->second.expiry_timer -= dtime;
2682 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2689 const auto &name = it->second.filename;
2690 if (!name.empty()) {
2691 assert(m_media.count(name));
2692 // if no_announce isn't set we're definitely deleting the wrong file!
2693 sanity_check(m_media[name].no_announce);
2695 fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2696 m_media.erase(name);
2698 getScriptIface()->freeDynamicMediaCallback(it->first);
2699 it = m_pending_dyn_media.erase(it);
2703 void Server::SendMinimapModes(session_t peer_id,
2704 std::vector<MinimapMode> &modes, size_t wanted_mode)
2706 RemotePlayer *player = m_env->getPlayer(peer_id);
2708 if (player->getPeerId() == PEER_ID_INEXISTENT)
2711 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2712 pkt << (u16)modes.size() << (u16)wanted_mode;
2714 for (auto &mode : modes)
2715 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2720 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2722 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2726 pkt << false; // Remove inventory
2728 pkt << true; // Update inventory
2730 // Serialization & NetworkPacket isn't a love story
2731 std::ostringstream os(std::ios_base::binary);
2732 inventory->serialize(os);
2733 inventory->setModified(false);
2735 const std::string &os_str = os.str();
2736 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2737 pkt.putRawString(os_str);
2740 if (peer_id == PEER_ID_INEXISTENT)
2741 m_clients.sendToAll(&pkt);
2746 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2748 // Lookup player name, to filter detached inventories just after
2749 std::string peer_name;
2750 if (peer_id != PEER_ID_INEXISTENT) {
2751 peer_name = getClient(peer_id, CS_Created)->getName();
2754 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2755 sendDetachedInventory(inv, name, peer_id);
2758 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2765 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2767 infostream << "Server::DiePlayer(): Player "
2768 << playersao->getPlayer()->getName()
2769 << " dies" << std::endl;
2771 playersao->clearParentAttachment();
2773 // Trigger scripted stuff
2774 m_script->on_dieplayer(playersao, reason);
2776 SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2779 void Server::RespawnPlayer(session_t peer_id)
2781 PlayerSAO *playersao = getPlayerSAO(peer_id);
2784 infostream << "Server::RespawnPlayer(): Player "
2785 << playersao->getPlayer()->getName()
2786 << " respawns" << std::endl;
2788 const auto *prop = playersao->accessObjectProperties();
2789 playersao->setHP(prop->hp_max,
2790 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2791 playersao->setBreath(prop->breath_max);
2793 bool repositioned = m_script->on_respawnplayer(playersao);
2794 if (!repositioned) {
2795 // setPos will send the new position to client
2796 playersao->setPos(findSpawnPos());
2801 void Server::DenySudoAccess(session_t peer_id)
2803 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2808 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2809 const std::string &custom_reason, bool reconnect)
2811 SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2812 m_clients.event(peer_id, CSE_SetDenied);
2813 DisconnectPeer(peer_id);
2816 void Server::DisconnectPeer(session_t peer_id)
2818 m_modchannel_mgr->leaveAllChannels(peer_id);
2819 m_con->DisconnectPeer(peer_id);
2822 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2825 RemoteClient* client = getClient(peer_id, CS_Invalid);
2827 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2829 // Right now, the auth mechs don't change between login and sudo mode.
2830 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2831 client->allowed_sudo_mechs = sudo_auth_mechs;
2833 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2834 << g_settings->getFloat("dedicated_server_step")
2838 m_clients.event(peer_id, CSE_AuthAccept);
2840 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2842 // We only support SRP right now
2843 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2845 resp_pkt << sudo_auth_mechs;
2847 m_clients.event(peer_id, CSE_SudoSuccess);
2851 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2853 std::wstring message;
2856 Clear references to playing sounds
2858 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2859 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2860 ServerPlayingSound &psound = i->second;
2861 psound.clients.erase(peer_id);
2862 if (psound.clients.empty())
2863 m_playing_sounds.erase(i++);
2868 // clear formspec info so the next client can't abuse the current state
2869 m_formspec_state_data.erase(peer_id);
2871 RemotePlayer *player = m_env->getPlayer(peer_id);
2873 /* Run scripts and remove from environment */
2875 PlayerSAO *playersao = player->getPlayerSAO();
2878 playersao->clearChildAttachments();
2879 playersao->clearParentAttachment();
2881 // inform connected clients
2882 const std::string &player_name = player->getName();
2883 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2884 // (u16) 1 + std::string represents a vector serialization representation
2885 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2886 m_clients.sendToAll(¬ice);
2888 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2890 playersao->disconnected();
2897 if (player && reason != CDR_DENY) {
2898 std::ostringstream os(std::ios_base::binary);
2899 std::vector<session_t> clients = m_clients.getClientIDs();
2901 for (const session_t client_id : clients) {
2903 RemotePlayer *player = m_env->getPlayer(client_id);
2907 // Get name of player
2908 os << player->getName() << " ";
2911 std::string name = player->getName();
2912 actionstream << name << " "
2913 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2914 << " List of players: " << os.str() << std::endl;
2916 m_admin_chat->outgoing_queue.push_back(
2917 new ChatEventNick(CET_NICK_REMOVE, name));
2921 MutexAutoLock env_lock(m_env_mutex);
2922 m_clients.DeleteClient(peer_id);
2926 // Send leave chat message to all remaining clients
2927 if (!message.empty()) {
2928 SendChatMessage(PEER_ID_INEXISTENT,
2929 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2933 void Server::UpdateCrafting(RemotePlayer *player)
2935 InventoryList *clist = player->inventory.getList("craft");
2936 if (!clist || clist->getSize() == 0)
2939 if (!clist->checkModified())
2942 // Get a preview for crafting
2944 InventoryLocation loc;
2945 loc.setPlayer(player->getName());
2946 std::vector<ItemStack> output_replacements;
2947 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2948 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2951 InventoryList *plist = player->inventory.getList("craftpreview");
2952 if (plist && plist->getSize() >= 1) {
2953 // Put the new preview in
2954 plist->changeItem(0, preview);
2958 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2960 if (evt->type == CET_NICK_ADD) {
2961 // The terminal informed us of its nick choice
2962 m_admin_nick = ((ChatEventNick *)evt)->nick;
2963 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2964 errorstream << "You haven't set up an account." << std::endl
2965 << "Please log in using the client as '"
2966 << m_admin_nick << "' with a secure password." << std::endl
2967 << "Until then, you can't execute admin tasks via the console," << std::endl
2968 << "and everybody can claim the user account instead of you," << std::endl
2969 << "giving them full control over this server." << std::endl;
2972 assert(evt->type == CET_CHAT);
2973 handleAdminChat((ChatEventChat *)evt);
2977 std::wstring Server::handleChat(const std::string &name,
2978 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2980 // If something goes wrong, this player is to blame
2981 RollbackScopeActor rollback_scope(m_rollback,
2982 std::string("player:") + name);
2984 if (g_settings->getBool("strip_color_codes"))
2985 wmessage = unescape_enriched(wmessage);
2988 switch (player->canSendChatMessage()) {
2989 case RPLAYER_CHATRESULT_FLOODING: {
2990 std::wstringstream ws;
2991 ws << L"You cannot send more messages. You are limited to "
2992 << g_settings->getFloat("chat_message_limit_per_10sec")
2993 << L" messages per 10 seconds.";
2996 case RPLAYER_CHATRESULT_KICK:
2997 DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
2998 "You have been kicked due to message flooding.");
3000 case RPLAYER_CHATRESULT_OK:
3003 FATAL_ERROR("Unhandled chat filtering result found.");
3007 if (m_max_chatmessage_length > 0
3008 && wmessage.length() > m_max_chatmessage_length) {
3009 return L"Your message exceed the maximum chat message limit set on the server. "
3010 L"It was refused. Send a shorter message";
3013 auto message = trim(wide_to_utf8(wmessage));
3014 if (message.empty())
3017 if (message.find_first_of("\n\r") != std::wstring::npos) {
3018 return L"Newlines are not permitted in chat messages";
3021 // Run script hook, exit if script ate the chat message
3022 if (m_script->on_chat_message(name, message))
3027 // Whether to send line to the player that sent the message, or to all players
3028 bool broadcast_line = true;
3030 if (check_shout_priv && !checkPriv(name, "shout")) {
3031 line += L"-!- You don't have permission to shout.";
3032 broadcast_line = false;
3035 Workaround for fixing chat on Android. Lua doesn't handle
3036 the Cyrillic alphabet and some characters on older Android devices
3039 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3041 line += utf8_to_wide(m_script->formatChatMessage(name,
3042 wide_to_utf8(wmessage)));
3047 Tell calling method to send the message to sender
3049 if (!broadcast_line)
3053 Send the message to others
3055 actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3057 ChatMessage chatmsg(line);
3059 std::vector<session_t> clients = m_clients.getClientIDs();
3060 for (u16 cid : clients)
3061 SendChatMessage(cid, chatmsg);
3066 void Server::handleAdminChat(const ChatEventChat *evt)
3068 std::string name = evt->nick;
3069 std::wstring wmessage = evt->evt_msg;
3071 std::wstring answer = handleChat(name, wmessage);
3073 // If asked to send answer to sender
3074 if (!answer.empty()) {
3075 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3079 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3081 RemoteClient *client = getClientNoEx(peer_id,state_min);
3083 throw ClientNotFoundException("Client not found");
3087 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3089 return m_clients.getClientNoEx(peer_id, state_min);
3092 std::string Server::getPlayerName(session_t peer_id)
3094 RemotePlayer *player = m_env->getPlayer(peer_id);
3096 return "[id="+itos(peer_id)+"]";
3097 return player->getName();
3100 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3102 RemotePlayer *player = m_env->getPlayer(peer_id);
3105 return player->getPlayerSAO();
3108 std::string Server::getStatusString()
3110 std::ostringstream os(std::ios_base::binary);
3113 os << "version: " << g_version_string;
3115 os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3117 os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3119 os << " | max lag: " << std::setprecision(3);
3120 os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3122 // Information about clients
3124 os << " | clients: ";
3126 std::vector<session_t> clients = m_clients.getClientIDs();
3127 for (session_t client_id : clients) {
3128 RemotePlayer *player = m_env->getPlayer(client_id);
3130 // Get name of player
3131 const char *name = player ? player->getName() : "<unknown>";
3133 // Add name to information string
3142 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3143 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3145 if (!g_settings->get("motd").empty())
3146 os << std::endl << "# Server: " << g_settings->get("motd");
3151 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3153 std::set<std::string> privs;
3154 m_script->getAuth(name, NULL, &privs);
3158 bool Server::checkPriv(const std::string &name, const std::string &priv)
3160 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3161 return (privs.count(priv) != 0);
3164 void Server::reportPrivsModified(const std::string &name)
3167 std::vector<session_t> clients = m_clients.getClientIDs();
3168 for (const session_t client_id : clients) {
3169 RemotePlayer *player = m_env->getPlayer(client_id);
3170 reportPrivsModified(player->getName());
3173 RemotePlayer *player = m_env->getPlayer(name.c_str());
3176 SendPlayerPrivileges(player->getPeerId());
3177 PlayerSAO *sao = player->getPlayerSAO();
3180 sao->updatePrivileges(
3181 getPlayerEffectivePrivs(name),
3186 void Server::reportInventoryFormspecModified(const std::string &name)
3188 RemotePlayer *player = m_env->getPlayer(name.c_str());
3191 SendPlayerInventoryFormspec(player->getPeerId());
3194 void Server::reportFormspecPrependModified(const std::string &name)
3196 RemotePlayer *player = m_env->getPlayer(name.c_str());
3199 SendPlayerFormspecPrepend(player->getPeerId());
3202 void Server::setIpBanned(const std::string &ip, const std::string &name)
3204 m_banmanager->add(ip, name);
3207 void Server::unsetIpBanned(const std::string &ip_or_name)
3209 m_banmanager->remove(ip_or_name);
3212 std::string Server::getBanDescription(const std::string &ip_or_name)
3214 return m_banmanager->getBanDescription(ip_or_name);
3217 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3219 // m_env will be NULL if the server is initializing
3223 if (m_admin_nick == name && !m_admin_nick.empty()) {
3224 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3227 RemotePlayer *player = m_env->getPlayer(name);
3232 if (player->getPeerId() == PEER_ID_INEXISTENT)
3235 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3238 bool Server::showFormspec(const char *playername, const std::string &formspec,
3239 const std::string &formname)
3241 // m_env will be NULL if the server is initializing
3245 RemotePlayer *player = m_env->getPlayer(playername);
3249 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3253 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3258 u32 id = player->addHud(form);
3260 SendHUDAdd(player->getPeerId(), id, form);
3265 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3269 HudElement* todel = player->removeHud(id);
3276 SendHUDRemove(player->getPeerId(), id);
3280 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3285 SendHUDChange(player->getPeerId(), id, stat, data);
3289 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3294 u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3295 if (new_hud_flags == player->hud_flags) // no change
3298 SendHUDSetFlags(player->getPeerId(), flags, mask);
3299 player->hud_flags = new_hud_flags;
3301 PlayerSAO* playersao = player->getPlayerSAO();
3306 m_script->player_event(playersao, "hud_changed");
3310 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3315 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3318 player->setHotbarItemcount(hotbar_itemcount);
3319 std::ostringstream os(std::ios::binary);
3320 writeS32(os, hotbar_itemcount);
3321 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3325 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3330 player->setHotbarImage(name);
3331 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3334 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3339 player->setHotbarSelectedImage(name);
3340 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3343 Address Server::getPeerAddress(session_t peer_id)
3345 // Note that this is only set after Init was received in Server::handleCommand_Init
3346 return getClient(peer_id, CS_Invalid)->getAddress();
3349 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3350 v2s32 animation_frames[4], f32 frame_speed)
3352 sanity_check(player);
3353 player->setLocalAnimations(animation_frames, frame_speed);
3354 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3357 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3359 sanity_check(player);
3360 player->eye_offset_first = first;
3361 player->eye_offset_third = third;
3362 SendEyeOffset(player->getPeerId(), first, third);
3365 void Server::setSky(RemotePlayer *player, const SkyboxParams ¶ms)
3367 sanity_check(player);
3368 player->setSky(params);
3369 SendSetSky(player->getPeerId(), params);
3372 void Server::setSun(RemotePlayer *player, const SunParams ¶ms)
3374 sanity_check(player);
3375 player->setSun(params);
3376 SendSetSun(player->getPeerId(), params);
3379 void Server::setMoon(RemotePlayer *player, const MoonParams ¶ms)
3381 sanity_check(player);
3382 player->setMoon(params);
3383 SendSetMoon(player->getPeerId(), params);
3386 void Server::setStars(RemotePlayer *player, const StarParams ¶ms)
3388 sanity_check(player);
3389 player->setStars(params);
3390 SendSetStars(player->getPeerId(), params);
3393 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3395 sanity_check(player);
3396 player->setCloudParams(params);
3397 SendCloudParams(player->getPeerId(), params);
3400 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3403 sanity_check(player);
3404 player->overrideDayNightRatio(do_override, ratio);
3405 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3408 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3410 sanity_check(player);
3411 player->setLighting(lighting);
3412 SendSetLighting(player->getPeerId(), lighting);
3415 void Server::notifyPlayers(const std::wstring &msg)
3417 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3420 void Server::spawnParticle(const std::string &playername,
3421 const ParticleParameters &p)
3423 // m_env will be NULL if the server is initializing
3427 session_t peer_id = PEER_ID_INEXISTENT;
3429 if (!playername.empty()) {
3430 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3433 peer_id = player->getPeerId();
3434 proto_ver = player->protocol_version;
3437 SendSpawnParticle(peer_id, proto_ver, p);
3440 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3441 ServerActiveObject *attached, const std::string &playername)
3443 // m_env will be NULL if the server is initializing
3447 session_t peer_id = PEER_ID_INEXISTENT;
3449 if (!playername.empty()) {
3450 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3453 peer_id = player->getPeerId();
3454 proto_ver = player->protocol_version;
3457 u16 attached_id = attached ? attached->getId() : 0;
3460 if (attached_id == 0)
3461 id = m_env->addParticleSpawner(p.time);
3463 id = m_env->addParticleSpawner(p.time, attached_id);
3465 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3469 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3471 // m_env will be NULL if the server is initializing
3473 throw ServerError("Can't delete particle spawners during initialisation!");
3475 session_t peer_id = PEER_ID_INEXISTENT;
3476 if (!playername.empty()) {
3477 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3480 peer_id = player->getPeerId();
3483 m_env->deleteParticleSpawner(id);
3484 SendDeleteParticleSpawner(peer_id, id);
3487 bool Server::dynamicAddMedia(std::string filepath,
3488 const u32 token, const std::string &to_player, bool ephemeral)
3490 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3491 auto it = m_media.find(filename);
3492 if (it != m_media.end()) {
3493 // Allow the same path to be "added" again in certain conditions
3494 if (ephemeral || it->second.path != filepath) {
3495 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3496 << "\" already exists in media cache" << std::endl;
3501 // Load the file and add it to our media cache
3502 std::string filedata, raw_hash;
3503 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3508 // Create a copy of the file and swap out the path, this removes the
3509 // requirement that mods keep the file accessible at the original path.
3510 filepath = fs::CreateTempFile();
3511 bool ok = ([&] () -> bool {
3512 if (filepath.empty())
3514 std::ofstream os(filepath.c_str(), std::ios::binary);
3522 errorstream << "Server: failed to create a copy of media file "
3523 << "\"" << filename << "\"" << std::endl;
3524 m_media.erase(filename);
3527 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3528 << filepath << std::endl;
3530 m_media[filename].path = filepath;
3531 m_media[filename].no_announce = true;
3532 // stepPendingDynMediaCallbacks will clean this up later.
3533 } else if (!to_player.empty()) {
3534 m_media[filename].no_announce = true;
3537 // Push file to existing clients
3538 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3539 pkt << raw_hash << filename << (bool)ephemeral;
3541 NetworkPacket legacy_pkt = pkt;
3543 // Newer clients get asked to fetch the file (asynchronous)
3545 // Older clients have an awful hack that just throws the data at them
3546 legacy_pkt.putLongString(filedata);
3548 std::unordered_set<session_t> delivered, waiting;
3550 ClientInterface::AutoLock clientlock(m_clients);
3551 for (auto &pair : m_clients.getClientList()) {
3552 if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3554 If a client is in the DefinitionsSent state it is too late to
3555 transfer the file via sendMediaAnnouncement() but at the same
3556 time the client cannot accept a media push yet.
3557 Short of artificially delaying the joining process there is no
3558 way for the server to resolve this so we (currently) opt not to.
3560 warningstream << "The media \"" << filename << "\" (dynamic) could "
3561 "not be delivered to " << pair.second->getName()
3562 << " due to a race condition." << std::endl;
3565 if (pair.second->getState() < CS_Active)
3568 const auto proto_ver = pair.second->net_proto_version;
3572 const session_t peer_id = pair.second->peer_id;
3573 if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3576 if (proto_ver < 40) {
3577 delivered.emplace(peer_id);
3579 The network layer only guarantees ordered delivery inside a channel.
3580 Since the very next packet could be one that uses the media, we have
3581 to push the media over ALL channels to ensure it is processed before
3582 it is used. In practice this means channels 1 and 0.
3584 m_clients.send(peer_id, 1, &legacy_pkt, true);
3585 m_clients.send(peer_id, 0, &legacy_pkt, true);
3587 waiting.emplace(peer_id);
3588 Send(peer_id, &pkt);
3593 // Run callback for players that already had the file delivered (legacy-only)
3594 for (session_t peer_id : delivered) {
3595 if (auto player = m_env->getPlayer(peer_id))
3596 getScriptIface()->on_dynamic_media_added(token, player->getName());
3599 // Save all others in our pending state
3600 auto &state = m_pending_dyn_media[token];
3601 state.waiting_players = std::move(waiting);
3602 // regardless of success throw away the callback after a while
3603 state.expiry_timer = 60.0f;
3605 state.filename = filename;
3610 // actions: time-reversed list
3611 // Return value: success/failure
3612 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3613 std::list<std::string> *log)
3615 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3616 ServerMap *map = (ServerMap*)(&m_env->getMap());
3618 // Fail if no actions to handle
3619 if (actions.empty()) {
3621 log->push_back("Nothing to do.");
3628 for (const RollbackAction &action : actions) {
3630 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3633 std::ostringstream os;
3634 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3635 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3637 log->push_back(os.str());
3639 std::ostringstream os;
3640 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3641 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3643 log->push_back(os.str());
3647 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3648 <<" failed"<<std::endl;
3650 // Call it done if less than half failed
3651 return num_failed <= num_tried/2;
3654 // IGameDef interface
3656 IItemDefManager *Server::getItemDefManager()
3661 const NodeDefManager *Server::getNodeDefManager()
3666 ICraftDefManager *Server::getCraftDefManager()
3671 u16 Server::allocateUnknownNodeId(const std::string &name)
3673 return m_nodedef->allocateDummy(name);
3676 IWritableItemDefManager *Server::getWritableItemDefManager()
3681 NodeDefManager *Server::getWritableNodeDefManager()
3686 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3691 const std::vector<ModSpec> & Server::getMods() const
3693 return m_modmgr->getMods();
3696 const ModSpec *Server::getModSpec(const std::string &modname) const
3698 return m_modmgr->getModSpec(modname);
3701 std::string Server::getBuiltinLuaPath()
3703 return porting::path_share + DIR_DELIM + "builtin";
3706 v3f Server::findSpawnPos()
3708 ServerMap &map = m_env->getServerMap();
3710 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3711 return nodeposf * BS;
3713 bool is_good = false;
3714 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3715 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3717 // Try to find a good place a few times
3718 for (s32 i = 0; i < 4000 && !is_good; i++) {
3719 s32 range = MYMIN(1 + i, range_max);
3720 // We're going to try to throw the player to this position
3721 v2s16 nodepos2d = v2s16(
3722 -range + (myrand() % (range * 2)),
3723 -range + (myrand() % (range * 2)));
3724 // Get spawn level at point
3725 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3726 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3727 // signify an unsuitable spawn position, or if outside limits.
3728 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3729 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3732 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3733 // Consecutive empty nodes
3736 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3737 // avoid obstructions in already-generated mapblocks.
3738 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3739 // no obstructions, but mapgen decorations are generated after spawn so
3740 // the player may end up inside one.
3741 for (s32 i = 0; i < 8; i++) {
3742 v3s16 blockpos = getNodeBlockPos(nodepos);
3743 map.emergeBlock(blockpos, true);
3744 content_t c = map.getNode(nodepos).getContent();
3746 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3747 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3748 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3750 if (air_count >= 2) {
3751 // Spawn in lower empty node
3753 nodeposf = intToFloat(nodepos, BS);
3754 // Don't spawn the player outside map boundaries
3755 if (objectpos_over_limit(nodeposf))
3756 // Exit this loop, positions above are probably over limit
3759 // Good position found, cause an exit from main loop
3773 // No suitable spawn point found, return fallback 0,0,0
3774 return v3f(0.0f, 0.0f, 0.0f);
3777 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3779 if (delay == 0.0f) {
3780 // No delay, shutdown immediately
3781 m_shutdown_state.is_requested = true;
3782 // only print to the infostream, a chat message saying
3783 // "Server Shutting Down" is sent when the server destructs.
3784 infostream << "*** Immediate Server shutdown requested." << std::endl;
3785 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3786 // Negative delay, cancel shutdown if requested
3787 m_shutdown_state.reset();
3788 std::wstringstream ws;
3790 ws << L"*** Server shutdown canceled.";
3792 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3793 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3794 // m_shutdown_* are already handled, skip.
3796 } else if (delay > 0.0f) {
3797 // Positive delay, tell the clients when the server will shut down
3798 std::wstringstream ws;
3800 ws << L"*** Server shutting down in "
3801 << duration_to_string(myround(delay)).c_str()
3804 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3805 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3808 m_shutdown_state.trigger(delay, msg, reconnect);
3811 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3814 Try to get an existing player
3816 RemotePlayer *player = m_env->getPlayer(name);
3818 // If player is already connected, cancel
3819 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3820 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3825 If player with the wanted peer_id already exists, cancel.
3827 if (m_env->getPlayer(peer_id)) {
3828 infostream<<"emergePlayer(): Player with wrong name but same"
3829 " peer_id already exists"<<std::endl;
3834 player = new RemotePlayer(name, idef());
3837 bool newplayer = false;
3840 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3842 // Complete init with server parts
3843 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3844 player->protocol_version = proto_version;
3848 m_script->on_newplayer(playersao);
3854 bool Server::registerModStorage(ModMetadata *storage)
3856 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3857 errorstream << "Unable to register same mod storage twice. Storage name: "
3858 << storage->getModName() << std::endl;
3862 m_mod_storages[storage->getModName()] = storage;
3866 void Server::unregisterModStorage(const std::string &name)
3868 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3869 if (it != m_mod_storages.end())
3870 m_mod_storages.erase(name);
3873 void dedicated_server_loop(Server &server, bool &kill)
3875 verbosestream<<"dedicated_server_loop()"<<std::endl;
3877 IntervalLimiter m_profiler_interval;
3879 static thread_local const float steplen =
3880 g_settings->getFloat("dedicated_server_step");
3881 static thread_local const float profiler_print_interval =
3882 g_settings->getFloat("profiler_print_interval");
3885 * The dedicated server loop only does time-keeping (in Server::step) and
3886 * provides a way to main.cpp to kill the server externally (bool &kill).
3890 // This is kind of a hack but can be done like this
3891 // because server.step() is very light
3892 sleep_ms((int)(steplen*1000.0));
3893 server.step(steplen);
3895 if (server.isShutdownRequested() || kill)
3901 if (profiler_print_interval != 0) {
3902 if(m_profiler_interval.step(steplen, profiler_print_interval))
3904 infostream<<"Profiler:"<<std::endl;
3905 g_profiler->print(infostream);
3906 g_profiler->clear();
3911 infostream << "Dedicated server quitting" << std::endl;
3913 if (g_settings->getBool("server_announce"))
3914 ServerList::sendAnnounce(ServerList::AA_DELETE,
3915 server.m_bind_addr.getPort());
3924 bool Server::joinModChannel(const std::string &channel)
3926 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3927 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3930 bool Server::leaveModChannel(const std::string &channel)
3932 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3935 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3937 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3940 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3944 ModChannel* Server::getModChannel(const std::string &channel)
3946 return m_modchannel_mgr->getModChannel(channel);
3949 void Server::broadcastModChannelMessage(const std::string &channel,
3950 const std::string &message, session_t from_peer)
3952 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3956 if (message.size() > STRING_MAX_LEN) {
3957 warningstream << "ModChannel message too long, dropping before sending "
3958 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3959 << channel << ")" << std::endl;
3964 if (from_peer != PEER_ID_SERVER) {
3965 sender = getPlayerName(from_peer);
3968 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3969 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3970 resp_pkt << channel << sender << message;
3971 for (session_t peer_id : peers) {
3973 if (peer_id == from_peer)
3976 Send(peer_id, &resp_pkt);
3979 if (from_peer != PEER_ID_SERVER) {
3980 m_script->on_modchannel_message(channel, sender, message);
3984 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3986 if (lang_code.empty())
3989 auto it = server_translations.find(lang_code);
3990 if (it != server_translations.end())
3991 return &it->second; // Already loaded
3993 // [] will create an entry
3994 auto *translations = &server_translations[lang_code];
3996 std::string suffix = "." + lang_code + ".tr";
3997 for (const auto &i : m_media) {
3998 if (str_ends_with(i.first, suffix)) {
4000 if (fs::ReadFile(i.second.path, data)) {
4001 translations->loadTranslation(data);
4006 return translations;
4009 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
4011 std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4013 if (!world_mt.readConfigFile(world_mt_path.c_str()))
4014 throw BaseException("Cannot read world.mt!");
4016 std::string backend = world_mt.exists("mod_storage_backend") ?
4017 world_mt.get("mod_storage_backend") : "files";
4018 if (backend == "files")
4019 warningstream << "/!\\ You are using the old mod storage files backend. "
4020 << "This backend is deprecated and may be removed in a future release /!\\"
4021 << std::endl << "Switching to SQLite3 is advised, "
4022 << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4024 return openModStorageDatabase(backend, world_path, world_mt);
4027 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
4028 const std::string &world_path, const Settings &world_mt)
4030 if (backend == "sqlite3")
4031 return new ModMetadataDatabaseSQLite3(world_path);
4033 if (backend == "files")
4034 return new ModMetadataDatabaseFiles(world_path);
4036 if (backend == "dummy")
4037 return new Database_Dummy();
4039 throw BaseException("Mod storage database backend " + backend + " not supported");
4042 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4044 std::string migrate_to = cmd_args.get("migrate-mod-storage");
4046 std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4047 if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4048 errorstream << "Cannot read world.mt!" << std::endl;
4052 std::string backend = world_mt.exists("mod_storage_backend") ?
4053 world_mt.get("mod_storage_backend") : "files";
4054 if (backend == migrate_to) {
4055 errorstream << "Cannot migrate: new backend is same"
4056 << " as the old one" << std::endl;
4060 ModMetadataDatabase *srcdb = nullptr;
4061 ModMetadataDatabase *dstdb = nullptr;
4063 bool succeeded = false;
4066 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4067 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4071 std::vector<std::string> mod_list;
4072 srcdb->listMods(&mod_list);
4073 for (const std::string &modname : mod_list) {
4075 srcdb->getModEntries(modname, &meta);
4076 for (const auto &pair : meta) {
4077 dstdb->setModEntry(modname, pair.first, pair.second);
4085 actionstream << "Successfully migrated the metadata of "
4086 << mod_list.size() << " mods" << std::endl;
4087 world_mt.set("mod_storage_backend", migrate_to);
4088 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4089 errorstream << "Failed to update world.mt!" << std::endl;
4091 actionstream << "world.mt updated" << std::endl;
4093 } catch (BaseException &e) {
4094 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4100 if (succeeded && backend == "files") {
4102 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4103 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4104 if (!fs::Rename(storage_path, backup_path))
4105 warningstream << "After migration, " << storage_path
4106 << " could not be renamed to " << backup_path << std::endl;