]> git.lizzy.rs Git - minetest.git/blob - src/server.cpp
bced71afa3b67998af6bf81ce3092046bc8633b4
[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 void Server::handlePeerChanges()
1307 {
1308         while(!m_peer_change_queue.empty())
1309         {
1310                 con::PeerChange c = m_peer_change_queue.front();
1311                 m_peer_change_queue.pop();
1312
1313                 verbosestream<<"Server: Handling peer change: "
1314                                 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1315                                 <<std::endl;
1316
1317                 switch(c.type)
1318                 {
1319                 case con::PEER_ADDED:
1320                         m_clients.CreateClient(c.peer_id);
1321                         break;
1322
1323                 case con::PEER_REMOVED:
1324                         DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1325                         break;
1326
1327                 default:
1328                         FATAL_ERROR("Invalid peer change event received!");
1329                         break;
1330                 }
1331         }
1332 }
1333
1334 void Server::printToConsoleOnly(const std::string &text)
1335 {
1336         if (m_admin_chat) {
1337                 m_admin_chat->outgoing_queue.push_back(
1338                         new ChatEventChat("", utf8_to_wide(text)));
1339         } else {
1340                 std::cout << text << std::endl;
1341         }
1342 }
1343
1344 void Server::Send(NetworkPacket *pkt)
1345 {
1346         Send(pkt->getPeerId(), pkt);
1347 }
1348
1349 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1350 {
1351         m_clients.send(peer_id,
1352                 clientCommandFactoryTable[pkt->getCommand()].channel,
1353                 pkt,
1354                 clientCommandFactoryTable[pkt->getCommand()].reliable);
1355 }
1356
1357 void Server::SendMovement(session_t peer_id)
1358 {
1359         NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1360
1361         pkt << g_settings->getFloat("movement_acceleration_default");
1362         pkt << g_settings->getFloat("movement_acceleration_air");
1363         pkt << g_settings->getFloat("movement_acceleration_fast");
1364         pkt << g_settings->getFloat("movement_speed_walk");
1365         pkt << g_settings->getFloat("movement_speed_crouch");
1366         pkt << g_settings->getFloat("movement_speed_fast");
1367         pkt << g_settings->getFloat("movement_speed_climb");
1368         pkt << g_settings->getFloat("movement_speed_jump");
1369         pkt << g_settings->getFloat("movement_liquid_fluidity");
1370         pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1371         pkt << g_settings->getFloat("movement_liquid_sink");
1372         pkt << g_settings->getFloat("movement_gravity");
1373
1374         Send(&pkt);
1375 }
1376
1377 void Server::HandlePlayerHPChange(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1378 {
1379         m_script->player_event(playersao, "health_changed");
1380         SendPlayerHP(playersao, reason.type != PlayerHPChangeReason::SET_HP_MAX);
1381
1382         // Send to other clients
1383         playersao->sendPunchCommand();
1384
1385         if (playersao->isDead())
1386                 HandlePlayerDeath(playersao, reason);
1387 }
1388
1389 void Server::SendPlayerHP(PlayerSAO *playersao, bool effect)
1390 {
1391         SendHP(playersao->getPeerID(), playersao->getHP(), effect);
1392 }
1393
1394 void Server::SendHP(session_t peer_id, u16 hp, bool effect)
1395 {
1396         NetworkPacket pkt(TOCLIENT_HP, 3, peer_id);
1397         pkt << hp << effect;
1398         Send(&pkt);
1399 }
1400
1401 void Server::SendBreath(session_t peer_id, u16 breath)
1402 {
1403         NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1404         pkt << (u16) breath;
1405         Send(&pkt);
1406 }
1407
1408 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1409                 const std::string &custom_reason, bool reconnect)
1410 {
1411         assert(reason < SERVER_ACCESSDENIED_MAX);
1412
1413         NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1414         pkt << (u8)reason;
1415         if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1416                 pkt << custom_reason;
1417         else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1418                         reason == SERVER_ACCESSDENIED_CRASH)
1419                 pkt << custom_reason << (u8)reconnect;
1420         Send(&pkt);
1421 }
1422
1423 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1424                 v3f camera_point_target)
1425 {
1426         NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1427         pkt << set_camera_point_target << camera_point_target;
1428         Send(&pkt);
1429 }
1430
1431 void Server::SendItemDef(session_t peer_id,
1432                 IItemDefManager *itemdef, u16 protocol_version)
1433 {
1434         NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1435
1436         /*
1437                 u16 command
1438                 u32 length of the next item
1439                 zlib-compressed serialized ItemDefManager
1440         */
1441         std::ostringstream tmp_os(std::ios::binary);
1442         itemdef->serialize(tmp_os, protocol_version);
1443         std::ostringstream tmp_os2(std::ios::binary);
1444         compressZlib(tmp_os.str(), tmp_os2);
1445         pkt.putLongString(tmp_os2.str());
1446
1447         // Make data buffer
1448         verbosestream << "Server: Sending item definitions to id(" << peer_id
1449                         << "): size=" << pkt.getSize() << std::endl;
1450
1451         Send(&pkt);
1452 }
1453
1454 void Server::SendNodeDef(session_t peer_id,
1455         const NodeDefManager *nodedef, u16 protocol_version)
1456 {
1457         NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1458
1459         /*
1460                 u16 command
1461                 u32 length of the next item
1462                 zlib-compressed serialized NodeDefManager
1463         */
1464         std::ostringstream tmp_os(std::ios::binary);
1465         nodedef->serialize(tmp_os, protocol_version);
1466         std::ostringstream tmp_os2(std::ios::binary);
1467         compressZlib(tmp_os.str(), tmp_os2);
1468
1469         pkt.putLongString(tmp_os2.str());
1470
1471         // Make data buffer
1472         verbosestream << "Server: Sending node definitions to id(" << peer_id
1473                         << "): size=" << pkt.getSize() << std::endl;
1474
1475         Send(&pkt);
1476 }
1477
1478 /*
1479         Non-static send methods
1480 */
1481
1482 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1483 {
1484         RemotePlayer *player = sao->getPlayer();
1485
1486         // Do not send new format to old clients
1487         incremental &= player->protocol_version >= 38;
1488
1489         UpdateCrafting(player);
1490
1491         /*
1492                 Serialize it
1493         */
1494
1495         NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1496
1497         std::ostringstream os(std::ios::binary);
1498         sao->getInventory()->serialize(os, incremental);
1499         sao->getInventory()->setModified(false);
1500         player->setModified(true);
1501
1502         const std::string &s = os.str();
1503         pkt.putRawString(s.c_str(), s.size());
1504         Send(&pkt);
1505 }
1506
1507 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1508 {
1509         NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1510         u8 version = 1;
1511         u8 type = message.type;
1512         pkt << version << type << message.sender << message.message
1513                 << static_cast<u64>(message.timestamp);
1514
1515         if (peer_id != PEER_ID_INEXISTENT) {
1516                 RemotePlayer *player = m_env->getPlayer(peer_id);
1517                 if (!player)
1518                         return;
1519
1520                 Send(&pkt);
1521         } else {
1522                 m_clients.sendToAll(&pkt);
1523         }
1524 }
1525
1526 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1527         const std::string &formname)
1528 {
1529         NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1530         if (formspec.empty()){
1531                 //the client should close the formspec
1532                 //but make sure there wasn't another one open in meantime
1533                 const auto it = m_formspec_state_data.find(peer_id);
1534                 if (it != m_formspec_state_data.end() && it->second == formname) {
1535                         m_formspec_state_data.erase(peer_id);
1536                 }
1537                 pkt.putLongString("");
1538         } else {
1539                 m_formspec_state_data[peer_id] = formname;
1540                 pkt.putLongString(formspec);
1541         }
1542         pkt << formname;
1543
1544         Send(&pkt);
1545 }
1546
1547 // Spawns a particle on peer with peer_id
1548 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1549         const ParticleParameters &p)
1550 {
1551         static thread_local const float radius =
1552                         g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1553
1554         if (peer_id == PEER_ID_INEXISTENT) {
1555                 std::vector<session_t> clients = m_clients.getClientIDs();
1556                 const v3f pos = p.pos * BS;
1557                 const float radius_sq = radius * radius;
1558
1559                 for (const session_t client_id : clients) {
1560                         RemotePlayer *player = m_env->getPlayer(client_id);
1561                         if (!player)
1562                                 continue;
1563
1564                         PlayerSAO *sao = player->getPlayerSAO();
1565                         if (!sao)
1566                                 continue;
1567
1568                         // Do not send to distant clients
1569                         if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1570                                 continue;
1571
1572                         SendSpawnParticle(client_id, player->protocol_version, p);
1573                 }
1574                 return;
1575         }
1576         assert(protocol_version != 0);
1577
1578         NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1579
1580         {
1581                 // NetworkPacket and iostreams are incompatible...
1582                 std::ostringstream oss(std::ios_base::binary);
1583                 p.serialize(oss, protocol_version);
1584                 pkt.putRawString(oss.str());
1585         }
1586
1587         Send(&pkt);
1588 }
1589
1590 // Adds a ParticleSpawner on peer with peer_id
1591 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1592         const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1593 {
1594         static thread_local const float radius =
1595                         g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1596
1597         if (peer_id == PEER_ID_INEXISTENT) {
1598                 std::vector<session_t> clients = m_clients.getClientIDs();
1599                 const v3f pos = (
1600                         p.pos.start.min.val +
1601                         p.pos.start.max.val +
1602                         p.pos.end.min.val +
1603                         p.pos.end.max.val
1604                 ) / 4.0f * BS;
1605                 const float radius_sq = radius * radius;
1606                 /* Don't send short-lived spawners to distant players.
1607                  * This could be replaced with proper tracking at some point. */
1608                 const bool distance_check = !attached_id && p.time <= 1.0f;
1609
1610                 for (const session_t client_id : clients) {
1611                         RemotePlayer *player = m_env->getPlayer(client_id);
1612                         if (!player)
1613                                 continue;
1614
1615                         if (distance_check) {
1616                                 PlayerSAO *sao = player->getPlayerSAO();
1617                                 if (!sao)
1618                                         continue;
1619                                 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1620                                         continue;
1621                         }
1622
1623                         SendAddParticleSpawner(client_id, player->protocol_version,
1624                                 p, attached_id, id);
1625                 }
1626                 return;
1627         }
1628         assert(protocol_version != 0);
1629
1630         NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1631
1632         pkt << p.amount << p.time;
1633         { // serialize legacy fields
1634                 std::ostringstream os(std::ios_base::binary);
1635                 p.pos.start.legacySerialize(os);
1636                 p.vel.start.legacySerialize(os);
1637                 p.acc.start.legacySerialize(os);
1638                 p.exptime.start.legacySerialize(os);
1639                 p.size.start.legacySerialize(os);
1640                 pkt.putRawString(os.str());
1641         }
1642         pkt << p.collisiondetection;
1643
1644         pkt.putLongString(p.texture.string);
1645
1646         pkt << id << p.vertical << p.collision_removal << attached_id;
1647         {
1648                 std::ostringstream os(std::ios_base::binary);
1649                 p.animation.serialize(os, protocol_version);
1650                 pkt.putRawString(os.str());
1651         }
1652         pkt << p.glow << p.object_collision;
1653         pkt << p.node.param0 << p.node.param2 << p.node_tile;
1654
1655         { // serialize new fields
1656                 // initial bias for older properties
1657                 pkt << p.pos.start.bias
1658                         << p.vel.start.bias
1659                         << p.acc.start.bias
1660                         << p.exptime.start.bias
1661                         << p.size.start.bias;
1662
1663                 std::ostringstream os(std::ios_base::binary);
1664
1665                 // final tween frames of older properties
1666                 p.pos.end.serialize(os);
1667                 p.vel.end.serialize(os);
1668                 p.acc.end.serialize(os);
1669                 p.exptime.end.serialize(os);
1670                 p.size.end.serialize(os);
1671
1672                 // properties for legacy texture field
1673                 p.texture.serialize(os, protocol_version, true);
1674
1675                 // new properties
1676                 p.drag.serialize(os);
1677                 p.jitter.serialize(os);
1678                 p.bounce.serialize(os);
1679                 ParticleParamTypes::serializeParameterValue(os, p.attractor_kind);
1680                 if (p.attractor_kind != ParticleParamTypes::AttractorKind::none) {
1681                         p.attract.serialize(os);
1682                         p.attractor_origin.serialize(os);
1683                         writeU16(os, p.attractor_attachment); /* object ID */
1684                         writeU8(os, p.attractor_kill);
1685                         if (p.attractor_kind != ParticleParamTypes::AttractorKind::point) {
1686                                 p.attractor_direction.serialize(os);
1687                                 writeU16(os, p.attractor_direction_attachment);
1688                         }
1689                 }
1690                 p.radius.serialize(os);
1691
1692                 ParticleParamTypes::serializeParameterValue(os, (u16)p.texpool.size());
1693                 for (const auto& tex : p.texpool) {
1694                         tex.serialize(os, protocol_version);
1695                 }
1696
1697                 pkt.putRawString(os.str());
1698         }
1699
1700         Send(&pkt);
1701 }
1702
1703 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1704 {
1705         NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1706
1707         pkt << id;
1708
1709         if (peer_id != PEER_ID_INEXISTENT)
1710                 Send(&pkt);
1711         else
1712                 m_clients.sendToAll(&pkt);
1713
1714 }
1715
1716 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1717 {
1718         NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1719
1720         pkt << id << (u8) form->type << form->pos << form->name << form->scale
1721                         << form->text << form->number << form->item << form->dir
1722                         << form->align << form->offset << form->world_pos << form->size
1723                         << form->z_index << form->text2 << form->style;
1724
1725         Send(&pkt);
1726 }
1727
1728 void Server::SendHUDRemove(session_t peer_id, u32 id)
1729 {
1730         NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1731         pkt << id;
1732         Send(&pkt);
1733 }
1734
1735 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1736 {
1737         NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1738         pkt << id << (u8) stat;
1739
1740         switch (stat) {
1741                 case HUD_STAT_POS:
1742                 case HUD_STAT_SCALE:
1743                 case HUD_STAT_ALIGN:
1744                 case HUD_STAT_OFFSET:
1745                         pkt << *(v2f *) value;
1746                         break;
1747                 case HUD_STAT_NAME:
1748                 case HUD_STAT_TEXT:
1749                 case HUD_STAT_TEXT2:
1750                         pkt << *(std::string *) value;
1751                         break;
1752                 case HUD_STAT_WORLD_POS:
1753                         pkt << *(v3f *) value;
1754                         break;
1755                 case HUD_STAT_SIZE:
1756                         pkt << *(v2s32 *) value;
1757                         break;
1758                 default: // all other types
1759                         pkt << *(u32 *) value;
1760                         break;
1761         }
1762
1763         Send(&pkt);
1764 }
1765
1766 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1767 {
1768         NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1769
1770         flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1771
1772         pkt << flags << mask;
1773
1774         Send(&pkt);
1775 }
1776
1777 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1778 {
1779         NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1780         pkt << param << value;
1781         Send(&pkt);
1782 }
1783
1784 void Server::SendSetSky(session_t peer_id, const SkyboxParams &params)
1785 {
1786         NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1787
1788         // Handle prior clients here
1789         if (m_clients.getProtocolVersion(peer_id) < 39) {
1790                 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1791
1792                 for (const std::string& texture : params.textures)
1793                         pkt << texture;
1794
1795                 pkt << params.clouds;
1796         } else { // Handle current clients and future clients
1797                 pkt << params.bgcolor << params.type
1798                 << params.clouds << params.fog_sun_tint
1799                 << params.fog_moon_tint << params.fog_tint_type;
1800
1801                 if (params.type == "skybox") {
1802                         pkt << (u16) params.textures.size();
1803                         for (const std::string &texture : params.textures)
1804                                 pkt << texture;
1805                 } else if (params.type == "regular") {
1806                         pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1807                                 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1808                                 << params.sky_color.night_sky << params.sky_color.night_horizon
1809                                 << params.sky_color.indoors;
1810                 }
1811         }
1812
1813         Send(&pkt);
1814 }
1815
1816 void Server::SendSetSun(session_t peer_id, const SunParams &params)
1817 {
1818         NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1819         pkt << params.visible << params.texture
1820                 << params.tonemap << params.sunrise
1821                 << params.sunrise_visible << params.scale;
1822
1823         Send(&pkt);
1824 }
1825 void Server::SendSetMoon(session_t peer_id, const MoonParams &params)
1826 {
1827         NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1828
1829         pkt << params.visible << params.texture
1830                 << params.tonemap << params.scale;
1831
1832         Send(&pkt);
1833 }
1834 void Server::SendSetStars(session_t peer_id, const StarParams &params)
1835 {
1836         NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1837
1838         pkt << params.visible << params.count
1839                 << params.starcolor << params.scale
1840                 << params.day_opacity;
1841
1842         Send(&pkt);
1843 }
1844
1845 void Server::SendCloudParams(session_t peer_id, const CloudParams &params)
1846 {
1847         NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1848         pkt << params.density << params.color_bright << params.color_ambient
1849                         << params.height << params.thickness << params.speed;
1850         Send(&pkt);
1851 }
1852
1853 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1854                 float ratio)
1855 {
1856         NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1857                         1 + 2, peer_id);
1858
1859         pkt << do_override << (u16) (ratio * 65535);
1860
1861         Send(&pkt);
1862 }
1863
1864 void Server::SendSetLighting(session_t peer_id, const Lighting &lighting)
1865 {
1866         NetworkPacket pkt(TOCLIENT_SET_LIGHTING,
1867                         4, peer_id);
1868
1869         pkt << lighting.shadow_intensity;
1870         pkt << lighting.saturation;
1871
1872         pkt << lighting.exposure.luminance_min
1873                         << lighting.exposure.luminance_max
1874                         << lighting.exposure.exposure_correction
1875                         << lighting.exposure.speed_dark_bright
1876                         << lighting.exposure.speed_bright_dark
1877                         << lighting.exposure.center_weight_power;
1878
1879         Send(&pkt);
1880 }
1881
1882 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1883 {
1884         NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1885         pkt << time << time_speed;
1886
1887         if (peer_id == PEER_ID_INEXISTENT) {
1888                 m_clients.sendToAll(&pkt);
1889         }
1890         else {
1891                 Send(&pkt);
1892         }
1893 }
1894
1895 void Server::SendPlayerBreath(PlayerSAO *sao)
1896 {
1897         assert(sao);
1898
1899         m_script->player_event(sao, "breath_changed");
1900         SendBreath(sao->getPeerID(), sao->getBreath());
1901 }
1902
1903 void Server::SendMovePlayer(session_t peer_id)
1904 {
1905         RemotePlayer *player = m_env->getPlayer(peer_id);
1906         assert(player);
1907         PlayerSAO *sao = player->getPlayerSAO();
1908         assert(sao);
1909
1910         // Send attachment updates instantly to the client prior updating position
1911         sao->sendOutdatedData();
1912
1913         NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1914         pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1915
1916         {
1917                 v3f pos = sao->getBasePosition();
1918                 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1919                                 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1920                                 << " pitch=" << sao->getLookPitch()
1921                                 << " yaw=" << sao->getRotation().Y
1922                                 << std::endl;
1923         }
1924
1925         Send(&pkt);
1926 }
1927
1928 void Server::SendPlayerFov(session_t peer_id)
1929 {
1930         NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1931
1932         PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1933         pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1934
1935         Send(&pkt);
1936 }
1937
1938 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1939                 f32 animation_speed)
1940 {
1941         NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1942                 peer_id);
1943
1944         pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1945                         << animation_frames[3] << animation_speed;
1946
1947         Send(&pkt);
1948 }
1949
1950 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1951 {
1952         NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1953         pkt << first << third;
1954         Send(&pkt);
1955 }
1956
1957 void Server::SendPlayerPrivileges(session_t peer_id)
1958 {
1959         RemotePlayer *player = m_env->getPlayer(peer_id);
1960         assert(player);
1961         if(player->getPeerId() == PEER_ID_INEXISTENT)
1962                 return;
1963
1964         std::set<std::string> privs;
1965         m_script->getAuth(player->getName(), NULL, &privs);
1966
1967         NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1968         pkt << (u16) privs.size();
1969
1970         for (const std::string &priv : privs) {
1971                 pkt << priv;
1972         }
1973
1974         Send(&pkt);
1975 }
1976
1977 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1978 {
1979         RemotePlayer *player = m_env->getPlayer(peer_id);
1980         assert(player);
1981         if (player->getPeerId() == PEER_ID_INEXISTENT)
1982                 return;
1983
1984         NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1985         pkt.putLongString(player->inventory_formspec);
1986
1987         Send(&pkt);
1988 }
1989
1990 void Server::SendPlayerFormspecPrepend(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_FORMSPEC_PREPEND, 0, peer_id);
1998         pkt << player->formspec_prepend;
1999         Send(&pkt);
2000 }
2001
2002 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
2003 {
2004         // Radius inside which objects are active
2005         static thread_local const s16 radius =
2006                 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
2007
2008         // Radius inside which players are active
2009         static thread_local const bool is_transfer_limited =
2010                 g_settings->exists("unlimited_player_transfer_distance") &&
2011                 !g_settings->getBool("unlimited_player_transfer_distance");
2012
2013         static thread_local const s16 player_transfer_dist =
2014                 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
2015
2016         s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
2017                 radius : player_transfer_dist;
2018
2019         s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
2020         if (my_radius <= 0)
2021                 my_radius = radius;
2022
2023         std::queue<u16> removed_objects, added_objects;
2024         m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
2025                 client->m_known_objects, removed_objects);
2026         m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
2027                 client->m_known_objects, added_objects);
2028
2029         int removed_count = removed_objects.size();
2030         int added_count   = added_objects.size();
2031
2032         if (removed_objects.empty() && added_objects.empty())
2033                 return;
2034
2035         char buf[4];
2036         std::string data;
2037
2038         // Handle removed objects
2039         writeU16((u8*)buf, removed_objects.size());
2040         data.append(buf, 2);
2041         while (!removed_objects.empty()) {
2042                 // Get object
2043                 u16 id = removed_objects.front();
2044                 ServerActiveObject* obj = m_env->getActiveObject(id);
2045
2046                 // Add to data buffer for sending
2047                 writeU16((u8*)buf, id);
2048                 data.append(buf, 2);
2049
2050                 // Remove from known objects
2051                 client->m_known_objects.erase(id);
2052
2053                 if (obj && obj->m_known_by_count > 0)
2054                         obj->m_known_by_count--;
2055
2056                 removed_objects.pop();
2057         }
2058
2059         // Handle added objects
2060         writeU16((u8*)buf, added_objects.size());
2061         data.append(buf, 2);
2062         while (!added_objects.empty()) {
2063                 // Get object
2064                 u16 id = added_objects.front();
2065                 ServerActiveObject *obj = m_env->getActiveObject(id);
2066                 added_objects.pop();
2067
2068                 if (!obj) {
2069                         warningstream << FUNCTION_NAME << ": NULL object id="
2070                                 << (int)id << std::endl;
2071                         continue;
2072                 }
2073
2074                 // Get object type
2075                 u8 type = obj->getSendType();
2076
2077                 // Add to data buffer for sending
2078                 writeU16((u8*)buf, id);
2079                 data.append(buf, 2);
2080                 writeU8((u8*)buf, type);
2081                 data.append(buf, 1);
2082
2083                 data.append(serializeString32(
2084                         obj->getClientInitializationData(client->net_proto_version)));
2085
2086                 // Add to known objects
2087                 client->m_known_objects.insert(id);
2088
2089                 obj->m_known_by_count++;
2090         }
2091
2092         NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2093         pkt.putRawString(data.c_str(), data.size());
2094         Send(&pkt);
2095
2096         verbosestream << "Server::SendActiveObjectRemoveAdd: "
2097                 << removed_count << " removed, " << added_count << " added, "
2098                 << "packet size is " << pkt.getSize() << std::endl;
2099 }
2100
2101 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2102                 bool reliable)
2103 {
2104         NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2105                         datas.size(), peer_id);
2106
2107         pkt.putRawString(datas.c_str(), datas.size());
2108
2109         m_clients.send(pkt.getPeerId(),
2110                         reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2111                         &pkt, reliable);
2112 }
2113
2114 void Server::SendCSMRestrictionFlags(session_t peer_id)
2115 {
2116         NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2117                 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2118         pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2119         Send(&pkt);
2120 }
2121
2122 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2123 {
2124         NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2125         pkt << added_vel;
2126         Send(&pkt);
2127 }
2128
2129 inline s32 Server::nextSoundId()
2130 {
2131         s32 ret = m_next_sound_id;
2132         if (m_next_sound_id == INT32_MAX)
2133                 m_next_sound_id = 0; // signed overflow is undefined
2134         else
2135                 m_next_sound_id++;
2136         return ret;
2137 }
2138
2139 s32 Server::playSound(ServerPlayingSound &params, bool ephemeral)
2140 {
2141         // Find out initial position of sound
2142         bool pos_exists = false;
2143         const v3f pos = params.getPos(m_env, &pos_exists);
2144         // If position is not found while it should be, cancel sound
2145         if(pos_exists != (params.type != SoundLocation::Local))
2146                 return -1;
2147
2148         // Filter destination clients
2149         std::vector<session_t> dst_clients;
2150         if (!params.to_player.empty()) {
2151                 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2152                 if(!player){
2153                         infostream<<"Server::playSound: Player \""<<params.to_player
2154                                         <<"\" not found"<<std::endl;
2155                         return -1;
2156                 }
2157                 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2158                         infostream<<"Server::playSound: Player \""<<params.to_player
2159                                         <<"\" not connected"<<std::endl;
2160                         return -1;
2161                 }
2162                 dst_clients.push_back(player->getPeerId());
2163         } else {
2164                 std::vector<session_t> clients = m_clients.getClientIDs();
2165
2166                 for (const session_t client_id : clients) {
2167                         RemotePlayer *player = m_env->getPlayer(client_id);
2168                         if (!player)
2169                                 continue;
2170                         if (!params.exclude_player.empty() &&
2171                                         params.exclude_player == player->getName())
2172                                 continue;
2173
2174                         PlayerSAO *sao = player->getPlayerSAO();
2175                         if (!sao)
2176                                 continue;
2177
2178                         if (pos_exists) {
2179                                 if(sao->getBasePosition().getDistanceFrom(pos) >
2180                                                 params.max_hear_distance)
2181                                         continue;
2182                         }
2183                         dst_clients.push_back(client_id);
2184                 }
2185         }
2186
2187         if(dst_clients.empty())
2188                 return -1;
2189
2190         // old clients will still use this, so pick a reserved ID (-1)
2191         const s32 id = ephemeral ? -1 : nextSoundId();
2192
2193         float gain = params.gain * params.spec.gain;
2194         NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2195         pkt << id << params.spec.name << gain
2196                         << (u8) params.type << pos << params.object
2197                         << params.spec.loop << params.spec.fade << params.spec.pitch
2198                         << ephemeral;
2199
2200         bool as_reliable = !ephemeral;
2201
2202         for (const session_t peer_id : dst_clients) {
2203                 if (!ephemeral)
2204                         params.clients.insert(peer_id);
2205                 m_clients.send(peer_id, 0, &pkt, as_reliable);
2206         }
2207
2208         if (!ephemeral)
2209                 m_playing_sounds[id] = std::move(params);
2210         return id;
2211 }
2212 void Server::stopSound(s32 handle)
2213 {
2214         auto it = m_playing_sounds.find(handle);
2215         if (it == m_playing_sounds.end())
2216                 return;
2217
2218         ServerPlayingSound &psound = it->second;
2219
2220         NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2221         pkt << handle;
2222
2223         for (session_t peer_id : psound.clients) {
2224                 // Send as reliable
2225                 m_clients.send(peer_id, 0, &pkt, true);
2226         }
2227
2228         // Remove sound reference
2229         m_playing_sounds.erase(it);
2230 }
2231
2232 void Server::fadeSound(s32 handle, float step, float gain)
2233 {
2234         auto it = m_playing_sounds.find(handle);
2235         if (it == m_playing_sounds.end())
2236                 return;
2237
2238         ServerPlayingSound &psound = it->second;
2239         psound.gain = gain; // destination gain
2240
2241         NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2242         pkt << handle << step << gain;
2243
2244         for (session_t peer_id : psound.clients) {
2245                 // Send as reliable
2246                 m_clients.send(peer_id, 0, &pkt, true);
2247         }
2248
2249         // Remove sound reference
2250         if (gain <= 0 || psound.clients.empty())
2251                 m_playing_sounds.erase(it);
2252 }
2253
2254 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2255                 float far_d_nodes)
2256 {
2257         v3f p_f = intToFloat(p, BS);
2258         v3s16 block_pos = getNodeBlockPos(p);
2259
2260         NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2261         pkt << p;
2262
2263         sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2264 }
2265
2266 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2267                 float far_d_nodes, bool remove_metadata)
2268 {
2269         v3f p_f = intToFloat(p, BS);
2270         v3s16 block_pos = getNodeBlockPos(p);
2271
2272         NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2273         pkt << p << n.param0 << n.param1 << n.param2
2274                         << (u8) (remove_metadata ? 0 : 1);
2275         sendNodeChangePkt(pkt, block_pos, p_f, far_d_nodes, far_players);
2276 }
2277
2278 void Server::sendNodeChangePkt(NetworkPacket &pkt, v3s16 block_pos,
2279                 v3f p, float far_d_nodes, std::unordered_set<u16> *far_players)
2280 {
2281         float maxd = far_d_nodes * BS;
2282         std::vector<session_t> clients = m_clients.getClientIDs();
2283         ClientInterface::AutoLock clientlock(m_clients);
2284
2285         for (session_t client_id : clients) {
2286                 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2287                 if (!client)
2288                         continue;
2289
2290                 RemotePlayer *player = m_env->getPlayer(client_id);
2291                 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2292
2293                 // If player is far away, only set modified blocks not sent
2294                 if (!client->isBlockSent(block_pos) || (sao &&
2295                                 sao->getBasePosition().getDistanceFrom(p) > maxd)) {
2296                         if (far_players)
2297                                 far_players->emplace(client_id);
2298                         else
2299                                 client->SetBlockNotSent(block_pos);
2300                         continue;
2301                 }
2302
2303                 // Send as reliable
2304                 m_clients.send(client_id, 0, &pkt, true);
2305         }
2306 }
2307
2308 void Server::sendMetadataChanged(const std::unordered_set<v3s16> &positions, float far_d_nodes)
2309 {
2310         NodeMetadataList meta_updates_list(false);
2311         std::ostringstream os(std::ios::binary);
2312
2313         std::vector<session_t> clients = m_clients.getClientIDs();
2314         ClientInterface::AutoLock clientlock(m_clients);
2315
2316         for (session_t i : clients) {
2317                 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2318                 if (!client)
2319                         continue;
2320
2321                 ServerActiveObject *player = getPlayerSAO(i);
2322                 v3s16 player_pos;
2323                 if (player)
2324                         player_pos = floatToInt(player->getBasePosition(), BS);
2325
2326                 for (const v3s16 pos : positions) {
2327                         NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2328
2329                         if (!meta)
2330                                 continue;
2331
2332                         v3s16 block_pos = getNodeBlockPos(pos);
2333                         if (!client->isBlockSent(block_pos) ||
2334                                         player_pos.getDistanceFrom(pos) > far_d_nodes) {
2335                                 client->SetBlockNotSent(block_pos);
2336                                 continue;
2337                         }
2338
2339                         // Add the change to send list
2340                         meta_updates_list.set(pos, meta);
2341                 }
2342                 if (meta_updates_list.size() == 0)
2343                         continue;
2344
2345                 // Send the meta changes
2346                 os.str("");
2347                 meta_updates_list.serialize(os, client->serialization_version, false, true, true);
2348                 std::string raw = os.str();
2349                 os.str("");
2350                 compressZlib(raw, os);
2351
2352                 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0, i);
2353                 pkt.putLongString(os.str());
2354                 Send(&pkt);
2355
2356                 meta_updates_list.clear();
2357         }
2358 }
2359
2360 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2361                 u16 net_proto_version, SerializedBlockCache *cache)
2362 {
2363         thread_local const int net_compression_level = rangelim(g_settings->getS16("map_compression_level_net"), -1, 9);
2364         std::string s, *sptr = nullptr;
2365
2366         if (cache) {
2367                 auto it = cache->find({block->getPos(), ver});
2368                 if (it != cache->end())
2369                         sptr = &it->second;
2370         }
2371
2372         // Serialize the block in the right format
2373         if (!sptr) {
2374                 std::ostringstream os(std::ios_base::binary);
2375                 block->serialize(os, ver, false, net_compression_level);
2376                 block->serializeNetworkSpecific(os);
2377                 s = os.str();
2378                 sptr = &s;
2379         }
2380
2381         NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + sptr->size(), peer_id);
2382         pkt << block->getPos();
2383         pkt.putRawString(*sptr);
2384         Send(&pkt);
2385
2386         // Store away in cache
2387         if (cache && sptr == &s)
2388                 (*cache)[{block->getPos(), ver}] = std::move(s);
2389 }
2390
2391 void Server::SendBlocks(float dtime)
2392 {
2393         MutexAutoLock envlock(m_env_mutex);
2394         //TODO check if one big lock could be faster then multiple small ones
2395
2396         std::vector<PrioritySortedBlockTransfer> queue;
2397
2398         u32 total_sending = 0, unique_clients = 0;
2399
2400         {
2401                 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2402
2403                 std::vector<session_t> clients = m_clients.getClientIDs();
2404
2405                 ClientInterface::AutoLock clientlock(m_clients);
2406                 for (const session_t client_id : clients) {
2407                         RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2408
2409                         if (!client)
2410                                 continue;
2411
2412                         total_sending += client->getSendingCount();
2413                         const auto old_count = queue.size();
2414                         client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2415                         unique_clients += queue.size() > old_count ? 1 : 0;
2416                 }
2417         }
2418
2419         // Sort.
2420         // Lowest priority number comes first.
2421         // Lowest is most important.
2422         std::sort(queue.begin(), queue.end());
2423
2424         ClientInterface::AutoLock clientlock(m_clients);
2425
2426         // Maximal total count calculation
2427         // The per-client block sends is halved with the maximal online users
2428         u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2429                 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2430
2431         ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2432         Map &map = m_env->getMap();
2433
2434         SerializedBlockCache cache, *cache_ptr = nullptr;
2435         if (unique_clients > 1) {
2436                 // caching is pointless with a single client
2437                 cache_ptr = &cache;
2438         }
2439
2440         for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2441                 if (total_sending >= max_blocks_to_send)
2442                         break;
2443
2444                 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2445                 if (!block)
2446                         continue;
2447
2448                 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2449                                 CS_Active);
2450                 if (!client)
2451                         continue;
2452
2453                 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2454                                 client->net_proto_version, cache_ptr);
2455
2456                 client->SentBlock(block_to_send.pos);
2457                 total_sending++;
2458         }
2459 }
2460
2461 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2462 {
2463         MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2464         if (!block)
2465                 return false;
2466
2467         ClientInterface::AutoLock clientlock(m_clients);
2468         RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2469         if (!client || client->isBlockSent(blockpos))
2470                 return false;
2471         SendBlockNoLock(peer_id, block, client->serialization_version,
2472                         client->net_proto_version);
2473
2474         return true;
2475 }
2476
2477 bool Server::addMediaFile(const std::string &filename,
2478         const std::string &filepath, std::string *filedata_to,
2479         std::string *digest_to)
2480 {
2481         // If name contains illegal characters, ignore the file
2482         if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2483                 infostream << "Server: ignoring illegal file name: \""
2484                                 << filename << "\"" << std::endl;
2485                 return false;
2486         }
2487         // If name is not in a supported format, ignore it
2488         const char *supported_ext[] = {
2489                 ".png", ".jpg", ".bmp", ".tga",
2490                 ".ogg",
2491                 ".x", ".b3d", ".obj",
2492                 // Custom translation file format
2493                 ".tr",
2494                 NULL
2495         };
2496         if (removeStringEnd(filename, supported_ext).empty()) {
2497                 infostream << "Server: ignoring unsupported file extension: \""
2498                                 << filename << "\"" << std::endl;
2499                 return false;
2500         }
2501         // Ok, attempt to load the file and add to cache
2502
2503         // Read data
2504         std::string filedata;
2505         if (!fs::ReadFile(filepath, filedata)) {
2506                 errorstream << "Server::addMediaFile(): Failed to open \""
2507                                         << filename << "\" for reading" << std::endl;
2508                 return false;
2509         }
2510
2511         if (filedata.empty()) {
2512                 errorstream << "Server::addMediaFile(): Empty file \""
2513                                 << filepath << "\"" << std::endl;
2514                 return false;
2515         }
2516
2517         SHA1 sha1;
2518         sha1.addBytes(filedata.c_str(), filedata.length());
2519
2520         unsigned char *digest = sha1.getDigest();
2521         std::string sha1_base64 = base64_encode(digest, 20);
2522         std::string sha1_hex = hex_encode((char*) digest, 20);
2523         if (digest_to)
2524                 *digest_to = std::string((char*) digest, 20);
2525         free(digest);
2526
2527         // Put in list
2528         m_media[filename] = MediaInfo(filepath, sha1_base64);
2529         verbosestream << "Server: " << sha1_hex << " is " << filename
2530                         << std::endl;
2531
2532         if (filedata_to)
2533                 *filedata_to = std::move(filedata);
2534         return true;
2535 }
2536
2537 void Server::fillMediaCache()
2538 {
2539         infostream << "Server: Calculating media file checksums" << std::endl;
2540
2541         // Collect all media file paths
2542         std::vector<std::string> paths;
2543
2544         // ordered in descending priority
2545         paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale");
2546         fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2547         fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2548         m_modmgr->getModsMediaPaths(paths);
2549
2550         // Collect media file information from paths into cache
2551         for (const std::string &mediapath : paths) {
2552                 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2553                 for (const fs::DirListNode &dln : dirlist) {
2554                         if (dln.dir) // Ignore dirs (already in paths)
2555                                 continue;
2556
2557                         const std::string &filename = dln.name;
2558                         if (m_media.find(filename) != m_media.end()) // Do not override
2559                                 continue;
2560
2561                         std::string filepath = mediapath;
2562                         filepath.append(DIR_DELIM).append(filename);
2563                         addMediaFile(filename, filepath);
2564                 }
2565         }
2566
2567         infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2568 }
2569
2570 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2571 {
2572         // Make packet
2573         NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2574
2575         u16 media_sent = 0;
2576         std::string lang_suffix;
2577         lang_suffix.append(".").append(lang_code).append(".tr");
2578         for (const auto &i : m_media) {
2579                 if (i.second.no_announce)
2580                         continue;
2581                 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2582                         continue;
2583                 media_sent++;
2584         }
2585
2586         pkt << media_sent;
2587
2588         for (const auto &i : m_media) {
2589                 if (i.second.no_announce)
2590                         continue;
2591                 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2592                         continue;
2593                 pkt << i.first << i.second.sha1_digest;
2594         }
2595
2596         pkt << g_settings->get("remote_media");
2597         Send(&pkt);
2598
2599         verbosestream << "Server: Announcing files to id(" << peer_id
2600                 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2601 }
2602
2603 struct SendableMedia
2604 {
2605         std::string name;
2606         std::string path;
2607         std::string data;
2608
2609         SendableMedia(const std::string &name, const std::string &path,
2610                         std::string &&data):
2611                 name(name), path(path), data(std::move(data))
2612         {}
2613 };
2614
2615 void Server::sendRequestedMedia(session_t peer_id,
2616                 const std::vector<std::string> &tosend)
2617 {
2618         verbosestream<<"Server::sendRequestedMedia(): "
2619                         <<"Sending files to client"<<std::endl;
2620
2621         /* Read files */
2622
2623         // Put 5kB in one bunch (this is not accurate)
2624         u32 bytes_per_bunch = 5000;
2625
2626         std::vector< std::vector<SendableMedia> > file_bunches;
2627         file_bunches.emplace_back();
2628
2629         u32 file_size_bunch_total = 0;
2630
2631         for (const std::string &name : tosend) {
2632                 if (m_media.find(name) == m_media.end()) {
2633                         errorstream<<"Server::sendRequestedMedia(): Client asked for "
2634                                         <<"unknown file \""<<(name)<<"\""<<std::endl;
2635                         continue;
2636                 }
2637
2638                 const auto &m = m_media[name];
2639
2640                 // Read data
2641                 std::string data;
2642                 if (!fs::ReadFile(m.path, data)) {
2643                         errorstream << "Server::sendRequestedMedia(): Failed to read \""
2644                                         << name << "\"" << std::endl;
2645                         continue;
2646                 }
2647                 file_size_bunch_total += data.size();
2648
2649                 // Put in list
2650                 file_bunches.back().emplace_back(name, m.path, std::move(data));
2651
2652                 // Start next bunch if got enough data
2653                 if(file_size_bunch_total >= bytes_per_bunch) {
2654                         file_bunches.emplace_back();
2655                         file_size_bunch_total = 0;
2656                 }
2657
2658         }
2659
2660         /* Create and send packets */
2661
2662         u16 num_bunches = file_bunches.size();
2663         for (u16 i = 0; i < num_bunches; i++) {
2664                 /*
2665                         u16 command
2666                         u16 total number of texture bunches
2667                         u16 index of this bunch
2668                         u32 number of files in this bunch
2669                         for each file {
2670                                 u16 length of name
2671                                 string name
2672                                 u32 length of data
2673                                 data
2674                         }
2675                 */
2676
2677                 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2678                 pkt << num_bunches << i << (u32) file_bunches[i].size();
2679
2680                 for (const SendableMedia &j : file_bunches[i]) {
2681                         pkt << j.name;
2682                         pkt.putLongString(j.data);
2683                 }
2684
2685                 verbosestream << "Server::sendRequestedMedia(): bunch "
2686                                 << i << "/" << num_bunches
2687                                 << " files=" << file_bunches[i].size()
2688                                 << " size="  << pkt.getSize() << std::endl;
2689                 Send(&pkt);
2690         }
2691 }
2692
2693 void Server::stepPendingDynMediaCallbacks(float dtime)
2694 {
2695         MutexAutoLock lock(m_env_mutex);
2696
2697         for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) {
2698                 it->second.expiry_timer -= dtime;
2699                 bool del = it->second.waiting_players.empty() || it->second.expiry_timer < 0;
2700
2701                 if (!del) {
2702                         it++;
2703                         continue;
2704                 }
2705
2706                 const auto &name = it->second.filename;
2707                 if (!name.empty()) {
2708                         assert(m_media.count(name));
2709                         // if no_announce isn't set we're definitely deleting the wrong file!
2710                         sanity_check(m_media[name].no_announce);
2711
2712                         fs::DeleteSingleFileOrEmptyDirectory(m_media[name].path);
2713                         m_media.erase(name);
2714                 }
2715                 getScriptIface()->freeDynamicMediaCallback(it->first);
2716                 it = m_pending_dyn_media.erase(it);
2717         }
2718 }
2719
2720 void Server::SendMinimapModes(session_t peer_id,
2721                 std::vector<MinimapMode> &modes, size_t wanted_mode)
2722 {
2723         RemotePlayer *player = m_env->getPlayer(peer_id);
2724         assert(player);
2725         if (player->getPeerId() == PEER_ID_INEXISTENT)
2726                 return;
2727
2728         NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2729         pkt << (u16)modes.size() << (u16)wanted_mode;
2730
2731         for (auto &mode : modes)
2732                 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2733
2734         Send(&pkt);
2735 }
2736
2737 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2738 {
2739         NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2740         pkt << name;
2741
2742         if (!inventory) {
2743                 pkt << false; // Remove inventory
2744         } else {
2745                 pkt << true; // Update inventory
2746
2747                 // Serialization & NetworkPacket isn't a love story
2748                 std::ostringstream os(std::ios_base::binary);
2749                 inventory->serialize(os);
2750                 inventory->setModified(false);
2751
2752                 const std::string &os_str = os.str();
2753                 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2754                 pkt.putRawString(os_str);
2755         }
2756
2757         if (peer_id == PEER_ID_INEXISTENT)
2758                 m_clients.sendToAll(&pkt);
2759         else
2760                 Send(&pkt);
2761 }
2762
2763 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2764 {
2765         // Lookup player name, to filter detached inventories just after
2766         std::string peer_name;
2767         if (peer_id != PEER_ID_INEXISTENT) {
2768                 peer_name = getClient(peer_id, CS_Created)->getName();
2769         }
2770
2771         auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2772                 sendDetachedInventory(inv, name, peer_id);
2773         };
2774
2775         m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2776 }
2777
2778 /*
2779         Something random
2780 */
2781
2782 void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
2783 {
2784         infostream << "Server::DiePlayer(): Player "
2785                         << playersao->getPlayer()->getName()
2786                         << " dies" << std::endl;
2787
2788         playersao->clearParentAttachment();
2789
2790         // Trigger scripted stuff
2791         m_script->on_dieplayer(playersao, reason);
2792
2793         SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0));
2794 }
2795
2796 void Server::RespawnPlayer(session_t peer_id)
2797 {
2798         PlayerSAO *playersao = getPlayerSAO(peer_id);
2799         assert(playersao);
2800
2801         infostream << "Server::RespawnPlayer(): Player "
2802                         << playersao->getPlayer()->getName()
2803                         << " respawns" << std::endl;
2804
2805         const auto *prop = playersao->accessObjectProperties();
2806         playersao->setHP(prop->hp_max,
2807                         PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2808         playersao->setBreath(prop->breath_max);
2809
2810         bool repositioned = m_script->on_respawnplayer(playersao);
2811         if (!repositioned) {
2812                 // setPos will send the new position to client
2813                 playersao->setPos(findSpawnPos());
2814         }
2815 }
2816
2817
2818 void Server::DenySudoAccess(session_t peer_id)
2819 {
2820         NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2821         Send(&pkt);
2822 }
2823
2824
2825 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2826                 const std::string &custom_reason, bool reconnect)
2827 {
2828         SendAccessDenied(peer_id, reason, custom_reason, reconnect);
2829         m_clients.event(peer_id, CSE_SetDenied);
2830         DisconnectPeer(peer_id);
2831 }
2832
2833 void Server::DisconnectPeer(session_t peer_id)
2834 {
2835         m_modchannel_mgr->leaveAllChannels(peer_id);
2836         m_con->DisconnectPeer(peer_id);
2837 }
2838
2839 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2840 {
2841         if (!forSudoMode) {
2842                 RemoteClient* client = getClient(peer_id, CS_Invalid);
2843
2844                 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2845
2846                 // Right now, the auth mechs don't change between login and sudo mode.
2847                 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2848                 client->allowed_sudo_mechs = sudo_auth_mechs;
2849
2850                 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2851                                 << g_settings->getFloat("dedicated_server_step")
2852                                 << sudo_auth_mechs;
2853
2854                 Send(&resp_pkt);
2855                 m_clients.event(peer_id, CSE_AuthAccept);
2856         } else {
2857                 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2858
2859                 // We only support SRP right now
2860                 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2861
2862                 resp_pkt << sudo_auth_mechs;
2863                 Send(&resp_pkt);
2864                 m_clients.event(peer_id, CSE_SudoSuccess);
2865         }
2866 }
2867
2868 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2869 {
2870         std::wstring message;
2871         {
2872                 /*
2873                         Clear references to playing sounds
2874                 */
2875                 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2876                                  i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2877                         ServerPlayingSound &psound = i->second;
2878                         psound.clients.erase(peer_id);
2879                         if (psound.clients.empty())
2880                                 m_playing_sounds.erase(i++);
2881                         else
2882                                 ++i;
2883                 }
2884
2885                 // clear formspec info so the next client can't abuse the current state
2886                 m_formspec_state_data.erase(peer_id);
2887
2888                 RemotePlayer *player = m_env->getPlayer(peer_id);
2889
2890                 /* Run scripts and remove from environment */
2891                 if (player) {
2892                         PlayerSAO *playersao = player->getPlayerSAO();
2893                         assert(playersao);
2894
2895                         playersao->clearChildAttachments();
2896                         playersao->clearParentAttachment();
2897
2898                         // inform connected clients
2899                         const std::string &player_name = player->getName();
2900                         NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2901                         // (u16) 1 + std::string represents a vector serialization representation
2902                         notice << (u8) PLAYER_LIST_REMOVE  << (u16) 1 << player_name;
2903                         m_clients.sendToAll(&notice);
2904                         // run scripts
2905                         m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2906
2907                         playersao->disconnected();
2908                 }
2909
2910                 /*
2911                         Print out action
2912                 */
2913                 {
2914                         if (player && reason != CDR_DENY) {
2915                                 std::ostringstream os(std::ios_base::binary);
2916                                 std::vector<session_t> clients = m_clients.getClientIDs();
2917
2918                                 for (const session_t client_id : clients) {
2919                                         // Get player
2920                                         RemotePlayer *player = m_env->getPlayer(client_id);
2921                                         if (!player)
2922                                                 continue;
2923
2924                                         // Get name of player
2925                                         os << player->getName() << " ";
2926                                 }
2927
2928                                 std::string name = player->getName();
2929                                 actionstream << name << " "
2930                                                 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2931                                                 << " List of players: " << os.str() << std::endl;
2932                                 if (m_admin_chat)
2933                                         m_admin_chat->outgoing_queue.push_back(
2934                                                 new ChatEventNick(CET_NICK_REMOVE, name));
2935                         }
2936                 }
2937                 {
2938                         MutexAutoLock env_lock(m_env_mutex);
2939                         m_clients.DeleteClient(peer_id);
2940                 }
2941         }
2942
2943         // Send leave chat message to all remaining clients
2944         if (!message.empty()) {
2945                 SendChatMessage(PEER_ID_INEXISTENT,
2946                                 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2947         }
2948 }
2949
2950 void Server::UpdateCrafting(RemotePlayer *player)
2951 {
2952         InventoryList *clist = player->inventory.getList("craft");
2953         if (!clist || clist->getSize() == 0)
2954                 return;
2955
2956         if (!clist->checkModified())
2957                 return;
2958
2959         // Get a preview for crafting
2960         ItemStack preview;
2961         InventoryLocation loc;
2962         loc.setPlayer(player->getName());
2963         std::vector<ItemStack> output_replacements;
2964         getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2965         m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2966                         clist, loc);
2967
2968         InventoryList *plist = player->inventory.getList("craftpreview");
2969         if (plist && plist->getSize() >= 1) {
2970                 // Put the new preview in
2971                 plist->changeItem(0, preview);
2972         }
2973 }
2974
2975 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2976 {
2977         if (evt->type == CET_NICK_ADD) {
2978                 // The terminal informed us of its nick choice
2979                 m_admin_nick = ((ChatEventNick *)evt)->nick;
2980                 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2981                         errorstream << "You haven't set up an account." << std::endl
2982                                 << "Please log in using the client as '"
2983                                 << m_admin_nick << "' with a secure password." << std::endl
2984                                 << "Until then, you can't execute admin tasks via the console," << std::endl
2985                                 << "and everybody can claim the user account instead of you," << std::endl
2986                                 << "giving them full control over this server." << std::endl;
2987                 }
2988         } else {
2989                 assert(evt->type == CET_CHAT);
2990                 handleAdminChat((ChatEventChat *)evt);
2991         }
2992 }
2993
2994 std::wstring Server::handleChat(const std::string &name,
2995         std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2996 {
2997         // If something goes wrong, this player is to blame
2998         RollbackScopeActor rollback_scope(m_rollback,
2999                         std::string("player:") + name);
3000
3001         if (g_settings->getBool("strip_color_codes"))
3002                 wmessage = unescape_enriched(wmessage);
3003
3004         if (player) {
3005                 switch (player->canSendChatMessage()) {
3006                 case RPLAYER_CHATRESULT_FLOODING: {
3007                         std::wstringstream ws;
3008                         ws << L"You cannot send more messages. You are limited to "
3009                                         << g_settings->getFloat("chat_message_limit_per_10sec")
3010                                         << L" messages per 10 seconds.";
3011                         return ws.str();
3012                 }
3013                 case RPLAYER_CHATRESULT_KICK:
3014                         DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
3015                                 "You have been kicked due to message flooding.");
3016                         return L"";
3017                 case RPLAYER_CHATRESULT_OK:
3018                         break;
3019                 default:
3020                         FATAL_ERROR("Unhandled chat filtering result found.");
3021                 }
3022         }
3023
3024         if (m_max_chatmessage_length > 0
3025                         && wmessage.length() > m_max_chatmessage_length) {
3026                 return L"Your message exceed the maximum chat message limit set on the server. "
3027                                 L"It was refused. Send a shorter message";
3028         }
3029
3030         auto message = trim(wide_to_utf8(wmessage));
3031         if (message.empty())
3032                 return L"";
3033
3034         if (message.find_first_of("\n\r") != std::wstring::npos) {
3035                 return L"Newlines are not permitted in chat messages";
3036         }
3037
3038         // Run script hook, exit if script ate the chat message
3039         if (m_script->on_chat_message(name, message))
3040                 return L"";
3041
3042         // Line to send
3043         std::wstring line;
3044         // Whether to send line to the player that sent the message, or to all players
3045         bool broadcast_line = true;
3046
3047         if (check_shout_priv && !checkPriv(name, "shout")) {
3048                 line += L"-!- You don't have permission to shout.";
3049                 broadcast_line = false;
3050         } else {
3051                 /*
3052                         Workaround for fixing chat on Android. Lua doesn't handle
3053                         the Cyrillic alphabet and some characters on older Android devices
3054                 */
3055 #ifdef __ANDROID__
3056                 line += L"<" + utf8_to_wide(name) + L"> " + wmessage;
3057 #else
3058                 line += utf8_to_wide(m_script->formatChatMessage(name,
3059                                 wide_to_utf8(wmessage)));
3060 #endif
3061         }
3062
3063         /*
3064                 Tell calling method to send the message to sender
3065         */
3066         if (!broadcast_line)
3067                 return line;
3068
3069         /*
3070                 Send the message to others
3071         */
3072         actionstream << "CHAT: " << wide_to_utf8(unescape_enriched(line)) << std::endl;
3073
3074         ChatMessage chatmsg(line);
3075
3076         std::vector<session_t> clients = m_clients.getClientIDs();
3077         for (u16 cid : clients)
3078                 SendChatMessage(cid, chatmsg);
3079
3080         return L"";
3081 }
3082
3083 void Server::handleAdminChat(const ChatEventChat *evt)
3084 {
3085         std::string name = evt->nick;
3086         std::wstring wmessage = evt->evt_msg;
3087
3088         std::wstring answer = handleChat(name, wmessage);
3089
3090         // If asked to send answer to sender
3091         if (!answer.empty()) {
3092                 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3093         }
3094 }
3095
3096 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3097 {
3098         RemoteClient *client = getClientNoEx(peer_id,state_min);
3099         if(!client)
3100                 throw ClientNotFoundException("Client not found");
3101
3102         return client;
3103 }
3104 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3105 {
3106         return m_clients.getClientNoEx(peer_id, state_min);
3107 }
3108
3109 std::string Server::getPlayerName(session_t peer_id)
3110 {
3111         RemotePlayer *player = m_env->getPlayer(peer_id);
3112         if (!player)
3113                 return "[id="+itos(peer_id)+"]";
3114         return player->getName();
3115 }
3116
3117 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3118 {
3119         RemotePlayer *player = m_env->getPlayer(peer_id);
3120         if (!player)
3121                 return NULL;
3122         return player->getPlayerSAO();
3123 }
3124
3125 std::string Server::getStatusString()
3126 {
3127         std::ostringstream os(std::ios_base::binary);
3128         os << "# Server: ";
3129         // Version
3130         os << "version: " << g_version_string;
3131         // Game
3132         os << " | game: " << (m_gamespec.title.empty() ? m_gamespec.id : m_gamespec.title);
3133         // Uptime
3134         os << " | uptime: " << duration_to_string((int) m_uptime_counter->get());
3135         // Max lag estimate
3136         os << " | max lag: " << std::setprecision(3);
3137         os << (m_env ? m_env->getMaxLagEstimate() : 0) << "s";
3138
3139         // Information about clients
3140         bool first = true;
3141         os << " | clients: ";
3142         if (m_env) {
3143                 std::vector<session_t> clients = m_clients.getClientIDs();
3144                 for (session_t client_id : clients) {
3145                         RemotePlayer *player = m_env->getPlayer(client_id);
3146
3147                         // Get name of player
3148                         const char *name = player ? player->getName() : "<unknown>";
3149
3150                         // Add name to information string
3151                         if (!first)
3152                                 os << ", ";
3153                         else
3154                                 first = false;
3155                         os << name;
3156                 }
3157         }
3158
3159         if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3160                 os << std::endl << "# Server: " << " WARNING: Map saving is disabled.";
3161
3162         if (!g_settings->get("motd").empty())
3163                 os << std::endl << "# Server: " << g_settings->get("motd");
3164
3165         return os.str();
3166 }
3167
3168 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3169 {
3170         std::set<std::string> privs;
3171         m_script->getAuth(name, NULL, &privs);
3172         return privs;
3173 }
3174
3175 bool Server::checkPriv(const std::string &name, const std::string &priv)
3176 {
3177         std::set<std::string> privs = getPlayerEffectivePrivs(name);
3178         return (privs.count(priv) != 0);
3179 }
3180
3181 void Server::reportPrivsModified(const std::string &name)
3182 {
3183         if (name.empty()) {
3184                 std::vector<session_t> clients = m_clients.getClientIDs();
3185                 for (const session_t client_id : clients) {
3186                         RemotePlayer *player = m_env->getPlayer(client_id);
3187                         reportPrivsModified(player->getName());
3188                 }
3189         } else {
3190                 RemotePlayer *player = m_env->getPlayer(name.c_str());
3191                 if (!player)
3192                         return;
3193                 SendPlayerPrivileges(player->getPeerId());
3194                 PlayerSAO *sao = player->getPlayerSAO();
3195                 if(!sao)
3196                         return;
3197                 sao->updatePrivileges(
3198                                 getPlayerEffectivePrivs(name),
3199                                 isSingleplayer());
3200         }
3201 }
3202
3203 void Server::reportInventoryFormspecModified(const std::string &name)
3204 {
3205         RemotePlayer *player = m_env->getPlayer(name.c_str());
3206         if (!player)
3207                 return;
3208         SendPlayerInventoryFormspec(player->getPeerId());
3209 }
3210
3211 void Server::reportFormspecPrependModified(const std::string &name)
3212 {
3213         RemotePlayer *player = m_env->getPlayer(name.c_str());
3214         if (!player)
3215                 return;
3216         SendPlayerFormspecPrepend(player->getPeerId());
3217 }
3218
3219 void Server::setIpBanned(const std::string &ip, const std::string &name)
3220 {
3221         m_banmanager->add(ip, name);
3222 }
3223
3224 void Server::unsetIpBanned(const std::string &ip_or_name)
3225 {
3226         m_banmanager->remove(ip_or_name);
3227 }
3228
3229 std::string Server::getBanDescription(const std::string &ip_or_name)
3230 {
3231         return m_banmanager->getBanDescription(ip_or_name);
3232 }
3233
3234 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3235 {
3236         // m_env will be NULL if the server is initializing
3237         if (!m_env)
3238                 return;
3239
3240         if (m_admin_nick == name && !m_admin_nick.empty()) {
3241                 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3242         }
3243
3244         RemotePlayer *player = m_env->getPlayer(name);
3245         if (!player) {
3246                 return;
3247         }
3248
3249         if (player->getPeerId() == PEER_ID_INEXISTENT)
3250                 return;
3251
3252         SendChatMessage(player->getPeerId(), ChatMessage(msg));
3253 }
3254
3255 bool Server::showFormspec(const char *playername, const std::string &formspec,
3256         const std::string &formname)
3257 {
3258         // m_env will be NULL if the server is initializing
3259         if (!m_env)
3260                 return false;
3261
3262         RemotePlayer *player = m_env->getPlayer(playername);
3263         if (!player)
3264                 return false;
3265
3266         SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3267         return true;
3268 }
3269
3270 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3271 {
3272         if (!player)
3273                 return -1;
3274
3275         u32 id = player->addHud(form);
3276
3277         SendHUDAdd(player->getPeerId(), id, form);
3278
3279         return id;
3280 }
3281
3282 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3283         if (!player)
3284                 return false;
3285
3286         HudElement* todel = player->removeHud(id);
3287
3288         if (!todel)
3289                 return false;
3290
3291         delete todel;
3292
3293         SendHUDRemove(player->getPeerId(), id);
3294         return true;
3295 }
3296
3297 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3298 {
3299         if (!player)
3300                 return false;
3301
3302         SendHUDChange(player->getPeerId(), id, stat, data);
3303         return true;
3304 }
3305
3306 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3307 {
3308         if (!player)
3309                 return false;
3310
3311         u32 new_hud_flags = (player->hud_flags & ~mask) | flags;
3312         if (new_hud_flags == player->hud_flags) // no change
3313                 return true;
3314
3315         SendHUDSetFlags(player->getPeerId(), flags, mask);
3316         player->hud_flags = new_hud_flags;
3317
3318         PlayerSAO* playersao = player->getPlayerSAO();
3319
3320         if (!playersao)
3321                 return false;
3322
3323         m_script->player_event(playersao, "hud_changed");
3324         return true;
3325 }
3326
3327 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3328 {
3329         if (!player)
3330                 return false;
3331
3332         if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3333                 return false;
3334
3335         player->setHotbarItemcount(hotbar_itemcount);
3336         std::ostringstream os(std::ios::binary);
3337         writeS32(os, hotbar_itemcount);
3338         SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3339         return true;
3340 }
3341
3342 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3343 {
3344         if (!player)
3345                 return;
3346
3347         player->setHotbarImage(name);
3348         SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3349 }
3350
3351 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3352 {
3353         if (!player)
3354                 return;
3355
3356         player->setHotbarSelectedImage(name);
3357         SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3358 }
3359
3360 Address Server::getPeerAddress(session_t peer_id)
3361 {
3362         // Note that this is only set after Init was received in Server::handleCommand_Init
3363         return getClient(peer_id, CS_Invalid)->getAddress();
3364 }
3365
3366 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3367                 v2s32 animation_frames[4], f32 frame_speed)
3368 {
3369         sanity_check(player);
3370         player->setLocalAnimations(animation_frames, frame_speed);
3371         SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3372 }
3373
3374 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3375 {
3376         sanity_check(player);
3377         player->eye_offset_first = first;
3378         player->eye_offset_third = third;
3379         SendEyeOffset(player->getPeerId(), first, third);
3380 }
3381
3382 void Server::setSky(RemotePlayer *player, const SkyboxParams &params)
3383 {
3384         sanity_check(player);
3385         player->setSky(params);
3386         SendSetSky(player->getPeerId(), params);
3387 }
3388
3389 void Server::setSun(RemotePlayer *player, const SunParams &params)
3390 {
3391         sanity_check(player);
3392         player->setSun(params);
3393         SendSetSun(player->getPeerId(), params);
3394 }
3395
3396 void Server::setMoon(RemotePlayer *player, const MoonParams &params)
3397 {
3398         sanity_check(player);
3399         player->setMoon(params);
3400         SendSetMoon(player->getPeerId(), params);
3401 }
3402
3403 void Server::setStars(RemotePlayer *player, const StarParams &params)
3404 {
3405         sanity_check(player);
3406         player->setStars(params);
3407         SendSetStars(player->getPeerId(), params);
3408 }
3409
3410 void Server::setClouds(RemotePlayer *player, const CloudParams &params)
3411 {
3412         sanity_check(player);
3413         player->setCloudParams(params);
3414         SendCloudParams(player->getPeerId(), params);
3415 }
3416
3417 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3418         float ratio)
3419 {
3420         sanity_check(player);
3421         player->overrideDayNightRatio(do_override, ratio);
3422         SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3423 }
3424
3425 void Server::setLighting(RemotePlayer *player, const Lighting &lighting)
3426 {
3427         sanity_check(player);
3428         player->setLighting(lighting);
3429         SendSetLighting(player->getPeerId(), lighting);
3430 }
3431
3432 void Server::notifyPlayers(const std::wstring &msg)
3433 {
3434         SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3435 }
3436
3437 void Server::spawnParticle(const std::string &playername,
3438         const ParticleParameters &p)
3439 {
3440         // m_env will be NULL if the server is initializing
3441         if (!m_env)
3442                 return;
3443
3444         session_t peer_id = PEER_ID_INEXISTENT;
3445         u16 proto_ver = 0;
3446         if (!playername.empty()) {
3447                 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3448                 if (!player)
3449                         return;
3450                 peer_id = player->getPeerId();
3451                 proto_ver = player->protocol_version;
3452         }
3453
3454         SendSpawnParticle(peer_id, proto_ver, p);
3455 }
3456
3457 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3458         ServerActiveObject *attached, const std::string &playername)
3459 {
3460         // m_env will be NULL if the server is initializing
3461         if (!m_env)
3462                 return -1;
3463
3464         session_t peer_id = PEER_ID_INEXISTENT;
3465         u16 proto_ver = 0;
3466         if (!playername.empty()) {
3467                 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3468                 if (!player)
3469                         return -1;
3470                 peer_id = player->getPeerId();
3471                 proto_ver = player->protocol_version;
3472         }
3473
3474         u16 attached_id = attached ? attached->getId() : 0;
3475
3476         u32 id;
3477         if (attached_id == 0)
3478                 id = m_env->addParticleSpawner(p.time);
3479         else
3480                 id = m_env->addParticleSpawner(p.time, attached_id);
3481
3482         SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3483         return id;
3484 }
3485
3486 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3487 {
3488         // m_env will be NULL if the server is initializing
3489         if (!m_env)
3490                 throw ServerError("Can't delete particle spawners during initialisation!");
3491
3492         session_t peer_id = PEER_ID_INEXISTENT;
3493         if (!playername.empty()) {
3494                 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3495                 if (!player)
3496                         return;
3497                 peer_id = player->getPeerId();
3498         }
3499
3500         m_env->deleteParticleSpawner(id);
3501         SendDeleteParticleSpawner(peer_id, id);
3502 }
3503
3504 bool Server::dynamicAddMedia(std::string filepath,
3505         const u32 token, const std::string &to_player, bool ephemeral)
3506 {
3507         std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3508         auto it = m_media.find(filename);
3509         if (it != m_media.end()) {
3510                 // Allow the same path to be "added" again in certain conditions
3511                 if (ephemeral || it->second.path != filepath) {
3512                         errorstream << "Server::dynamicAddMedia(): file \"" << filename
3513                                 << "\" already exists in media cache" << std::endl;
3514                         return false;
3515                 }
3516         }
3517
3518         // Load the file and add it to our media cache
3519         std::string filedata, raw_hash;
3520         bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3521         if (!ok)
3522                 return false;
3523
3524         if (ephemeral) {
3525                 // Create a copy of the file and swap out the path, this removes the
3526                 // requirement that mods keep the file accessible at the original path.
3527                 filepath = fs::CreateTempFile();
3528                 bool ok = ([&] () -> bool {
3529                         if (filepath.empty())
3530                                 return false;
3531                         std::ofstream os(filepath.c_str(), std::ios::binary);
3532                         if (!os.good())
3533                                 return false;
3534                         os << filedata;
3535                         os.close();
3536                         return !os.fail();
3537                 })();
3538                 if (!ok) {
3539                         errorstream << "Server: failed to create a copy of media file "
3540                                 << "\"" << filename << "\"" << std::endl;
3541                         m_media.erase(filename);
3542                         return false;
3543                 }
3544                 verbosestream << "Server: \"" << filename << "\" temporarily copied to "
3545                         << filepath << std::endl;
3546
3547                 m_media[filename].path = filepath;
3548                 m_media[filename].no_announce = true;
3549                 // stepPendingDynMediaCallbacks will clean this up later.
3550         } else if (!to_player.empty()) {
3551                 m_media[filename].no_announce = true;
3552         }
3553
3554         // Push file to existing clients
3555         NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3556         pkt << raw_hash << filename << (bool)ephemeral;
3557
3558         NetworkPacket legacy_pkt = pkt;
3559
3560         // Newer clients get asked to fetch the file (asynchronous)
3561         pkt << token;
3562         // Older clients have an awful hack that just throws the data at them
3563         legacy_pkt.putLongString(filedata);
3564
3565         std::unordered_set<session_t> delivered, waiting;
3566         {
3567                 ClientInterface::AutoLock clientlock(m_clients);
3568                 for (auto &pair : m_clients.getClientList()) {
3569                         if (pair.second->getState() == CS_DefinitionsSent && !ephemeral) {
3570                                 /*
3571                                         If a client is in the DefinitionsSent state it is too late to
3572                                         transfer the file via sendMediaAnnouncement() but at the same
3573                                         time the client cannot accept a media push yet.
3574                                         Short of artificially delaying the joining process there is no
3575                                         way for the server to resolve this so we (currently) opt not to.
3576                                 */
3577                                 warningstream << "The media \"" << filename << "\" (dynamic) could "
3578                                         "not be delivered to " << pair.second->getName()
3579                                         << " due to a race condition." << std::endl;
3580                                 continue;
3581                         }
3582                         if (pair.second->getState() < CS_Active)
3583                                 continue;
3584
3585                         const auto proto_ver = pair.second->net_proto_version;
3586                         if (proto_ver < 39)
3587                                 continue;
3588
3589                         const session_t peer_id = pair.second->peer_id;
3590                         if (!to_player.empty() && getPlayerName(peer_id) != to_player)
3591                                 continue;
3592
3593                         if (proto_ver < 40) {
3594                                 delivered.emplace(peer_id);
3595                                 /*
3596                                         The network layer only guarantees ordered delivery inside a channel.
3597                                         Since the very next packet could be one that uses the media, we have
3598                                         to push the media over ALL channels to ensure it is processed before
3599                                         it is used. In practice this means channels 1 and 0.
3600                                 */
3601                                 m_clients.send(peer_id, 1, &legacy_pkt, true);
3602                                 m_clients.send(peer_id, 0, &legacy_pkt, true);
3603                         } else {
3604                                 waiting.emplace(peer_id);
3605                                 Send(peer_id, &pkt);
3606                         }
3607                 }
3608         }
3609
3610         // Run callback for players that already had the file delivered (legacy-only)
3611         for (session_t peer_id : delivered) {
3612                 if (auto player = m_env->getPlayer(peer_id))
3613                         getScriptIface()->on_dynamic_media_added(token, player->getName());
3614         }
3615
3616         // Save all others in our pending state
3617         auto &state = m_pending_dyn_media[token];
3618         state.waiting_players = std::move(waiting);
3619         // regardless of success throw away the callback after a while
3620         state.expiry_timer = 60.0f;
3621         if (ephemeral)
3622                 state.filename = filename;
3623
3624         return true;
3625 }
3626
3627 // actions: time-reversed list
3628 // Return value: success/failure
3629 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3630                 std::list<std::string> *log)
3631 {
3632         infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3633         ServerMap *map = (ServerMap*)(&m_env->getMap());
3634
3635         // Fail if no actions to handle
3636         if (actions.empty()) {
3637                 assert(log);
3638                 log->push_back("Nothing to do.");
3639                 return false;
3640         }
3641
3642         int num_tried = 0;
3643         int num_failed = 0;
3644
3645         for (const RollbackAction &action : actions) {
3646                 num_tried++;
3647                 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3648                 if(!success){
3649                         num_failed++;
3650                         std::ostringstream os;
3651                         os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3652                         infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3653                         if (log)
3654                                 log->push_back(os.str());
3655                 }else{
3656                         std::ostringstream os;
3657                         os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3658                         infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3659                         if (log)
3660                                 log->push_back(os.str());
3661                 }
3662         }
3663
3664         infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3665                         <<" failed"<<std::endl;
3666
3667         // Call it done if less than half failed
3668         return num_failed <= num_tried/2;
3669 }
3670
3671 // IGameDef interface
3672 // Under envlock
3673 IItemDefManager *Server::getItemDefManager()
3674 {
3675         return m_itemdef;
3676 }
3677
3678 const NodeDefManager *Server::getNodeDefManager()
3679 {
3680         return m_nodedef;
3681 }
3682
3683 ICraftDefManager *Server::getCraftDefManager()
3684 {
3685         return m_craftdef;
3686 }
3687
3688 u16 Server::allocateUnknownNodeId(const std::string &name)
3689 {
3690         return m_nodedef->allocateDummy(name);
3691 }
3692
3693 IWritableItemDefManager *Server::getWritableItemDefManager()
3694 {
3695         return m_itemdef;
3696 }
3697
3698 NodeDefManager *Server::getWritableNodeDefManager()
3699 {
3700         return m_nodedef;
3701 }
3702
3703 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3704 {
3705         return m_craftdef;
3706 }
3707
3708 const std::vector<ModSpec> & Server::getMods() const
3709 {
3710         return m_modmgr->getMods();
3711 }
3712
3713 const ModSpec *Server::getModSpec(const std::string &modname) const
3714 {
3715         return m_modmgr->getModSpec(modname);
3716 }
3717
3718 std::string Server::getBuiltinLuaPath()
3719 {
3720         return porting::path_share + DIR_DELIM + "builtin";
3721 }
3722
3723 v3f Server::findSpawnPos()
3724 {
3725         ServerMap &map = m_env->getServerMap();
3726         v3f nodeposf;
3727         if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3728                 return nodeposf * BS;
3729
3730         bool is_good = false;
3731         // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3732         s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3733
3734         // Try to find a good place a few times
3735         for (s32 i = 0; i < 4000 && !is_good; i++) {
3736                 s32 range = MYMIN(1 + i, range_max);
3737                 // We're going to try to throw the player to this position
3738                 v2s16 nodepos2d = v2s16(
3739                         -range + myrand_range(0, range*2),
3740                         -range + myrand_range(0, range*2));
3741                 // Get spawn level at point
3742                 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3743                 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3744                 // signify an unsuitable spawn position, or if outside limits.
3745                 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3746                                 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3747                         continue;
3748
3749                 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3750                 // Consecutive empty nodes
3751                 s32 air_count = 0;
3752
3753                 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3754                 // avoid obstructions in already-generated mapblocks.
3755                 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3756                 // no obstructions, but mapgen decorations are generated after spawn so
3757                 // the player may end up inside one.
3758                 for (s32 i = 0; i < 8; i++) {
3759                         v3s16 blockpos = getNodeBlockPos(nodepos);
3760                         map.emergeBlock(blockpos, true);
3761                         content_t c = map.getNode(nodepos).getContent();
3762
3763                         // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3764                         // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3765                         if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3766                                 air_count++;
3767                                 if (air_count >= 2) {
3768                                         // Spawn in lower empty node
3769                                         nodepos.Y--;
3770                                         nodeposf = intToFloat(nodepos, BS);
3771                                         // Don't spawn the player outside map boundaries
3772                                         if (objectpos_over_limit(nodeposf))
3773                                                 // Exit this loop, positions above are probably over limit
3774                                                 break;
3775
3776                                         // Good position found, cause an exit from main loop
3777                                         is_good = true;
3778                                         break;
3779                                 }
3780                         } else {
3781                                 air_count = 0;
3782                         }
3783                         nodepos.Y++;
3784                 }
3785         }
3786
3787         if (is_good)
3788                 return nodeposf;
3789
3790         // No suitable spawn point found, return fallback 0,0,0
3791         return v3f(0.0f, 0.0f, 0.0f);
3792 }
3793
3794 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3795 {
3796         if (delay == 0.0f) {
3797         // No delay, shutdown immediately
3798                 m_shutdown_state.is_requested = true;
3799                 // only print to the infostream, a chat message saying
3800                 // "Server Shutting Down" is sent when the server destructs.
3801                 infostream << "*** Immediate Server shutdown requested." << std::endl;
3802         } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3803                 // Negative delay, cancel shutdown if requested
3804                 m_shutdown_state.reset();
3805                 std::wstringstream ws;
3806
3807                 ws << L"*** Server shutdown canceled.";
3808
3809                 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3810                 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3811                 // m_shutdown_* are already handled, skip.
3812                 return;
3813         } else if (delay > 0.0f) {
3814         // Positive delay, tell the clients when the server will shut down
3815                 std::wstringstream ws;
3816
3817                 ws << L"*** Server shutting down in "
3818                                 << duration_to_string(myround(delay)).c_str()
3819                                 << ".";
3820
3821                 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3822                 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3823         }
3824
3825         m_shutdown_state.trigger(delay, msg, reconnect);
3826 }
3827
3828 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3829 {
3830         /*
3831                 Try to get an existing player
3832         */
3833         RemotePlayer *player = m_env->getPlayer(name);
3834
3835         // If player is already connected, cancel
3836         if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3837                 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3838                 return NULL;
3839         }
3840
3841         /*
3842                 If player with the wanted peer_id already exists, cancel.
3843         */
3844         if (m_env->getPlayer(peer_id)) {
3845                 infostream<<"emergePlayer(): Player with wrong name but same"
3846                                 " peer_id already exists"<<std::endl;
3847                 return NULL;
3848         }
3849
3850         if (!player) {
3851                 player = new RemotePlayer(name, idef());
3852         }
3853
3854         bool newplayer = false;
3855
3856         // Load player
3857         PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3858
3859         // Complete init with server parts
3860         playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3861         player->protocol_version = proto_version;
3862
3863         /* Run scripts */
3864         if (newplayer) {
3865                 m_script->on_newplayer(playersao);
3866         }
3867
3868         return playersao;
3869 }
3870
3871 void dedicated_server_loop(Server &server, bool &kill)
3872 {
3873         verbosestream<<"dedicated_server_loop()"<<std::endl;
3874
3875         IntervalLimiter m_profiler_interval;
3876
3877         static thread_local const float steplen =
3878                         g_settings->getFloat("dedicated_server_step");
3879         static thread_local const float profiler_print_interval =
3880                         g_settings->getFloat("profiler_print_interval");
3881
3882         /*
3883          * The dedicated server loop only does time-keeping (in Server::step) and
3884          * provides a way to main.cpp to kill the server externally (bool &kill).
3885          */
3886
3887         for(;;) {
3888                 // This is kind of a hack but can be done like this
3889                 // because server.step() is very light
3890                 sleep_ms((int)(steplen*1000.0));
3891                 server.step(steplen);
3892
3893                 if (server.isShutdownRequested() || kill)
3894                         break;
3895
3896                 /*
3897                         Profiler
3898                 */
3899                 if (profiler_print_interval != 0) {
3900                         if(m_profiler_interval.step(steplen, profiler_print_interval))
3901                         {
3902                                 infostream<<"Profiler:"<<std::endl;
3903                                 g_profiler->print(infostream);
3904                                 g_profiler->clear();
3905                         }
3906                 }
3907         }
3908
3909         infostream << "Dedicated server quitting" << std::endl;
3910 #if USE_CURL
3911         if (g_settings->getBool("server_announce"))
3912                 ServerList::sendAnnounce(ServerList::AA_DELETE,
3913                         server.m_bind_addr.getPort());
3914 #endif
3915 }
3916
3917 /*
3918  * Mod channels
3919  */
3920
3921
3922 bool Server::joinModChannel(const std::string &channel)
3923 {
3924         return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3925                         m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3926 }
3927
3928 bool Server::leaveModChannel(const std::string &channel)
3929 {
3930         return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3931 }
3932
3933 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3934 {
3935         if (!m_modchannel_mgr->canWriteOnChannel(channel))
3936                 return false;
3937
3938         broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3939         return true;
3940 }
3941
3942 ModChannel* Server::getModChannel(const std::string &channel)
3943 {
3944         return m_modchannel_mgr->getModChannel(channel);
3945 }
3946
3947 void Server::broadcastModChannelMessage(const std::string &channel,
3948                 const std::string &message, session_t from_peer)
3949 {
3950         const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3951         if (peers.empty())
3952                 return;
3953
3954         if (message.size() > STRING_MAX_LEN) {
3955                 warningstream << "ModChannel message too long, dropping before sending "
3956                                 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3957                                 << channel << ")" << std::endl;
3958                 return;
3959         }
3960
3961         std::string sender;
3962         if (from_peer != PEER_ID_SERVER) {
3963                 sender = getPlayerName(from_peer);
3964         }
3965
3966         NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3967                         2 + channel.size() + 2 + sender.size() + 2 + message.size());
3968         resp_pkt << channel << sender << message;
3969         for (session_t peer_id : peers) {
3970                 // Ignore sender
3971                 if (peer_id == from_peer)
3972                         continue;
3973
3974                 Send(peer_id, &resp_pkt);
3975         }
3976
3977         if (from_peer != PEER_ID_SERVER) {
3978                 m_script->on_modchannel_message(channel, sender, message);
3979         }
3980 }
3981
3982 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3983 {
3984         if (lang_code.empty())
3985                 return nullptr;
3986
3987         auto it = server_translations.find(lang_code);
3988         if (it != server_translations.end())
3989                 return &it->second; // Already loaded
3990
3991         // [] will create an entry
3992         auto *translations = &server_translations[lang_code];
3993
3994         std::string suffix = "." + lang_code + ".tr";
3995         for (const auto &i : m_media) {
3996                 if (str_ends_with(i.first, suffix)) {
3997                         std::string data;
3998                         if (fs::ReadFile(i.second.path, data)) {
3999                                 translations->loadTranslation(data);
4000                         }
4001                 }
4002         }
4003
4004         return translations;
4005 }
4006
4007 ModStorageDatabase *Server::openModStorageDatabase(const std::string &world_path)
4008 {
4009         std::string world_mt_path = world_path + DIR_DELIM + "world.mt";
4010         Settings world_mt;
4011         if (!world_mt.readConfigFile(world_mt_path.c_str()))
4012                 throw BaseException("Cannot read world.mt!");
4013
4014         std::string backend = world_mt.exists("mod_storage_backend") ?
4015                 world_mt.get("mod_storage_backend") : "files";
4016         if (backend == "files")
4017                 warningstream << "/!\\ You are using the old mod storage files backend. "
4018                         << "This backend is deprecated and may be removed in a future release /!\\"
4019                         << std::endl << "Switching to SQLite3 is advised, "
4020                         << "please read http://wiki.minetest.net/Database_backends." << std::endl;
4021
4022         return openModStorageDatabase(backend, world_path, world_mt);
4023 }
4024
4025 ModStorageDatabase *Server::openModStorageDatabase(const std::string &backend,
4026                 const std::string &world_path, const Settings &world_mt)
4027 {
4028         if (backend == "sqlite3")
4029                 return new ModStorageDatabaseSQLite3(world_path);
4030
4031 #if USE_POSTGRESQL
4032         if (backend == "postgresql") {
4033                 std::string connect_string;
4034                 world_mt.getNoEx("pgsql_mod_storage_connection", connect_string);
4035                 return new ModStorageDatabasePostgreSQL(connect_string);
4036         }
4037 #endif // USE_POSTGRESQL
4038
4039         if (backend == "files")
4040                 return new ModStorageDatabaseFiles(world_path);
4041
4042         if (backend == "dummy")
4043                 return new Database_Dummy();
4044
4045         throw BaseException("Mod storage database backend " + backend + " not supported");
4046 }
4047
4048 bool Server::migrateModStorageDatabase(const GameParams &game_params, const Settings &cmd_args)
4049 {
4050         std::string migrate_to = cmd_args.get("migrate-mod-storage");
4051         Settings world_mt;
4052         std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
4053         if (!world_mt.readConfigFile(world_mt_path.c_str())) {
4054                 errorstream << "Cannot read world.mt!" << std::endl;
4055                 return false;
4056         }
4057
4058         std::string backend = world_mt.exists("mod_storage_backend") ?
4059                 world_mt.get("mod_storage_backend") : "files";
4060         if (backend == migrate_to) {
4061                 errorstream << "Cannot migrate: new backend is same"
4062                         << " as the old one" << std::endl;
4063                 return false;
4064         }
4065
4066         ModStorageDatabase *srcdb = nullptr;
4067         ModStorageDatabase *dstdb = nullptr;
4068
4069         bool succeeded = false;
4070
4071         try {
4072                 srcdb = Server::openModStorageDatabase(backend, game_params.world_path, world_mt);
4073                 dstdb = Server::openModStorageDatabase(migrate_to, game_params.world_path, world_mt);
4074
4075                 dstdb->beginSave();
4076
4077                 std::vector<std::string> mod_list;
4078                 srcdb->listMods(&mod_list);
4079                 for (const std::string &modname : mod_list) {
4080                         StringMap meta;
4081                         srcdb->getModEntries(modname, &meta);
4082                         for (const auto &pair : meta) {
4083                                 dstdb->setModEntry(modname, pair.first, pair.second);
4084                         }
4085                 }
4086
4087                 dstdb->endSave();
4088
4089                 succeeded = true;
4090
4091                 actionstream << "Successfully migrated the metadata of "
4092                         << mod_list.size() << " mods" << std::endl;
4093                 world_mt.set("mod_storage_backend", migrate_to);
4094                 if (!world_mt.updateConfigFile(world_mt_path.c_str()))
4095                         errorstream << "Failed to update world.mt!" << std::endl;
4096                 else
4097                         actionstream << "world.mt updated" << std::endl;
4098
4099         } catch (BaseException &e) {
4100                 errorstream << "An error occurred during migration: " << e.what() << std::endl;
4101         }
4102
4103         delete srcdb;
4104         delete dstdb;
4105
4106         if (succeeded && backend == "files") {
4107                 // Back up files
4108                 const std::string storage_path = game_params.world_path + DIR_DELIM + "mod_storage";
4109                 const std::string backup_path = game_params.world_path + DIR_DELIM + "mod_storage.bak";
4110                 if (!fs::Rename(storage_path, backup_path))
4111                         warningstream << "After migration, " << storage_path
4112                                 << " could not be renamed to " << backup_path << std::endl;
4113         }
4114
4115         return succeeded;
4116 }