clientobject: add null checks
[waspsaliva.git] / src / server.cpp
blob8f6257afe83443f86c23fa0c97948e056cbed901
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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"
70 class ClientNotFoundException : public BaseException
72 public:
73 ClientNotFoundException(const char *s):
74 BaseException(s)
78 class ServerThread : public Thread
80 public:
82 ServerThread(Server *server):
83 Thread("Server"),
84 m_server(server)
87 void *run();
89 private:
90 Server *m_server;
93 void *ServerThread::run()
95 BEGIN_DEBUG_EXCEPTION_HANDLER
98 * The real business of the server happens on the ServerThread.
99 * How this works:
100 * AsyncRunStep() runs an actual server step as soon as enough time has
101 * passed (dedicated_server_loop keeps track of that).
102 * Receive() blocks at least(!) 30ms waiting for a packet (so this loop
103 * doesn't busy wait) and will process any remaining packets.
106 m_server->AsyncRunStep(true);
108 while (!stopRequested()) {
109 try {
110 m_server->AsyncRunStep();
112 m_server->Receive();
114 } catch (con::PeerNotFoundException &e) {
115 infostream<<"Server: PeerNotFoundException"<<std::endl;
116 } catch (ClientNotFoundException &e) {
117 } catch (con::ConnectionBindFailed &e) {
118 m_server->setAsyncFatalError(e.what());
119 } catch (LuaError &e) {
120 m_server->setAsyncFatalError(
121 "ServerThread::run Lua: " + std::string(e.what()));
125 END_DEBUG_EXCEPTION_HANDLER
127 return nullptr;
130 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
132 if(pos_exists) *pos_exists = false;
133 switch(type){
134 case SSP_LOCAL:
135 return v3f(0,0,0);
136 case SSP_POSITIONAL:
137 if(pos_exists) *pos_exists = true;
138 return pos;
139 case SSP_OBJECT: {
140 if(object == 0)
141 return v3f(0,0,0);
142 ServerActiveObject *sao = env->getActiveObject(object);
143 if(!sao)
144 return v3f(0,0,0);
145 if(pos_exists) *pos_exists = true;
146 return sao->getBasePosition(); }
148 return v3f(0,0,0);
151 void Server::ShutdownState::reset()
153 m_timer = 0.0f;
154 message.clear();
155 should_reconnect = false;
156 is_requested = false;
159 void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
161 m_timer = delay;
162 message = msg;
163 should_reconnect = reconnect;
166 void Server::ShutdownState::tick(float dtime, Server *server)
168 if (m_timer <= 0.0f)
169 return;
171 // Timed shutdown
172 static const float shutdown_msg_times[] =
174 1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
177 // Automated messages
178 if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
179 for (float t : shutdown_msg_times) {
180 // If shutdown timer matches an automessage, shot it
181 if (m_timer > t && m_timer - dtime < t) {
182 std::wstring periodicMsg = getShutdownTimerMessage();
184 infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
185 server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
186 break;
191 m_timer -= dtime;
192 if (m_timer < 0.0f) {
193 m_timer = 0.0f;
194 is_requested = true;
198 std::wstring Server::ShutdownState::getShutdownTimerMessage() const
200 std::wstringstream ws;
201 ws << L"*** Server shutting down in "
202 << duration_to_string(myround(m_timer)).c_str() << ".";
203 return ws.str();
207 Server
210 Server::Server(
211 const std::string &path_world,
212 const SubgameSpec &gamespec,
213 bool simple_singleplayer_mode,
214 Address bind_addr,
215 bool dedicated,
216 ChatInterface *iface,
217 std::string *on_shutdown_errmsg
219 m_bind_addr(bind_addr),
220 m_path_world(path_world),
221 m_gamespec(gamespec),
222 m_simple_singleplayer_mode(simple_singleplayer_mode),
223 m_dedicated(dedicated),
224 m_async_fatal_error(""),
225 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
226 512,
227 CONNECTION_TIMEOUT,
228 m_bind_addr.isIPv6(),
229 this)),
230 m_itemdef(createItemDefManager()),
231 m_nodedef(createNodeDefManager()),
232 m_craftdef(createCraftDefManager()),
233 m_thread(new ServerThread(this)),
234 m_clients(m_con),
235 m_admin_chat(iface),
236 m_on_shutdown_errmsg(on_shutdown_errmsg),
237 m_modchannel_mgr(new ModChannelMgr())
239 if (m_path_world.empty())
240 throw ServerError("Supplied empty world path");
242 if (!gamespec.isValid())
243 throw ServerError("Supplied invalid gamespec");
245 #if USE_PROMETHEUS
246 m_metrics_backend = std::unique_ptr<MetricsBackend>(createPrometheusMetricsBackend());
247 #else
248 m_metrics_backend = std::unique_ptr<MetricsBackend>(new MetricsBackend());
249 #endif
251 m_uptime_counter = m_metrics_backend->addCounter("minetest_core_server_uptime", "Server uptime (in seconds)");
252 m_player_gauge = m_metrics_backend->addGauge("minetest_core_player_number", "Number of connected players");
254 m_timeofday_gauge = m_metrics_backend->addGauge(
255 "minetest_core_timeofday",
256 "Time of day value");
258 m_lag_gauge = m_metrics_backend->addGauge(
259 "minetest_core_latency",
260 "Latency value (in seconds)");
262 m_aom_buffer_counter = m_metrics_backend->addCounter(
263 "minetest_core_aom_generated_count",
264 "Number of active object messages generated");
266 m_packet_recv_counter = m_metrics_backend->addCounter(
267 "minetest_core_server_packet_recv",
268 "Processable packets received");
270 m_packet_recv_processed_counter = m_metrics_backend->addCounter(
271 "minetest_core_server_packet_recv_processed",
272 "Valid received packets processed");
274 m_lag_gauge->set(g_settings->getFloat("dedicated_server_step"));
277 Server::~Server()
280 // Send shutdown message
281 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
282 L"*** Server shutting down"));
284 if (m_env) {
285 MutexAutoLock envlock(m_env_mutex);
287 infostream << "Server: Saving players" << std::endl;
288 m_env->saveLoadedPlayers();
290 infostream << "Server: Kicking players" << std::endl;
291 std::string kick_msg;
292 bool reconnect = false;
293 if (isShutdownRequested()) {
294 reconnect = m_shutdown_state.should_reconnect;
295 kick_msg = m_shutdown_state.message;
297 if (kick_msg.empty()) {
298 kick_msg = g_settings->get("kick_msg_shutdown");
300 m_env->saveLoadedPlayers(true);
301 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
302 kick_msg, reconnect);
305 actionstream << "Server: Shutting down" << std::endl;
307 // Do this before stopping the server in case mapgen callbacks need to access
308 // server-controlled resources (like ModStorages). Also do them before
309 // shutdown callbacks since they may modify state that is finalized in a
310 // callback.
311 if (m_emerge)
312 m_emerge->stopThreads();
314 if (m_env) {
315 MutexAutoLock envlock(m_env_mutex);
317 // Execute script shutdown hooks
318 infostream << "Executing shutdown hooks" << std::endl;
319 try {
320 m_script->on_shutdown();
321 } catch (ModError &e) {
322 errorstream << "ModError: " << e.what() << std::endl;
323 if (m_on_shutdown_errmsg) {
324 if (m_on_shutdown_errmsg->empty()) {
325 *m_on_shutdown_errmsg = std::string("ModError: ") + e.what();
326 } else {
327 *m_on_shutdown_errmsg += std::string("\nModError: ") + e.what();
332 infostream << "Server: Saving environment metadata" << std::endl;
333 m_env->saveMeta();
336 // Stop threads
337 if (m_thread) {
338 stop();
339 delete m_thread;
342 // Delete things in the reverse order of creation
343 delete m_emerge;
344 delete m_env;
345 delete m_rollback;
346 delete m_banmanager;
347 delete m_itemdef;
348 delete m_nodedef;
349 delete m_craftdef;
351 // Deinitialize scripting
352 infostream << "Server: Deinitializing scripting" << std::endl;
353 delete m_script;
355 while (!m_unsent_map_edit_queue.empty()) {
356 delete m_unsent_map_edit_queue.front();
357 m_unsent_map_edit_queue.pop();
361 void Server::init()
363 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
364 if (m_simple_singleplayer_mode)
365 infostream << " in simple singleplayer mode" << std::endl;
366 else
367 infostream << std::endl;
368 infostream << "- world: " << m_path_world << std::endl;
369 infostream << "- game: " << m_gamespec.path << std::endl;
371 // Create world if it doesn't exist
372 try {
373 loadGameConfAndInitWorld(m_path_world,
374 fs::GetFilenameFromPath(m_path_world.c_str()),
375 m_gamespec, false);
376 } catch (const BaseException &e) {
377 throw ServerError(std::string("Failed to initialize world: ") + e.what());
380 // Create emerge manager
381 m_emerge = new EmergeManager(this);
383 // Create ban manager
384 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
385 m_banmanager = new BanManager(ban_path);
387 m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager(m_path_world));
388 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
389 // complain about mods with unsatisfied dependencies
390 if (!m_modmgr->isConsistent()) {
391 m_modmgr->printUnsatisfiedModsError();
394 //lock environment
395 MutexAutoLock envlock(m_env_mutex);
397 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
398 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge, m_metrics_backend.get());
400 // Initialize scripting
401 infostream << "Server: Initializing Lua" << std::endl;
403 m_script = new ServerScripting(this);
405 // Must be created before mod loading because we have some inventory creation
406 m_inventory_mgr = std::unique_ptr<ServerInventoryManager>(new ServerInventoryManager());
408 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
410 m_modmgr->loadMods(m_script);
412 // Read Textures and calculate sha1 sums
413 fillMediaCache();
415 // Apply item aliases in the node definition manager
416 m_nodedef->updateAliases(m_itemdef);
418 // Apply texture overrides from texturepack/override.txt
419 std::vector<std::string> paths;
420 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
421 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
422 for (const std::string &path : paths) {
423 TextureOverrideSource override_source(path + DIR_DELIM + "override.txt");
424 m_nodedef->applyTextureOverrides(override_source.getNodeTileOverrides());
425 m_itemdef->applyTextureOverrides(override_source.getItemTextureOverrides());
428 m_nodedef->setNodeRegistrationStatus(true);
430 // Perform pending node name resolutions
431 m_nodedef->runNodeResolveCallbacks();
433 // unmap node names in cross-references
434 m_nodedef->resolveCrossrefs();
436 // init the recipe hashes to speed up crafting
437 m_craftdef->initHashes(this);
439 // Initialize Environment
440 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
442 m_inventory_mgr->setEnv(m_env);
443 m_clients.setEnv(m_env);
445 if (!servermap->settings_mgr.makeMapgenParams())
446 FATAL_ERROR("Couldn't create any mapgen type");
448 // Initialize mapgens
449 m_emerge->initMapgens(servermap->getMapgenParams());
451 if (g_settings->getBool("enable_rollback_recording")) {
452 // Create rollback manager
453 m_rollback = new RollbackManager(m_path_world, this);
456 // Give environment reference to scripting api
457 m_script->initializeEnvironment(m_env);
459 // Register us to receive map edit events
460 servermap->addEventReceiver(this);
462 m_env->loadMeta();
464 // Those settings can be overwritten in world.mt, they are
465 // intended to be cached after environment loading.
466 m_liquid_transform_every = g_settings->getFloat("liquid_update");
467 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
468 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
469 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
472 void Server::start()
474 init();
476 infostream << "Starting server on " << m_bind_addr.serializeString()
477 << "..." << std::endl;
479 // Stop thread if already running
480 m_thread->stop();
482 // Initialize connection
483 m_con->SetTimeoutMs(30);
484 m_con->Serve(m_bind_addr);
486 // Start thread
487 m_thread->start();
489 // ASCII art for the win!
490 std::cerr
491 << " .__ __ __ " << std::endl
492 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
493 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
494 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
495 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
496 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
497 actionstream << "World at [" << m_path_world << "]" << std::endl;
498 actionstream << "Server for gameid=\"" << m_gamespec.id
499 << "\" listening on " << m_bind_addr.serializeString() << ":"
500 << m_bind_addr.getPort() << "." << std::endl;
503 void Server::stop()
505 infostream<<"Server: Stopping and waiting threads"<<std::endl;
507 // Stop threads (set run=false first so both start stopping)
508 m_thread->stop();
509 //m_emergethread.setRun(false);
510 m_thread->wait();
511 //m_emergethread.stop();
513 infostream<<"Server: Threads stopped"<<std::endl;
516 void Server::step(float dtime)
518 // Limit a bit
519 if (dtime > 2.0)
520 dtime = 2.0;
522 MutexAutoLock lock(m_step_dtime_mutex);
523 m_step_dtime += dtime;
525 // Throw if fatal error occurred in thread
526 std::string async_err = m_async_fatal_error.get();
527 if (!async_err.empty()) {
528 if (!m_simple_singleplayer_mode) {
529 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
530 g_settings->get("kick_msg_crash"),
531 g_settings->getBool("ask_reconnect_on_crash"));
533 throw ServerError("AsyncErr: " + async_err);
537 void Server::AsyncRunStep(bool initial_step)
540 float dtime;
542 MutexAutoLock lock1(m_step_dtime_mutex);
543 dtime = m_step_dtime;
547 // Send blocks to clients
548 SendBlocks(dtime);
551 if((dtime < 0.001) && !initial_step)
552 return;
554 ScopeProfiler sp(g_profiler, "Server::AsyncRunStep()", SPT_AVG);
557 MutexAutoLock lock1(m_step_dtime_mutex);
558 m_step_dtime -= dtime;
562 Update uptime
564 m_uptime_counter->increment(dtime);
566 handlePeerChanges();
569 Update time of day and overall game time
571 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
574 Send to clients at constant intervals
577 m_time_of_day_send_timer -= dtime;
578 if (m_time_of_day_send_timer < 0.0) {
579 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
580 u16 time = m_env->getTimeOfDay();
581 float time_speed = g_settings->getFloat("time_speed");
582 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
584 m_timeofday_gauge->set(time);
588 MutexAutoLock lock(m_env_mutex);
589 // Figure out and report maximum lag to environment
590 float max_lag = m_env->getMaxLagEstimate();
591 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
592 if(dtime > max_lag){
593 if(dtime > 0.1 && dtime > max_lag * 2.0)
594 infostream<<"Server: Maximum lag peaked to "<<dtime
595 <<" s"<<std::endl;
596 max_lag = dtime;
598 m_env->reportMaxLagEstimate(max_lag);
599 // Step environment
600 m_env->step(dtime);
603 static const float map_timer_and_unload_dtime = 2.92;
604 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
606 MutexAutoLock lock(m_env_mutex);
607 // Run Map's timers and unload unused data
608 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
609 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
610 g_settings->getFloat("server_unload_unused_data_timeout"),
611 U32_MAX);
615 Listen to the admin chat, if available
617 if (m_admin_chat) {
618 if (!m_admin_chat->command_queue.empty()) {
619 MutexAutoLock lock(m_env_mutex);
620 while (!m_admin_chat->command_queue.empty()) {
621 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
622 handleChatInterfaceEvent(evt);
623 delete evt;
626 m_admin_chat->outgoing_queue.push_back(
627 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
631 Do background stuff
634 /* Transform liquids */
635 m_liquid_transform_timer += dtime;
636 if(m_liquid_transform_timer >= m_liquid_transform_every)
638 m_liquid_transform_timer -= m_liquid_transform_every;
640 MutexAutoLock lock(m_env_mutex);
642 ScopeProfiler sp(g_profiler, "Server: liquid transform");
644 std::map<v3s16, MapBlock*> modified_blocks;
645 m_env->getMap().transformLiquids(modified_blocks, m_env);
648 Set the modified blocks unsent for all the clients
650 if (!modified_blocks.empty()) {
651 SetBlocksNotSent(modified_blocks);
654 m_clients.step(dtime);
656 // increase/decrease lag gauge gradually
657 if (m_lag_gauge->get() > dtime) {
658 m_lag_gauge->decrement(dtime/100);
659 } else {
660 m_lag_gauge->increment(dtime/100);
662 #if USE_CURL
663 // send masterserver announce
665 float &counter = m_masterserver_timer;
666 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
667 g_settings->getBool("server_announce")) {
668 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
669 ServerList::AA_START,
670 m_bind_addr.getPort(),
671 m_clients.getPlayerNames(),
672 m_uptime_counter->get(),
673 m_env->getGameTime(),
674 m_lag_gauge->get(),
675 m_gamespec.id,
676 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
677 m_modmgr->getMods(),
678 m_dedicated);
679 counter = 0.01;
681 counter += dtime;
683 #endif
686 Check added and deleted active objects
689 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
690 MutexAutoLock envlock(m_env_mutex);
692 m_clients.lock();
693 const RemoteClientMap &clients = m_clients.getClientList();
694 ScopeProfiler sp(g_profiler, "Server: update objects within range");
696 m_player_gauge->set(clients.size());
697 for (const auto &client_it : clients) {
698 RemoteClient *client = client_it.second;
700 if (client->getState() < CS_DefinitionsSent)
701 continue;
703 // This can happen if the client times out somehow
704 if (!m_env->getPlayer(client->peer_id))
705 continue;
707 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
708 if (!playersao)
709 continue;
711 SendActiveObjectRemoveAdd(client, playersao);
713 m_clients.unlock();
715 // Save mod storages if modified
716 m_mod_storage_save_timer -= dtime;
717 if (m_mod_storage_save_timer <= 0.0f) {
718 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
719 int n = 0;
720 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
721 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
722 if (it->second->isModified()) {
723 it->second->save(getModStoragePath());
724 n++;
727 if (n > 0)
728 infostream << "Saved " << n << " modified mod storages." << std::endl;
733 Send object messages
736 MutexAutoLock envlock(m_env_mutex);
737 ScopeProfiler sp(g_profiler, "Server: send SAO messages");
739 // Key = object id
740 // Value = data sent by object
741 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
743 // Get active object messages from environment
744 ActiveObjectMessage aom(0);
745 u32 aom_count = 0;
746 for(;;) {
747 if (!m_env->getActiveObjectMessage(&aom))
748 break;
750 std::vector<ActiveObjectMessage>* message_list = nullptr;
751 auto n = buffered_messages.find(aom.id);
752 if (n == buffered_messages.end()) {
753 message_list = new std::vector<ActiveObjectMessage>;
754 buffered_messages[aom.id] = message_list;
755 } else {
756 message_list = n->second;
758 message_list->push_back(std::move(aom));
759 aom_count++;
762 m_aom_buffer_counter->increment(aom_count);
764 m_clients.lock();
765 const RemoteClientMap &clients = m_clients.getClientList();
766 // Route data to every client
767 std::string reliable_data, unreliable_data;
768 for (const auto &client_it : clients) {
769 reliable_data.clear();
770 unreliable_data.clear();
771 RemoteClient *client = client_it.second;
772 PlayerSAO *player = getPlayerSAO(client->peer_id);
773 // Go through all objects in message buffer
774 for (const auto &buffered_message : buffered_messages) {
775 // If object does not exist or is not known by client, skip it
776 u16 id = buffered_message.first;
777 ServerActiveObject *sao = m_env->getActiveObject(id);
778 if (!sao || client->m_known_objects.find(id) == client->m_known_objects.end())
779 continue;
781 // Get message list of object
782 std::vector<ActiveObjectMessage>* list = buffered_message.second;
783 // Go through every message
784 for (const ActiveObjectMessage &aom : *list) {
785 // Send position updates to players who do not see the attachment
786 if (aom.datastring[0] == AO_CMD_UPDATE_POSITION) {
787 if (sao->getId() == player->getId())
788 continue;
790 // Do not send position updates for attached players
791 // as long the parent is known to the client
792 ServerActiveObject *parent = sao->getParent();
793 if (parent && client->m_known_objects.find(parent->getId()) !=
794 client->m_known_objects.end())
795 continue;
798 // Add full new data to appropriate buffer
799 std::string &buffer = aom.reliable ? reliable_data : unreliable_data;
800 char idbuf[2];
801 writeU16((u8*) idbuf, aom.id);
802 // u16 id
803 // std::string data
804 buffer.append(idbuf, sizeof(idbuf));
805 buffer.append(serializeString16(aom.datastring));
809 reliable_data and unreliable_data are now ready.
810 Send them.
812 if (!reliable_data.empty()) {
813 SendActiveObjectMessages(client->peer_id, reliable_data);
816 if (!unreliable_data.empty()) {
817 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
820 m_clients.unlock();
822 // Clear buffered_messages
823 for (auto &buffered_message : buffered_messages) {
824 delete buffered_message.second;
829 Send queued-for-sending map edit events.
832 // We will be accessing the environment
833 MutexAutoLock lock(m_env_mutex);
835 // Don't send too many at a time
836 //u32 count = 0;
838 // Single change sending is disabled if queue size is not small
839 bool disable_single_change_sending = false;
840 if(m_unsent_map_edit_queue.size() >= 4)
841 disable_single_change_sending = true;
843 int event_count = m_unsent_map_edit_queue.size();
845 // We'll log the amount of each
846 Profiler prof;
848 std::list<v3s16> node_meta_updates;
850 while (!m_unsent_map_edit_queue.empty()) {
851 MapEditEvent* event = m_unsent_map_edit_queue.front();
852 m_unsent_map_edit_queue.pop();
854 // Players far away from the change are stored here.
855 // Instead of sending the changes, MapBlocks are set not sent
856 // for them.
857 std::unordered_set<u16> far_players;
859 switch (event->type) {
860 case MEET_ADDNODE:
861 case MEET_SWAPNODE:
862 prof.add("MEET_ADDNODE", 1);
863 sendAddNode(event->p, event->n, &far_players,
864 disable_single_change_sending ? 5 : 30,
865 event->type == MEET_ADDNODE);
866 break;
867 case MEET_REMOVENODE:
868 prof.add("MEET_REMOVENODE", 1);
869 sendRemoveNode(event->p, &far_players,
870 disable_single_change_sending ? 5 : 30);
871 break;
872 case MEET_BLOCK_NODE_METADATA_CHANGED: {
873 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
874 if (!event->is_private_change) {
875 // Don't send the change yet. Collect them to eliminate dupes.
876 node_meta_updates.remove(event->p);
877 node_meta_updates.push_back(event->p);
880 if (MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(
881 getNodeBlockPos(event->p))) {
882 block->raiseModified(MOD_STATE_WRITE_NEEDED,
883 MOD_REASON_REPORT_META_CHANGE);
885 break;
887 case MEET_OTHER:
888 prof.add("MEET_OTHER", 1);
889 for (const v3s16 &modified_block : event->modified_blocks) {
890 m_clients.markBlockposAsNotSent(modified_block);
892 break;
893 default:
894 prof.add("unknown", 1);
895 warningstream << "Server: Unknown MapEditEvent "
896 << ((u32)event->type) << std::endl;
897 break;
901 Set blocks not sent to far players
903 if (!far_players.empty()) {
904 // Convert list format to that wanted by SetBlocksNotSent
905 std::map<v3s16, MapBlock*> modified_blocks2;
906 for (const v3s16 &modified_block : event->modified_blocks) {
907 modified_blocks2[modified_block] =
908 m_env->getMap().getBlockNoCreateNoEx(modified_block);
911 // Set blocks not sent
912 for (const u16 far_player : far_players) {
913 if (RemoteClient *client = getClient(far_player))
914 client->SetBlocksNotSent(modified_blocks2);
918 delete event;
921 if (event_count >= 5) {
922 infostream << "Server: MapEditEvents:" << std::endl;
923 prof.print(infostream);
924 } else if (event_count != 0) {
925 verbosestream << "Server: MapEditEvents:" << std::endl;
926 prof.print(verbosestream);
929 // Send all metadata updates
930 if (node_meta_updates.size())
931 sendMetadataChanged(node_meta_updates);
935 Trigger emergethread (it somehow gets to a non-triggered but
936 bysy state sometimes)
939 float &counter = m_emergethread_trigger_timer;
940 counter += dtime;
941 if (counter >= 2.0) {
942 counter = 0.0;
944 m_emerge->startThreads();
948 // Save map, players and auth stuff
950 float &counter = m_savemap_timer;
951 counter += dtime;
952 static thread_local const float save_interval =
953 g_settings->getFloat("server_map_save_interval");
954 if (counter >= save_interval) {
955 counter = 0.0;
956 MutexAutoLock lock(m_env_mutex);
958 ScopeProfiler sp(g_profiler, "Server: map saving (sum)");
960 // Save ban file
961 if (m_banmanager->isModified()) {
962 m_banmanager->save();
965 // Save changed parts of map
966 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
968 // Save players
969 m_env->saveLoadedPlayers();
971 // Save environment metadata
972 m_env->saveMeta();
976 m_shutdown_state.tick(dtime, this);
979 void Server::Receive()
981 NetworkPacket pkt;
982 session_t peer_id;
983 bool first = true;
984 for (;;) {
985 pkt.clear();
986 peer_id = 0;
987 try {
989 In the first iteration *wait* for a packet, afterwards process
990 all packets that are immediately available (no waiting).
992 if (first) {
993 m_con->Receive(&pkt);
994 first = false;
995 } else {
996 if (!m_con->TryReceive(&pkt))
997 return;
1000 peer_id = pkt.getPeerId();
1001 m_packet_recv_counter->increment();
1002 ProcessData(&pkt);
1003 m_packet_recv_processed_counter->increment();
1004 } catch (const con::InvalidIncomingDataException &e) {
1005 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
1006 << e.what() << std::endl;
1007 } catch (const SerializationError &e) {
1008 infostream << "Server::Receive(): SerializationError: what()="
1009 << e.what() << std::endl;
1010 } catch (const ClientStateError &e) {
1011 errorstream << "ProcessData: peer=" << peer_id << " what()="
1012 << e.what() << std::endl;
1013 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
1014 L"Try reconnecting or updating your client");
1015 } catch (const con::PeerNotFoundException &e) {
1016 // Do nothing
1017 } catch (const con::NoIncomingDataException &e) {
1018 return;
1023 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1025 std::string playername;
1026 PlayerSAO *playersao = NULL;
1027 m_clients.lock();
1028 try {
1029 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1030 if (client) {
1031 playername = client->getName();
1032 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1034 } catch (std::exception &e) {
1035 m_clients.unlock();
1036 throw;
1038 m_clients.unlock();
1040 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1042 // If failed, cancel
1043 if (!playersao || !player) {
1044 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1045 actionstream << "Server: Failed to emerge player \"" << playername
1046 << "\" (player allocated to an another client)" << std::endl;
1047 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1048 L"name. If your client closed unexpectedly, try again in "
1049 L"a minute.");
1050 } else {
1051 errorstream << "Server: " << playername << ": Failed to emerge player"
1052 << std::endl;
1053 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1055 return NULL;
1059 Send complete position information
1061 SendMovePlayer(peer_id);
1063 // Send privileges
1064 SendPlayerPrivileges(peer_id);
1066 // Send inventory formspec
1067 SendPlayerInventoryFormspec(peer_id);
1069 // Send inventory
1070 SendInventory(playersao, false);
1072 // Send HP or death screen
1073 if (playersao->isDead())
1074 SendDeathscreen(peer_id, false, v3f(0,0,0));
1075 else
1076 SendPlayerHPOrDie(playersao,
1077 PlayerHPChangeReason(PlayerHPChangeReason::SET_HP));
1079 // Send Breath
1080 SendPlayerBreath(playersao);
1083 Print out action
1086 Address addr = getPeerAddress(player->getPeerId());
1087 std::string ip_str = addr.serializeString();
1088 const std::vector<std::string> &names = m_clients.getPlayerNames();
1090 actionstream << player->getName() << " [" << ip_str << "] joins game. List of players: ";
1092 for (const std::string &name : names) {
1093 actionstream << name << " ";
1096 actionstream << player->getName() <<std::endl;
1098 return playersao;
1101 inline void Server::handleCommand(NetworkPacket *pkt)
1103 const ToServerCommandHandler &opHandle = toServerCommandTable[pkt->getCommand()];
1104 (this->*opHandle.handler)(pkt);
1107 void Server::ProcessData(NetworkPacket *pkt)
1109 // Environment is locked first.
1110 MutexAutoLock envlock(m_env_mutex);
1112 ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)");
1113 u32 peer_id = pkt->getPeerId();
1115 try {
1116 Address address = getPeerAddress(peer_id);
1117 std::string addr_s = address.serializeString();
1119 if(m_banmanager->isIpBanned(addr_s)) {
1120 std::string ban_name = m_banmanager->getBanName(addr_s);
1121 infostream << "Server: A banned client tried to connect from "
1122 << addr_s << "; banned name was "
1123 << ban_name << std::endl;
1124 // This actually doesn't seem to transfer to the client
1125 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1126 + utf8_to_wide(ban_name));
1127 return;
1130 catch(con::PeerNotFoundException &e) {
1132 * no peer for this packet found
1133 * most common reason is peer timeout, e.g. peer didn't
1134 * respond for some time, your server was overloaded or
1135 * things like that.
1137 infostream << "Server::ProcessData(): Canceling: peer "
1138 << peer_id << " not found" << std::endl;
1139 return;
1142 try {
1143 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1145 // Command must be handled into ToServerCommandHandler
1146 if (command >= TOSERVER_NUM_MSG_TYPES) {
1147 infostream << "Server: Ignoring unknown command "
1148 << command << std::endl;
1149 return;
1152 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1153 handleCommand(pkt);
1154 return;
1157 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1159 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1160 errorstream << "Server::ProcessData(): Cancelling: Peer"
1161 " serialization format invalid or not initialized."
1162 " Skipping incoming command=" << command << std::endl;
1163 return;
1166 /* Handle commands related to client startup */
1167 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1168 handleCommand(pkt);
1169 return;
1172 if (m_clients.getClientState(peer_id) < CS_Active) {
1173 if (command == TOSERVER_PLAYERPOS) return;
1175 errorstream << "Got packet command: " << command << " for peer id "
1176 << peer_id << " but client isn't active yet. Dropping packet "
1177 << std::endl;
1178 return;
1181 handleCommand(pkt);
1182 } catch (SendFailedException &e) {
1183 errorstream << "Server::ProcessData(): SendFailedException: "
1184 << "what=" << e.what()
1185 << std::endl;
1186 } catch (PacketError &e) {
1187 actionstream << "Server::ProcessData(): PacketError: "
1188 << "what=" << e.what()
1189 << std::endl;
1193 void Server::setTimeOfDay(u32 time)
1195 m_env->setTimeOfDay(time);
1196 m_time_of_day_send_timer = 0;
1199 void Server::onMapEditEvent(const MapEditEvent &event)
1201 if (m_ignore_map_edit_events_area.contains(event.getArea()))
1202 return;
1204 m_unsent_map_edit_queue.push(new MapEditEvent(event));
1207 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1209 std::vector<session_t> clients = m_clients.getClientIDs();
1210 m_clients.lock();
1211 // Set the modified blocks unsent for all the clients
1212 for (const session_t client_id : clients) {
1213 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1214 client->SetBlocksNotSent(block);
1216 m_clients.unlock();
1219 void Server::peerAdded(con::Peer *peer)
1221 verbosestream<<"Server::peerAdded(): peer->id="
1222 <<peer->id<<std::endl;
1224 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1227 void Server::deletingPeer(con::Peer *peer, bool timeout)
1229 verbosestream<<"Server::deletingPeer(): peer->id="
1230 <<peer->id<<", timeout="<<timeout<<std::endl;
1232 m_clients.event(peer->id, CSE_Disconnect);
1233 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1236 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1238 *retval = m_con->getPeerStat(peer_id,type);
1239 return *retval != -1;
1242 bool Server::getClientInfo(
1243 session_t peer_id,
1244 ClientState* state,
1245 u32* uptime,
1246 u8* ser_vers,
1247 u16* prot_vers,
1248 u8* major,
1249 u8* minor,
1250 u8* patch,
1251 std::string* vers_string,
1252 std::string* lang_code
1255 *state = m_clients.getClientState(peer_id);
1256 m_clients.lock();
1257 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1259 if (!client) {
1260 m_clients.unlock();
1261 return false;
1264 *uptime = client->uptime();
1265 *ser_vers = client->serialization_version;
1266 *prot_vers = client->net_proto_version;
1268 *major = client->getMajor();
1269 *minor = client->getMinor();
1270 *patch = client->getPatch();
1271 *vers_string = client->getFull();
1272 *lang_code = client->getLangCode();
1274 m_clients.unlock();
1276 return true;
1279 void Server::handlePeerChanges()
1281 while(!m_peer_change_queue.empty())
1283 con::PeerChange c = m_peer_change_queue.front();
1284 m_peer_change_queue.pop();
1286 verbosestream<<"Server: Handling peer change: "
1287 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1288 <<std::endl;
1290 switch(c.type)
1292 case con::PEER_ADDED:
1293 m_clients.CreateClient(c.peer_id);
1294 break;
1296 case con::PEER_REMOVED:
1297 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1298 break;
1300 default:
1301 FATAL_ERROR("Invalid peer change event received!");
1302 break;
1307 void Server::printToConsoleOnly(const std::string &text)
1309 if (m_admin_chat) {
1310 m_admin_chat->outgoing_queue.push_back(
1311 new ChatEventChat("", utf8_to_wide(text)));
1312 } else {
1313 std::cout << text << std::endl;
1317 void Server::Send(NetworkPacket *pkt)
1319 Send(pkt->getPeerId(), pkt);
1322 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1324 m_clients.send(peer_id,
1325 clientCommandFactoryTable[pkt->getCommand()].channel,
1326 pkt,
1327 clientCommandFactoryTable[pkt->getCommand()].reliable);
1330 void Server::SendMovement(session_t peer_id)
1332 std::ostringstream os(std::ios_base::binary);
1334 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1336 pkt << g_settings->getFloat("movement_acceleration_default");
1337 pkt << g_settings->getFloat("movement_acceleration_air");
1338 pkt << g_settings->getFloat("movement_acceleration_fast");
1339 pkt << g_settings->getFloat("movement_speed_walk");
1340 pkt << g_settings->getFloat("movement_speed_crouch");
1341 pkt << g_settings->getFloat("movement_speed_fast");
1342 pkt << g_settings->getFloat("movement_speed_climb");
1343 pkt << g_settings->getFloat("movement_speed_jump");
1344 pkt << g_settings->getFloat("movement_liquid_fluidity");
1345 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1346 pkt << g_settings->getFloat("movement_liquid_sink");
1347 pkt << g_settings->getFloat("movement_gravity");
1349 Send(&pkt);
1352 void Server::SendPlayerHPOrDie(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1354 if (playersao->isImmortal())
1355 return;
1357 session_t peer_id = playersao->getPeerID();
1358 bool is_alive = playersao->getHP() > 0;
1360 if (is_alive)
1361 SendPlayerHP(peer_id);
1362 else
1363 DiePlayer(peer_id, reason);
1366 void Server::SendHP(session_t peer_id, u16 hp)
1368 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1369 pkt << hp;
1370 Send(&pkt);
1373 void Server::SendBreath(session_t peer_id, u16 breath)
1375 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1376 pkt << (u16) breath;
1377 Send(&pkt);
1380 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1381 const std::string &custom_reason, bool reconnect)
1383 assert(reason < SERVER_ACCESSDENIED_MAX);
1385 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1386 pkt << (u8)reason;
1387 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1388 pkt << custom_reason;
1389 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1390 reason == SERVER_ACCESSDENIED_CRASH)
1391 pkt << custom_reason << (u8)reconnect;
1392 Send(&pkt);
1395 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1397 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1398 pkt << reason;
1399 Send(&pkt);
1402 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1403 v3f camera_point_target)
1405 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1406 pkt << set_camera_point_target << camera_point_target;
1407 Send(&pkt);
1410 void Server::SendItemDef(session_t peer_id,
1411 IItemDefManager *itemdef, u16 protocol_version)
1413 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1416 u16 command
1417 u32 length of the next item
1418 zlib-compressed serialized ItemDefManager
1420 std::ostringstream tmp_os(std::ios::binary);
1421 itemdef->serialize(tmp_os, protocol_version);
1422 std::ostringstream tmp_os2(std::ios::binary);
1423 compressZlib(tmp_os.str(), tmp_os2);
1424 pkt.putLongString(tmp_os2.str());
1426 // Make data buffer
1427 verbosestream << "Server: Sending item definitions to id(" << peer_id
1428 << "): size=" << pkt.getSize() << std::endl;
1430 Send(&pkt);
1433 void Server::SendNodeDef(session_t peer_id,
1434 const NodeDefManager *nodedef, u16 protocol_version)
1436 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1439 u16 command
1440 u32 length of the next item
1441 zlib-compressed serialized NodeDefManager
1443 std::ostringstream tmp_os(std::ios::binary);
1444 nodedef->serialize(tmp_os, protocol_version);
1445 std::ostringstream tmp_os2(std::ios::binary);
1446 compressZlib(tmp_os.str(), tmp_os2);
1448 pkt.putLongString(tmp_os2.str());
1450 // Make data buffer
1451 verbosestream << "Server: Sending node definitions to id(" << peer_id
1452 << "): size=" << pkt.getSize() << std::endl;
1454 Send(&pkt);
1458 Non-static send methods
1461 void Server::SendInventory(PlayerSAO *sao, bool incremental)
1463 RemotePlayer *player = sao->getPlayer();
1465 // Do not send new format to old clients
1466 incremental &= player->protocol_version >= 38;
1468 UpdateCrafting(player);
1471 Serialize it
1474 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, sao->getPeerID());
1476 std::ostringstream os(std::ios::binary);
1477 sao->getInventory()->serialize(os, incremental);
1478 sao->getInventory()->setModified(false);
1479 player->setModified(true);
1481 const std::string &s = os.str();
1482 pkt.putRawString(s.c_str(), s.size());
1483 Send(&pkt);
1486 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1488 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1489 u8 version = 1;
1490 u8 type = message.type;
1491 pkt << version << type << std::wstring(L"") << message.message << (u64)message.timestamp;
1493 if (peer_id != PEER_ID_INEXISTENT) {
1494 RemotePlayer *player = m_env->getPlayer(peer_id);
1495 if (!player)
1496 return;
1498 Send(&pkt);
1499 } else {
1500 m_clients.sendToAll(&pkt);
1504 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1505 const std::string &formname)
1507 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0, peer_id);
1508 if (formspec.empty()){
1509 //the client should close the formspec
1510 //but make sure there wasn't another one open in meantime
1511 const auto it = m_formspec_state_data.find(peer_id);
1512 if (it != m_formspec_state_data.end() && it->second == formname) {
1513 m_formspec_state_data.erase(peer_id);
1515 pkt.putLongString("");
1516 } else {
1517 m_formspec_state_data[peer_id] = formname;
1518 pkt.putLongString(formspec);
1520 pkt << formname;
1522 Send(&pkt);
1525 // Spawns a particle on peer with peer_id
1526 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1527 const ParticleParameters &p)
1529 static thread_local const float radius =
1530 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1532 if (peer_id == PEER_ID_INEXISTENT) {
1533 std::vector<session_t> clients = m_clients.getClientIDs();
1534 const v3f pos = p.pos * BS;
1535 const float radius_sq = radius * radius;
1537 for (const session_t client_id : clients) {
1538 RemotePlayer *player = m_env->getPlayer(client_id);
1539 if (!player)
1540 continue;
1542 PlayerSAO *sao = player->getPlayerSAO();
1543 if (!sao)
1544 continue;
1546 // Do not send to distant clients
1547 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1548 continue;
1550 SendSpawnParticle(client_id, player->protocol_version, p);
1552 return;
1554 assert(protocol_version != 0);
1556 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1559 // NetworkPacket and iostreams are incompatible...
1560 std::ostringstream oss(std::ios_base::binary);
1561 p.serialize(oss, protocol_version);
1562 pkt.putRawString(oss.str());
1565 Send(&pkt);
1568 // Adds a ParticleSpawner on peer with peer_id
1569 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1570 const ParticleSpawnerParameters &p, u16 attached_id, u32 id)
1572 static thread_local const float radius =
1573 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1575 if (peer_id == PEER_ID_INEXISTENT) {
1576 std::vector<session_t> clients = m_clients.getClientIDs();
1577 const v3f pos = (p.minpos + p.maxpos) / 2.0f * BS;
1578 const float radius_sq = radius * radius;
1579 /* Don't send short-lived spawners to distant players.
1580 * This could be replaced with proper tracking at some point. */
1581 const bool distance_check = !attached_id && p.time <= 1.0f;
1583 for (const session_t client_id : clients) {
1584 RemotePlayer *player = m_env->getPlayer(client_id);
1585 if (!player)
1586 continue;
1588 if (distance_check) {
1589 PlayerSAO *sao = player->getPlayerSAO();
1590 if (!sao)
1591 continue;
1592 if (sao->getBasePosition().getDistanceFromSQ(pos) > radius_sq)
1593 continue;
1596 SendAddParticleSpawner(client_id, player->protocol_version,
1597 p, attached_id, id);
1599 return;
1601 assert(protocol_version != 0);
1603 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id);
1605 pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel
1606 << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime
1607 << p.minsize << p.maxsize << p.collisiondetection;
1609 pkt.putLongString(p.texture);
1611 pkt << id << p.vertical << p.collision_removal << attached_id;
1613 std::ostringstream os(std::ios_base::binary);
1614 p.animation.serialize(os, protocol_version);
1615 pkt.putRawString(os.str());
1617 pkt << p.glow << p.object_collision;
1618 pkt << p.node.param0 << p.node.param2 << p.node_tile;
1620 Send(&pkt);
1623 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1625 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1627 pkt << id;
1629 if (peer_id != PEER_ID_INEXISTENT)
1630 Send(&pkt);
1631 else
1632 m_clients.sendToAll(&pkt);
1636 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1638 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1640 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1641 << form->text << form->number << form->item << form->dir
1642 << form->align << form->offset << form->world_pos << form->size
1643 << form->z_index << form->text2;
1645 Send(&pkt);
1648 void Server::SendHUDRemove(session_t peer_id, u32 id)
1650 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1651 pkt << id;
1652 Send(&pkt);
1655 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1657 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1658 pkt << id << (u8) stat;
1660 switch (stat) {
1661 case HUD_STAT_POS:
1662 case HUD_STAT_SCALE:
1663 case HUD_STAT_ALIGN:
1664 case HUD_STAT_OFFSET:
1665 pkt << *(v2f *) value;
1666 break;
1667 case HUD_STAT_NAME:
1668 case HUD_STAT_TEXT:
1669 case HUD_STAT_TEXT2:
1670 pkt << *(std::string *) value;
1671 break;
1672 case HUD_STAT_WORLD_POS:
1673 pkt << *(v3f *) value;
1674 break;
1675 case HUD_STAT_SIZE:
1676 pkt << *(v2s32 *) value;
1677 break;
1678 case HUD_STAT_NUMBER:
1679 case HUD_STAT_ITEM:
1680 case HUD_STAT_DIR:
1681 default:
1682 pkt << *(u32 *) value;
1683 break;
1686 Send(&pkt);
1689 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1691 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1693 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1695 pkt << flags << mask;
1697 Send(&pkt);
1700 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1702 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1703 pkt << param << value;
1704 Send(&pkt);
1707 void Server::SendSetSky(session_t peer_id, const SkyboxParams &params)
1709 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1711 // Handle prior clients here
1712 if (m_clients.getProtocolVersion(peer_id) < 39) {
1713 pkt << params.bgcolor << params.type << (u16) params.textures.size();
1715 for (const std::string& texture : params.textures)
1716 pkt << texture;
1718 pkt << params.clouds;
1719 } else { // Handle current clients and future clients
1720 pkt << params.bgcolor << params.type
1721 << params.clouds << params.fog_sun_tint
1722 << params.fog_moon_tint << params.fog_tint_type;
1724 if (params.type == "skybox") {
1725 pkt << (u16) params.textures.size();
1726 for (const std::string &texture : params.textures)
1727 pkt << texture;
1728 } else if (params.type == "regular") {
1729 pkt << params.sky_color.day_sky << params.sky_color.day_horizon
1730 << params.sky_color.dawn_sky << params.sky_color.dawn_horizon
1731 << params.sky_color.night_sky << params.sky_color.night_horizon
1732 << params.sky_color.indoors;
1736 Send(&pkt);
1739 void Server::SendSetSun(session_t peer_id, const SunParams &params)
1741 NetworkPacket pkt(TOCLIENT_SET_SUN, 0, peer_id);
1742 pkt << params.visible << params.texture
1743 << params.tonemap << params.sunrise
1744 << params.sunrise_visible << params.scale;
1746 Send(&pkt);
1748 void Server::SendSetMoon(session_t peer_id, const MoonParams &params)
1750 NetworkPacket pkt(TOCLIENT_SET_MOON, 0, peer_id);
1752 pkt << params.visible << params.texture
1753 << params.tonemap << params.scale;
1755 Send(&pkt);
1757 void Server::SendSetStars(session_t peer_id, const StarParams &params)
1759 NetworkPacket pkt(TOCLIENT_SET_STARS, 0, peer_id);
1761 pkt << params.visible << params.count
1762 << params.starcolor << params.scale;
1764 Send(&pkt);
1767 void Server::SendCloudParams(session_t peer_id, const CloudParams &params)
1769 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1770 pkt << params.density << params.color_bright << params.color_ambient
1771 << params.height << params.thickness << params.speed;
1772 Send(&pkt);
1775 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1776 float ratio)
1778 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1779 1 + 2, peer_id);
1781 pkt << do_override << (u16) (ratio * 65535);
1783 Send(&pkt);
1786 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1788 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1789 pkt << time << time_speed;
1791 if (peer_id == PEER_ID_INEXISTENT) {
1792 m_clients.sendToAll(&pkt);
1794 else {
1795 Send(&pkt);
1799 void Server::SendPlayerHP(session_t peer_id)
1801 PlayerSAO *playersao = getPlayerSAO(peer_id);
1802 assert(playersao);
1804 SendHP(peer_id, playersao->getHP());
1805 m_script->player_event(playersao,"health_changed");
1807 // Send to other clients
1808 playersao->sendPunchCommand();
1811 void Server::SendPlayerBreath(PlayerSAO *sao)
1813 assert(sao);
1815 m_script->player_event(sao, "breath_changed");
1816 SendBreath(sao->getPeerID(), sao->getBreath());
1819 void Server::SendMovePlayer(session_t peer_id)
1821 RemotePlayer *player = m_env->getPlayer(peer_id);
1822 assert(player);
1823 PlayerSAO *sao = player->getPlayerSAO();
1824 assert(sao);
1826 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1827 pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y;
1830 v3f pos = sao->getBasePosition();
1831 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1832 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1833 << " pitch=" << sao->getLookPitch()
1834 << " yaw=" << sao->getRotation().Y
1835 << std::endl;
1838 Send(&pkt);
1841 void Server::SendPlayerFov(session_t peer_id)
1843 NetworkPacket pkt(TOCLIENT_FOV, 4 + 1 + 4, peer_id);
1845 PlayerFovSpec fov_spec = m_env->getPlayer(peer_id)->getFov();
1846 pkt << fov_spec.fov << fov_spec.is_multiplier << fov_spec.transition_time;
1848 Send(&pkt);
1851 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1852 f32 animation_speed)
1854 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1855 peer_id);
1857 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1858 << animation_frames[3] << animation_speed;
1860 Send(&pkt);
1863 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1865 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1866 pkt << first << third;
1867 Send(&pkt);
1870 void Server::SendPlayerPrivileges(session_t peer_id)
1872 RemotePlayer *player = m_env->getPlayer(peer_id);
1873 assert(player);
1874 if(player->getPeerId() == PEER_ID_INEXISTENT)
1875 return;
1877 std::set<std::string> privs;
1878 m_script->getAuth(player->getName(), NULL, &privs);
1880 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1881 pkt << (u16) privs.size();
1883 for (const std::string &priv : privs) {
1884 pkt << priv;
1887 Send(&pkt);
1890 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1892 RemotePlayer *player = m_env->getPlayer(peer_id);
1893 assert(player);
1894 if (player->getPeerId() == PEER_ID_INEXISTENT)
1895 return;
1897 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1898 pkt.putLongString(player->inventory_formspec);
1900 Send(&pkt);
1903 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1905 RemotePlayer *player = m_env->getPlayer(peer_id);
1906 assert(player);
1907 if (player->getPeerId() == PEER_ID_INEXISTENT)
1908 return;
1910 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1911 pkt << player->formspec_prepend;
1912 Send(&pkt);
1915 void Server::SendActiveObjectRemoveAdd(RemoteClient *client, PlayerSAO *playersao)
1917 // Radius inside which objects are active
1918 static thread_local const s16 radius =
1919 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
1921 // Radius inside which players are active
1922 static thread_local const bool is_transfer_limited =
1923 g_settings->exists("unlimited_player_transfer_distance") &&
1924 !g_settings->getBool("unlimited_player_transfer_distance");
1926 static thread_local const s16 player_transfer_dist =
1927 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
1929 s16 player_radius = player_transfer_dist == 0 && is_transfer_limited ?
1930 radius : player_transfer_dist;
1932 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
1933 if (my_radius <= 0)
1934 my_radius = radius;
1936 std::queue<u16> removed_objects, added_objects;
1937 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
1938 client->m_known_objects, removed_objects);
1939 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
1940 client->m_known_objects, added_objects);
1942 int removed_count = removed_objects.size();
1943 int added_count = added_objects.size();
1945 if (removed_objects.empty() && added_objects.empty())
1946 return;
1948 char buf[4];
1949 std::string data;
1951 // Handle removed objects
1952 writeU16((u8*)buf, removed_objects.size());
1953 data.append(buf, 2);
1954 while (!removed_objects.empty()) {
1955 // Get object
1956 u16 id = removed_objects.front();
1957 ServerActiveObject* obj = m_env->getActiveObject(id);
1959 // Add to data buffer for sending
1960 writeU16((u8*)buf, id);
1961 data.append(buf, 2);
1963 // Remove from known objects
1964 client->m_known_objects.erase(id);
1966 if (obj && obj->m_known_by_count > 0)
1967 obj->m_known_by_count--;
1969 removed_objects.pop();
1972 // Handle added objects
1973 writeU16((u8*)buf, added_objects.size());
1974 data.append(buf, 2);
1975 while (!added_objects.empty()) {
1976 // Get object
1977 u16 id = added_objects.front();
1978 ServerActiveObject *obj = m_env->getActiveObject(id);
1979 added_objects.pop();
1981 if (!obj) {
1982 warningstream << FUNCTION_NAME << ": NULL object id="
1983 << (int)id << std::endl;
1984 continue;
1987 // Get object type
1988 u8 type = obj->getSendType();
1990 // Add to data buffer for sending
1991 writeU16((u8*)buf, id);
1992 data.append(buf, 2);
1993 writeU8((u8*)buf, type);
1994 data.append(buf, 1);
1996 data.append(serializeString32(
1997 obj->getClientInitializationData(client->net_proto_version)));
1999 // Add to known objects
2000 client->m_known_objects.insert(id);
2002 obj->m_known_by_count++;
2005 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, data.size(), client->peer_id);
2006 pkt.putRawString(data.c_str(), data.size());
2007 Send(&pkt);
2009 verbosestream << "Server::SendActiveObjectRemoveAdd: "
2010 << removed_count << " removed, " << added_count << " added, "
2011 << "packet size is " << pkt.getSize() << std::endl;
2014 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
2015 bool reliable)
2017 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
2018 datas.size(), peer_id);
2020 pkt.putRawString(datas.c_str(), datas.size());
2022 m_clients.send(pkt.getPeerId(),
2023 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2024 &pkt, reliable);
2027 void Server::SendCSMRestrictionFlags(session_t peer_id)
2029 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
2030 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
2031 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
2032 Send(&pkt);
2035 void Server::SendPlayerSpeed(session_t peer_id, const v3f &added_vel)
2037 NetworkPacket pkt(TOCLIENT_PLAYER_SPEED, 0, peer_id);
2038 pkt << added_vel;
2039 Send(&pkt);
2042 inline s32 Server::nextSoundId()
2044 s32 ret = m_next_sound_id;
2045 if (m_next_sound_id == INT32_MAX)
2046 m_next_sound_id = 0; // signed overflow is undefined
2047 else
2048 m_next_sound_id++;
2049 return ret;
2052 s32 Server::playSound(const SimpleSoundSpec &spec,
2053 const ServerSoundParams &params, bool ephemeral)
2055 // Find out initial position of sound
2056 bool pos_exists = false;
2057 v3f pos = params.getPos(m_env, &pos_exists);
2058 // If position is not found while it should be, cancel sound
2059 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2060 return -1;
2062 // Filter destination clients
2063 std::vector<session_t> dst_clients;
2064 if (!params.to_player.empty()) {
2065 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2066 if(!player){
2067 infostream<<"Server::playSound: Player \""<<params.to_player
2068 <<"\" not found"<<std::endl;
2069 return -1;
2071 if (player->getPeerId() == PEER_ID_INEXISTENT) {
2072 infostream<<"Server::playSound: Player \""<<params.to_player
2073 <<"\" not connected"<<std::endl;
2074 return -1;
2076 dst_clients.push_back(player->getPeerId());
2077 } else {
2078 std::vector<session_t> clients = m_clients.getClientIDs();
2080 for (const session_t client_id : clients) {
2081 RemotePlayer *player = m_env->getPlayer(client_id);
2082 if (!player)
2083 continue;
2084 if (!params.exclude_player.empty() &&
2085 params.exclude_player == player->getName())
2086 continue;
2088 PlayerSAO *sao = player->getPlayerSAO();
2089 if (!sao)
2090 continue;
2092 if (pos_exists) {
2093 if(sao->getBasePosition().getDistanceFrom(pos) >
2094 params.max_hear_distance)
2095 continue;
2097 dst_clients.push_back(client_id);
2101 if(dst_clients.empty())
2102 return -1;
2104 // Create the sound
2105 s32 id;
2106 ServerPlayingSound *psound = nullptr;
2107 if (ephemeral) {
2108 id = -1; // old clients will still use this, so pick a reserved ID
2109 } else {
2110 id = nextSoundId();
2111 // The sound will exist as a reference in m_playing_sounds
2112 m_playing_sounds[id] = ServerPlayingSound();
2113 psound = &m_playing_sounds[id];
2114 psound->params = params;
2115 psound->spec = spec;
2118 float gain = params.gain * spec.gain;
2119 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2120 pkt << id << spec.name << gain
2121 << (u8) params.type << pos << params.object
2122 << params.loop << params.fade << params.pitch
2123 << ephemeral;
2125 bool as_reliable = !ephemeral;
2127 for (const u16 dst_client : dst_clients) {
2128 if (psound)
2129 psound->clients.insert(dst_client);
2130 m_clients.send(dst_client, 0, &pkt, as_reliable);
2132 return id;
2134 void Server::stopSound(s32 handle)
2136 // Get sound reference
2137 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2138 m_playing_sounds.find(handle);
2139 if (i == m_playing_sounds.end())
2140 return;
2141 ServerPlayingSound &psound = i->second;
2143 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2144 pkt << handle;
2146 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2147 si != psound.clients.end(); ++si) {
2148 // Send as reliable
2149 m_clients.send(*si, 0, &pkt, true);
2151 // Remove sound reference
2152 m_playing_sounds.erase(i);
2155 void Server::fadeSound(s32 handle, float step, float gain)
2157 // Get sound reference
2158 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2159 m_playing_sounds.find(handle);
2160 if (i == m_playing_sounds.end())
2161 return;
2163 ServerPlayingSound &psound = i->second;
2164 psound.params.gain = gain;
2166 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2167 pkt << handle << step << gain;
2169 // Backwards compability
2170 bool play_sound = gain > 0;
2171 ServerPlayingSound compat_psound = psound;
2172 compat_psound.clients.clear();
2174 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2175 compat_pkt << handle;
2177 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2178 it != psound.clients.end();) {
2179 if (m_clients.getProtocolVersion(*it) >= 32) {
2180 // Send as reliable
2181 m_clients.send(*it, 0, &pkt, true);
2182 ++it;
2183 } else {
2184 compat_psound.clients.insert(*it);
2185 // Stop old sound
2186 m_clients.send(*it, 0, &compat_pkt, true);
2187 psound.clients.erase(it++);
2191 // Remove sound reference
2192 if (!play_sound || psound.clients.empty())
2193 m_playing_sounds.erase(i);
2195 if (play_sound && !compat_psound.clients.empty()) {
2196 // Play new sound volume on older clients
2197 playSound(compat_psound.spec, compat_psound.params);
2201 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2202 float far_d_nodes)
2204 float maxd = far_d_nodes * BS;
2205 v3f p_f = intToFloat(p, BS);
2206 v3s16 block_pos = getNodeBlockPos(p);
2208 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2209 pkt << p;
2211 std::vector<session_t> clients = m_clients.getClientIDs();
2212 m_clients.lock();
2214 for (session_t client_id : clients) {
2215 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2216 if (!client)
2217 continue;
2219 RemotePlayer *player = m_env->getPlayer(client_id);
2220 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2222 // If player is far away, only set modified blocks not sent
2223 if (!client->isBlockSent(block_pos) || (sao &&
2224 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2225 if (far_players)
2226 far_players->emplace(client_id);
2227 else
2228 client->SetBlockNotSent(block_pos);
2229 continue;
2232 // Send as reliable
2233 m_clients.send(client_id, 0, &pkt, true);
2236 m_clients.unlock();
2239 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2240 float far_d_nodes, bool remove_metadata)
2242 float maxd = far_d_nodes * BS;
2243 v3f p_f = intToFloat(p, BS);
2244 v3s16 block_pos = getNodeBlockPos(p);
2246 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2247 pkt << p << n.param0 << n.param1 << n.param2
2248 << (u8) (remove_metadata ? 0 : 1);
2250 std::vector<session_t> clients = m_clients.getClientIDs();
2251 m_clients.lock();
2253 for (session_t client_id : clients) {
2254 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2255 if (!client)
2256 continue;
2258 RemotePlayer *player = m_env->getPlayer(client_id);
2259 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2261 // If player is far away, only set modified blocks not sent
2262 if (!client->isBlockSent(block_pos) || (sao &&
2263 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2264 if (far_players)
2265 far_players->emplace(client_id);
2266 else
2267 client->SetBlockNotSent(block_pos);
2268 continue;
2271 // Send as reliable
2272 m_clients.send(client_id, 0, &pkt, true);
2275 m_clients.unlock();
2278 void Server::sendMetadataChanged(const std::list<v3s16> &meta_updates, float far_d_nodes)
2280 float maxd = far_d_nodes * BS;
2281 NodeMetadataList meta_updates_list(false);
2282 std::vector<session_t> clients = m_clients.getClientIDs();
2284 m_clients.lock();
2286 for (session_t i : clients) {
2287 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2288 if (!client)
2289 continue;
2291 ServerActiveObject *player = m_env->getActiveObject(i);
2292 v3f player_pos = player ? player->getBasePosition() : v3f();
2294 for (const v3s16 &pos : meta_updates) {
2295 NodeMetadata *meta = m_env->getMap().getNodeMetadata(pos);
2297 if (!meta)
2298 continue;
2300 v3s16 block_pos = getNodeBlockPos(pos);
2301 if (!client->isBlockSent(block_pos) || (player &&
2302 player_pos.getDistanceFrom(intToFloat(pos, BS)) > maxd)) {
2303 client->SetBlockNotSent(block_pos);
2304 continue;
2307 // Add the change to send list
2308 meta_updates_list.set(pos, meta);
2310 if (meta_updates_list.size() == 0)
2311 continue;
2313 // Send the meta changes
2314 std::ostringstream os(std::ios::binary);
2315 meta_updates_list.serialize(os, client->net_proto_version, false, true);
2316 std::ostringstream oss(std::ios::binary);
2317 compressZlib(os.str(), oss);
2319 NetworkPacket pkt(TOCLIENT_NODEMETA_CHANGED, 0);
2320 pkt.putLongString(oss.str());
2321 m_clients.send(i, 0, &pkt, true);
2323 meta_updates_list.clear();
2326 m_clients.unlock();
2329 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2330 u16 net_proto_version)
2333 Create a packet with the block in the right format
2336 std::ostringstream os(std::ios_base::binary);
2337 block->serialize(os, ver, false);
2338 block->serializeNetworkSpecific(os);
2339 std::string s = os.str();
2341 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + s.size(), peer_id);
2343 pkt << block->getPos();
2344 pkt.putRawString(s.c_str(), s.size());
2345 Send(&pkt);
2348 void Server::SendBlocks(float dtime)
2350 MutexAutoLock envlock(m_env_mutex);
2351 //TODO check if one big lock could be faster then multiple small ones
2353 std::vector<PrioritySortedBlockTransfer> queue;
2355 u32 total_sending = 0;
2358 ScopeProfiler sp2(g_profiler, "Server::SendBlocks(): Collect list");
2360 std::vector<session_t> clients = m_clients.getClientIDs();
2362 m_clients.lock();
2363 for (const session_t client_id : clients) {
2364 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2366 if (!client)
2367 continue;
2369 total_sending += client->getSendingCount();
2370 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2372 m_clients.unlock();
2375 // Sort.
2376 // Lowest priority number comes first.
2377 // Lowest is most important.
2378 std::sort(queue.begin(), queue.end());
2380 m_clients.lock();
2382 // Maximal total count calculation
2383 // The per-client block sends is halved with the maximal online users
2384 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2385 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2387 ScopeProfiler sp(g_profiler, "Server::SendBlocks(): Send to clients");
2388 Map &map = m_env->getMap();
2390 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2391 if (total_sending >= max_blocks_to_send)
2392 break;
2394 MapBlock *block = map.getBlockNoCreateNoEx(block_to_send.pos);
2395 if (!block)
2396 continue;
2398 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2399 CS_Active);
2400 if (!client)
2401 continue;
2403 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2404 client->net_proto_version);
2406 client->SentBlock(block_to_send.pos);
2407 total_sending++;
2409 m_clients.unlock();
2412 bool Server::SendBlock(session_t peer_id, const v3s16 &blockpos)
2414 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2415 if (!block)
2416 return false;
2418 m_clients.lock();
2419 RemoteClient *client = m_clients.lockedGetClientNoEx(peer_id, CS_Active);
2420 if (!client || client->isBlockSent(blockpos)) {
2421 m_clients.unlock();
2422 return false;
2424 SendBlockNoLock(peer_id, block, client->serialization_version,
2425 client->net_proto_version);
2426 m_clients.unlock();
2428 return true;
2431 bool Server::addMediaFile(const std::string &filename,
2432 const std::string &filepath, std::string *filedata_to,
2433 std::string *digest_to)
2435 // If name contains illegal characters, ignore the file
2436 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2437 infostream << "Server: ignoring illegal file name: \""
2438 << filename << "\"" << std::endl;
2439 return false;
2441 // If name is not in a supported format, ignore it
2442 const char *supported_ext[] = {
2443 ".png", ".jpg", ".bmp", ".tga",
2444 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2445 ".ogg",
2446 ".x", ".b3d", ".md2", ".obj",
2447 // Custom translation file format
2448 ".tr",
2449 NULL
2451 if (removeStringEnd(filename, supported_ext).empty()) {
2452 infostream << "Server: ignoring unsupported file extension: \""
2453 << filename << "\"" << std::endl;
2454 return false;
2456 // Ok, attempt to load the file and add to cache
2458 // Read data
2459 std::string filedata;
2460 if (!fs::ReadFile(filepath, filedata)) {
2461 errorstream << "Server::addMediaFile(): Failed to open \""
2462 << filename << "\" for reading" << std::endl;
2463 return false;
2466 if (filedata.empty()) {
2467 errorstream << "Server::addMediaFile(): Empty file \""
2468 << filepath << "\"" << std::endl;
2469 return false;
2472 SHA1 sha1;
2473 sha1.addBytes(filedata.c_str(), filedata.length());
2475 unsigned char *digest = sha1.getDigest();
2476 std::string sha1_base64 = base64_encode(digest, 20);
2477 std::string sha1_hex = hex_encode((char*) digest, 20);
2478 if (digest_to)
2479 *digest_to = std::string((char*) digest, 20);
2480 free(digest);
2482 // Put in list
2483 m_media[filename] = MediaInfo(filepath, sha1_base64);
2484 verbosestream << "Server: " << sha1_hex << " is " << filename
2485 << std::endl;
2487 if (filedata_to)
2488 *filedata_to = std::move(filedata);
2489 return true;
2492 void Server::fillMediaCache()
2494 infostream << "Server: Calculating media file checksums" << std::endl;
2496 // Collect all media file paths
2497 std::vector<std::string> paths;
2498 // The paths are ordered in descending priority
2499 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2500 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2501 m_modmgr->getModsMediaPaths(paths);
2503 // Collect media file information from paths into cache
2504 for (const std::string &mediapath : paths) {
2505 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2506 for (const fs::DirListNode &dln : dirlist) {
2507 if (dln.dir) // Ignore dirs (already in paths)
2508 continue;
2510 const std::string &filename = dln.name;
2511 if (m_media.find(filename) != m_media.end()) // Do not override
2512 continue;
2514 std::string filepath = mediapath;
2515 filepath.append(DIR_DELIM).append(filename);
2516 addMediaFile(filename, filepath);
2520 infostream << "Server: " << m_media.size() << " media files collected" << std::endl;
2523 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2525 // Make packet
2526 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2528 u16 media_sent = 0;
2529 std::string lang_suffix;
2530 lang_suffix.append(".").append(lang_code).append(".tr");
2531 for (const auto &i : m_media) {
2532 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2533 continue;
2534 media_sent++;
2537 pkt << media_sent;
2539 for (const auto &i : m_media) {
2540 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2541 continue;
2542 pkt << i.first << i.second.sha1_digest;
2545 pkt << g_settings->get("remote_media");
2546 Send(&pkt);
2548 verbosestream << "Server: Announcing files to id(" << peer_id
2549 << "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
2552 struct SendableMedia
2554 std::string name;
2555 std::string path;
2556 std::string data;
2558 SendableMedia(const std::string &name_="", const std::string &path_="",
2559 const std::string &data_=""):
2560 name(name_),
2561 path(path_),
2562 data(data_)
2566 void Server::sendRequestedMedia(session_t peer_id,
2567 const std::vector<std::string> &tosend)
2569 verbosestream<<"Server::sendRequestedMedia(): "
2570 <<"Sending files to client"<<std::endl;
2572 /* Read files */
2574 // Put 5kB in one bunch (this is not accurate)
2575 u32 bytes_per_bunch = 5000;
2577 std::vector< std::vector<SendableMedia> > file_bunches;
2578 file_bunches.emplace_back();
2580 u32 file_size_bunch_total = 0;
2582 for (const std::string &name : tosend) {
2583 if (m_media.find(name) == m_media.end()) {
2584 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2585 <<"unknown file \""<<(name)<<"\""<<std::endl;
2586 continue;
2589 //TODO get path + name
2590 std::string tpath = m_media[name].path;
2592 // Read data
2593 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2594 if(!fis.good()){
2595 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2596 <<tpath<<"\" for reading"<<std::endl;
2597 continue;
2599 std::ostringstream tmp_os(std::ios_base::binary);
2600 bool bad = false;
2601 for(;;) {
2602 char buf[1024];
2603 fis.read(buf, 1024);
2604 std::streamsize len = fis.gcount();
2605 tmp_os.write(buf, len);
2606 file_size_bunch_total += len;
2607 if(fis.eof())
2608 break;
2609 if(!fis.good()) {
2610 bad = true;
2611 break;
2614 if (bad) {
2615 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2616 <<name<<"\""<<std::endl;
2617 continue;
2619 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2620 <<tname<<"\""<<std::endl;*/
2621 // Put in list
2622 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2624 // Start next bunch if got enough data
2625 if(file_size_bunch_total >= bytes_per_bunch) {
2626 file_bunches.emplace_back();
2627 file_size_bunch_total = 0;
2632 /* Create and send packets */
2634 u16 num_bunches = file_bunches.size();
2635 for (u16 i = 0; i < num_bunches; i++) {
2637 u16 command
2638 u16 total number of texture bunches
2639 u16 index of this bunch
2640 u32 number of files in this bunch
2641 for each file {
2642 u16 length of name
2643 string name
2644 u32 length of data
2645 data
2649 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2650 pkt << num_bunches << i << (u32) file_bunches[i].size();
2652 for (const SendableMedia &j : file_bunches[i]) {
2653 pkt << j.name;
2654 pkt.putLongString(j.data);
2657 verbosestream << "Server::sendRequestedMedia(): bunch "
2658 << i << "/" << num_bunches
2659 << " files=" << file_bunches[i].size()
2660 << " size=" << pkt.getSize() << std::endl;
2661 Send(&pkt);
2665 void Server::SendMinimapModes(session_t peer_id,
2666 std::vector<MinimapMode> &modes, size_t wanted_mode)
2668 RemotePlayer *player = m_env->getPlayer(peer_id);
2669 assert(player);
2670 if (player->getPeerId() == PEER_ID_INEXISTENT)
2671 return;
2673 NetworkPacket pkt(TOCLIENT_MINIMAP_MODES, 0, peer_id);
2674 pkt << (u16)modes.size() << (u16)wanted_mode;
2676 for (auto &mode : modes)
2677 pkt << (u16)mode.type << mode.label << mode.size << mode.texture << mode.scale;
2679 Send(&pkt);
2682 void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id)
2684 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2685 pkt << name;
2687 if (!inventory) {
2688 pkt << false; // Remove inventory
2689 } else {
2690 pkt << true; // Update inventory
2692 // Serialization & NetworkPacket isn't a love story
2693 std::ostringstream os(std::ios_base::binary);
2694 inventory->serialize(os);
2695 inventory->setModified(false);
2697 const std::string &os_str = os.str();
2698 pkt << static_cast<u16>(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients
2699 pkt.putRawString(os_str);
2702 if (peer_id == PEER_ID_INEXISTENT)
2703 m_clients.sendToAll(&pkt);
2704 else
2705 Send(&pkt);
2708 void Server::sendDetachedInventories(session_t peer_id, bool incremental)
2710 // Lookup player name, to filter detached inventories just after
2711 std::string peer_name;
2712 if (peer_id != PEER_ID_INEXISTENT) {
2713 peer_name = getClient(peer_id, CS_Created)->getName();
2716 auto send_cb = [this, peer_id](const std::string &name, Inventory *inv) {
2717 sendDetachedInventory(inv, name, peer_id);
2720 m_inventory_mgr->sendDetachedInventories(peer_name, incremental, send_cb);
2724 Something random
2727 void Server::DiePlayer(session_t peer_id, const PlayerHPChangeReason &reason)
2729 PlayerSAO *playersao = getPlayerSAO(peer_id);
2730 assert(playersao);
2732 infostream << "Server::DiePlayer(): Player "
2733 << playersao->getPlayer()->getName()
2734 << " dies" << std::endl;
2736 playersao->setHP(0, reason);
2737 playersao->clearParentAttachment();
2739 // Trigger scripted stuff
2740 m_script->on_dieplayer(playersao, reason);
2742 SendPlayerHP(peer_id);
2743 SendDeathscreen(peer_id, false, v3f(0,0,0));
2746 void Server::RespawnPlayer(session_t peer_id)
2748 PlayerSAO *playersao = getPlayerSAO(peer_id);
2749 assert(playersao);
2751 infostream << "Server::RespawnPlayer(): Player "
2752 << playersao->getPlayer()->getName()
2753 << " respawns" << std::endl;
2755 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2756 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2757 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2759 bool repositioned = m_script->on_respawnplayer(playersao);
2760 if (!repositioned) {
2761 // setPos will send the new position to client
2762 playersao->setPos(findSpawnPos());
2765 SendPlayerHP(peer_id);
2769 void Server::DenySudoAccess(session_t peer_id)
2771 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2772 Send(&pkt);
2776 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2777 const std::string &str_reason, bool reconnect)
2779 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2781 m_clients.event(peer_id, CSE_SetDenied);
2782 DisconnectPeer(peer_id);
2786 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2787 const std::string &custom_reason)
2789 SendAccessDenied(peer_id, reason, custom_reason);
2790 m_clients.event(peer_id, CSE_SetDenied);
2791 DisconnectPeer(peer_id);
2794 // 13/03/15: remove this function when protocol version 25 will become
2795 // the minimum version for MT users, maybe in 1 year
2796 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2798 SendAccessDenied_Legacy(peer_id, reason);
2799 m_clients.event(peer_id, CSE_SetDenied);
2800 DisconnectPeer(peer_id);
2803 void Server::DisconnectPeer(session_t peer_id)
2805 m_modchannel_mgr->leaveAllChannels(peer_id);
2806 m_con->DisconnectPeer(peer_id);
2809 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2811 if (!forSudoMode) {
2812 RemoteClient* client = getClient(peer_id, CS_Invalid);
2814 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2816 // Right now, the auth mechs don't change between login and sudo mode.
2817 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2818 client->allowed_sudo_mechs = sudo_auth_mechs;
2820 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2821 << g_settings->getFloat("dedicated_server_step")
2822 << sudo_auth_mechs;
2824 Send(&resp_pkt);
2825 m_clients.event(peer_id, CSE_AuthAccept);
2826 } else {
2827 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2829 // We only support SRP right now
2830 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2832 resp_pkt << sudo_auth_mechs;
2833 Send(&resp_pkt);
2834 m_clients.event(peer_id, CSE_SudoSuccess);
2838 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2840 std::wstring message;
2843 Clear references to playing sounds
2845 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2846 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2847 ServerPlayingSound &psound = i->second;
2848 psound.clients.erase(peer_id);
2849 if (psound.clients.empty())
2850 m_playing_sounds.erase(i++);
2851 else
2852 ++i;
2855 // clear formspec info so the next client can't abuse the current state
2856 m_formspec_state_data.erase(peer_id);
2858 RemotePlayer *player = m_env->getPlayer(peer_id);
2860 /* Run scripts and remove from environment */
2861 if (player) {
2862 PlayerSAO *playersao = player->getPlayerSAO();
2863 assert(playersao);
2865 playersao->clearChildAttachments();
2866 playersao->clearParentAttachment();
2868 // inform connected clients
2869 const std::string &player_name = player->getName();
2870 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2871 // (u16) 1 + std::string represents a vector serialization representation
2872 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2873 m_clients.sendToAll(&notice);
2874 // run scripts
2875 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2877 playersao->disconnected();
2881 Print out action
2884 if (player && reason != CDR_DENY) {
2885 std::ostringstream os(std::ios_base::binary);
2886 std::vector<session_t> clients = m_clients.getClientIDs();
2888 for (const session_t client_id : clients) {
2889 // Get player
2890 RemotePlayer *player = m_env->getPlayer(client_id);
2891 if (!player)
2892 continue;
2894 // Get name of player
2895 os << player->getName() << " ";
2898 std::string name = player->getName();
2899 actionstream << name << " "
2900 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2901 << " List of players: " << os.str() << std::endl;
2902 if (m_admin_chat)
2903 m_admin_chat->outgoing_queue.push_back(
2904 new ChatEventNick(CET_NICK_REMOVE, name));
2908 MutexAutoLock env_lock(m_env_mutex);
2909 m_clients.DeleteClient(peer_id);
2913 // Send leave chat message to all remaining clients
2914 if (!message.empty()) {
2915 SendChatMessage(PEER_ID_INEXISTENT,
2916 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2920 void Server::UpdateCrafting(RemotePlayer *player)
2922 InventoryList *clist = player->inventory.getList("craft");
2923 if (!clist || clist->getSize() == 0)
2924 return;
2926 if (!clist->checkModified())
2927 return;
2929 // Get a preview for crafting
2930 ItemStack preview;
2931 InventoryLocation loc;
2932 loc.setPlayer(player->getName());
2933 std::vector<ItemStack> output_replacements;
2934 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2935 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2936 clist, loc);
2938 InventoryList *plist = player->inventory.getList("craftpreview");
2939 if (plist && plist->getSize() >= 1) {
2940 // Put the new preview in
2941 plist->changeItem(0, preview);
2945 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2947 if (evt->type == CET_NICK_ADD) {
2948 // The terminal informed us of its nick choice
2949 m_admin_nick = ((ChatEventNick *)evt)->nick;
2950 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2951 errorstream << "You haven't set up an account." << std::endl
2952 << "Please log in using the client as '"
2953 << m_admin_nick << "' with a secure password." << std::endl
2954 << "Until then, you can't execute admin tasks via the console," << std::endl
2955 << "and everybody can claim the user account instead of you," << std::endl
2956 << "giving them full control over this server." << std::endl;
2958 } else {
2959 assert(evt->type == CET_CHAT);
2960 handleAdminChat((ChatEventChat *)evt);
2964 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2965 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2967 // If something goes wrong, this player is to blame
2968 RollbackScopeActor rollback_scope(m_rollback,
2969 std::string("player:") + name);
2971 if (g_settings->getBool("strip_color_codes"))
2972 wmessage = unescape_enriched(wmessage);
2974 if (player) {
2975 switch (player->canSendChatMessage()) {
2976 case RPLAYER_CHATRESULT_FLOODING: {
2977 std::wstringstream ws;
2978 ws << L"You cannot send more messages. You are limited to "
2979 << g_settings->getFloat("chat_message_limit_per_10sec")
2980 << L" messages per 10 seconds.";
2981 return ws.str();
2983 case RPLAYER_CHATRESULT_KICK:
2984 DenyAccess_Legacy(player->getPeerId(),
2985 L"You have been kicked due to message flooding.");
2986 return L"";
2987 case RPLAYER_CHATRESULT_OK:
2988 break;
2989 default:
2990 FATAL_ERROR("Unhandled chat filtering result found.");
2994 if (m_max_chatmessage_length > 0
2995 && wmessage.length() > m_max_chatmessage_length) {
2996 return L"Your message exceed the maximum chat message limit set on the server. "
2997 L"It was refused. Send a shorter message";
3000 auto message = trim(wide_to_utf8(wmessage));
3001 if (message.find_first_of("\n\r") != std::wstring::npos) {
3002 return L"New lines are not permitted in chat messages";
3005 // Run script hook, exit if script ate the chat message
3006 if (m_script->on_chat_message(name, message))
3007 return L"";
3009 // Line to send
3010 std::wstring line;
3011 // Whether to send line to the player that sent the message, or to all players
3012 bool broadcast_line = true;
3014 if (check_shout_priv && !checkPriv(name, "shout")) {
3015 line += L"-!- You don't have permission to shout.";
3016 broadcast_line = false;
3017 } else {
3019 Workaround for fixing chat on Android. Lua doesn't handle
3020 the Cyrillic alphabet and some characters on older Android devices
3022 #ifdef __ANDROID__
3023 line += L"<" + wname + L"> " + wmessage;
3024 #else
3025 line += narrow_to_wide(m_script->formatChatMessage(name,
3026 wide_to_narrow(wmessage)));
3027 #endif
3031 Tell calling method to send the message to sender
3033 if (!broadcast_line)
3034 return line;
3037 Send the message to others
3039 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
3041 std::vector<session_t> clients = m_clients.getClientIDs();
3044 Send the message back to the inital sender
3045 if they are using protocol version >= 29
3048 session_t peer_id_to_avoid_sending =
3049 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
3051 if (player && player->protocol_version >= 29)
3052 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
3054 for (u16 cid : clients) {
3055 if (cid != peer_id_to_avoid_sending)
3056 SendChatMessage(cid, ChatMessage(line));
3058 return L"";
3061 void Server::handleAdminChat(const ChatEventChat *evt)
3063 std::string name = evt->nick;
3064 std::wstring wname = utf8_to_wide(name);
3065 std::wstring wmessage = evt->evt_msg;
3067 std::wstring answer = handleChat(name, wname, wmessage);
3069 // If asked to send answer to sender
3070 if (!answer.empty()) {
3071 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
3075 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
3077 RemoteClient *client = getClientNoEx(peer_id,state_min);
3078 if(!client)
3079 throw ClientNotFoundException("Client not found");
3081 return client;
3083 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
3085 return m_clients.getClientNoEx(peer_id, state_min);
3088 std::string Server::getPlayerName(session_t peer_id)
3090 RemotePlayer *player = m_env->getPlayer(peer_id);
3091 if (!player)
3092 return "[id="+itos(peer_id)+"]";
3093 return player->getName();
3096 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
3098 RemotePlayer *player = m_env->getPlayer(peer_id);
3099 if (!player)
3100 return NULL;
3101 return player->getPlayerSAO();
3104 std::wstring Server::getStatusString()
3106 std::wostringstream os(std::ios_base::binary);
3107 os << L"# Server: ";
3108 // Version
3109 os << L"version=" << narrow_to_wide(g_version_string);
3110 // Uptime
3111 os << L", uptime=" << m_uptime_counter->get();
3112 // Max lag estimate
3113 os << L", max_lag=" << (m_env ? m_env->getMaxLagEstimate() : 0);
3115 // Information about clients
3116 bool first = true;
3117 os << L", clients={";
3118 if (m_env) {
3119 std::vector<session_t> clients = m_clients.getClientIDs();
3120 for (session_t client_id : clients) {
3121 RemotePlayer *player = m_env->getPlayer(client_id);
3123 // Get name of player
3124 std::wstring name = L"unknown";
3125 if (player)
3126 name = narrow_to_wide(player->getName());
3128 // Add name to information string
3129 if (!first)
3130 os << L", ";
3131 else
3132 first = false;
3134 os << name;
3137 os << L"}";
3139 if (m_env && !((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
3140 os << std::endl << L"# Server: " << " WARNING: Map saving is disabled.";
3142 if (!g_settings->get("motd").empty())
3143 os << std::endl << L"# Server: " << narrow_to_wide(g_settings->get("motd"));
3145 return os.str();
3148 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
3150 std::set<std::string> privs;
3151 m_script->getAuth(name, NULL, &privs);
3152 return privs;
3155 bool Server::checkPriv(const std::string &name, const std::string &priv)
3157 std::set<std::string> privs = getPlayerEffectivePrivs(name);
3158 return (privs.count(priv) != 0);
3161 void Server::reportPrivsModified(const std::string &name)
3163 if (name.empty()) {
3164 std::vector<session_t> clients = m_clients.getClientIDs();
3165 for (const session_t client_id : clients) {
3166 RemotePlayer *player = m_env->getPlayer(client_id);
3167 reportPrivsModified(player->getName());
3169 } else {
3170 RemotePlayer *player = m_env->getPlayer(name.c_str());
3171 if (!player)
3172 return;
3173 SendPlayerPrivileges(player->getPeerId());
3174 PlayerSAO *sao = player->getPlayerSAO();
3175 if(!sao)
3176 return;
3177 sao->updatePrivileges(
3178 getPlayerEffectivePrivs(name),
3179 isSingleplayer());
3183 void Server::reportInventoryFormspecModified(const std::string &name)
3185 RemotePlayer *player = m_env->getPlayer(name.c_str());
3186 if (!player)
3187 return;
3188 SendPlayerInventoryFormspec(player->getPeerId());
3191 void Server::reportFormspecPrependModified(const std::string &name)
3193 RemotePlayer *player = m_env->getPlayer(name.c_str());
3194 if (!player)
3195 return;
3196 SendPlayerFormspecPrepend(player->getPeerId());
3199 void Server::setIpBanned(const std::string &ip, const std::string &name)
3201 m_banmanager->add(ip, name);
3204 void Server::unsetIpBanned(const std::string &ip_or_name)
3206 m_banmanager->remove(ip_or_name);
3209 std::string Server::getBanDescription(const std::string &ip_or_name)
3211 return m_banmanager->getBanDescription(ip_or_name);
3214 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3216 // m_env will be NULL if the server is initializing
3217 if (!m_env)
3218 return;
3220 if (m_admin_nick == name && !m_admin_nick.empty()) {
3221 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3224 RemotePlayer *player = m_env->getPlayer(name);
3225 if (!player) {
3226 return;
3229 if (player->getPeerId() == PEER_ID_INEXISTENT)
3230 return;
3232 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3235 bool Server::showFormspec(const char *playername, const std::string &formspec,
3236 const std::string &formname)
3238 // m_env will be NULL if the server is initializing
3239 if (!m_env)
3240 return false;
3242 RemotePlayer *player = m_env->getPlayer(playername);
3243 if (!player)
3244 return false;
3246 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3247 return true;
3250 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3252 if (!player)
3253 return -1;
3255 u32 id = player->addHud(form);
3257 SendHUDAdd(player->getPeerId(), id, form);
3259 return id;
3262 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3263 if (!player)
3264 return false;
3266 HudElement* todel = player->removeHud(id);
3268 if (!todel)
3269 return false;
3271 delete todel;
3273 SendHUDRemove(player->getPeerId(), id);
3274 return true;
3277 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3279 if (!player)
3280 return false;
3282 SendHUDChange(player->getPeerId(), id, stat, data);
3283 return true;
3286 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3288 if (!player)
3289 return false;
3291 SendHUDSetFlags(player->getPeerId(), flags, mask);
3292 player->hud_flags &= ~mask;
3293 player->hud_flags |= flags;
3295 PlayerSAO* playersao = player->getPlayerSAO();
3297 if (!playersao)
3298 return false;
3300 m_script->player_event(playersao, "hud_changed");
3301 return true;
3304 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3306 if (!player)
3307 return false;
3309 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3310 return false;
3312 player->setHotbarItemcount(hotbar_itemcount);
3313 std::ostringstream os(std::ios::binary);
3314 writeS32(os, hotbar_itemcount);
3315 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3316 return true;
3319 void Server::hudSetHotbarImage(RemotePlayer *player, const std::string &name)
3321 if (!player)
3322 return;
3324 player->setHotbarImage(name);
3325 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3328 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name)
3330 if (!player)
3331 return;
3333 player->setHotbarSelectedImage(name);
3334 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3337 Address Server::getPeerAddress(session_t peer_id)
3339 return m_con->GetPeerAddress(peer_id);
3342 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3343 v2s32 animation_frames[4], f32 frame_speed)
3345 sanity_check(player);
3346 player->setLocalAnimations(animation_frames, frame_speed);
3347 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3350 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3352 sanity_check(player);
3353 player->eye_offset_first = first;
3354 player->eye_offset_third = third;
3355 SendEyeOffset(player->getPeerId(), first, third);
3358 void Server::setSky(RemotePlayer *player, const SkyboxParams &params)
3360 sanity_check(player);
3361 player->setSky(params);
3362 SendSetSky(player->getPeerId(), params);
3365 void Server::setSun(RemotePlayer *player, const SunParams &params)
3367 sanity_check(player);
3368 player->setSun(params);
3369 SendSetSun(player->getPeerId(), params);
3372 void Server::setMoon(RemotePlayer *player, const MoonParams &params)
3374 sanity_check(player);
3375 player->setMoon(params);
3376 SendSetMoon(player->getPeerId(), params);
3379 void Server::setStars(RemotePlayer *player, const StarParams &params)
3381 sanity_check(player);
3382 player->setStars(params);
3383 SendSetStars(player->getPeerId(), params);
3386 void Server::setClouds(RemotePlayer *player, const CloudParams &params)
3388 sanity_check(player);
3389 player->setCloudParams(params);
3390 SendCloudParams(player->getPeerId(), params);
3393 void Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3394 float ratio)
3396 sanity_check(player);
3397 player->overrideDayNightRatio(do_override, ratio);
3398 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3401 void Server::notifyPlayers(const std::wstring &msg)
3403 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3406 void Server::spawnParticle(const std::string &playername,
3407 const ParticleParameters &p)
3409 // m_env will be NULL if the server is initializing
3410 if (!m_env)
3411 return;
3413 session_t peer_id = PEER_ID_INEXISTENT;
3414 u16 proto_ver = 0;
3415 if (!playername.empty()) {
3416 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3417 if (!player)
3418 return;
3419 peer_id = player->getPeerId();
3420 proto_ver = player->protocol_version;
3423 SendSpawnParticle(peer_id, proto_ver, p);
3426 u32 Server::addParticleSpawner(const ParticleSpawnerParameters &p,
3427 ServerActiveObject *attached, const std::string &playername)
3429 // m_env will be NULL if the server is initializing
3430 if (!m_env)
3431 return -1;
3433 session_t peer_id = PEER_ID_INEXISTENT;
3434 u16 proto_ver = 0;
3435 if (!playername.empty()) {
3436 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3437 if (!player)
3438 return -1;
3439 peer_id = player->getPeerId();
3440 proto_ver = player->protocol_version;
3443 u16 attached_id = attached ? attached->getId() : 0;
3445 u32 id;
3446 if (attached_id == 0)
3447 id = m_env->addParticleSpawner(p.time);
3448 else
3449 id = m_env->addParticleSpawner(p.time, attached_id);
3451 SendAddParticleSpawner(peer_id, proto_ver, p, attached_id, id);
3452 return id;
3455 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3457 // m_env will be NULL if the server is initializing
3458 if (!m_env)
3459 throw ServerError("Can't delete particle spawners during initialisation!");
3461 session_t peer_id = PEER_ID_INEXISTENT;
3462 if (!playername.empty()) {
3463 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3464 if (!player)
3465 return;
3466 peer_id = player->getPeerId();
3469 m_env->deleteParticleSpawner(id);
3470 SendDeleteParticleSpawner(peer_id, id);
3473 bool Server::dynamicAddMedia(const std::string &filepath)
3475 std::string filename = fs::GetFilenameFromPath(filepath.c_str());
3476 if (m_media.find(filename) != m_media.end()) {
3477 errorstream << "Server::dynamicAddMedia(): file \"" << filename
3478 << "\" already exists in media cache" << std::endl;
3479 return false;
3482 // Load the file and add it to our media cache
3483 std::string filedata, raw_hash;
3484 bool ok = addMediaFile(filename, filepath, &filedata, &raw_hash);
3485 if (!ok)
3486 return false;
3488 // Push file to existing clients
3489 NetworkPacket pkt(TOCLIENT_MEDIA_PUSH, 0);
3490 pkt << raw_hash << filename << (bool) true;
3491 pkt.putLongString(filedata);
3493 auto client_ids = m_clients.getClientIDs(CS_DefinitionsSent);
3494 for (session_t client_id : client_ids) {
3496 The network layer only guarantees ordered delivery inside a channel.
3497 Since the very next packet could be one that uses the media, we have
3498 to push the media over ALL channels to ensure it is processed before
3499 it is used.
3500 In practice this means we have to send it twice:
3501 - channel 1 (HUD)
3502 - channel 0 (everything else: e.g. play_sound, object messages)
3504 m_clients.send(client_id, 1, &pkt, true);
3505 m_clients.send(client_id, 0, &pkt, true);
3508 return true;
3511 // actions: time-reversed list
3512 // Return value: success/failure
3513 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3514 std::list<std::string> *log)
3516 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3517 ServerMap *map = (ServerMap*)(&m_env->getMap());
3519 // Fail if no actions to handle
3520 if (actions.empty()) {
3521 assert(log);
3522 log->push_back("Nothing to do.");
3523 return false;
3526 int num_tried = 0;
3527 int num_failed = 0;
3529 for (const RollbackAction &action : actions) {
3530 num_tried++;
3531 bool success = action.applyRevert(map, m_inventory_mgr.get(), this);
3532 if(!success){
3533 num_failed++;
3534 std::ostringstream os;
3535 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3536 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3537 if (log)
3538 log->push_back(os.str());
3539 }else{
3540 std::ostringstream os;
3541 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3542 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3543 if (log)
3544 log->push_back(os.str());
3548 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3549 <<" failed"<<std::endl;
3551 // Call it done if less than half failed
3552 return num_failed <= num_tried/2;
3555 // IGameDef interface
3556 // Under envlock
3557 IItemDefManager *Server::getItemDefManager()
3559 return m_itemdef;
3562 const NodeDefManager *Server::getNodeDefManager()
3564 return m_nodedef;
3567 ICraftDefManager *Server::getCraftDefManager()
3569 return m_craftdef;
3572 u16 Server::allocateUnknownNodeId(const std::string &name)
3574 return m_nodedef->allocateDummy(name);
3577 IWritableItemDefManager *Server::getWritableItemDefManager()
3579 return m_itemdef;
3582 NodeDefManager *Server::getWritableNodeDefManager()
3584 return m_nodedef;
3587 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3589 return m_craftdef;
3592 const std::vector<ModSpec> & Server::getMods() const
3594 return m_modmgr->getMods();
3597 const ModSpec *Server::getModSpec(const std::string &modname) const
3599 return m_modmgr->getModSpec(modname);
3602 void Server::getModNames(std::vector<std::string> &modlist)
3604 m_modmgr->getModNames(modlist);
3607 std::string Server::getBuiltinLuaPath()
3609 return porting::path_share + DIR_DELIM + "builtin";
3612 std::string Server::getModStoragePath() const
3614 return m_path_world + DIR_DELIM + "mod_storage";
3617 v3f Server::findSpawnPos()
3619 ServerMap &map = m_env->getServerMap();
3620 v3f nodeposf;
3621 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
3622 return nodeposf * BS;
3624 bool is_good = false;
3625 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3626 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3628 // Try to find a good place a few times
3629 for (s32 i = 0; i < 4000 && !is_good; i++) {
3630 s32 range = MYMIN(1 + i, range_max);
3631 // We're going to try to throw the player to this position
3632 v2s16 nodepos2d = v2s16(
3633 -range + (myrand() % (range * 2)),
3634 -range + (myrand() % (range * 2)));
3635 // Get spawn level at point
3636 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3637 // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
3638 // signify an unsuitable spawn position, or if outside limits.
3639 if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
3640 spawn_level <= -MAX_MAP_GENERATION_LIMIT)
3641 continue;
3643 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3644 // Consecutive empty nodes
3645 s32 air_count = 0;
3647 // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
3648 // avoid obstructions in already-generated mapblocks.
3649 // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
3650 // no obstructions, but mapgen decorations are generated after spawn so
3651 // the player may end up inside one.
3652 for (s32 i = 0; i < 8; i++) {
3653 v3s16 blockpos = getNodeBlockPos(nodepos);
3654 map.emergeBlock(blockpos, true);
3655 content_t c = map.getNode(nodepos).getContent();
3657 // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
3658 // In ungenerated mapblocks allow spawn in 'ignore' nodes.
3659 if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
3660 air_count++;
3661 if (air_count >= 2) {
3662 // Spawn in lower empty node
3663 nodepos.Y--;
3664 nodeposf = intToFloat(nodepos, BS);
3665 // Don't spawn the player outside map boundaries
3666 if (objectpos_over_limit(nodeposf))
3667 // Exit this loop, positions above are probably over limit
3668 break;
3670 // Good position found, cause an exit from main loop
3671 is_good = true;
3672 break;
3674 } else {
3675 air_count = 0;
3677 nodepos.Y++;
3681 if (is_good)
3682 return nodeposf;
3684 // No suitable spawn point found, return fallback 0,0,0
3685 return v3f(0.0f, 0.0f, 0.0f);
3688 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3690 if (delay == 0.0f) {
3691 // No delay, shutdown immediately
3692 m_shutdown_state.is_requested = true;
3693 // only print to the infostream, a chat message saying
3694 // "Server Shutting Down" is sent when the server destructs.
3695 infostream << "*** Immediate Server shutdown requested." << std::endl;
3696 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3697 // Negative delay, cancel shutdown if requested
3698 m_shutdown_state.reset();
3699 std::wstringstream ws;
3701 ws << L"*** Server shutdown canceled.";
3703 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3704 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3705 // m_shutdown_* are already handled, skip.
3706 return;
3707 } else if (delay > 0.0f) {
3708 // Positive delay, tell the clients when the server will shut down
3709 std::wstringstream ws;
3711 ws << L"*** Server shutting down in "
3712 << duration_to_string(myround(delay)).c_str()
3713 << ".";
3715 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3716 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3719 m_shutdown_state.trigger(delay, msg, reconnect);
3722 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3725 Try to get an existing player
3727 RemotePlayer *player = m_env->getPlayer(name);
3729 // If player is already connected, cancel
3730 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3731 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3732 return NULL;
3736 If player with the wanted peer_id already exists, cancel.
3738 if (m_env->getPlayer(peer_id)) {
3739 infostream<<"emergePlayer(): Player with wrong name but same"
3740 " peer_id already exists"<<std::endl;
3741 return NULL;
3744 if (!player) {
3745 player = new RemotePlayer(name, idef());
3748 bool newplayer = false;
3750 // Load player
3751 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3753 // Complete init with server parts
3754 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3755 player->protocol_version = proto_version;
3757 /* Run scripts */
3758 if (newplayer) {
3759 m_script->on_newplayer(playersao);
3762 return playersao;
3765 bool Server::registerModStorage(ModMetadata *storage)
3767 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3768 errorstream << "Unable to register same mod storage twice. Storage name: "
3769 << storage->getModName() << std::endl;
3770 return false;
3773 m_mod_storages[storage->getModName()] = storage;
3774 return true;
3777 void Server::unregisterModStorage(const std::string &name)
3779 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3780 if (it != m_mod_storages.end()) {
3781 // Save unconditionaly on unregistration
3782 it->second->save(getModStoragePath());
3783 m_mod_storages.erase(name);
3787 void dedicated_server_loop(Server &server, bool &kill)
3789 verbosestream<<"dedicated_server_loop()"<<std::endl;
3791 IntervalLimiter m_profiler_interval;
3793 static thread_local const float steplen =
3794 g_settings->getFloat("dedicated_server_step");
3795 static thread_local const float profiler_print_interval =
3796 g_settings->getFloat("profiler_print_interval");
3799 * The dedicated server loop only does time-keeping (in Server::step) and
3800 * provides a way to main.cpp to kill the server externally (bool &kill).
3803 for(;;) {
3804 // This is kind of a hack but can be done like this
3805 // because server.step() is very light
3806 sleep_ms((int)(steplen*1000.0));
3807 server.step(steplen);
3809 if (server.isShutdownRequested() || kill)
3810 break;
3813 Profiler
3815 if (profiler_print_interval != 0) {
3816 if(m_profiler_interval.step(steplen, profiler_print_interval))
3818 infostream<<"Profiler:"<<std::endl;
3819 g_profiler->print(infostream);
3820 g_profiler->clear();
3825 infostream << "Dedicated server quitting" << std::endl;
3826 #if USE_CURL
3827 if (g_settings->getBool("server_announce"))
3828 ServerList::sendAnnounce(ServerList::AA_DELETE,
3829 server.m_bind_addr.getPort());
3830 #endif
3834 * Mod channels
3838 bool Server::joinModChannel(const std::string &channel)
3840 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3841 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3844 bool Server::leaveModChannel(const std::string &channel)
3846 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3849 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3851 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3852 return false;
3854 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3855 return true;
3858 ModChannel* Server::getModChannel(const std::string &channel)
3860 return m_modchannel_mgr->getModChannel(channel);
3863 void Server::broadcastModChannelMessage(const std::string &channel,
3864 const std::string &message, session_t from_peer)
3866 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3867 if (peers.empty())
3868 return;
3870 if (message.size() > STRING_MAX_LEN) {
3871 warningstream << "ModChannel message too long, dropping before sending "
3872 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3873 << channel << ")" << std::endl;
3874 return;
3877 std::string sender;
3878 if (from_peer != PEER_ID_SERVER) {
3879 sender = getPlayerName(from_peer);
3882 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3883 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3884 resp_pkt << channel << sender << message;
3885 for (session_t peer_id : peers) {
3886 // Ignore sender
3887 if (peer_id == from_peer)
3888 continue;
3890 Send(peer_id, &resp_pkt);
3893 if (from_peer != PEER_ID_SERVER) {
3894 m_script->on_modchannel_message(channel, sender, message);
3898 Translations *Server::getTranslationLanguage(const std::string &lang_code)
3900 if (lang_code.empty())
3901 return nullptr;
3903 auto it = server_translations.find(lang_code);
3904 if (it != server_translations.end())
3905 return &it->second; // Already loaded
3907 // [] will create an entry
3908 auto *translations = &server_translations[lang_code];
3910 std::string suffix = "." + lang_code + ".tr";
3911 for (const auto &i : m_media) {
3912 if (str_ends_with(i.first, suffix)) {
3913 std::string data;
3914 if (fs::ReadFile(i.second.path, data)) {
3915 translations->loadTranslation(data);
3920 return translations;