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