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