]> git.lizzy.rs Git - minetest.git/blob - src/server.cpp
Prevent loading a world with unresolved dependencies (#12542)
[minetest.git] / src / server.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
20 #include "server.h"
21 #include <iostream>
22 #include <queue>
23 #include <algorithm>
24 #include "network/connection.h"
25 #include "network/networkprotocol.h"
26 #include "network/serveropcodes.h"
27 #include "ban.h"
28 #include "environment.h"
29 #include "map.h"
30 #include "threading/mutex_auto_lock.h"
31 #include "constants.h"
32 #include "voxel.h"
33 #include "config.h"
34 #include "version.h"
35 #include "filesys.h"
36 #include "mapblock.h"
37 #include "server/serveractiveobject.h"
38 #include "settings.h"
39 #include "profiler.h"
40 #include "log.h"
41 #include "scripting_server.h"
42 #include "nodedef.h"
43 #include "itemdef.h"
44 #include "craftdef.h"
45 #include "emerge.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"
54 #include "rollback.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"
61 #include "util/hex.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"
73
74 class ClientNotFoundException : public BaseException
75 {
76 public:
77         ClientNotFoundException(const char *s):
78                 BaseException(s)
79         {}
80 };
81
82 class ServerThread : public Thread
83 {
84 public:
85
86         ServerThread(Server *server):
87                 Thread("Server"),
88                 m_server(server)
89         {}
90
91         void *run();
92
93 private:
94         Server *m_server;
95 };
96
97 void *ServerThread::run()
98 {
99         BEGIN_DEBUG_EXCEPTION_HANDLER
100
101         /*
102          * The real business of the server happens on the ServerThread.
103          * How this works:
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.
108          */
109
110         try {
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);
116         }
117
118         while (!stopRequested()) {
119                 try {
120                         m_server->AsyncRunStep();
121
122                         m_server->Receive();
123
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);
131                 }
132         }
133
134         END_DEBUG_EXCEPTION_HANDLER
135
136         return nullptr;
137 }
138
139 v3f ServerPlayingSound::getPos(ServerEnvironment *env, bool *pos_exists) const
140 {
141         if (pos_exists)
142                 *pos_exists = false;
143
144         switch (type ){
145         case SoundLocation::Local:
146                 return v3f(0,0,0);
147         case SoundLocation::Position:
148                 if (pos_exists)
149                         *pos_exists = true;
150                 return pos;
151         case SoundLocation::Object:
152                 {
153                         if (object == 0)
154                                 return v3f(0,0,0);
155                         ServerActiveObject *sao = env->getActiveObject(object);
156                         if (!sao)
157                                 return v3f(0,0,0);
158                         if (pos_exists)
159                                 *pos_exists = true;
160                         return sao->getBasePosition();
161                 }
162         }
163
164         return v3f(0,0,0);
165 }
166
167 void Server::ShutdownState::reset()
168 {
169         m_timer = 0.0f;
170         message.clear();
171         should_reconnect = false;
172         is_requested = false;
173 }
174
175 void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
176 {
177         m_timer = delay;
178         message = msg;
179         should_reconnect = reconnect;
180 }
181
182 void Server::ShutdownState::tick(float dtime, Server *server)
183 {
184         if (m_timer <= 0.0f)
185                 return;
186
187         // Timed shutdown
188         static const float shutdown_msg_times[] =
189         {
190                 1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
191         };
192
193         // Automated messages
194         if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
195                 for (float t : shutdown_msg_times) {
196                         // If shutdown timer matches an automessage, shot it
197                         if (m_timer > t && m_timer - dtime < t) {
198                                 std::wstring periodicMsg = getShutdownTimerMessage();
199
200                                 infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
201                                 server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
202                                 break;
203                         }
204                 }
205         }
206
207         m_timer -= dtime;
208         if (m_timer < 0.0f) {
209                 m_timer = 0.0f;
210                 is_requested = true;
211         }
212 }
213
214 std::wstring Server::ShutdownState::getShutdownTimerMessage() const
215 {
216         std::wstringstream ws;
217         ws << L"*** Server shutting down in "
218                 << duration_to_string(myround(m_timer)).c_str() << ".";
219         return ws.str();
220 }
221
222 /*
223         Server
224 */
225
226 Server::Server(
227                 const std::string &path_world,
228                 const SubgameSpec &gamespec,
229                 bool simple_singleplayer_mode,
230                 Address bind_addr,
231                 bool dedicated,
232                 ChatInterface *iface,
233                 std::string *on_shutdown_errmsg
234         ):
235         m_bind_addr(bind_addr),
236         m_path_world(path_world),
237         m_gamespec(gamespec),
238         m_simple_singleplayer_mode(simple_singleplayer_mode),
239         m_dedicated(dedicated),
240         m_async_fatal_error(""),
241         m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
242                         512,
243                         CONNECTION_TIMEOUT,
244                         m_bind_addr.isIPv6(),
245                         this)),
246         m_itemdef(createItemDefManager()),
247         m_nodedef(createNodeDefManager()),
248         m_craftdef(createCraftDefManager()),
249         m_thread(new ServerThread(this)),
250         m_clients(m_con),
251         m_admin_chat(iface),
252         m_on_shutdown_errmsg(on_shutdown_errmsg),
253         m_modchannel_mgr(new ModChannelMgr())
254 {
255         if (m_path_world.empty())
256                 throw ServerError("Supplied empty world path");
257
258         if (!gamespec.isValid())
259                 throw ServerError("Supplied invalid gamespec");
260
261 #if USE_PROMETHEUS
262         m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
263 #else
264         m_metrics_backend = std::make_unique<MetricsBackend>();
265 #endif
266
267         m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
268         m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
269
270         m_timeofday_gauge = m_metrics_backend->addGauge(
271                         "minetest_core_timeofday",
272                         "Time of day value");
273
274         m_lag_gauge = m_metrics_backend->addGauge(
275                         "minetest_core_latency",
276                         "Latency value (in seconds)");
277
278
279         const std::string aom_types[] = {"reliable", "unreliable"};
280         for (u32 i = 0; i < ARRLEN(aom_types); i++) {
281                 std::string help_str("Number of active object messages generated (");
282                 help_str.append(aom_types[i]).append(")");
283                 m_aom_buffer_counter[i] = m_metrics_backend->addCounter(
284                                 "minetest_core_aom_generated_count", help_str,
285                                 {{"type", aom_types[i]}});
286         }
287
288         m_packet_recv_counter = m_metrics_backend->addCounter(
289                         "minetest_core_server_packet_recv",
290                         "Processable packets received");
291
292         m_packet_recv_processed_counter = m_metrics_backend->addCounter(
293                         "minetest_core_server_packet_recv_processed",
294                         "Valid received packets processed");
295
296         m_map_edit_event_counter = m_metrics_backend->addCounter(
297                         "minetest_core_map_edit_events",
298                         "Number of map edit events");
299
300         m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
301 }
302
303 Server::~Server()
304 {
305
306         // Send shutdown message
307         SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
308                         L"*** Server shutting down"));
309
310         if (m_env) {
311                 MutexAutoLock envlock(m_env_mutex);
312
313                 infostream << "Server: Saving players" << std::endl;
314                 m_env->saveLoadedPlayers();
315
316                 infostream << "Server: Kicking players" << std::endl;
317                 std::string kick_msg;
318                 bool reconnect = false;
319                 if (isShutdownRequested()) {
320                         reconnect = m_shutdown_state.should_reconnect;
321                         kick_msg = m_shutdown_state.message;
322                 }
323                 if (kick_msg.empty()) {
324                         kick_msg = g_settings->get("kick_msg_shutdown");
325                 }
326                 m_env->saveLoadedPlayers(true);
327                 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
328                         kick_msg, reconnect);
329         }
330
331         actionstream << "Server: Shutting down" << std::endl;
332
333         // Do this before stopping the server in case mapgen callbacks need to access
334         // server-controlled resources (like ModStorages). Also do them before
335         // shutdown callbacks since they may modify state that is finalized in a
336         // callback.
337         if (m_emerge)
338                 m_emerge->stopThreads();
339
340         if (m_env) {
341                 MutexAutoLock envlock(m_env_mutex);
342
343                 // Execute script shutdown hooks
344                 infostream << "Executing shutdown hooks" << std::endl;
345                 try {
346                         m_script->on_shutdown();
347                 } catch (ModError &e) {
348                         errorstream << "ModError: " << e.what() << std::endl;
349                         if (m_on_shutdown_errmsg) {
350                                 if (m_on_shutdown_errmsg->empty()) {
351                                         *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
352                                 } else {
353                                         *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
354                                 }
355                         }
356                 }
357
358                 infostream << "Server: Saving environment metadata" << std::endl;
359                 m_env->saveMeta();
360         }
361
362         // Stop threads
363         if (m_thread) {
364                 stop();
365                 delete m_thread;
366         }
367
368         // Write any changes before deletion.
369         if (m_mod_storage_database)
370                 m_mod_storage_database->endSave();
371
372         // Delete things in the reverse order of creation
373         delete m_emerge;
374         delete m_env;
375         delete m_rollback;
376         delete m_mod_storage_database;
377         delete m_banmanager;
378         delete m_itemdef;
379         delete m_nodedef;
380         delete m_craftdef;
381
382         // Deinitialize scripting
383         infostream << "Server: Deinitializing scripting" << std::endl;
384         delete m_script;
385         delete m_startup_server_map; // if available
386         delete m_game_settings;
387
388         while (!m_unsent_map_edit_queue.empty()) {
389                 delete m_unsent_map_edit_queue.front();
390                 m_unsent_map_edit_queue.pop();
391         }
392 }
393
394 void Server::init()
395 {
396         infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
397         if (m_simple_singleplayer_mode)
398                 infostream << " in simple singleplayer mode" << std::endl;
399         else
400                 infostream << std::endl;
401         infostream << "- world:  " << m_path_world << std::endl;
402         infostream << "- game:   " << m_gamespec.path << std::endl;
403
404         m_game_settings = Settings::createLayer(SL_GAME);
405
406         // Create world if it doesn't exist
407         try {
408                 loadGameConfAndInitWorld(m_path_world,
409                                 fs::GetFilenameFromPath(m_path_world.c_str()),
410                                 m_gamespec, false);
411         } catch (const BaseException &e) {
412                 throw ServerError(std::string("Failed to initialize world: ") + e.what());
413         }
414
415         // Create emerge manager
416         m_emerge = new EmergeManager(this, m_metrics_backend.get());
417
418         // Create ban manager
419         std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
420         m_banmanager = new BanManager(ban_path);
421
422         // Create mod storage database and begin a save for later
423         m_mod_storage_database = openModStorageDatabase(m_path_world);
424         m_mod_storage_database->beginSave();
425
426         m_modmgr = std::make_unique<ServerModManager>(m_path_world);
427         std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
428         // complain about mods with unsatisfied dependencies
429         if (!m_modmgr->isConsistent()) {
430                 std::string error = m_modmgr->getUnsatisfiedModsError();
431                 throw ServerError(error);
432         }
433
434         //lock environment
435         MutexAutoLock envlock(m_env_mutex);
436
437         // Create the Map (loads map_meta.txt, overriding configured mapgen params)
438         ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
439         m_startup_server_map = servermap;
440
441         // Initialize scripting
442         infostream << "Server: Initializing Lua" << std::endl;
443
444         m_script = new ServerScripting(this);
445
446         // Must be created before mod loading because we have some inventory creation
447         m_inventory_mgr = std::make_unique<ServerInventoryManager>();
448
449         m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
450
451         m_gamespec.checkAndLog();
452         m_modmgr->loadMods(m_script);
453
454         // Read Textures and calculate sha1 sums
455         fillMediaCache();
456
457         // Apply item aliases in the node definition manager
458         m_nodedef->updateAliases(m_itemdef);
459
460         // Apply texture overrides from texturepack/override.txt
461         std::vector<std::string> paths;
462         fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
463         fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
464         for (const std::string &path : paths) {
465                 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
466                 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
467                 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
468         }
469
470         m_nodedef->setNodeRegistrationStatus(true);
471
472         // Perform pending node name resolutions
473         m_nodedef->runNodeResolveCallbacks();
474
475         // unmap node names in cross-references
476         m_nodedef->resolveCrossrefs();
477
478         // init the recipe hashes to speed up crafting
479         m_craftdef->initHashes(this);
480
481         // Initialize Environment
482         m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
483         m_env = new ServerEnvironment(servermap, m_script, this,
484                 m_path_world, m_metrics_backend.get());
485
486         m_inventory_mgr->setEnv(m_env);
487         m_clients.setEnv(m_env);
488
489         if (!servermap->settings_mgr.makeMapgenParams())
490                 FATAL_ERROR("Couldn't create any mapgen type");
491
492         // Initialize mapgens
493         m_emerge->initMapgens(servermap->getMapgenParams());
494
495         if (g_settings->getBool("enable_rollback_recording")) {
496                 // Create rollback manager
497                 m_rollback = new RollbackManager(m_path_world, this);
498         }
499
500         // Give environment reference to scripting api
501         m_script->initializeEnvironment(m_env);
502
503         // Do this after regular script init is done
504         m_script->initAsync();
505
506         // Register us to receive map edit events
507         servermap->addEventReceiver(this);
508
509         m_env->loadMeta();
510
511         // Those settings can be overwritten in world.mt, they are
512         // intended to be cached after environment loading.
513         m_liquid_transform_every = g_settings->getFloat("liquid_update");
514         m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
515         m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
516         m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
517 }
518
519 void Server::start()
520 {
521         init();
522
523         infostream << "Starting server on " << m_bind_addr.serializeString()
524                         << "..." << std::endl;
525
526         // Stop thread if already running
527         m_thread->stop();
528
529         // Initialize connection
530         m_con->SetTimeoutMs(30);
531         m_con->Serve(m_bind_addr);
532
533         // Start thread
534         m_thread->start();
535
536         // ASCII art for the win!
537         std::cerr
538                 << "         __.               __.                 __.  " << std::endl
539                 << "  _____ |__| ____   _____ /  |_  _____  _____ /  |_ " << std::endl
540                 << " /     \\|  |/    \\ /  __ \\    _\\/  __ \\/   __>    _\\" << std::endl
541                 << "|  Y Y  \\  |   |  \\   ___/|  | |   ___/\\___  \\|  |  " << std::endl
542                 << "|__|_|  /  |___|  /\\______>  |  \\______>_____/|  |  " << std::endl
543                 << "      \\/ \\/     \\/         \\/                  \\/   " << std::endl;
544         actionstream << "World at [" << m_path_world << "]" << std::endl;
545         actionstream << "Server for gameid=\"" << m_gamespec.id
546                         << "\" listening on ";
547         m_bind_addr.print(actionstream);
548         actionstream << "." << std::endl;
549 }
550
551 void Server::stop()
552 {
553         infostream<<"Server: Stopping and waiting threads"<<std::endl;
554
555         // Stop threads (set run=false first so both start stopping)
556         m_thread->stop();
557         m_thread->wait();
558
559         infostream<<"Server: Threads stopped"<<std::endl;
560 }
561
562 void Server::step(float dtime)
563 {
564         // Limit a bit
565         if (dtime > 2.0)
566                 dtime = 2.0;
567         {
568                 MutexAutoLock lock(m_step_dtime_mutex);
569                 m_step_dtime += dtime;
570         }
571         // Throw if fatal error occurred in thread
572         std::string async_err = m_async_fatal_error.get();
573         if (!async_err.empty()) {
574                 if (!m_simple_singleplayer_mode) {
575                         m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
576                                 g_settings->get("kick_msg_crash"),
577                                 g_settings->getBool("ask_reconnect_on_crash"));
578                 }
579                 throw ServerError("AsyncErr: " + async_err);
580         }
581 }
582
583 void Server::AsyncRunStep(bool initial_step)
584 {
585
586         float dtime;
587         {
588                 MutexAutoLock lock1(m_step_dtime_mutex);
589                 dtime = m_step_dtime;
590         }
591
592         {
593                 // Send blocks to clients
594                 SendBlocks(dtime);
595         }
596
597         if((dtime < 0.001) && !initial_step)
598                 return;
599
600         ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
601
602         {
603                 MutexAutoLock lock1(m_step_dtime_mutex);
604                 m_step_dtime -= dtime;
605         }
606
607         /*
608                 Update uptime
609         */
610         m_uptime_counter->increment(dtime);
611
612         handlePeerChanges();
613
614         /*
615                 Update time of day and overall game time
616         */
617         m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
618
619         /*
620                 Send to clients at constant intervals
621         */
622
623         m_time_of_day_send_timer -= dtime;
624         if (m_time_of_day_send_timer < 0.0) {
625                 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
626                 u16 time = m_env->getTimeOfDay();
627                 float time_speed = g_settings->getFloat("time_speed");
628                 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
629
630                 m_timeofday_gauge->set(time);
631         }
632
633         {
634                 MutexAutoLock lock(m_env_mutex);
635                 // Figure out and report maximum lag to environment
636                 float max_lag = m_env->getMaxLagEstimate();
637                 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
638                 if(dtime > max_lag){
639                         if(dtime > 0.1 && dtime > max_lag * 2.0)
640                                 infostream<<"Server: Maximum lag peaked to "<<dtime
641                                                 <<" s"<<std::endl;
642                         max_lag = dtime;
643                 }
644                 m_env->reportMaxLagEstimate(max_lag);
645
646                 // Step environment
647                 m_env->step(dtime);
648         }
649
650         static const float map_timer_and_unload_dtime = 2.92;
651         if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
652         {
653                 MutexAutoLock lock(m_env_mutex);
654                 // Run Map's timers and unload unused data
655                 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
656                 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
657                         std::max(g_settings->getFloat("server_unload_unused_data_timeout"), 0.0f),
658                         -1);
659         }
660
661         /*
662                 Listen to the admin chat, if available
663         */
664         if (m_admin_chat) {
665                 if (!m_admin_chat->command_queue.empty()) {
666                         MutexAutoLock lock(m_env_mutex);
667                         while (!m_admin_chat->command_queue.empty()) {
668                                 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
669                                 handleChatInterfaceEvent(evt);
670                                 delete evt;
671                         }
672                 }
673                 m_admin_chat->outgoing_queue.push_back(
674                         new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
675         }
676
677         /*
678                 Do background stuff
679         */
680
681         /* Transform liquids */
682         m_liquid_transform_timer += dtime;
683         if(m_liquid_transform_timer >= m_liquid_transform_every)
684         {
685                 m_liquid_transform_timer -= m_liquid_transform_every;
686
687                 MutexAutoLock lock(m_env_mutex);
688
689                 ScopeProfiler sp(g_profiler, "Server: liquid transform");
690
691                 std::map<v3s16, MapBlock*> modified_blocks;
692                 m_env->getServerMap().transformLiquids(modified_blocks, m_env);
693
694                 /*
695                         Set the modified blocks unsent for all the clients
696                 */
697                 if (!modified_blocks.empty()) {
698                         SetBlocksNotSent(modified_blocks);
699                 }
700         }
701         m_clients.step(dtime);
702
703         // increase/decrease lag gauge gradually
704         if (m_lag_gauge->get() > dtime) {
705                 m_lag_gauge->decrement(dtime/100);
706         } else {
707                 m_lag_gauge->increment(dtime/100);
708         }
709
710         {
711                 float &counter = m_step_pending_dyn_media_timer;
712                 counter += dtime;
713                 if (counter >= 5.0f) {
714                         stepPendingDynMediaCallbacks(counter);
715                         counter = 0;
716                 }
717         }
718
719
720 #if USE_CURL
721         // send masterserver announce
722         {
723                 float &counter = m_masterserver_timer;
724                 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
725                                 g_settings->getBool("server_announce")) {
726                         ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
727                                                 ServerList::AA_START,
728                                         m_bind_addr.getPort(),
729                                         m_clients.getPlayerNames(),
730                                         m_uptime_counter->get(),
731                                         m_env->getGameTime(),
732                                         m_lag_gauge->get(),
733                                         m_gamespec.id,
734                                         Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
735                                         m_modmgr->getMods(),
736                                         m_dedicated);
737                         counter = 0.01;
738                 }
739                 counter += dtime;
740         }
741 #endif
742
743         /*
744                 Check added and deleted active objects
745         */
746         {
747                 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
748                 MutexAutoLock envlock(m_env_mutex);
749
750                 {
751                         ClientInterface::AutoLock clientlock(m_clients);
752                         const RemoteClientMap &clients = m_clients.getClientList();
753                         ScopeProfiler sp(g_profiler, "Server: update objects within range");
754
755                         m_player_gauge->set(clients.size());
756                         for (const auto &client_it : clients) {
757                                 RemoteClient *client = client_it.second;
758
759                                 if (client->getState() < CS_DefinitionsSent)
760                                         continue;
761
762                                 // This can happen if the client times out somehow
763                                 if (!m_env->getPlayer(client->peer_id))
764                                         continue;
765
766                                 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
767                                 if (!playersao)
768                                         continue;
769
770                                 SendActiveObjectRemoveAdd(client, playersao);
771                         }
772                 }
773
774                 // Write changes to the mod storage
775                 m_mod_storage_save_timer -= dtime;
776                 if (m_mod_storage_save_timer <= 0.0f) {
777                         m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
778                         m_mod_storage_database->endSave();
779                         m_mod_storage_database->beginSave();
780                 }
781         }
782
783         /*
784                 Send object messages
785         */
786         {
787                 MutexAutoLock envlock(m_env_mutex);
788                 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
789
790                 // Key = object id
791                 // Value = data sent by object
792                 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
793
794                 // Get active object messages from environment
795                 ActiveObjectMessage aom(0);
796                 u32 count_reliable = 0, count_unreliable = 0;
797                 for(;;) {
798                         if (!m_env->getActiveObjectMessage(&aom))
799                                 break;
800                         if (aom.reliable)
801                                 count_reliable++;
802                         else
803                                 count_unreliable++;
804
805                         std::vector<ActiveObjectMessage>* message_list = nullptr;
806                         auto n = buffered_messages.find(aom.id);
807                         if (n == buffered_messages.end()) {
808                                 message_list = new std::vector<ActiveObjectMessage>;
809                                 buffered_messages[aom.id] = message_list;
810                         } else {
811                                 message_list = n->second;
812                         }
813                         message_list->push_back(std::move(aom));
814                 }
815
816                 m_aom_buffer_counter[0]->increment(count_reliable);
817                 m_aom_buffer_counter[1]->increment(count_unreliable);
818
819                 {
820                         ClientInterface::AutoLock clientlock(m_clients);
821                         const RemoteClientMap &clients = m_clients.getClientList();
822                         // Route data to every client
823                         std::string reliable_data, unreliable_data;
824                         for (const auto &client_it : clients) {
825                                 reliable_data.clear();
826                                 unreliable_data.clear();
827                                 RemoteClient *client = client_it.second;
828                                 PlayerSAO *player = getPlayerSAO(client->peer_id);
829                                 // Go through all objects in message buffer
830                                 for (const auto &buffered_message : buffered_messages) {
831                                         // If object does not exist or is not known by client, skip it
832                                         u16 id = buffered_message.first;
833                                         ServerActiveObject *sao = m_env->getActiveObject(id);
834                                         if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
835                                                 continue;
836
837                                         // Get message list of object
838                                         std::vector<ActiveObjectMessage>* list = buffered_message.second;
839                                         // Go through every message
840                                         for (const ActiveObjectMessage &aom : *list) {
841                                                 // Send position updates to players who do not see the attachment
842                                                 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
843                                                         if (sao->getId() == player->getId())
844                                                                 continue;
845
846                                                         // Do not send position updates for attached players
847                                                         // as long the parent is known to the client
848                                                         ServerActiveObject *parent = sao->getParent();
849                                                         if (parent && client->m_known_objects.find(parent->getId()) !=
850                                                                         client->m_known_objects.end())
851                                                                 continue;
852                                                 }
853
854                                                 // Add full new data to appropriate buffer
855                                                 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
856                                                 char idbuf[2];
857                                                 writeU16((u8*) idbuf, aom.id);
858                                                 // u16 id
859                                                 // std::string data
860                                                 buffer.append(idbuf, sizeof(idbuf));
861                                                 buffer.append(serializeString16(aom.datastring));
862                                         }
863                                 }
864                                 /*
865                                         reliable_data and unreliable_data are now ready.
866                                         Send them.
867                                 */
868                                 if (!reliable_data.empty()) {
869                                         SendActiveObjectMessages(client->peer_id, reliable_data);
870                                 }
871
872                                 if (!unreliable_data.empty()) {
873                                         SendActiveObjectMessages(client->peer_id, unreliable_data, false);
874                                 }
875                         }
876                 }
877
878                 // Clear buffered_messages
879                 for (auto &buffered_message : buffered_messages) {
880                         delete buffered_message.second;
881                 }
882         }
883
884         /*
885                 Send queued-for-sending map edit events.
886         */
887         {
888                 // We will be accessing the environment
889                 MutexAutoLock lock(m_env_mutex);
890
891                 // Single change sending is disabled if queue size is big
892                 bool disable_single_change_sending = false;
893                 if(m_unsent_map_edit_queue.size() >= 4)
894                         disable_single_change_sending = true;
895
896                 const auto event_count = m_unsent_map_edit_queue.size();
897                 m_map_edit_event_counter->increment(event_count);
898
899                 // We'll log the amount of each
900                 Profiler prof;
901
902                 std::unordered_set<v3s16> node_meta_updates;
903
904                 while (!m_unsent_map_edit_queue.empty()) {
905                         MapEditEvent* event = m_unsent_map_edit_queue.front();
906                         m_unsent_map_edit_queue.pop();
907
908                         // Players far away from the change are stored here.
909                         // Instead of sending the changes, MapBlocks are set not sent
910                         // for them.
911                         std::unordered_set<u16> far_players;
912
913                         switch (event->type) {
914                         case MEET_ADDNODE:
915                         case MEET_SWAPNODE:
916                                 prof.add("MEET_ADDNODE", 1);
917                                 sendAddNode(event->p, event->n, &far_players,
918                                                 disable_single_change_sending ? 5 : 30,
919                                                 event->type == MEET_ADDNODE);
920                                 break;
921                         case MEET_REMOVENODE:
922                                 prof.add("MEET_REMOVENODE", 1);
923                                 sendRemoveNode(event->p, &far_players,
924                                                 disable_single_change_sending ? 5 : 30);
925                                 break;
926                         case MEET_BLOCK_NODE_METADATA_CHANGED: {
927                                 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
928                                 if (!event->is_private_change) {
929                                         node_meta_updates.emplace(event->p);
930                                 }
931
932                                 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
933                                                 getNodeBlockPos(event->p))) {
934                                         block->raiseModified(MOD_STATE_WRITE_NEEDED,
935                                                 MOD_REASON_REPORT_META_CHANGE);
936                                 }
937                                 break;
938                         }
939                         case MEET_OTHER:
940                                 prof.add("MEET_OTHER", 1);
941                                 for (const v3s16 &modified_block : event->modified_blocks) {
942                                         m_clients.markBlockposAsNotSent(modified_block);
943                                 }
944                                 break;
945                         default:
946                                 prof.add("unknown", 1);
947                                 warningstream << "Server: Unknown MapEditEvent "
948                                                 << ((u32)event->type) << std::endl;
949                                 break;
950                         }
951
952                         /*
953                                 Set blocks not sent to far players
954                         */
955                         if (!far_players.empty()) {
956                                 // Convert list format to that wanted by SetBlocksNotSent
957                                 std::map<v3s16, MapBlock*> modified_blocks2;
958                                 for (const v3s16 &modified_block : event->modified_blocks) {
959                                         modified_blocks2[modified_block] =
960                                                         m_env->getMap().getBlockNoCreateNoEx(modified_block);
961                                 }
962
963                                 // Set blocks not sent
964                                 for (const u16 far_player : far_players) {
965                                         if (RemoteClient *client = getClient(far_player))
966                                                 client->SetBlocksNotSent(modified_blocks2);
967                                 }
968                         }
969
970                         delete event;
971                 }
972
973                 if (event_count >= 5) {
974                         infostream << "Server: MapEditEvents:" << std::endl;
975                         prof.print(infostream);
976                 } else if (event_count != 0) {
977                         verbosestream << "Server: MapEditEvents:" << std::endl;
978                         prof.print(verbosestream);
979                 }
980
981                 // Send all metadata updates
982                 if (!node_meta_updates.empty())
983                         sendMetadataChanged(node_meta_updates);
984         }
985
986         /*
987                 Trigger emerge thread
988                 Doing this every 2s is left over from old code, unclear if this is still needed.
989         */
990         {
991                 float &counter = m_emergethread_trigger_timer;
992                 counter -= dtime;
993                 if (counter <= 0.0f) {
994                         counter = 2.0f;
995
996                         m_emerge->startThreads();
997                 }
998         }
999
1000         // Save map, players and auth stuff
1001         {
1002                 float &counter = m_savemap_timer;
1003                 counter += dtime;
1004                 static thread_local const float save_interval =
1005                         g_settings->getFloat("server_map_save_interval");
1006                 if (counter >= save_interval) {
1007                         counter = 0.0;
1008                         MutexAutoLock lock(m_env_mutex);
1009
1010                         ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
1011
1012                         // Save ban file
1013                         if (m_banmanager->isModified()) {
1014                                 m_banmanager->save();
1015                         }
1016
1017                         // Save changed parts of map
1018                         m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1019
1020                         // Save players
1021                         m_env->saveLoadedPlayers();
1022
1023                         // Save environment metadata
1024                         m_env->saveMeta();
1025                 }
1026         }
1027
1028         m_shutdown_state.tick(dtime, this);
1029 }
1030
1031 void Server::Receive()
1032 {
1033         NetworkPacket pkt;
1034         session_t peer_id;
1035         bool first = true;
1036         for (;;) {
1037                 pkt.clear();
1038                 peer_id = 0;
1039                 try {
1040                         /*
1041                                 In the first iteration *wait* for a packet, afterwards process
1042                                 all packets that are immediately available (no waiting).
1043                         */
1044                         if (first) {
1045                                 m_con->Receive(&pkt);
1046                                 first = false;
1047                         } else {
1048                                 if (!m_con->TryReceive(&pkt))
1049                                         return;
1050                         }
1051
1052                         peer_id = pkt.getPeerId();
1053                         m_packet_recv_counter->increment();
1054                         ProcessData(&pkt);
1055                         m_packet_recv_processed_counter->increment();
1056                 } catch (const con::InvalidIncomingDataException &e) {
1057                         infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1058                                         << e.what() << std::endl;
1059                 } catch (const SerializationError &e) {
1060                         infostream << "Server::Receive(): SerializationError: what()="
1061                                         << e.what() << std::endl;
1062                 } catch (const ClientStateError &e) {
1063                         errorstream << "ProcessData: peer=" << peer_id << " what()="
1064                                          << e.what() << std::endl;
1065                         DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
1066                 } catch (const con::PeerNotFoundException &e) {
1067                         // Do nothing
1068                 } catch (const con::NoIncomingDataException &e) {
1069                         return;
1070                 }
1071         }
1072 }
1073
1074 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1075 {
1076         std::string playername;
1077         PlayerSAO *playersao = NULL;
1078         {
1079                 ClientInterface::AutoLock clientlock(m_clients);
1080                 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1081                 if (client) {
1082                         playername = client->getName();
1083                         playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1084                 }
1085         }
1086
1087         RemotePlayer *player = m_env->getPlayer(playername.c_str());
1088
1089         // If failed, cancel
1090         if (!playersao || !player) {
1091                 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1092                         actionstream << "Server: Failed to emerge player \"" << playername
1093                                         << "\" (player allocated to an another client)" << std::endl;
1094                         DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
1095                 } else {
1096                         errorstream << "Server: " << playername << ": Failed to emerge player"
1097                                         << std::endl;
1098                         DenyAccess(peer_id, SERVER_ACCESSDENIED_SERVER_FAIL);
1099                 }
1100                 return nullptr;
1101         }
1102
1103         /*
1104                 Send complete position information
1105         */
1106         SendMovePlayer(peer_id);
1107
1108         // Send privileges
1109         SendPlayerPrivileges(peer_id);
1110
1111         // Send inventory formspec
1112         SendPlayerInventoryFormspec(peer_id);
1113
1114         // Send inventory
1115         SendInventory(playersao, false);
1116
1117         // Send HP
1118         SendPlayerHP(playersao, false);
1119
1120         // Send death screen
1121         if (playersao->isDead())
1122                 SendDeathscreen(peer_id, false, v3f(0,0,0));
1123
1124         // Send Breath
1125         SendPlayerBreath(playersao);
1126
1127         /*
1128                 Update player list and print action
1129         */
1130         {
1131                 NetworkPacket notice_pkt(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
1132                 notice_pkt << (u8) PLAYER_LIST_ADD << (u16) 1 << std::string(player->getName());
1133                 m_clients.sendToAll(&notice_pkt);
1134         }
1135         {
1136                 std::string ip_str = getPeerAddress(player->getPeerId()).serializeString();
1137                 const auto &names = m_clients.getPlayerNames();
1138
1139                 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1140                 for (const std::string &name : names)
1141                         actionstream << name << " ";
1142                 actionstream << player->getName() << std::endl;
1143         }
1144         return playersao;
1145 }
1146
1147 inline void Server::handleCommand(NetworkPacket *pkt)
1148 {
1149         const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1150         (this->*opHandle.handler)(pkt);
1151 }
1152
1153 void Server::ProcessData(NetworkPacket *pkt)
1154 {
1155         // Environment is locked first.
1156         MutexAutoLock envlock(m_env_mutex);
1157
1158         ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1159         u32 peer_id = pkt->getPeerId();
1160
1161         try {
1162                 Address address = getPeerAddress(peer_id);
1163                 std::string addr_s = address.serializeString();
1164
1165                 // FIXME: Isn't it a bit excessive to check this for every packet?
1166                 if (m_banmanager->isIpBanned(addr_s)) {
1167                         std::string ban_name = m_banmanager->getBanName(addr_s);
1168                         infostream << "Server: A banned client tried to connect from "
1169                                         << addr_s << "; banned name was " << ban_name << std::endl;
1170                         DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING,
1171                                 "Your IP is banned. Banned name was " + ban_name);
1172                         return;
1173                 }
1174         } catch (con::PeerNotFoundException &e) {
1175                 /*
1176                  * no peer for this packet found
1177                  * most common reason is peer timeout, e.g. peer didn't
1178                  * respond for some time, your server was overloaded or
1179                  * things like that.
1180                  */
1181                 infostream << "Server::ProcessData(): Canceling: peer "
1182                                 << peer_id << " not found" << std::endl;
1183                 return;
1184         }
1185
1186         try {
1187                 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1188
1189                 // Command must be handled into ToServerCommandHandler
1190                 if (command >= TOSERVER_NUM_MSG_TYPES) {
1191                         infostream << "Server: Ignoring unknown command "
1192                                          << command << std::endl;
1193                         return;
1194                 }
1195
1196                 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1197                         handleCommand(pkt);
1198                         return;
1199                 }
1200
1201                 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1202
1203                 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1204                         errorstream << "Server::ProcessData(): Cancelling: Peer"
1205                                         " serialization format invalid or not initialized."
1206                                         " Skipping incoming command=" << command << std::endl;
1207                         return;
1208                 }
1209
1210                 /* Handle commands related to client startup */
1211                 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1212                         handleCommand(pkt);
1213                         return;
1214                 }
1215
1216                 if (m_clients.getClientState(peer_id) < CS_Active) {
1217                         if (command == TOSERVER_PLAYERPOS) return;
1218
1219                         errorstream << "Got packet command: " << command << " for peer id "
1220                                         << peer_id << " but client isn't active yet. Dropping packet "
1221                                         << std::endl;
1222                         return;
1223                 }
1224
1225                 handleCommand(pkt);
1226         } catch (SendFailedException &e) {
1227                 errorstream << "Server::ProcessData(): SendFailedException: "
1228                                 << "what=" << e.what()
1229                                 << std::endl;
1230         } catch (PacketError &e) {
1231                 actionstream << "Server::ProcessData(): PacketError: "
1232                                 << "what=" << e.what()
1233                                 << std::endl;
1234         }
1235 }
1236
1237 void Server::setTimeOfDay(u32 time)
1238 {
1239         m_env->setTimeOfDay(time);
1240         m_time_of_day_send_timer = 0;
1241 }
1242
1243 void Server::onMapEditEvent(const MapEditEvent &event)
1244 {
1245         if (m_ignore_map_edit_events_area.contains(event.getArea()))
1246                 return;
1247
1248         m_unsent_map_edit_queue.push(new MapEditEvent(event));
1249 }
1250
1251 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1252 {
1253         std::vector<session_t> clients = m_clients.getClientIDs();
1254         ClientInterface::AutoLock clientlock(m_clients);
1255         // Set the modified blocks unsent for all the clients
1256         for (const session_t client_id : clients) {
1257                         if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1258                                 client->SetBlocksNotSent(block);
1259         }
1260 }
1261
1262 void Server::peerAdded(con::Peer *peer)
1263 {
1264         verbosestream<<"Server::peerAdded(): peer->id="
1265                         <<peer->id<<std::endl;
1266
1267         m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1268 }
1269
1270 void Server::deletingPeer(con::Peer *peer, bool timeout)
1271 {
1272         verbosestream<<"Server::deletingPeer(): peer->id="
1273                         <<peer->id<<", timeout="<<timeout<<std::endl;
1274
1275         m_clients.event(peer->id, CSE_Disconnect);
1276         m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1277 }
1278
1279 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1280 {
1281         *retval = m_con->getPeerStat(peer_id,type);
1282         return *retval != -1;
1283 }
1284
1285 bool Server::getClientInfo(session_t peer_id, ClientInfo &ret)
1286 {
1287         ClientInterface::AutoLock clientlock(m_clients);
1288         RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1289
1290         if (!client)
1291                 return false;
1292
1293         ret.state = client->getState();
1294         ret.addr = client->getAddress();
1295         ret.uptime = client->uptime();
1296         ret.ser_vers = client->serialization_version;
1297         ret.prot_vers = client->net_proto_version;
1298
1299         ret.major = client->getMajor();
1300         ret.minor = client->getMinor();
1301         ret.patch = client->getPatch();
1302         ret.vers_string = client->getFullVer();
1303
1304         ret.lang_code = client->getLangCode();
1305
1306         return true;
1307 }
1308
1309 void Server::handlePeerChanges()
1310 {
1311         while(!m_peer_change_queue.empty())
1312         {
1313                 con::PeerChange c = m_peer_change_queue.front();
1314                 m_peer_change_queue.pop();
1315
1316                 verbosestream<<"Server: Handling peer change: "
1317                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1318                                 <<std::endl;
1319
1320                 switch(c.type)
1321                 {
1322                 case con::PEER_ADDED:
1323                         m_clients.CreateClient(c.peer_id);
1324                         break;
1325
1326                 case con::PEER_REMOVED:
1327                         DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1328                         break;
1329
1330                 default:
1331                         FATAL_ERROR("Invalid peer change event received!");
1332                         break;
1333                 }
1334         }
1335 }
1336
1337 void Server::printToConsoleOnly(const std::string &text)
1338 {
1339         if (m_admin_chat) {
1340                 m_admin_chat->outgoing_queue.push_back(
1341                         new ChatEventChat("", utf8_to_wide(text)));
1342         } else {
1343                 std::cout << text << std::endl;
1344         }
1345 }
1346
1347 void Server::Send(NetworkPacket *pkt)
1348 {
1349         Send(pkt->getPeerId(), pkt);
1350 }
1351
1352 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1353 {
1354         m_clients.send(peer_id,
1355                 clientCommandFactoryTable[pkt->getCommand()].channel,
1356                 pkt,
1357                 clientCommandFactoryTable[pkt->getCommand()].reliable);
1358 }
1359
1360 void Server::SendMovement(session_t peer_id)
1361 {
1362         std::ostringstream os(std::ios_base::binary);
1363
1364         NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1365
1366         pkt << g_settings->getFloat("movement_acceleration_default");
1367         pkt << g_settings->getFloat("movement_acceleration_air");
1368         pkt << g_settings->getFloat("movement_acceleration_fast");
1369         pkt << g_settings->getFloat("movement_speed_walk");
1370         pkt << g_settings->getFloat("movement_speed_crouch");
1371         pkt << g_settings->getFloat("movement_speed_fast");
1372         pkt << g_settings->getFloat("movement_speed_climb");
1373         pkt << g_settings->getFloat("movement_speed_jump");
1374         pkt << g_settings->getFloat("movement_liquid_fluidity");
1375         pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1376         pkt << g_settings->getFloat("movement_liquid_sink");
1377         pkt << g_settings->getFloat("movement_gravity");
1378
1379         Send(&pkt);
1380 }
1381
1382 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1383 {
1384         m_script->player_event(playersao, "health_changed");
1385         SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX);
1386
1387         // Send to other clients
1388         playersao->sendPunchCommand();
1389
1390         if (playersao->isDead())
1391                 HandlePlayerDeath(playersao, reason);
1392 }
1393
1394 void Server::SendPlayerHP(PlayerSAO *playersao, bool effect)
1395 {
1396         SendHP(playersao->getPeerID(), playersao->getHP(), effect);
1397 }
1398
1399 void Server::SendHP(session_t peer_id, u16 hp, bool effect)
1400 {
1401         NetworkPacket pkt(TOCLIENT_HP, 3, peer_id);
1402         pkt << hp << effect;
1403         Send(&pkt);
1404 }
1405
1406 void Server::SendBreath(session_t peer_id, u16 breath)
1407 {
1408         NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1409         pkt << (u16) breath;
1410         Send(&pkt);
1411 }
1412
1413 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1414                 const std::string &custom_reason, bool reconnect)
1415 {
1416         assert(reason < SERVER_ACCESSDENIED_MAX);
1417
1418         NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1419         pkt << (u8)reason;
1420         if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1421                 pkt << custom_reason;
1422         else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1423                         reason == SERVER_ACCESSDENIED_CRASH)
1424                 pkt << custom_reason << (u8)reconnect;
1425         Send(&pkt);
1426 }
1427
1428 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1429                 v3f camera_point_target)
1430 {
1431         NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1432         pkt << set_camera_point_target << camera_point_target;
1433         Send(&pkt);
1434 }
1435
1436 void Server::SendItemDef(session_t peer_id,
1437                 IItemDefManager *itemdef, u16 protocol_version)
1438 {
1439         NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1440
1441         /*
1442                 u16 command
1443                 u32 length of the next item
1444                 zlib-compressed serialized ItemDefManager
1445         */
1446         std::ostringstream tmp_os(std::ios::binary);
1447         itemdef->serialize(tmp_os, protocol_version);
1448         std::ostringstream tmp_os2(std::ios::binary);
1449         compressZlib(tmp_os.str(), tmp_os2);
1450         pkt.putLongString(tmp_os2.str());
1451
1452         // Make data buffer
1453         verbosestream << "Server: Sending item definitions to id(" << peer_id
1454                         << "): size=" << pkt.getSize() << std::endl;
1455
1456         Send(&pkt);
1457 }
1458
1459 void Server::SendNodeDef(session_t peer_id,
1460         const NodeDefManager *nodedef, u16 protocol_version)
1461 {
1462         NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1463
1464         /*
1465                 u16 command
1466                 u32 length of the next item
1467                 zlib-compressed serialized NodeDefManager
1468         */
1469         std::ostringstream tmp_os(std::ios::binary);
1470         nodedef->serialize(tmp_os, protocol_version);
1471         std::ostringstream tmp_os2(std::ios::binary);
1472         compressZlib(tmp_os.str(), tmp_os2);
1473
1474         pkt.putLongString(tmp_os2.str());
1475
1476         // Make data buffer
1477         verbosestream << "Server: Sending node definitions to id(" << peer_id
1478                         << "): size=" << pkt.getSize() << std::endl;
1479
1480         Send(&pkt);
1481 }
1482
1483 /*
1484         Non-static send methods
1485 */
1486
1487 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1488 {
1489         RemotePlayer *player = sao->getPlayer();
1490
1491         // Do not send new format to old clients
1492         incremental &= player->protocol_version >= 38;
1493
1494         UpdateCrafting(player);
1495
1496         /*
1497                 Serialize it
1498         */
1499
1500         NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1501
1502         std::ostringstream os(std::ios::binary);
1503         sao->getInventory()->serialize(os, incremental);
1504         sao->getInventory()->setModified(false);
1505         player->setModified(true);
1506
1507         const std::string &s = os.str();
1508         pkt.putRawString(s.c_str(), s.size());
1509         Send(&pkt);
1510 }
1511
1512 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1513 {
1514         NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1515         u8 version = 1;
1516         u8 type = message.type;
1517         pkt << version << type << message.sender << message.message
1518                 << static_cast<u64>(message.timestamp);
1519
1520         if (peer_id != PEER_ID_INEXISTENT) {
1521                 RemotePlayer *player = m_env->getPlayer(peer_id);
1522                 if (!player)
1523                         return;
1524
1525                 Send(&pkt);
1526         } else {
1527                 m_clients.sendToAll(&pkt);
1528         }
1529 }
1530
1531 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1532         const std::string &formname)
1533 {
1534         NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1535         if (formspec.empty()){
1536                 //the client should close the formspec
1537                 //but make sure there wasn't another one open in meantime
1538                 const auto it = m_formspec_state_data.find(peer_id);
1539                 if (it != m_formspec_state_data.end() && it->second == formname) {
1540                         m_formspec_state_data.erase(peer_id);
1541                 }
1542                 pkt.putLongString("");
1543         } else {
1544                 m_formspec_state_data[peer_id] = formname;
1545                 pkt.putLongString(formspec);
1546         }
1547         pkt << formname;
1548
1549         Send(&pkt);
1550 }
1551
1552 // Spawns a particle on peer with peer_id
1553 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1554         const ParticleParameters &p)
1555 {
1556         static thread_local const float radius =
1557                         g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1558
1559         if (peer_id == PEER_ID_INEXISTENT) {
1560                 std::vector<session_t> clients = m_clients.getClientIDs();
1561                 const v3f pos = p.pos * BS;
1562                 const float radius_sq = radius * radius;
1563
1564                 for (const session_t client_id : clients) {
1565                         RemotePlayer *player = m_env->getPlayer(client_id);
1566                         if (!player)
1567                                 continue;
1568
1569                         PlayerSAO *sao = player->getPlayerSAO();
1570                         if (!sao)
1571                                 continue;
1572
1573                         // Do not send to distant clients
1574                         if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1575                                 continue;
1576
1577                         SendSpawnParticle(client_id, player->protocol_version, p);
1578                 }
1579                 return;
1580         }
1581         assert(protocol_version != 0);
1582
1583         NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1584
1585         {
1586                 // NetworkPacket and iostreams are incompatible...
1587                 std::ostringstream oss(std::ios_base::binary);
1588                 p.serialize(oss, protocol_version);
1589                 pkt.putRawString(oss.str());
1590         }
1591
1592         Send(&pkt);
1593 }
1594
1595 // Adds a ParticleSpawner on peer with peer_id
1596 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1597         const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1598 {
1599         static thread_local const float radius =
1600                         g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1601
1602         if (peer_id == PEER_ID_INEXISTENT) {
1603                 std::vector<session_t> clients = m_clients.getClientIDs();
1604                 const v3f pos = (
1605                         p.pos.start.min.val +
1606                         p.pos.start.max.val +
1607                         p.pos.end.min.val +
1608                         p.pos.end.max.val
1609                 ) / 4.0f * BS;
1610                 const float radius_sq = radius * radius;
1611                 /* Don't send short-lived spawners to distant players.
1612                  * This could be replaced with proper tracking at some point. */
1613                 const bool distance_check = !attached_id && p.time <= 1.0f;
1614
1615                 for (const session_t client_id : clients) {
1616                         RemotePlayer *player = m_env->getPlayer(client_id);
1617                         if (!player)
1618                                 continue;
1619
1620                         if (distance_check) {
1621                                 PlayerSAO *sao = player->getPlayerSAO();
1622                                 if (!sao)
1623                                         continue;
1624                                 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1625                                         continue;
1626                         }
1627
1628                         SendAddParticleSpawner(client_id, player->protocol_version,
1629                                 p, attached_id, id);
1630                 }
1631                 return;
1632         }
1633         assert(protocol_version != 0);
1634
1635         NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1636
1637         pkt << p.amount << p.time;
1638         { // serialize legacy fields
1639                 std::ostringstream os(std::ios_base::binary);
1640                 p.pos.start.legacySerialize(os);
1641                 p.vel.start.legacySerialize(os);
1642                 p.acc.start.legacySerialize(os);
1643                 p.exptime.start.legacySerialize(os);
1644                 p.size.start.legacySerialize(os);
1645                 pkt.putRawString(os.str());
1646         }
1647         pkt << p.collisiondetection;
1648
1649         pkt.putLongString(p.texture.string);
1650
1651         pkt << id << p.vertical << p.collision_removal << attached_id;
1652         {
1653                 std::ostringstream os(std::ios_base::binary);
1654                 p.animation.serialize(os, protocol_version);
1655                 pkt.putRawString(os.str());
1656         }
1657         pkt << p.glow << p.object_collision;
1658         pkt << p.node.param0 << p.node.param2 << p.node_tile;
1659
1660         { // serialize new fields
1661                 // initial bias for older properties
1662                 pkt << p.pos.start.bias
1663                         << p.vel.start.bias
1664                         << p.acc.start.bias
1665                         << p.exptime.start.bias
1666                         << p.size.start.bias;
1667
1668                 std::ostringstream os(std::ios_base::binary);
1669
1670                 // final tween frames of older properties
1671                 p.pos.end.serialize(os);
1672                 p.vel.end.serialize(os);
1673                 p.acc.end.serialize(os);
1674                 p.exptime.end.serialize(os);
1675                 p.size.end.serialize(os);
1676
1677                 // properties for legacy texture field
1678                 p.texture.serialize(os, protocol_version, true);
1679
1680                 // new properties
1681                 p.drag.serialize(os);
1682                 p.jitter.serialize(os);
1683                 p.bounce.serialize(os);
1684                 ParticleParamTypes::serializeParameterValue(os, p.attractor_kind);
1685                 if (p.attractor_kind != ParticleParamTypes::AttractorKind::none) {
1686                         p.attract.serialize(os);
1687                         p.attractor_origin.serialize(os);
1688                         writeU16(os, p.attractor_attachment); /* object ID */
1689                         writeU8(os, p.attractor_kill);
1690                         if (p.attractor_kind != ParticleParamTypes::AttractorKind::point) {
1691                                 p.attractor_direction.serialize(os);
1692                                 writeU16(os, p.attractor_direction_attachment);
1693                         }
1694                 }
1695                 p.radius.serialize(os);
1696
1697                 ParticleParamTypes::serializeParameterValue(os, (u16)p.texpool.size());
1698                 for (const auto& tex : p.texpool) {
1699                         tex.serialize(os, protocol_version);
1700                 }
1701
1702                 pkt.putRawString(os.str());
1703         }
1704
1705         Send(&pkt);
1706 }
1707
1708 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1709 {
1710         NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1711
1712         pkt << id;
1713
1714         if (peer_id != PEER_ID_INEXISTENT)
1715                 Send(&pkt);
1716         else
1717                 m_clients.sendToAll(&pkt);
1718
1719 }
1720
1721 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1722 {
1723         NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1724
1725         pkt << id << (u8) form->type << form->pos << form->name << form->scale
1726                         << form->text << form->number << form->item << form->dir
1727                         << form->align << form->offset << form->world_pos << form->size
1728                         << form->z_index << form->text2 << form->style;
1729
1730         Send(&pkt);
1731 }
1732
1733 void Server::SendHUDRemove(session_t peer_id, u32 id)
1734 {
1735         NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1736         pkt << id;
1737         Send(&pkt);
1738 }
1739
1740 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1741 {
1742         NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1743         pkt << id << (u8) stat;
1744
1745         switch (stat) {
1746                 case HUD_STAT_POS:
1747                 case HUD_STAT_SCALE:
1748                 case HUD_STAT_ALIGN:
1749                 case HUD_STAT_OFFSET:
1750                         pkt << *(v2f *) value;
1751                         break;
1752                 case HUD_STAT_NAME:
1753                 case HUD_STAT_TEXT:
1754                 case HUD_STAT_TEXT2:
1755                         pkt << *(std::string *) value;
1756                         break;
1757                 case HUD_STAT_WORLD_POS:
1758                         pkt << *(v3f *) value;
1759                         break;
1760                 case HUD_STAT_SIZE:
1761                         pkt << *(v2s32 *) value;
1762                         break;
1763                 default: // all other types
1764                         pkt << *(u32 *) value;
1765                         break;
1766         }
1767
1768         Send(&pkt);
1769 }
1770
1771 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1772 {
1773         NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1774
1775         flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1776
1777         pkt << flags << mask;
1778
1779         Send(&pkt);
1780 }
1781
1782 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1783 {
1784         NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1785         pkt << param << value;
1786         Send(&pkt);
1787 }
1788
1789 void Server::SendSetSky(session_t peer_id, const SkyboxParams &params)
1790 {
1791         NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1792
1793         // Handle prior clients here
1794         if (m_clients.getProtocolVersion(peer_id) < 39) {
1795                 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1796
1797                 for (const std::string& texture : params.textures)
1798                         pkt << texture;
1799
1800                 pkt << params.clouds;
1801         } else { // Handle current clients and future clients
1802                 pkt << params.bgcolor << params.type
1803                 << params.clouds << params.fog_sun_tint
1804                 << params.fog_moon_tint << params.fog_tint_type;
1805
1806                 if (params.type == "skybox") {
1807                         pkt << (u16) params.textures.size();
1808                         for (const std::string &texture : params.textures)
1809                                 pkt << texture;
1810                 } else if (params.type == "regular") {
1811                         pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1812                                 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1813                                 << params.sky_color.night_sky << params.sky_color.night_horizon
1814                                 << params.sky_color.indoors;
1815                 }
1816         }
1817
1818         Send(&pkt);
1819 }
1820
1821 void Server::SendSetSun(session_t peer_id, const SunParams &params)
1822 {
1823         NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1824         pkt << params.visible << params.texture
1825                 << params.tonemap << params.sunrise
1826                 << params.sunrise_visible << params.scale;
1827
1828         Send(&pkt);
1829 }
1830 void Server::SendSetMoon(session_t peer_id, const MoonParams &params)
1831 {
1832         NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1833
1834         pkt << params.visible << params.texture
1835                 << params.tonemap << params.scale;
1836
1837         Send(&pkt);
1838 }
1839 void Server::SendSetStars(session_t peer_id, const StarParams &params)
1840 {
1841         NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1842
1843         pkt << params.visible << params.count
1844                 << params.starcolor << params.scale
1845                 << params.day_opacity;
1846
1847         Send(&pkt);
1848 }
1849
1850 void Server::SendCloudParams(session_t peer_id, const CloudParams &params)
1851 {
1852         NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1853         pkt << params.density << params.color_bright << params.color_ambient
1854                         << params.height << params.thickness << params.speed;
1855         Send(&pkt);
1856 }
1857
1858 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1859                 float ratio)
1860 {
1861         NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1862                         1 + 2, peer_id);
1863
1864         pkt << do_override << (u16) (ratio * 65535);
1865
1866         Send(&pkt);
1867 }
1868
1869 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1870 {
1871         NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1872                         4, peer_id);
1873         
1874         pkt << lighting.shadow_intensity;
1875
1876         Send(&pkt);
1877 }
1878
1879 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1880 {
1881         NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1882         pkt << time << time_speed;
1883
1884         if (peer_id == PEER_ID_INEXISTENT) {
1885                 m_clients.sendToAll(&pkt);
1886         }
1887         else {
1888                 Send(&pkt);
1889         }
1890 }
1891
1892 void Server::SendPlayerBreath(PlayerSAO *sao)
1893 {
1894         assert(sao);
1895
1896         m_script->player_event(sao, "breath_changed");
1897         SendBreath(sao->getPeerID(), sao->getBreath());
1898 }
1899
1900 void Server::SendMovePlayer(session_t peer_id)
1901 {
1902         RemotePlayer *player = m_env->getPlayer(peer_id);
1903         assert(player);
1904         PlayerSAO *sao = player->getPlayerSAO();
1905         assert(sao);
1906
1907         // Send attachment updates instantly to the client prior updating position
1908         sao->sendOutdatedData();
1909
1910         NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1911         pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1912
1913         {
1914                 v3f pos = sao->getBasePosition();
1915                 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1916                                 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1917                                 << " pitch=" << sao->getLookPitch()
1918                                 << " yaw=" << sao->getRotation().Y
1919                                 << std::endl;
1920         }
1921
1922         Send(&pkt);
1923 }
1924
1925 void Server::SendPlayerFov(session_t peer_id)
1926 {
1927         NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1928
1929         PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1930         pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1931
1932         Send(&pkt);
1933 }
1934
1935 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1936                 f32 animation_speed)
1937 {
1938         NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1939                 peer_id);
1940
1941         pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1942                         << animation_frames[3] << animation_speed;
1943
1944         Send(&pkt);
1945 }
1946
1947 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1948 {
1949         NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1950         pkt << first << third;
1951         Send(&pkt);
1952 }
1953
1954 void Server::SendPlayerPrivileges(session_t peer_id)
1955 {
1956         RemotePlayer *player = m_env->getPlayer(peer_id);
1957         assert(player);
1958         if(player->getPeerId() == PEER_ID_INEXISTENT)
1959                 return;
1960
1961         std::set<std::string> privs;
1962         m_script->getAuth(player->getName(), NULL, &privs);
1963
1964         NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1965         pkt << (u16) privs.size();
1966
1967         for (const std::string &priv : privs) {
1968                 pkt << priv;
1969         }
1970
1971         Send(&pkt);
1972 }
1973
1974 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1975 {
1976         RemotePlayer *player = m_env->getPlayer(peer_id);
1977         assert(player);
1978         if (player->getPeerId() == PEER_ID_INEXISTENT)
1979                 return;
1980
1981         NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1982         pkt.putLongString(player->inventory_formspec);
1983
1984         Send(&pkt);
1985 }
1986
1987 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1988 {
1989         RemotePlayer *player = m_env->getPlayer(peer_id);
1990         assert(player);
1991         if (player->getPeerId() == PEER_ID_INEXISTENT)
1992                 return;
1993
1994         NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1995         pkt << player->formspec_prepend;
1996         Send(&pkt);
1997 }
1998
1999 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
2000 {
2001         // Radius inside which objects are active
2002         static thread_local const s16 radius =
2003                 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
2004
2005         // Radius inside which players are active
2006         static thread_local const bool is_transfer_limited =
2007                 g_settings->exists("unlimited_player_transfer_distance") &&
2008                 !g_settings->getBool("unlimited_player_transfer_distance");
2009
2010         static thread_local const s16 player_transfer_dist =
2011                 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
2012
2013         s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
2014                 radius : player_transfer_dist;
2015
2016         s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
2017         if (my_radius <= 0)
2018                 my_radius = radius;
2019
2020         std::queue<u16> removed_objects, added_objects;
2021         m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
2022                 client->m_known_objects, removed_objects);
2023         m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
2024                 client->m_known_objects, added_objects);
2025
2026         int removed_count = removed_objects.size();
2027         int added_count   = added_objects.size();
2028
2029         if (removed_objects.empty() && added_objects.empty())
2030                 return;
2031
2032         char buf[4];
2033         std::string data;
2034
2035         // Handle removed objects
2036         writeU16((u8*)buf, removed_objects.size());
2037         data.append(buf, 2);
2038         while (!removed_objects.empty()) {
2039                 // Get object
2040                 u16 id = removed_objects.front();
2041                 ServerActiveObject* obj = m_env->getActiveObject(id);
2042
2043                 // Add to data buffer for sending
2044                 writeU16((u8*)buf, id);
2045                 data.append(buf, 2);
2046
2047                 // Remove from known objects
2048                 client->m_known_objects.erase(id);
2049
2050                 if (obj && obj->m_known_by_count > 0)
2051                         obj->m_known_by_count--;
2052
2053                 removed_objects.pop();
2054         }
2055
2056         // Handle added objects
2057         writeU16((u8*)buf, added_objects.size());
2058         data.append(buf, 2);
2059         while (!added_objects.empty()) {
2060                 // Get object
2061                 u16 id = added_objects.front();
2062                 ServerActiveObject *obj = m_env->getActiveObject(id);
2063                 added_objects.pop();
2064
2065                 if (!obj) {
2066                         warningstream << FUNCTION_NAME << ": NULL object id="
2067                                 << (int)id << std::endl;
2068                         continue;
2069                 }
2070
2071                 // Get object type
2072                 u8 type = obj->getSendType();
2073
2074                 // Add to data buffer for sending
2075                 writeU16((u8*)buf, id);
2076                 data.append(buf, 2);
2077                 writeU8((u8*)buf, type);
2078                 data.append(buf, 1);
2079
2080                 data.append(serializeString32(
2081                         obj->getClientInitializationData(client->net_proto_version)));
2082
2083                 // Add to known objects
2084                 client->m_known_objects.insert(id);
2085
2086                 obj->m_known_by_count++;
2087         }
2088
2089         NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2090         pkt.putRawString(data.c_str(), data.size());
2091         Send(&pkt);
2092
2093         verbosestream << "Server::SendActiveObjectRemoveAdd: "
2094                 << removed_count << " removed, " << added_count << " added, "
2095                 << "packet size is " << pkt.getSize() << std::endl;
2096 }
2097
2098 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2099                 bool reliable)
2100 {
2101         NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2102                         datas.size(), peer_id);
2103
2104         pkt.putRawString(datas.c_str(), datas.size());
2105
2106         m_clients.send(pkt.getPeerId(),
2107                         reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2108                         &pkt, reliable);
2109 }
2110
2111 void Server::SendCSMRestrictionFlags(session_t peer_id)
2112 {
2113         NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2114                 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2115         pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2116         Send(&pkt);
2117 }
2118
2119 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2120 {
2121         NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2122         pkt << added_vel;
2123         Send(&pkt);
2124 }
2125
2126 inline s32 Server::nextSoundId()
2127 {
2128         s32 ret = m_next_sound_id;
2129         if (m_next_sound_id == INT32_MAX)
2130                 m_next_sound_id = 0; // signed overflow is undefined
2131         else
2132                 m_next_sound_id++;
2133         return ret;
2134 }
2135
2136 s32 Server::playSound(ServerPlayingSound &params, bool ephemeral)
2137 {
2138         // Find out initial position of sound
2139         bool pos_exists = false;
2140         const v3f pos = params.getPos(m_env, &pos_exists);
2141         // If position is not found while it should be, cancel sound
2142         if(pos_exists != (params.type != SoundLocation::Local))
2143                 return -1;
2144
2145         // Filter destination clients
2146         std::vector<session_t> dst_clients;
2147         if (!params.to_player.empty()) {
2148                 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2149                 if(!player){
2150                         infostream<<"Server::playSound: Player \""<<params.to_player
2151                                         <<"\" not found"<<std::endl;
2152                         return -1;
2153                 }
2154                 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2155                         infostream<<"Server::playSound: Player \""<<params.to_player
2156                                         <<"\" not connected"<<std::endl;
2157                         return -1;
2158                 }
2159                 dst_clients.push_back(player->getPeerId());
2160         } else {
2161                 std::vector<session_t> clients = m_clients.getClientIDs();
2162
2163                 for (const session_t client_id : clients) {
2164                         RemotePlayer *player = m_env->getPlayer(client_id);
2165                         if (!player)
2166                                 continue;
2167                         if (!params.exclude_player.empty() &&
2168                                         params.exclude_player == player->getName())
2169                                 continue;
2170
2171                         PlayerSAO *sao = player->getPlayerSAO();
2172                         if (!sao)
2173                                 continue;
2174
2175                         if (pos_exists) {
2176                                 if(sao->getBasePosition().getDistanceFrom(pos) >
2177                                                 params.max_hear_distance)
2178                                         continue;
2179                         }
2180                         dst_clients.push_back(client_id);
2181                 }
2182         }
2183
2184         if(dst_clients.empty())
2185                 return -1;
2186
2187         // old clients will still use this, so pick a reserved ID (-1)
2188         const s32 id = ephemeral ? -1 : nextSoundId();
2189
2190         float gain = params.gain * params.spec.gain;
2191         NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2192         pkt << id << params.spec.name << gain
2193                         << (u8) params.type << pos << params.object
2194                         << params.spec.loop << params.spec.fade << params.spec.pitch
2195                         << ephemeral;
2196
2197         bool as_reliable = !ephemeral;
2198
2199         for (const session_t peer_id : dst_clients) {
2200                 if (!ephemeral)
2201                         params.clients.insert(peer_id);
2202                 m_clients.send(peer_id, 0, &pkt, as_reliable);
2203         }
2204
2205         if (!ephemeral)
2206                 m_playing_sounds[id] = std::move(params);
2207         return id;
2208 }
2209 void Server::stopSound(s32 handle)
2210 {
2211         auto it = m_playing_sounds.find(handle);
2212         if (it == m_playing_sounds.end())
2213                 return;
2214
2215         ServerPlayingSound &psound = it->second;
2216
2217         NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2218         pkt << handle;
2219
2220         for (session_t peer_id : psound.clients) {
2221                 // Send as reliable
2222                 m_clients.send(peer_id, 0, &pkt, true);
2223         }
2224
2225         // Remove sound reference
2226         m_playing_sounds.erase(it);
2227 }
2228
2229 void Server::fadeSound(s32 handle, float step, float gain)
2230 {
2231         auto it = m_playing_sounds.find(handle);
2232         if (it == m_playing_sounds.end())
2233                 return;
2234
2235         ServerPlayingSound &psound = it->second;
2236         psound.gain = gain; // destination gain
2237
2238         NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2239         pkt << handle << step << gain;
2240
2241         for (session_t peer_id : psound.clients) {
2242                 // Send as reliable
2243                 m_clients.send(peer_id, 0, &pkt, true);
2244         }
2245
2246         // Remove sound reference
2247         if (gain <= 0 || psound.clients.empty())
2248                 m_playing_sounds.erase(it);
2249 }
2250
2251 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2252                 float far_d_nodes)
2253 {
2254         float maxd = far_d_nodes * BS;
2255         v3f p_f = intToFloat(p, BS);
2256         v3s16 block_pos = getNodeBlockPos(p);
2257
2258         NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2259         pkt << p;
2260
2261         std::vector<session_t> clients = m_clients.getClientIDs();
2262         ClientInterface::AutoLock clientlock(m_clients);
2263
2264         for (session_t client_id : clients) {
2265                 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2266                 if (!client)
2267                         continue;
2268
2269                 RemotePlayer *player = m_env->getPlayer(client_id);
2270                 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2271
2272                 // If player is far away, only set modified blocks not sent
2273                 if (!client->isBlockSent(block_pos) || (sao &&
2274                                 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2275                         if (far_players)
2276                                 far_players->emplace(client_id);
2277                         else
2278                                 client->SetBlockNotSent(block_pos);
2279                         continue;
2280                 }
2281
2282                 // Send as reliable
2283                 m_clients.send(client_id, 0, &pkt, true);
2284         }
2285 }
2286
2287 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2288                 float far_d_nodes, bool remove_metadata)
2289 {
2290         float maxd = far_d_nodes * BS;
2291         v3f p_f = intToFloat(p, BS);
2292         v3s16 block_pos = getNodeBlockPos(p);
2293
2294         NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2295         pkt << p << n.param0 << n.param1 << n.param2
2296                         << (u8) (remove_metadata ? 0 : 1);
2297
2298         std::vector<session_t> clients = m_clients.getClientIDs();
2299         ClientInterface::AutoLock clientlock(m_clients);
2300
2301         for (session_t client_id : clients) {
2302                 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2303                 if (!client)
2304                         continue;
2305
2306                 RemotePlayer *player = m_env->getPlayer(client_id);
2307                 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2308
2309                 // If player is far away, only set modified blocks not sent
2310                 if (!client->isBlockSent(block_pos) || (sao &&
2311                                 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2312                         if (far_players)
2313                                 far_players->emplace(client_id);
2314                         else
2315                                 client->SetBlockNotSent(block_pos);
2316                         continue;
2317                 }
2318
2319                 // Send as reliable
2320                 m_clients.send(client_id, 0, &pkt, true);
2321         }
2322 }
2323
2324 void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
2325 {
2326         NodeMetadataList meta_updates_list(false);
2327         std::ostringstream os(std::ios::binary);
2328
2329         std::vector<session_t> clients = m_clients.getClientIDs();
2330         ClientInterface::AutoLock clientlock(m_clients);
2331
2332         for (session_t i : clients) {
2333                 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2334                 if (!client)
2335                         continue;
2336
2337                 ServerActiveObject *player = getPlayerSAO(i);
2338                 v3s16 player_pos;
2339                 if (player)
2340                         player_pos = floatToInt(player->getBasePosition(), BS);
2341
2342                 for (const v3s16 pos : positions) {
2343                         NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2344
2345                         if (!meta)
2346                                 continue;
2347
2348                         v3s16 block_pos = getNodeBlockPos(pos);
2349                         if (!client->isBlockSent(block_pos) ||
2350                                         player_pos.getDistanceFrom(pos) > far_d_nodes) {
2351                                 client->SetBlockNotSent(block_pos);
2352                                 continue;
2353                         }
2354
2355                         // Add the change to send list
2356                         meta_updates_list.set(pos, meta);
2357                 }
2358                 if (meta_updates_list.size() == 0)
2359                         continue;
2360
2361                 // Send the meta changes
2362                 os.str("");
2363                 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2364                 std::string raw = os.str();
2365                 os.str("");
2366                 compressZlib(raw, os);
2367
2368                 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
2369                 pkt.putLongString(os.str());
2370                 Send(&pkt);
2371
2372                 meta_updates_list.clear();
2373         }
2374 }
2375
2376 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2377                 u16 net_proto_version, SerializedBlockCache *cache)
2378 {
2379         thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2380         std::string s, *sptr = nullptr;
2381
2382         if (cache) {
2383                 auto it = cache->find({block->getPos(), ver});
2384                 if (it != cache->end())
2385                         sptr = &it->second;
2386         }
2387
2388         // Serialize the block in the right format
2389         if (!sptr) {
2390                 std::ostringstream os(std::ios_base::binary);
2391                 block->serialize(os, ver, false, net_compression_level);
2392                 block->serializeNetworkSpecific(os);
2393                 s = os.str();
2394                 sptr = &s;
2395         }
2396
2397         NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2398         pkt << block->getPos();
2399         pkt.putRawString(*sptr);
2400         Send(&pkt);
2401
2402         // Store away in cache
2403         if (cache && sptr == &s)
2404                 (*cache)[{block->getPos(), ver}] = std::move(s);
2405 }
2406
2407 void Server::SendBlocks(float dtime)
2408 {
2409         MutexAutoLock envlock(m_env_mutex);
2410         //TODO check if one big lock could be faster then multiple small ones
2411
2412         std::vector<PrioritySortedBlockTransfer> queue;
2413
2414         u32 total_sending = 0, unique_clients = 0;
2415
2416         {
2417                 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2418
2419                 std::vector<session_t> clients = m_clients.getClientIDs();
2420
2421                 ClientInterface::AutoLock clientlock(m_clients);
2422                 for (const session_t client_id : clients) {
2423                         RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2424
2425                         if (!client)
2426                                 continue;
2427
2428                         total_sending += client->getSendingCount();
2429                         const auto old_count = queue.size();
2430                         client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2431                         unique_clients += queue.size() > old_count ? 1 : 0;
2432                 }
2433         }
2434
2435         // Sort.
2436         // Lowest priority number comes first.
2437         // Lowest is most important.
2438         std::sort(queue.begin(), queue.end());
2439
2440         ClientInterface::AutoLock clientlock(m_clients);
2441
2442         // Maximal total count calculation
2443         // The per-client block sends is halved with the maximal online users
2444         u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2445                 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2446
2447         ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2448         Map &map = m_env->getMap();
2449
2450         SerializedBlockCache cache, *cache_ptr = nullptr;
2451         if (unique_clients > 1) {
2452                 // caching is pointless with a single client
2453                 cache_ptr = &cache;
2454         }
2455
2456         for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2457                 if (total_sending >= max_blocks_to_send)
2458                         break;
2459
2460                 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2461                 if (!block)
2462                         continue;
2463
2464                 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2465                                 CS_Active);
2466                 if (!client)
2467                         continue;
2468
2469                 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2470                                 client->net_proto_version, cache_ptr);
2471
2472                 client->SentBlock(block_to_send.pos);
2473                 total_sending++;
2474         }
2475 }
2476
2477 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2478 {
2479         MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2480         if (!block)
2481                 return false;
2482
2483         ClientInterface::AutoLock clientlock(m_clients);
2484         RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2485         if (!client || client->isBlockSent(blockpos))
2486                 return false;
2487         SendBlockNoLock(peer_id, block, client->serialization_version,
2488                         client->net_proto_version);
2489
2490         return true;
2491 }
2492
2493 bool Server::addMediaFile(const std::string &filename,
2494         const std::string &filepath, std::string *filedata_to,
2495         std::string *digest_to)
2496 {
2497         // If name contains illegal characters, ignore the file
2498         if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2499                 infostream << "Server: ignoring illegal file name: \""
2500                                 << filename << "\"" << std::endl;
2501                 return false;
2502         }
2503         // If name is not in a supported format, ignore it
2504         const char *supported_ext[] = {
2505                 ".png", ".jpg", ".bmp", ".tga",
2506                 ".ogg",
2507                 ".x", ".b3d", ".obj",
2508                 // Custom translation file format
2509                 ".tr",
2510                 NULL
2511         };
2512         if (removeStringEnd(filename, supported_ext).empty()) {
2513                 infostream << "Server: ignoring unsupported file extension: \""
2514                                 << filename << "\"" << std::endl;
2515                 return false;
2516         }
2517         // Ok, attempt to load the file and add to cache
2518
2519         // Read data
2520         std::string filedata;
2521         if (!fs::ReadFile(filepath, filedata)) {
2522                 errorstream << "Server::addMediaFile(): Failed to open \""
2523                                         << filename << "\" for reading" << std::endl;
2524                 return false;
2525         }
2526
2527         if (filedata.empty()) {
2528                 errorstream << "Server::addMediaFile(): Empty file \""
2529                                 << filepath << "\"" << std::endl;
2530                 return false;
2531         }
2532
2533         SHA1 sha1;
2534         sha1.addBytes(filedata.c_str(), filedata.length());
2535
2536         unsigned char *digest = sha1.getDigest();
2537         std::string sha1_base64 = base64_encode(digest, 20);
2538         std::string sha1_hex = hex_encode((char*) digest, 20);
2539         if (digest_to)
2540                 *digest_to = std::string((char*) digest, 20);
2541         free(digest);
2542
2543         // Put in list
2544         m_media[filename] = MediaInfo(filepath, sha1_base64);
2545         verbosestream << "Server: " << sha1_hex << " is " << filename
2546                         << std::endl;
2547
2548         if (filedata_to)
2549                 *filedata_to = std::move(filedata);
2550         return true;
2551 }
2552
2553 void Server::fillMediaCache()
2554 {
2555         infostream << "Server: Calculating media file checksums" << std::endl;
2556
2557         // Collect all media file paths
2558         std::vector<std::string> paths;
2559
2560         // ordered in descending priority
2561         paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2562         fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2563         fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2564         m_modmgr->getModsMediaPaths(paths);
2565
2566         // Collect media file information from paths into cache
2567         for (const std::string &mediapath : paths) {
2568                 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2569                 for (const fs::DirListNode &dln : dirlist) {
2570                         if (dln.dir) // Ignore dirs (already in paths)
2571                                 continue;
2572
2573                         const std::string &filename = dln.name;
2574                         if (m_media.find(filename) != m_media.end()) // Do not override
2575                                 continue;
2576
2577                         std::string filepath = mediapath;
2578                         filepath.append(DIR_DELIM).append(filename);
2579                         addMediaFile(filename, filepath);
2580                 }
2581         }
2582
2583         infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2584 }
2585
2586 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2587 {
2588         // Make packet
2589         NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2590
2591         u16 media_sent = 0;
2592         std::string lang_suffix;
2593         lang_suffix.append(".").append(lang_code).append(".tr");
2594         for (const auto &i : m_media) {
2595                 if (i.second.no_announce)
2596                         continue;
2597                 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2598                         continue;
2599                 media_sent++;
2600         }
2601
2602         pkt << media_sent;
2603
2604         for (const auto &i : m_media) {
2605                 if (i.second.no_announce)
2606                         continue;
2607                 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2608                         continue;
2609                 pkt << i.first << i.second.sha1_digest;
2610         }
2611
2612         pkt << g_settings->get("remote_media");
2613         Send(&pkt);
2614
2615         verbosestream << "Server: Announcing files to id(" << peer_id
2616                 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2617 }
2618
2619 struct SendableMedia
2620 {
2621         std::string name;
2622         std::string path;
2623         std::string data;
2624
2625         SendableMedia(const std::string &name, const std::string &path,
2626                         std::string &&data):
2627                 name(name), path(path), data(std::move(data))
2628         {}
2629 };
2630
2631 void Server::sendRequestedMedia(session_t peer_id,
2632                 const std::vector<std::string> &tosend)
2633 {
2634         verbosestream<<"Server::sendRequestedMedia(): "
2635                         <<"Sending files to client"<<std::endl;
2636
2637         /* Read files */
2638
2639         // Put 5kB in one bunch (this is not accurate)
2640         u32 bytes_per_bunch = 5000;
2641
2642         std::vector< std::vector<SendableMedia> > file_bunches;
2643         file_bunches.emplace_back();
2644
2645         u32 file_size_bunch_total = 0;
2646
2647         for (const std::string &name : tosend) {
2648                 if (m_media.find(name) == m_media.end()) {
2649                         errorstream<<"Server::sendRequestedMedia(): Client asked for "
2650                                         <<"unknown file \""<<(name)<<"\""<<std::endl;
2651                         continue;
2652                 }
2653
2654                 const auto &m = m_media[name];
2655
2656                 // Read data
2657                 std::string data;
2658                 if (!fs::ReadFile(m.path, data)) {
2659                         errorstream << "Server::sendRequestedMedia(): Failed to read \""
2660                                         << name << "\"" << std::endl;
2661                         continue;
2662                 }
2663                 file_size_bunch_total += data.size();
2664
2665                 // Put in list
2666                 file_bunches.back().emplace_back(name, m.path, std::move(data));
2667
2668                 // Start next bunch if got enough data
2669                 if(file_size_bunch_total >= bytes_per_bunch) {
2670                         file_bunches.emplace_back();
2671                         file_size_bunch_total = 0;
2672                 }
2673
2674         }
2675
2676         /* Create and send packets */
2677
2678         u16 num_bunches = file_bunches.size();
2679         for (u16 i = 0; i < num_bunches; i++) {
2680                 /*
2681                         u16 command
2682                         u16 total number of texture bunches
2683                         u16 index of this bunch
2684                         u32 number of files in this bunch
2685                         for each file {
2686                                 u16 length of name
2687                                 string name
2688                                 u32 length of data
2689                                 data
2690                         }
2691                 */
2692
2693                 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2694                 pkt << num_bunches << i << (u32) file_bunches[i].size();
2695
2696                 for (const SendableMedia &j : file_bunches[i]) {
2697                         pkt << j.name;
2698                         pkt.putLongString(j.data);
2699                 }
2700
2701                 verbosestream << "Server::sendRequestedMedia(): bunch "
2702                                 << i << "/" << num_bunches
2703                                 << " files=" << file_bunches[i].size()
2704                                 << " size="  << pkt.getSize() << std::endl;
2705                 Send(&pkt);
2706         }
2707 }
2708
2709 void Server::stepPendingDynMediaCallbacks(float dtime)
2710 {
2711         MutexAutoLock lock(m_env_mutex);
2712
2713         for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2714                 it->second.expiry_timer -= dtime;
2715                 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2716
2717                 if (!del) {
2718                         it++;
2719                         continue;
2720                 }
2721
2722                 const auto &name = it->second.filename;
2723                 if (!name.empty()) {
2724                         assert(m_media.count(name));
2725                         // if no_announce isn't set we're definitely deleting the wrong file!
2726                         sanity_check(m_media[name].no_announce);
2727
2728                         fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2729                         m_media.erase(name);
2730                 }
2731                 getScriptIface()->freeDynamicMediaCallback(it->first);
2732                 it = m_pending_dyn_media.erase(it);
2733         }
2734 }
2735
2736 void Server::SendMinimapModes(session_t peer_id,
2737                 std::vector<MinimapMode> &modes, size_t wanted_mode)
2738 {
2739         RemotePlayer *player = m_env->getPlayer(peer_id);
2740         assert(player);
2741         if (player->getPeerId() == PEER_ID_INEXISTENT)
2742                 return;
2743
2744         NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2745         pkt << (u16)modes.size() << (u16)wanted_mode;
2746
2747         for (auto &mode : modes)
2748                 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2749
2750         Send(&pkt);
2751 }
2752
2753 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2754 {
2755         NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2756         pkt << name;
2757
2758         if (!inventory) {
2759                 pkt << false; // Remove inventory
2760         } else {
2761                 pkt << true; // Update inventory
2762
2763                 // Serialization & NetworkPacket isn't a love story
2764                 std::ostringstream os(std::ios_base::binary);
2765                 inventory->serialize(os);
2766                 inventory->setModified(false);
2767
2768                 const std::string &os_str = os.str();
2769                 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2770                 pkt.putRawString(os_str);
2771         }
2772
2773         if (peer_id == PEER_ID_INEXISTENT)
2774                 m_clients.sendToAll(&pkt);
2775         else
2776                 Send(&pkt);
2777 }
2778
2779 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2780 {
2781         // Lookup player name, to filter detached inventories just after
2782         std::string peer_name;
2783         if (peer_id != PEER_ID_INEXISTENT) {
2784                 peer_name = getClient(peer_id, CS_Created)->getName();
2785         }
2786
2787         auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2788                 sendDetachedInventory(inv, name, peer_id);
2789         };
2790
2791         m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2792 }
2793
2794 /*
2795         Something random
2796 */
2797
2798 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2799 {
2800         infostream << "Server::DiePlayer(): Player "
2801                         << playersao->getPlayer()->getName()
2802                         << " dies" << std::endl;
2803
2804         playersao->clearParentAttachment();
2805
2806         // Trigger scripted stuff
2807         m_script->on_dieplayer(playersao, reason);
2808
2809         SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2810 }
2811
2812 void Server::RespawnPlayer(session_t peer_id)
2813 {
2814         PlayerSAO *playersao = getPlayerSAO(peer_id);
2815         assert(playersao);
2816
2817         infostream << "Server::RespawnPlayer(): Player "
2818                         << playersao->getPlayer()->getName()
2819                         << " respawns" << std::endl;
2820
2821         const auto *prop = playersao->accessObjectProperties();
2822         playersao->setHP(prop->hp_max,
2823                         PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2824         playersao->setBreath(prop->breath_max);
2825
2826         bool repositioned = m_script->on_respawnplayer(playersao);
2827         if (!repositioned) {
2828                 // setPos will send the new position to client
2829                 playersao->setPos(findSpawnPos());
2830         }
2831 }
2832
2833
2834 void Server::DenySudoAccess(session_t peer_id)
2835 {
2836         NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2837         Send(&pkt);
2838 }
2839
2840
2841 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2842                 const std::string &custom_reason, bool reconnect)
2843 {
2844         SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2845         m_clients.event(peer_id, CSE_SetDenied);
2846         DisconnectPeer(peer_id);
2847 }
2848
2849 void Server::DisconnectPeer(session_t peer_id)
2850 {
2851         m_modchannel_mgr->leaveAllChannels(peer_id);
2852         m_con->DisconnectPeer(peer_id);
2853 }
2854
2855 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2856 {
2857         if (!forSudoMode) {
2858                 RemoteClient* client = getClient(peer_id, CS_Invalid);
2859
2860                 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2861
2862                 // Right now, the auth mechs don't change between login and sudo mode.
2863                 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2864                 client->allowed_sudo_mechs = sudo_auth_mechs;
2865
2866                 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2867                                 << g_settings->getFloat("dedicated_server_step")
2868                                 << sudo_auth_mechs;
2869
2870                 Send(&resp_pkt);
2871                 m_clients.event(peer_id, CSE_AuthAccept);
2872         } else {
2873                 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2874
2875                 // We only support SRP right now
2876                 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2877
2878                 resp_pkt << sudo_auth_mechs;
2879                 Send(&resp_pkt);
2880                 m_clients.event(peer_id, CSE_SudoSuccess);
2881         }
2882 }
2883
2884 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2885 {
2886         std::wstring message;
2887         {
2888                 /*
2889                         Clear references to playing sounds
2890                 */
2891                 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2892                                  i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2893                         ServerPlayingSound &psound = i->second;
2894                         psound.clients.erase(peer_id);
2895                         if (psound.clients.empty())
2896                                 m_playing_sounds.erase(i++);
2897                         else
2898                                 ++i;
2899                 }
2900
2901                 // clear formspec info so the next client can't abuse the current state
2902                 m_formspec_state_data.erase(peer_id);
2903
2904                 RemotePlayer *player = m_env->getPlayer(peer_id);
2905
2906                 /* Run scripts and remove from environment */
2907                 if (player) {
2908                         PlayerSAO *playersao = player->getPlayerSAO();
2909                         assert(playersao);
2910
2911                         playersao->clearChildAttachments();
2912                         playersao->clearParentAttachment();
2913
2914                         // inform connected clients
2915                         const std::string &player_name = player->getName();
2916                         NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2917                         // (u16) 1 + std::string represents a vector serialization representation
2918                         notice << (u8) PLAYER_LIST_REMOVE  << (u16) 1 << player_name;
2919                         m_clients.sendToAll(&notice);
2920                         // run scripts
2921                         m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2922
2923                         playersao->disconnected();
2924                 }
2925
2926                 /*
2927                         Print out action
2928                 */
2929                 {
2930                         if (player && reason != CDR_DENY) {
2931                                 std::ostringstream os(std::ios_base::binary);
2932                                 std::vector<session_t> clients = m_clients.getClientIDs();
2933
2934                                 for (const session_t client_id : clients) {
2935                                         // Get player
2936                                         RemotePlayer *player = m_env->getPlayer(client_id);
2937                                         if (!player)
2938                                                 continue;
2939
2940                                         // Get name of player
2941                                         os << player->getName() << " ";
2942                                 }
2943
2944                                 std::string name = player->getName();
2945                                 actionstream << name << " "
2946                                                 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2947                                                 << " List of players: " << os.str() << std::endl;
2948                                 if (m_admin_chat)
2949                                         m_admin_chat->outgoing_queue.push_back(
2950                                                 new ChatEventNick(CET_NICK_REMOVE, name));
2951                         }
2952                 }
2953                 {
2954                         MutexAutoLock env_lock(m_env_mutex);
2955                         m_clients.DeleteClient(peer_id);
2956                 }
2957         }
2958
2959         // Send leave chat message to all remaining clients
2960         if (!message.empty()) {
2961                 SendChatMessage(PEER_ID_INEXISTENT,
2962                                 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2963         }
2964 }
2965
2966 void Server::UpdateCrafting(RemotePlayer *player)
2967 {
2968         InventoryList *clist = player->inventory.getList("craft");
2969         if (!clist || clist->getSize() == 0)
2970                 return;
2971
2972         if (!clist->checkModified())
2973                 return;
2974
2975         // Get a preview for crafting
2976         ItemStack preview;
2977         InventoryLocation loc;
2978         loc.setPlayer(player->getName());
2979         std::vector<ItemStack> output_replacements;
2980         getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2981         m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2982                         clist, loc);
2983
2984         InventoryList *plist = player->inventory.getList("craftpreview");
2985         if (plist && plist->getSize() >= 1) {
2986                 // Put the new preview in
2987                 plist->changeItem(0, preview);
2988         }
2989 }
2990
2991 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2992 {
2993         if (evt->type == CET_NICK_ADD) {
2994                 // The terminal informed us of its nick choice
2995                 m_admin_nick = ((ChatEventNick *)evt)->nick;
2996                 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2997                         errorstream << "You haven't set up an account." << std::endl
2998                                 << "Please log in using the client as '"
2999                                 << m_admin_nick << "' with a secure password." << std::endl
3000                                 << "Until then, you can't execute admin tasks via the console," << std::endl
3001                                 << "and everybody can claim the user account instead of you," << std::endl
3002                                 << "giving them full control over this server." << std::endl;
3003                 }
3004         } else {
3005                 assert(evt->type == CET_CHAT);
3006                 handleAdminChat((ChatEventChat *)evt);
3007         }
3008 }
3009
3010 std::wstring Server::handleChat(const std::string &name,
3011         std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
3012 {
3013         // If something goes wrong, this player is to blame
3014         RollbackScopeActor rollback_scope(m_rollback,
3015                         std::string("player:") + name);
3016
3017         if (g_settings->getBool("strip_color_codes"))
3018                 wmessage = unescape_enriched(wmessage);
3019
3020         if (player) {
3021                 switch (player->canSendChatMessage()) {
3022                 case RPLAYER_CHATRESULT_FLOODING: {
3023                         std::wstringstream ws;
3024                         ws << L"You cannot send more messages. You are limited to "
3025                                         << g_settings->getFloat("chat_message_limit_per_10sec")
3026                                         << L" messages per 10 seconds.";
3027                         return ws.str();
3028                 }
3029                 case RPLAYER_CHATRESULT_KICK:
3030                         DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
3031                                 "You have been kicked due to message flooding.");
3032                         return L"";
3033                 case RPLAYER_CHATRESULT_OK:
3034                         break;
3035                 default:
3036                         FATAL_ERROR("Unhandled chat filtering result found.");
3037                 }
3038         }
3039
3040         if (m_max_chatmessage_length > 0
3041                         && wmessage.length() > m_max_chatmessage_length) {
3042                 return L"Your message exceed the maximum chat message limit set on the server. "
3043                                 L"It was refused. Send a shorter message";
3044         }
3045
3046         auto message = trim(wide_to_utf8(wmessage));
3047         if (message.empty())
3048                 return L"";
3049
3050         if (message.find_first_of("\n\r") != std::wstring::npos) {
3051                 return L"Newlines are not permitted in chat messages";
3052         }
3053
3054         // Run script hook, exit if script ate the chat message
3055         if (m_script->on_chat_message(name, message))
3056                 return L"";
3057
3058         // Line to send
3059         std::wstring line;
3060         // Whether to send line to the player that sent the message, or to all players
3061         bool broadcast_line = true;
3062
3063         if (check_shout_priv && !checkPriv(name, "shout")) {
3064                 line += L"-!- You don't have permission to shout.";
3065                 broadcast_line = false;
3066         } else {
3067                 /*
3068                         Workaround for fixing chat on Android. Lua doesn't handle
3069                         the Cyrillic alphabet and some characters on older Android devices
3070                 */
3071 #ifdef __ANDROID__
3072                 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3073 #else
3074                 line += utf8_to_wide(m_script->formatChatMessage(name,
3075                                 wide_to_utf8(wmessage)));
3076 #endif
3077         }
3078
3079         /*
3080                 Tell calling method to send the message to sender
3081         */
3082         if (!broadcast_line)
3083                 return line;
3084
3085         /*
3086                 Send the message to others
3087         */
3088         actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3089
3090         ChatMessage chatmsg(line);
3091
3092         std::vector<session_t> clients = m_clients.getClientIDs();
3093         for (u16 cid : clients)
3094                 SendChatMessage(cid, chatmsg);
3095
3096         return L"";
3097 }
3098
3099 void Server::handleAdminChat(const ChatEventChat *evt)
3100 {
3101         std::string name = evt->nick;
3102         std::wstring wmessage = evt->evt_msg;
3103
3104         std::wstring answer = handleChat(name, wmessage);
3105
3106         // If asked to send answer to sender
3107         if (!answer.empty()) {
3108                 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3109         }
3110 }
3111
3112 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3113 {
3114         RemoteClient *client = getClientNoEx(peer_id,state_min);
3115         if(!client)
3116                 throw ClientNotFoundException("Client not found");
3117
3118         return client;
3119 }
3120 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3121 {
3122         return m_clients.getClientNoEx(peer_id, state_min);
3123 }
3124
3125 std::string Server::getPlayerName(session_t peer_id)
3126 {
3127         RemotePlayer *player = m_env->getPlayer(peer_id);
3128         if (!player)
3129                 return "[id="+itos(peer_id)+"]";
3130         return player->getName();
3131 }
3132
3133 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3134 {
3135         RemotePlayer *player = m_env->getPlayer(peer_id);
3136         if (!player)
3137                 return NULL;
3138         return player->getPlayerSAO();
3139 }
3140
3141 std::string Server::getStatusString()
3142 {
3143         std::ostringstream os(std::ios_base::binary);
3144         os << "# Server: ";
3145         // Version
3146         os << "version: " << g_version_string;
3147         // Game
3148         os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3149         // Uptime
3150         os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3151         // Max lag estimate
3152         os << " | max lag: " << std::setprecision(3);
3153         os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3154
3155         // Information about clients
3156         bool first = true;
3157         os << " | clients: ";
3158         if (m_env) {
3159                 std::vector<session_t> clients = m_clients.getClientIDs();
3160                 for (session_t client_id : clients) {
3161                         RemotePlayer *player = m_env->getPlayer(client_id);
3162
3163                         // Get name of player
3164                         const char *name = player ? player->getName() : "<unknown>";
3165
3166                         // Add name to information string
3167                         if (!first)
3168                                 os << ", ";
3169                         else
3170                                 first = false;
3171                         os << name;
3172                 }
3173         }
3174
3175         if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3176                 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3177
3178         if (!g_settings->get("motd").empty())
3179                 os << std::endl << "# Server: " << g_settings->get("motd");
3180
3181         return os.str();
3182 }
3183
3184 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3185 {
3186         std::set<std::string> privs;
3187         m_script->getAuth(name, NULL, &privs);
3188         return privs;
3189 }
3190
3191 bool Server::checkPriv(const std::string &name, const std::string &priv)
3192 {
3193         std::set<std::string> privs = getPlayerEffectivePrivs(name);
3194         return (privs.count(priv) != 0);
3195 }
3196
3197 void Server::reportPrivsModified(const std::string &name)
3198 {
3199         if (name.empty()) {
3200                 std::vector<session_t> clients = m_clients.getClientIDs();
3201                 for (const session_t client_id : clients) {
3202                         RemotePlayer *player = m_env->getPlayer(client_id);
3203                         reportPrivsModified(player->getName());
3204                 }
3205         } else {
3206                 RemotePlayer *player = m_env->getPlayer(name.c_str());
3207                 if (!player)
3208                         return;
3209                 SendPlayerPrivileges(player->getPeerId());
3210                 PlayerSAO *sao = player->getPlayerSAO();
3211                 if(!sao)
3212                         return;
3213                 sao->updatePrivileges(
3214                                 getPlayerEffectivePrivs(name),
3215                                 isSingleplayer());
3216         }
3217 }
3218
3219 void Server::reportInventoryFormspecModified(const std::string &name)
3220 {
3221         RemotePlayer *player = m_env->getPlayer(name.c_str());
3222         if (!player)
3223                 return;
3224         SendPlayerInventoryFormspec(player->getPeerId());
3225 }
3226
3227 void Server::reportFormspecPrependModified(const std::string &name)
3228 {
3229         RemotePlayer *player = m_env->getPlayer(name.c_str());
3230         if (!player)
3231                 return;
3232         SendPlayerFormspecPrepend(player->getPeerId());
3233 }
3234
3235 void Server::setIpBanned(const std::string &ip, const std::string &name)
3236 {
3237         m_banmanager->add(ip, name);
3238 }
3239
3240 void Server::unsetIpBanned(const std::string &ip_or_name)
3241 {
3242         m_banmanager->remove(ip_or_name);
3243 }
3244
3245 std::string Server::getBanDescription(const std::string &ip_or_name)
3246 {
3247         return m_banmanager->getBanDescription(ip_or_name);
3248 }
3249
3250 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3251 {
3252         // m_env will be NULL if the server is initializing
3253         if (!m_env)
3254                 return;
3255
3256         if (m_admin_nick == name && !m_admin_nick.empty()) {
3257                 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3258         }
3259
3260         RemotePlayer *player = m_env->getPlayer(name);
3261         if (!player) {
3262                 return;
3263         }
3264
3265         if (player->getPeerId() == PEER_ID_INEXISTENT)
3266                 return;
3267
3268         SendChatMessage(player->getPeerId(), ChatMessage(msg));
3269 }
3270
3271 bool Server::showFormspec(const char *playername, const std::string &formspec,
3272         const std::string &formname)
3273 {
3274         // m_env will be NULL if the server is initializing
3275         if (!m_env)
3276                 return false;
3277
3278         RemotePlayer *player = m_env->getPlayer(playername);
3279         if (!player)
3280                 return false;
3281
3282         SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3283         return true;
3284 }
3285
3286 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3287 {
3288         if (!player)
3289                 return -1;
3290
3291         u32 id = player->addHud(form);
3292
3293         SendHUDAdd(player->getPeerId(), id, form);
3294
3295         return id;
3296 }
3297
3298 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3299         if (!player)
3300                 return false;
3301
3302         HudElement* todel = player->removeHud(id);
3303
3304         if (!todel)
3305                 return false;
3306
3307         delete todel;
3308
3309         SendHUDRemove(player->getPeerId(), id);
3310         return true;
3311 }
3312
3313 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3314 {
3315         if (!player)
3316                 return false;
3317
3318         SendHUDChange(player->getPeerId(), id, stat, data);
3319         return true;
3320 }
3321
3322 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3323 {
3324         if (!player)
3325                 return false;
3326
3327         u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3328         if (new_hud_flags == player->hud_flags) // no change
3329                 return true;
3330
3331         SendHUDSetFlags(player->getPeerId(), flags, mask);
3332         player->hud_flags = new_hud_flags;
3333
3334         PlayerSAO* playersao = player->getPlayerSAO();
3335
3336         if (!playersao)
3337                 return false;
3338
3339         m_script->player_event(playersao, "hud_changed");
3340         return true;
3341 }
3342
3343 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3344 {
3345         if (!player)
3346                 return false;
3347
3348         if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3349                 return false;
3350
3351         player->setHotbarItemcount(hotbar_itemcount);
3352         std::ostringstream os(std::ios::binary);
3353         writeS32(os, hotbar_itemcount);
3354         SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3355         return true;
3356 }
3357
3358 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3359 {
3360         if (!player)
3361                 return;
3362
3363         player->setHotbarImage(name);
3364         SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3365 }
3366
3367 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3368 {
3369         if (!player)
3370                 return;
3371
3372         player->setHotbarSelectedImage(name);
3373         SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3374 }
3375
3376 Address Server::getPeerAddress(session_t peer_id)
3377 {
3378         // Note that this is only set after Init was received in Server::handleCommand_Init
3379         return getClient(peer_id, CS_Invalid)->getAddress();
3380 }
3381
3382 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3383                 v2s32 animation_frames[4], f32 frame_speed)
3384 {
3385         sanity_check(player);
3386         player->setLocalAnimations(animation_frames, frame_speed);
3387         SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3388 }
3389
3390 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3391 {
3392         sanity_check(player);
3393         player->eye_offset_first = first;
3394         player->eye_offset_third = third;
3395         SendEyeOffset(player->getPeerId(), first, third);
3396 }
3397
3398 void Server::setSky(RemotePlayer *player, const SkyboxParams &params)
3399 {
3400         sanity_check(player);
3401         player->setSky(params);
3402         SendSetSky(player->getPeerId(), params);
3403 }
3404
3405 void Server::setSun(RemotePlayer *player, const SunParams &params)
3406 {
3407         sanity_check(player);
3408         player->setSun(params);
3409         SendSetSun(player->getPeerId(), params);
3410 }
3411
3412 void Server::setMoon(RemotePlayer *player, const MoonParams &params)
3413 {
3414         sanity_check(player);
3415         player->setMoon(params);
3416         SendSetMoon(player->getPeerId(), params);
3417 }
3418
3419 void Server::setStars(RemotePlayer *player, const StarParams &params)
3420 {
3421         sanity_check(player);
3422         player->setStars(params);
3423         SendSetStars(player->getPeerId(), params);
3424 }
3425
3426 void Server::setClouds(RemotePlayer *player, const CloudParams &params)
3427 {
3428         sanity_check(player);
3429         player->setCloudParams(params);
3430         SendCloudParams(player->getPeerId(), params);
3431 }
3432
3433 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3434         float ratio)
3435 {
3436         sanity_check(player);
3437         player->overrideDayNightRatio(do_override, ratio);
3438         SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3439 }
3440
3441 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3442 {
3443         sanity_check(player);
3444         player->setLighting(lighting);
3445         SendSetLighting(player->getPeerId(), lighting);
3446 }
3447
3448 void Server::notifyPlayers(const std::wstring &msg)
3449 {
3450         SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3451 }
3452
3453 void Server::spawnParticle(const std::string &playername,
3454         const ParticleParameters &p)
3455 {
3456         // m_env will be NULL if the server is initializing
3457         if (!m_env)
3458                 return;
3459
3460         session_t peer_id = PEER_ID_INEXISTENT;
3461         u16 proto_ver = 0;
3462         if (!playername.empty()) {
3463                 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3464                 if (!player)
3465                         return;
3466                 peer_id = player->getPeerId();
3467                 proto_ver = player->protocol_version;
3468         }
3469
3470         SendSpawnParticle(peer_id, proto_ver, p);
3471 }
3472
3473 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3474         ServerActiveObject *attached, const std::string &playername)
3475 {
3476         // m_env will be NULL if the server is initializing
3477         if (!m_env)
3478                 return -1;
3479
3480         session_t peer_id = PEER_ID_INEXISTENT;
3481         u16 proto_ver = 0;
3482         if (!playername.empty()) {
3483                 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3484                 if (!player)
3485                         return -1;
3486                 peer_id = player->getPeerId();
3487                 proto_ver = player->protocol_version;
3488         }
3489
3490         u16 attached_id = attached ? attached->getId() : 0;
3491
3492         u32 id;
3493         if (attached_id == 0)
3494                 id = m_env->addParticleSpawner(p.time);
3495         else
3496                 id = m_env->addParticleSpawner(p.time, attached_id);
3497
3498         SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3499         return id;
3500 }
3501
3502 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3503 {
3504         // m_env will be NULL if the server is initializing
3505         if (!m_env)
3506                 throw ServerError("Can't delete particle spawners during initialisation!");
3507
3508         session_t peer_id = PEER_ID_INEXISTENT;
3509         if (!playername.empty()) {
3510                 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3511                 if (!player)
3512                         return;
3513                 peer_id = player->getPeerId();
3514         }
3515
3516         m_env->deleteParticleSpawner(id);
3517         SendDeleteParticleSpawner(peer_id, id);
3518 }
3519
3520 bool Server::dynamicAddMedia(std::string filepath,
3521         const u32 token, const std::string &to_player, bool ephemeral)
3522 {
3523         std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3524         auto it = m_media.find(filename);
3525         if (it != m_media.end()) {
3526                 // Allow the same path to be "added" again in certain conditions
3527                 if (ephemeral || it->second.path != filepath) {
3528                         errorstream << "Server::dynamicAddMedia(): file \"" << filename
3529                                 << "\" already exists in media cache" << std::endl;
3530                         return false;
3531                 }
3532         }
3533
3534         // Load the file and add it to our media cache
3535         std::string filedata, raw_hash;
3536         bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3537         if (!ok)
3538                 return false;
3539
3540         if (ephemeral) {
3541                 // Create a copy of the file and swap out the path, this removes the
3542                 // requirement that mods keep the file accessible at the original path.
3543                 filepath = fs::CreateTempFile();
3544                 bool ok = ([&] () -> bool {
3545                         if (filepath.empty())
3546                                 return false;
3547                         std::ofstream os(filepath.c_str(), std::ios::binary);
3548                         if (!os.good())
3549                                 return false;
3550                         os << filedata;
3551                         os.close();
3552                         return !os.fail();
3553                 })();
3554                 if (!ok) {
3555                         errorstream << "Server: failed to create a copy of media file "
3556                                 << "\"" << filename << "\"" << std::endl;
3557                         m_media.erase(filename);
3558                         return false;
3559                 }
3560                 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3561                         << filepath << std::endl;
3562
3563                 m_media[filename].path = filepath;
3564                 m_media[filename].no_announce = true;
3565                 // stepPendingDynMediaCallbacks will clean this up later.
3566         } else if (!to_player.empty()) {
3567                 m_media[filename].no_announce = true;
3568         }
3569
3570         // Push file to existing clients
3571         NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3572         pkt << raw_hash << filename << (bool)ephemeral;
3573
3574         NetworkPacket legacy_pkt = pkt;
3575
3576         // Newer clients get asked to fetch the file (asynchronous)
3577         pkt << token;
3578         // Older clients have an awful hack that just throws the data at them
3579         legacy_pkt.putLongString(filedata);
3580
3581         std::unordered_set<session_t> delivered, waiting;
3582         {
3583                 ClientInterface::AutoLock clientlock(m_clients);
3584                 for (auto &pair : m_clients.getClientList()) {
3585                         if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3586                                 /*
3587                                         If a client is in the DefinitionsSent state it is too late to
3588                                         transfer the file via sendMediaAnnouncement() but at the same
3589                                         time the client cannot accept a media push yet.
3590                                         Short of artificially delaying the joining process there is no
3591                                         way for the server to resolve this so we (currently) opt not to.
3592                                 */
3593                                 warningstream << "The media \"" << filename << "\" (dynamic) could "
3594                                         "not be delivered to " << pair.second->getName()
3595                                         << " due to a race condition." << std::endl;
3596                                 continue;
3597                         }
3598                         if (pair.second->getState() < CS_Active)
3599                                 continue;
3600
3601                         const auto proto_ver = pair.second->net_proto_version;
3602                         if (proto_ver < 39)
3603                                 continue;
3604
3605                         const session_t peer_id = pair.second->peer_id;
3606                         if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3607                                 continue;
3608
3609                         if (proto_ver < 40) {
3610                                 delivered.emplace(peer_id);
3611                                 /*
3612                                         The network layer only guarantees ordered delivery inside a channel.
3613                                         Since the very next packet could be one that uses the media, we have
3614                                         to push the media over ALL channels to ensure it is processed before
3615                                         it is used. In practice this means channels 1 and 0.
3616                                 */
3617                                 m_clients.send(peer_id, 1, &legacy_pkt, true);
3618                                 m_clients.send(peer_id, 0, &legacy_pkt, true);
3619                         } else {
3620                                 waiting.emplace(peer_id);
3621                                 Send(peer_id, &pkt);
3622                         }
3623                 }
3624         }
3625
3626         // Run callback for players that already had the file delivered (legacy-only)
3627         for (session_t peer_id : delivered) {
3628                 if (auto player = m_env->getPlayer(peer_id))
3629                         getScriptIface()->on_dynamic_media_added(token, player->getName());
3630         }
3631
3632         // Save all others in our pending state
3633         auto &state = m_pending_dyn_media[token];
3634         state.waiting_players = std::move(waiting);
3635         // regardless of success throw away the callback after a while
3636         state.expiry_timer = 60.0f;
3637         if (ephemeral)
3638                 state.filename = filename;
3639
3640         return true;
3641 }
3642
3643 // actions: time-reversed list
3644 // Return value: success/failure
3645 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3646                 std::list<std::string> *log)
3647 {
3648         infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3649         ServerMap *map = (ServerMap*)(&m_env->getMap());
3650
3651         // Fail if no actions to handle
3652         if (actions.empty()) {
3653                 assert(log);
3654                 log->push_back("Nothing to do.");
3655                 return false;
3656         }
3657
3658         int num_tried = 0;
3659         int num_failed = 0;
3660
3661         for (const RollbackAction &action : actions) {
3662                 num_tried++;
3663                 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3664                 if(!success){
3665                         num_failed++;
3666                         std::ostringstream os;
3667                         os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3668                         infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3669                         if (log)
3670                                 log->push_back(os.str());
3671                 }else{
3672                         std::ostringstream os;
3673                         os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3674                         infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3675                         if (log)
3676                                 log->push_back(os.str());
3677                 }
3678         }
3679
3680         infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3681                         <<" failed"<<std::endl;
3682
3683         // Call it done if less than half failed
3684         return num_failed <= num_tried/2;
3685 }
3686
3687 // IGameDef interface
3688 // Under envlock
3689 IItemDefManager *Server::getItemDefManager()
3690 {
3691         return m_itemdef;
3692 }
3693
3694 const NodeDefManager *Server::getNodeDefManager()
3695 {
3696         return m_nodedef;
3697 }
3698
3699 ICraftDefManager *Server::getCraftDefManager()
3700 {
3701         return m_craftdef;
3702 }
3703
3704 u16 Server::allocateUnknownNodeId(const std::string &name)
3705 {
3706         return m_nodedef->allocateDummy(name);
3707 }
3708
3709 IWritableItemDefManager *Server::getWritableItemDefManager()
3710 {
3711         return m_itemdef;
3712 }
3713
3714 NodeDefManager *Server::getWritableNodeDefManager()
3715 {
3716         return m_nodedef;
3717 }
3718
3719 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3720 {
3721         return m_craftdef;
3722 }
3723
3724 const std::vector<ModSpec> & Server::getMods() const
3725 {
3726         return m_modmgr->getMods();
3727 }
3728
3729 const ModSpec *Server::getModSpec(const std::string &modname) const
3730 {
3731         return m_modmgr->getModSpec(modname);
3732 }
3733
3734 std::string Server::getBuiltinLuaPath()
3735 {
3736         return porting::path_share + DIR_DELIM + "builtin";
3737 }
3738
3739 v3f Server::findSpawnPos()
3740 {
3741         ServerMap &map = m_env->getServerMap();
3742         v3f nodeposf;
3743         if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3744                 return nodeposf * BS;
3745
3746         bool is_good = false;
3747         // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3748         s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3749
3750         // Try to find a good place a few times
3751         for (s32 i = 0; i < 4000 && !is_good; i++) {
3752                 s32 range = MYMIN(1 + i, range_max);
3753                 // We're going to try to throw the player to this position
3754                 v2s16 nodepos2d = v2s16(
3755                         -range + myrand_range(0, range*2),
3756                         -range + myrand_range(0, range*2));
3757                 // Get spawn level at point
3758                 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3759                 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3760                 // signify an unsuitable spawn position, or if outside limits.
3761                 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3762                                 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3763                         continue;
3764
3765                 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3766                 // Consecutive empty nodes
3767                 s32 air_count = 0;
3768
3769                 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3770                 // avoid obstructions in already-generated mapblocks.
3771                 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3772                 // no obstructions, but mapgen decorations are generated after spawn so
3773                 // the player may end up inside one.
3774                 for (s32 i = 0; i < 8; i++) {
3775                         v3s16 blockpos = getNodeBlockPos(nodepos);
3776                         map.emergeBlock(blockpos, true);
3777                         content_t c = map.getNode(nodepos).getContent();
3778
3779                         // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3780                         // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3781                         if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3782                                 air_count++;
3783                                 if (air_count >= 2) {
3784                                         // Spawn in lower empty node
3785                                         nodepos.Y--;
3786                                         nodeposf = intToFloat(nodepos, BS);
3787                                         // Don't spawn the player outside map boundaries
3788                                         if (objectpos_over_limit(nodeposf))
3789                                                 // Exit this loop, positions above are probably over limit
3790                                                 break;
3791
3792                                         // Good position found, cause an exit from main loop
3793                                         is_good = true;
3794                                         break;
3795                                 }
3796                         } else {
3797                                 air_count = 0;
3798                         }
3799                         nodepos.Y++;
3800                 }
3801         }
3802
3803         if (is_good)
3804                 return nodeposf;
3805
3806         // No suitable spawn point found, return fallback 0,0,0
3807         return v3f(0.0f, 0.0f, 0.0f);
3808 }
3809
3810 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3811 {
3812         if (delay == 0.0f) {
3813         // No delay, shutdown immediately
3814                 m_shutdown_state.is_requested = true;
3815                 // only print to the infostream, a chat message saying
3816                 // "Server Shutting Down" is sent when the server destructs.
3817                 infostream << "*** Immediate Server shutdown requested." << std::endl;
3818         } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3819                 // Negative delay, cancel shutdown if requested
3820                 m_shutdown_state.reset();
3821                 std::wstringstream ws;
3822
3823                 ws << L"*** Server shutdown canceled.";
3824
3825                 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3826                 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3827                 // m_shutdown_* are already handled, skip.
3828                 return;
3829         } else if (delay > 0.0f) {
3830         // Positive delay, tell the clients when the server will shut down
3831                 std::wstringstream ws;
3832
3833                 ws << L"*** Server shutting down in "
3834                                 << duration_to_string(myround(delay)).c_str()
3835                                 << ".";
3836
3837                 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3838                 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3839         }
3840
3841         m_shutdown_state.trigger(delay, msg, reconnect);
3842 }
3843
3844 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3845 {
3846         /*
3847                 Try to get an existing player
3848         */
3849         RemotePlayer *player = m_env->getPlayer(name);
3850
3851         // If player is already connected, cancel
3852         if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3853                 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3854                 return NULL;
3855         }
3856
3857         /*
3858                 If player with the wanted peer_id already exists, cancel.
3859         */
3860         if (m_env->getPlayer(peer_id)) {
3861                 infostream<<"emergePlayer(): Player with wrong name but same"
3862                                 " peer_id already exists"<<std::endl;
3863                 return NULL;
3864         }
3865
3866         if (!player) {
3867                 player = new RemotePlayer(name, idef());
3868         }
3869
3870         bool newplayer = false;
3871
3872         // Load player
3873         PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3874
3875         // Complete init with server parts
3876         playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3877         player->protocol_version = proto_version;
3878
3879         /* Run scripts */
3880         if (newplayer) {
3881                 m_script->on_newplayer(playersao);
3882         }
3883
3884         return playersao;
3885 }
3886
3887 bool Server::registerModStorage(ModMetadata *storage)
3888 {
3889         if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3890                 errorstream << "Unable to register same mod storage twice. Storage name: "
3891                                 << storage->getModName() << std::endl;
3892                 return false;
3893         }
3894
3895         m_mod_storages[storage->getModName()] = storage;
3896         return true;
3897 }
3898
3899 void Server::unregisterModStorage(const std::string &name)
3900 {
3901         std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3902         if (it != m_mod_storages.end())
3903                 m_mod_storages.erase(name);
3904 }
3905
3906 void dedicated_server_loop(Server &server, bool &kill)
3907 {
3908         verbosestream<<"dedicated_server_loop()"<<std::endl;
3909
3910         IntervalLimiter m_profiler_interval;
3911
3912         static thread_local const float steplen =
3913                         g_settings->getFloat("dedicated_server_step");
3914         static thread_local const float profiler_print_interval =
3915                         g_settings->getFloat("profiler_print_interval");
3916
3917         /*
3918          * The dedicated server loop only does time-keeping (in Server::step) and
3919          * provides a way to main.cpp to kill the server externally (bool &kill).
3920          */
3921
3922         for(;;) {
3923                 // This is kind of a hack but can be done like this
3924                 // because server.step() is very light
3925                 sleep_ms((int)(steplen*1000.0));
3926                 server.step(steplen);
3927
3928                 if (server.isShutdownRequested() || kill)
3929                         break;
3930
3931                 /*
3932                         Profiler
3933                 */
3934                 if (profiler_print_interval != 0) {
3935                         if(m_profiler_interval.step(steplen, profiler_print_interval))
3936                         {
3937                                 infostream<<"Profiler:"<<std::endl;
3938                                 g_profiler->print(infostream);
3939                                 g_profiler->clear();
3940                         }
3941                 }
3942         }
3943
3944         infostream << "Dedicated server quitting" << std::endl;
3945 #if USE_CURL
3946         if (g_settings->getBool("server_announce"))
3947                 ServerList::sendAnnounce(ServerList::AA_DELETE,
3948                         server.m_bind_addr.getPort());
3949 #endif
3950 }
3951
3952 /*
3953  * Mod channels
3954  */
3955
3956
3957 bool Server::joinModChannel(const std::string &channel)
3958 {
3959         return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3960                         m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3961 }
3962
3963 bool Server::leaveModChannel(const std::string &channel)
3964 {
3965         return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3966 }
3967
3968 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3969 {
3970         if (!m_modchannel_mgr->canWriteOnChannel(channel))
3971                 return false;
3972
3973         broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3974         return true;
3975 }
3976
3977 ModChannel* Server::getModChannel(const std::string &channel)
3978 {
3979         return m_modchannel_mgr->getModChannel(channel);
3980 }
3981
3982 void Server::broadcastModChannelMessage(const std::string &channel,
3983                 const std::string &message, session_t from_peer)
3984 {
3985         const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3986         if (peers.empty())
3987                 return;
3988
3989         if (message.size() > STRING_MAX_LEN) {
3990                 warningstream << "ModChannel message too long, dropping before sending "
3991                                 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3992                                 << channel << ")" << std::endl;
3993                 return;
3994         }
3995
3996         std::string sender;
3997         if (from_peer != PEER_ID_SERVER) {
3998                 sender = getPlayerName(from_peer);
3999         }
4000
4001         NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
4002                         2 + channel.size() + 2 + sender.size() + 2 + message.size());
4003         resp_pkt << channel << sender << message;
4004         for (session_t peer_id : peers) {
4005                 // Ignore sender
4006                 if (peer_id == from_peer)
4007                         continue;
4008
4009                 Send(peer_id, &resp_pkt);
4010         }
4011
4012         if (from_peer != PEER_ID_SERVER) {
4013                 m_script->on_modchannel_message(channel, sender, message);
4014         }
4015 }
4016
4017 Translations *Server::getTranslationLanguage(const std::string &lang_code)
4018 {
4019         if (lang_code.empty())
4020                 return nullptr;
4021
4022         auto it = server_translations.find(lang_code);
4023         if (it != server_translations.end())
4024                 return &it->second; // Already loaded
4025
4026         // [] will create an entry
4027         auto *translations = &server_translations[lang_code];
4028
4029         std::string suffix = "." + lang_code + ".tr";
4030         for (const auto &i : m_media) {
4031                 if (str_ends_with(i.first, suffix)) {
4032                         std::string data;
4033                         if (fs::ReadFile(i.second.path, data)) {
4034                                 translations->loadTranslation(data);
4035                         }
4036                 }
4037         }
4038
4039         return translations;
4040 }
4041
4042 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &world_path)
4043 {
4044         std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4045         Settings world_mt;
4046         if (!world_mt.readConfigFile(world_mt_path.c_str()))
4047                 throw BaseException("Cannot read world.mt!");
4048
4049         std::string backend = world_mt.exists("mod_storage_backend") ?
4050                 world_mt.get("mod_storage_backend") : "files";
4051         if (backend == "files")
4052                 warningstream << "/!\\ You are using the old mod storage files backend. "
4053                         << "This backend is deprecated and may be removed in a future release /!\\"
4054                         << std::endl << "Switching to SQLite3 is advised, "
4055                         << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4056
4057         return openModStorageDatabase(backend, world_path, world_mt);
4058 }
4059
4060 ModMetadataDatabase *Server::openModStorageDatabase(const std::string &backend,
4061                 const std::string &world_path, const Settings &world_mt)
4062 {
4063         if (backend == "sqlite3")
4064                 return new ModMetadataDatabaseSQLite3(world_path);
4065
4066         if (backend == "files")
4067                 return new ModMetadataDatabaseFiles(world_path);
4068
4069         if (backend == "dummy")
4070                 return new Database_Dummy();
4071
4072         throw BaseException("Mod storage database backend " + backend + " not supported");
4073 }
4074
4075 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4076 {
4077         std::string migrate_to = cmd_args.get("migrate-mod-storage");
4078         Settings world_mt;
4079         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4080         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4081                 errorstream << "Cannot read world.mt!" << std::endl;
4082                 return false;
4083         }
4084
4085         std::string backend = world_mt.exists("mod_storage_backend") ?
4086                 world_mt.get("mod_storage_backend") : "files";
4087         if (backend == migrate_to) {
4088                 errorstream << "Cannot migrate: new backend is same"
4089                         << " as the old one" << std::endl;
4090                 return false;
4091         }
4092
4093         ModMetadataDatabase *srcdb = nullptr;
4094         ModMetadataDatabase *dstdb = nullptr;
4095
4096         bool succeeded = false;
4097
4098         try {
4099                 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4100                 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4101
4102                 dstdb->beginSave();
4103
4104                 std::vector<std::string> mod_list;
4105                 srcdb->listMods(&mod_list);
4106                 for (const std::string &modname : mod_list) {
4107                         StringMap meta;
4108                         srcdb->getModEntries(modname, &meta);
4109                         for (const auto &pair : meta) {
4110                                 dstdb->setModEntry(modname, pair.first, pair.second);
4111                         }
4112                 }
4113
4114                 dstdb->endSave();
4115
4116                 succeeded = true;
4117
4118                 actionstream << "Successfully migrated the metadata of "
4119                         << mod_list.size() << " mods" << std::endl;
4120                 world_mt.set("mod_storage_backend", migrate_to);
4121                 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4122                         errorstream << "Failed to update world.mt!" << std::endl;
4123                 else
4124                         actionstream << "world.mt updated" << std::endl;
4125
4126         } catch (BaseException &e) {
4127                 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4128         }
4129
4130         delete srcdb;
4131         delete dstdb;
4132
4133         if (succeeded && backend == "files") {
4134                 // Back up files
4135                 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4136                 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4137                 if (!fs::Rename(storage_path, backup_path))
4138                         warningstream << "After migration, " << storage_path
4139                                 << " could not be renamed to " << backup_path << std::endl;
4140         }
4141
4142         return succeeded;
4143 }