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.
24 #include "network/connection.h"
25 #include "network/networkprotocol.h"
26 #include "network/serveropcodes.h"
28 #include "environment.h"
30 #include "threading/mutex_auto_lock.h"
31 #include "constants.h"
37 #include "serverobject.h"
38 #include "genericobject.h"
42 #include "scripting_server.h"
47 #include "mapgen/mapgen.h"
48 #include "mapgen/mg_biome.h"
49 #include "content_mapnode.h"
50 #include "content_nodemeta.h"
51 #include "content_sao.h"
52 #include "content/mods.h"
53 #include "modchannels.h"
54 #include "serverlist.h"
55 #include "util/string.h"
57 #include "util/serialize.h"
58 #include "util/thread.h"
59 #include "defaultsettings.h"
60 #include "server/mods.h"
61 #include "util/base64.h"
62 #include "util/sha1.h"
64 #include "database/database.h"
65 #include "chatmessage.h"
66 #include "chat_interface.h"
67 #include "remoteplayer.h"
69 class ClientNotFoundException : public BaseException
72 ClientNotFoundException(const char *s):
77 class ServerThread : public Thread
81 ServerThread(Server *server):
92 void *ServerThread::run()
94 BEGIN_DEBUG_EXCEPTION_HANDLER
96 m_server->AsyncRunStep(true);
98 while (!stopRequested()) {
100 m_server->AsyncRunStep();
104 } catch (con::NoIncomingDataException &e) {
105 } catch (con::PeerNotFoundException &e) {
106 infostream<<"Server: PeerNotFoundException"<<std::endl;
107 } catch (ClientNotFoundException &e) {
108 } catch (con::ConnectionBindFailed &e) {
109 m_server->setAsyncFatalError(e.what());
110 } catch (LuaError &e) {
111 m_server->setAsyncFatalError(
112 "ServerThread::run Lua: " + std::string(e.what()));
116 END_DEBUG_EXCEPTION_HANDLER
121 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
123 if(pos_exists) *pos_exists = false;
128 if(pos_exists) *pos_exists = true;
133 ServerActiveObject *sao = env->getActiveObject(object);
136 if(pos_exists) *pos_exists = true;
137 return sao->getBasePosition(); }
149 const std::string &path_world,
150 const SubgameSpec &gamespec,
151 bool simple_singleplayer_mode,
156 m_bind_addr(bind_addr),
157 m_path_world(path_world),
158 m_gamespec(gamespec),
159 m_simple_singleplayer_mode(simple_singleplayer_mode),
160 m_dedicated(dedicated),
161 m_async_fatal_error(""),
162 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
165 m_bind_addr.isIPv6(),
167 m_itemdef(createItemDefManager()),
168 m_nodedef(createNodeDefManager()),
169 m_craftdef(createCraftDefManager()),
173 m_modchannel_mgr(new ModChannelMgr())
175 m_lag = g_settings->getFloat("dedicated_server_step");
177 if (path_world.empty())
178 throw ServerError("Supplied empty world path");
180 if(!gamespec.isValid())
181 throw ServerError("Supplied invalid gamespec");
183 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
184 if(m_simple_singleplayer_mode)
185 infostream<<" in simple singleplayer mode"<<std::endl;
187 infostream<<std::endl;
188 infostream<<"- world: "<<m_path_world<<std::endl;
189 infostream<<"- game: "<<m_gamespec.path<<std::endl;
191 // Create world if it doesn't exist
192 if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
193 throw ServerError("Failed to initialize world");
195 // Create server thread
196 m_thread = new ServerThread(this);
198 // Create emerge manager
199 m_emerge = new EmergeManager(this);
201 // Create ban manager
202 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
203 m_banmanager = new BanManager(ban_path);
205 m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager(
207 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
208 // complain about mods with unsatisfied dependencies
209 if (!m_modmgr->isConsistent()) {
210 m_modmgr->printUnsatisfiedModsError();
214 MutexAutoLock envlock(m_env_mutex);
216 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
217 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
219 // Initialize scripting
220 infostream<<"Server: Initializing Lua"<<std::endl;
222 m_script = new ServerScripting(this);
224 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
226 m_modmgr->loadMods(m_script);
228 // Read Textures and calculate sha1 sums
231 // Apply item aliases in the node definition manager
232 m_nodedef->updateAliases(m_itemdef);
234 // Apply texture overrides from texturepack/override.txt
235 std::vector<std::string> paths;
236 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
237 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
238 for (const std::string &path : paths)
239 m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
241 m_nodedef->setNodeRegistrationStatus(true);
243 // Perform pending node name resolutions
244 m_nodedef->runNodeResolveCallbacks();
246 // unmap node names for connected nodeboxes
247 m_nodedef->mapNodeboxConnections();
249 // init the recipe hashes to speed up crafting
250 m_craftdef->initHashes(this);
252 // Initialize Environment
253 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
255 m_clients.setEnv(m_env);
257 if (!servermap->settings_mgr.makeMapgenParams())
258 FATAL_ERROR("Couldn't create any mapgen type");
260 // Initialize mapgens
261 m_emerge->initMapgens(servermap->getMapgenParams());
263 if (g_settings->getBool("enable_rollback_recording")) {
264 // Create rollback manager
265 m_rollback = new RollbackManager(m_path_world, this);
268 // Give environment reference to scripting api
269 m_script->initializeEnvironment(m_env);
271 // Register us to receive map edit events
272 servermap->addEventReceiver(this);
276 m_liquid_transform_every = g_settings->getFloat("liquid_update");
277 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
278 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
279 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
284 infostream << "Server destructing" << std::endl;
286 // Send shutdown message
287 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
288 L"*** Server shutting down"));
291 MutexAutoLock envlock(m_env_mutex);
293 infostream << "Server: Saving players" << std::endl;
294 m_env->saveLoadedPlayers();
296 infostream << "Server: Kicking players" << std::endl;
297 std::string kick_msg;
298 bool reconnect = false;
299 if (getShutdownRequested()) {
300 reconnect = m_shutdown_ask_reconnect;
301 kick_msg = m_shutdown_msg;
303 if (kick_msg.empty()) {
304 kick_msg = g_settings->get("kick_msg_shutdown");
306 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
307 kick_msg, reconnect);
310 // Do this before stopping the server in case mapgen callbacks need to access
311 // server-controlled resources (like ModStorages). Also do them before
312 // shutdown callbacks since they may modify state that is finalized in a
314 m_emerge->stopThreads();
317 MutexAutoLock envlock(m_env_mutex);
319 // Execute script shutdown hooks
320 infostream << "Executing shutdown hooks" << std::endl;
321 m_script->on_shutdown();
323 infostream << "Server: Saving environment metadata" << std::endl;
331 // Delete things in the reverse order of creation
340 // Deinitialize scripting
341 infostream << "Server: Deinitializing scripting" << std::endl;
344 // Delete detached inventories
345 for (auto &detached_inventory : m_detached_inventories) {
346 delete detached_inventory.second;
352 infostream << "Starting server on " << m_bind_addr.serializeString()
353 << "..." << std::endl;
355 // Stop thread if already running
358 // Initialize connection
359 m_con->SetTimeoutMs(30);
360 m_con->Serve(m_bind_addr);
365 // ASCII art for the win!
367 << " .__ __ __ " << std::endl
368 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
369 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
370 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
371 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
372 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
373 actionstream << "World at [" << m_path_world << "]" << std::endl;
374 actionstream << "Server for gameid=\"" << m_gamespec.id
375 << "\" listening on " << m_bind_addr.serializeString() << ":"
376 << m_bind_addr.getPort() << "." << std::endl;
381 infostream<<"Server: Stopping and waiting threads"<<std::endl;
383 // Stop threads (set run=false first so both start stopping)
385 //m_emergethread.setRun(false);
387 //m_emergethread.stop();
389 infostream<<"Server: Threads stopped"<<std::endl;
392 void Server::step(float dtime)
398 MutexAutoLock lock(m_step_dtime_mutex);
399 m_step_dtime += dtime;
401 // Throw if fatal error occurred in thread
402 std::string async_err = m_async_fatal_error.get();
403 if (!async_err.empty()) {
404 if (!m_simple_singleplayer_mode) {
405 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
406 g_settings->get("kick_msg_crash"),
407 g_settings->getBool("ask_reconnect_on_crash"));
409 throw ServerError("AsyncErr: " + async_err);
413 void Server::AsyncRunStep(bool initial_step)
415 g_profiler->add("Server::AsyncRunStep (num)", 1);
419 MutexAutoLock lock1(m_step_dtime_mutex);
420 dtime = m_step_dtime;
424 // Send blocks to clients
428 if((dtime < 0.001) && !initial_step)
431 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
433 //infostream<<"Server steps "<<dtime<<std::endl;
434 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
437 MutexAutoLock lock1(m_step_dtime_mutex);
438 m_step_dtime -= dtime;
445 m_uptime.set(m_uptime.get() + dtime);
451 Update time of day and overall game time
453 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
456 Send to clients at constant intervals
459 m_time_of_day_send_timer -= dtime;
460 if(m_time_of_day_send_timer < 0.0) {
461 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
462 u16 time = m_env->getTimeOfDay();
463 float time_speed = g_settings->getFloat("time_speed");
464 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
468 MutexAutoLock lock(m_env_mutex);
469 // Figure out and report maximum lag to environment
470 float max_lag = m_env->getMaxLagEstimate();
471 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
473 if(dtime > 0.1 && dtime > max_lag * 2.0)
474 infostream<<"Server: Maximum lag peaked to "<<dtime
478 m_env->reportMaxLagEstimate(max_lag);
480 ScopeProfiler sp(g_profiler, "SEnv step");
481 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
485 static const float map_timer_and_unload_dtime = 2.92;
486 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
488 MutexAutoLock lock(m_env_mutex);
489 // Run Map's timers and unload unused data
490 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
491 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
492 g_settings->getFloat("server_unload_unused_data_timeout"),
497 Listen to the admin chat, if available
500 if (!m_admin_chat->command_queue.empty()) {
501 MutexAutoLock lock(m_env_mutex);
502 while (!m_admin_chat->command_queue.empty()) {
503 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
504 handleChatInterfaceEvent(evt);
508 m_admin_chat->outgoing_queue.push_back(
509 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
516 /* Transform liquids */
517 m_liquid_transform_timer += dtime;
518 if(m_liquid_transform_timer >= m_liquid_transform_every)
520 m_liquid_transform_timer -= m_liquid_transform_every;
522 MutexAutoLock lock(m_env_mutex);
524 ScopeProfiler sp(g_profiler, "Server: liquid transform");
526 std::map<v3s16, MapBlock*> modified_blocks;
527 m_env->getMap().transformLiquids(modified_blocks, m_env);
530 Set the modified blocks unsent for all the clients
532 if (!modified_blocks.empty()) {
533 SetBlocksNotSent(modified_blocks);
536 m_clients.step(dtime);
538 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
540 // send masterserver announce
542 float &counter = m_masterserver_timer;
543 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
544 g_settings->getBool("server_announce")) {
545 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
546 ServerList::AA_START,
547 m_bind_addr.getPort(),
548 m_clients.getPlayerNames(),
550 m_env->getGameTime(),
553 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
563 Check added and deleted active objects
566 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
567 MutexAutoLock envlock(m_env_mutex);
570 const RemoteClientMap &clients = m_clients.getClientList();
571 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
573 // Radius inside which objects are active
574 static thread_local const s16 radius =
575 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
577 // Radius inside which players are active
578 static thread_local const bool is_transfer_limited =
579 g_settings->exists("unlimited_player_transfer_distance") &&
580 !g_settings->getBool("unlimited_player_transfer_distance");
581 static thread_local const s16 player_transfer_dist =
582 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
583 s16 player_radius = player_transfer_dist;
584 if (player_radius == 0 && is_transfer_limited)
585 player_radius = radius;
587 for (const auto &client_it : clients) {
588 RemoteClient *client = client_it.second;
590 // If definitions and textures have not been sent, don't
591 // send objects either
592 if (client->getState() < CS_DefinitionsSent)
595 RemotePlayer *player = m_env->getPlayer(client->peer_id);
597 // This can happen if the client timeouts somehow
601 PlayerSAO *playersao = player->getPlayerSAO();
605 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
606 if (my_radius <= 0) my_radius = radius;
607 //infostream << "Server: Active Radius " << my_radius << std::endl;
609 std::queue<u16> removed_objects;
610 std::queue<u16> added_objects;
611 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
612 client->m_known_objects, removed_objects);
613 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
614 client->m_known_objects, added_objects);
616 // Ignore if nothing happened
617 if (removed_objects.empty() && added_objects.empty()) {
621 std::string data_buffer;
625 // Handle removed objects
626 writeU16((u8*)buf, removed_objects.size());
627 data_buffer.append(buf, 2);
628 while (!removed_objects.empty()) {
630 u16 id = removed_objects.front();
631 ServerActiveObject* obj = m_env->getActiveObject(id);
633 // Add to data buffer for sending
634 writeU16((u8*)buf, id);
635 data_buffer.append(buf, 2);
637 // Remove from known objects
638 client->m_known_objects.erase(id);
640 if(obj && obj->m_known_by_count > 0)
641 obj->m_known_by_count--;
642 removed_objects.pop();
645 // Handle added objects
646 writeU16((u8*)buf, added_objects.size());
647 data_buffer.append(buf, 2);
648 while (!added_objects.empty()) {
650 u16 id = added_objects.front();
651 ServerActiveObject* obj = m_env->getActiveObject(id);
654 u8 type = ACTIVEOBJECT_TYPE_INVALID;
656 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
658 type = obj->getSendType();
660 // Add to data buffer for sending
661 writeU16((u8*)buf, id);
662 data_buffer.append(buf, 2);
663 writeU8((u8*)buf, type);
664 data_buffer.append(buf, 1);
667 data_buffer.append(serializeLongString(
668 obj->getClientInitializationData(client->net_proto_version)));
670 data_buffer.append(serializeLongString(""));
672 // Add to known objects
673 client->m_known_objects.insert(id);
676 obj->m_known_by_count++;
681 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
682 verbosestream << "Server: Sent object remove/add: "
683 << removed_objects.size() << " removed, "
684 << added_objects.size() << " added, "
685 << "packet size is " << pktSize << std::endl;
689 m_mod_storage_save_timer -= dtime;
690 if (m_mod_storage_save_timer <= 0.0f) {
691 infostream << "Saving registered mod storages." << std::endl;
692 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
693 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
694 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
695 if (it->second->isModified()) {
696 it->second->save(getModStoragePath());
706 MutexAutoLock envlock(m_env_mutex);
707 ScopeProfiler sp(g_profiler, "Server: sending object messages");
710 // Value = data sent by object
711 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
713 // Get active object messages from environment
715 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
719 std::vector<ActiveObjectMessage>* message_list = nullptr;
720 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
721 n = buffered_messages.find(aom.id);
722 if (n == buffered_messages.end()) {
723 message_list = new std::vector<ActiveObjectMessage>;
724 buffered_messages[aom.id] = message_list;
727 message_list = n->second;
729 message_list->push_back(aom);
733 const RemoteClientMap &clients = m_clients.getClientList();
734 // Route data to every client
735 for (const auto &client_it : clients) {
736 RemoteClient *client = client_it.second;
737 std::string reliable_data;
738 std::string unreliable_data;
739 // Go through all objects in message buffer
740 for (const auto &buffered_message : buffered_messages) {
741 // If object is not known by client, skip it
742 u16 id = buffered_message.first;
743 if (client->m_known_objects.find(id) == client->m_known_objects.end())
746 // Get message list of object
747 std::vector<ActiveObjectMessage>* list = buffered_message.second;
748 // Go through every message
749 for (const ActiveObjectMessage &aom : *list) {
750 // Compose the full new data with header
751 std::string new_data;
754 writeU16((u8*)&buf[0], aom.id);
755 new_data.append(buf, 2);
757 new_data += serializeString(aom.datastring);
758 // Add data to buffer
760 reliable_data += new_data;
762 unreliable_data += new_data;
766 reliable_data and unreliable_data are now ready.
769 if (!reliable_data.empty()) {
770 SendActiveObjectMessages(client->peer_id, reliable_data);
773 if (!unreliable_data.empty()) {
774 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
779 // Clear buffered_messages
780 for (auto &buffered_message : buffered_messages) {
781 delete buffered_message.second;
786 Send queued-for-sending map edit events.
789 // We will be accessing the environment
790 MutexAutoLock lock(m_env_mutex);
792 // Don't send too many at a time
795 // Single change sending is disabled if queue size is not small
796 bool disable_single_change_sending = false;
797 if(m_unsent_map_edit_queue.size() >= 4)
798 disable_single_change_sending = true;
800 int event_count = m_unsent_map_edit_queue.size();
802 // We'll log the amount of each
805 while (!m_unsent_map_edit_queue.empty()) {
806 MapEditEvent* event = m_unsent_map_edit_queue.front();
807 m_unsent_map_edit_queue.pop();
809 // Players far away from the change are stored here.
810 // Instead of sending the changes, MapBlocks are set not sent
812 std::vector<u16> far_players;
814 switch (event->type) {
817 prof.add("MEET_ADDNODE", 1);
818 sendAddNode(event->p, event->n, event->already_known_by_peer,
819 &far_players, disable_single_change_sending ? 5 : 30,
820 event->type == MEET_ADDNODE);
822 case MEET_REMOVENODE:
823 prof.add("MEET_REMOVENODE", 1);
824 sendRemoveNode(event->p, event->already_known_by_peer,
825 &far_players, disable_single_change_sending ? 5 : 30);
827 case MEET_BLOCK_NODE_METADATA_CHANGED:
828 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
829 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
830 m_clients.markBlockposAsNotSent(event->p);
833 infostream << "Server: MEET_OTHER" << std::endl;
834 prof.add("MEET_OTHER", 1);
835 for (const v3s16 &modified_block : event->modified_blocks) {
836 m_clients.markBlockposAsNotSent(modified_block);
840 prof.add("unknown", 1);
841 warningstream << "Server: Unknown MapEditEvent "
842 << ((u32)event->type) << std::endl;
847 Set blocks not sent to far players
849 if (!far_players.empty()) {
850 // Convert list format to that wanted by SetBlocksNotSent
851 std::map<v3s16, MapBlock*> modified_blocks2;
852 for (const v3s16 &modified_block : event->modified_blocks) {
853 modified_blocks2[modified_block] =
854 m_env->getMap().getBlockNoCreateNoEx(modified_block);
857 // Set blocks not sent
858 for (const u16 far_player : far_players) {
859 if (RemoteClient *client = getClient(far_player))
860 client->SetBlocksNotSent(modified_blocks2);
867 if (event_count >= 5) {
868 infostream << "Server: MapEditEvents:" << std::endl;
869 prof.print(infostream);
870 } else if (event_count != 0) {
871 verbosestream << "Server: MapEditEvents:" << std::endl;
872 prof.print(verbosestream);
878 Trigger emergethread (it somehow gets to a non-triggered but
879 bysy state sometimes)
882 float &counter = m_emergethread_trigger_timer;
884 if (counter >= 2.0) {
887 m_emerge->startThreads();
891 // Save map, players and auth stuff
893 float &counter = m_savemap_timer;
895 static thread_local const float save_interval =
896 g_settings->getFloat("server_map_save_interval");
897 if (counter >= save_interval) {
899 MutexAutoLock lock(m_env_mutex);
901 ScopeProfiler sp(g_profiler, "Server: saving stuff");
904 if (m_banmanager->isModified()) {
905 m_banmanager->save();
908 // Save changed parts of map
909 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
912 m_env->saveLoadedPlayers();
914 // Save environment metadata
920 static const float shutdown_msg_times[] =
922 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
925 if (m_shutdown_timer > 0.0f) {
926 // Automated messages
927 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
928 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
929 // If shutdown timer matches an automessage, shot it
930 if (m_shutdown_timer > shutdown_msg_times[i] &&
931 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
932 std::wstringstream ws;
934 ws << L"*** Server shutting down in "
935 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
938 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
939 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
945 m_shutdown_timer -= dtime;
946 if (m_shutdown_timer < 0.0f) {
947 m_shutdown_timer = 0.0f;
948 m_shutdown_requested = true;
953 void Server::Receive()
958 m_con->Receive(&pkt);
959 peer_id = pkt.getPeerId();
961 } catch (const con::InvalidIncomingDataException &e) {
962 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
963 << e.what() << std::endl;
964 } catch (const SerializationError &e) {
965 infostream << "Server::Receive(): SerializationError: what()="
966 << e.what() << std::endl;
967 } catch (const ClientStateError &e) {
968 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
969 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
970 L"Try reconnecting or updating your client");
971 } catch (const con::PeerNotFoundException &e) {
976 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
978 std::string playername;
979 PlayerSAO *playersao = NULL;
982 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
984 playername = client->getName();
985 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
987 } catch (std::exception &e) {
993 RemotePlayer *player = m_env->getPlayer(playername.c_str());
996 if (!playersao || !player) {
997 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
998 actionstream << "Server: Failed to emerge player \"" << playername
999 << "\" (player allocated to an another client)" << std::endl;
1000 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1001 L"name. If your client closed unexpectedly, try again in "
1004 errorstream << "Server: " << playername << ": Failed to emerge player"
1006 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1012 Send complete position information
1014 SendMovePlayer(peer_id);
1017 SendPlayerPrivileges(peer_id);
1019 // Send inventory formspec
1020 SendPlayerInventoryFormspec(peer_id);
1023 SendInventory(playersao);
1025 // Send HP or death screen
1026 if (playersao->isDead())
1027 SendDeathscreen(peer_id, false, v3f(0,0,0));
1029 SendPlayerHPOrDie(playersao,
1030 PlayerHPChangeReason(PlayerHPChangeReason::SET_HP));
1033 SendPlayerBreath(playersao);
1035 // Note things in chat if not in simple singleplayer mode
1036 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1037 // Send information about server to player in chat
1038 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1040 Address addr = getPeerAddress(player->getPeerId());
1041 std::string ip_str = addr.serializeString();
1042 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1047 const std::vector<std::string> &names = m_clients.getPlayerNames();
1049 actionstream << player->getName() << " joins game. List of players: ";
1051 for (const std::string &name : names) {
1052 actionstream << name << " ";
1055 actionstream << player->getName() <<std::endl;
1060 inline void Server::handleCommand(NetworkPacket* pkt)
1062 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1063 (this->*opHandle.handler)(pkt);
1066 void Server::ProcessData(NetworkPacket *pkt)
1068 // Environment is locked first.
1069 MutexAutoLock envlock(m_env_mutex);
1071 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1072 u32 peer_id = pkt->getPeerId();
1075 Address address = getPeerAddress(peer_id);
1076 std::string addr_s = address.serializeString();
1078 if(m_banmanager->isIpBanned(addr_s)) {
1079 std::string ban_name = m_banmanager->getBanName(addr_s);
1080 infostream << "Server: A banned client tried to connect from "
1081 << addr_s << "; banned name was "
1082 << ban_name << std::endl;
1083 // This actually doesn't seem to transfer to the client
1084 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1085 + utf8_to_wide(ban_name));
1089 catch(con::PeerNotFoundException &e) {
1091 * no peer for this packet found
1092 * most common reason is peer timeout, e.g. peer didn't
1093 * respond for some time, your server was overloaded or
1096 infostream << "Server::ProcessData(): Canceling: peer "
1097 << peer_id << " not found" << std::endl;
1102 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1104 // Command must be handled into ToServerCommandHandler
1105 if (command >= TOSERVER_NUM_MSG_TYPES) {
1106 infostream << "Server: Ignoring unknown command "
1107 << command << std::endl;
1111 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1116 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1118 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1119 errorstream << "Server::ProcessData(): Cancelling: Peer"
1120 " serialization format invalid or not initialized."
1121 " Skipping incoming command=" << command << std::endl;
1125 /* Handle commands related to client startup */
1126 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1131 if (m_clients.getClientState(peer_id) < CS_Active) {
1132 if (command == TOSERVER_PLAYERPOS) return;
1134 errorstream << "Got packet command: " << command << " for peer id "
1135 << peer_id << " but client isn't active yet. Dropping packet "
1141 } catch (SendFailedException &e) {
1142 errorstream << "Server::ProcessData(): SendFailedException: "
1143 << "what=" << e.what()
1145 } catch (PacketError &e) {
1146 actionstream << "Server::ProcessData(): PacketError: "
1147 << "what=" << e.what()
1152 void Server::setTimeOfDay(u32 time)
1154 m_env->setTimeOfDay(time);
1155 m_time_of_day_send_timer = 0;
1158 void Server::onMapEditEvent(MapEditEvent *event)
1160 if(m_ignore_map_edit_events)
1162 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1164 MapEditEvent *e = event->clone();
1165 m_unsent_map_edit_queue.push(e);
1168 Inventory* Server::getInventory(const InventoryLocation &loc)
1171 case InventoryLocation::UNDEFINED:
1172 case InventoryLocation::CURRENT_PLAYER:
1174 case InventoryLocation::PLAYER:
1176 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1179 PlayerSAO *playersao = player->getPlayerSAO();
1182 return playersao->getInventory();
1185 case InventoryLocation::NODEMETA:
1187 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1190 return meta->getInventory();
1193 case InventoryLocation::DETACHED:
1195 if(m_detached_inventories.count(loc.name) == 0)
1197 return m_detached_inventories[loc.name];
1201 sanity_check(false); // abort
1206 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1209 case InventoryLocation::UNDEFINED:
1211 case InventoryLocation::PLAYER:
1216 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1221 PlayerSAO *playersao = player->getPlayerSAO();
1225 SendInventory(playersao);
1228 case InventoryLocation::NODEMETA:
1230 v3s16 blockpos = getNodeBlockPos(loc.p);
1232 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1234 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1236 m_clients.markBlockposAsNotSent(blockpos);
1239 case InventoryLocation::DETACHED:
1241 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1245 sanity_check(false); // abort
1250 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1252 std::vector<session_t> clients = m_clients.getClientIDs();
1254 // Set the modified blocks unsent for all the clients
1255 for (const session_t client_id : clients) {
1256 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1257 client->SetBlocksNotSent(block);
1262 void Server::peerAdded(con::Peer *peer)
1264 verbosestream<<"Server::peerAdded(): peer->id="
1265 <<peer->id<<std::endl;
1267 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1270 void Server::deletingPeer(con::Peer *peer, bool timeout)
1272 verbosestream<<"Server::deletingPeer(): peer->id="
1273 <<peer->id<<", timeout="<<timeout<<std::endl;
1275 m_clients.event(peer->id, CSE_Disconnect);
1276 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1279 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1281 *retval = m_con->getPeerStat(peer_id,type);
1282 return *retval != -1;
1285 bool Server::getClientInfo(
1294 std::string* vers_string
1297 *state = m_clients.getClientState(peer_id);
1299 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1306 *uptime = client->uptime();
1307 *ser_vers = client->serialization_version;
1308 *prot_vers = client->net_proto_version;
1310 *major = client->getMajor();
1311 *minor = client->getMinor();
1312 *patch = client->getPatch();
1313 *vers_string = client->getPatch();
1320 void Server::handlePeerChanges()
1322 while(!m_peer_change_queue.empty())
1324 con::PeerChange c = m_peer_change_queue.front();
1325 m_peer_change_queue.pop();
1327 verbosestream<<"Server: Handling peer change: "
1328 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1333 case con::PEER_ADDED:
1334 m_clients.CreateClient(c.peer_id);
1337 case con::PEER_REMOVED:
1338 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1342 FATAL_ERROR("Invalid peer change event received!");
1348 void Server::printToConsoleOnly(const std::string &text)
1351 m_admin_chat->outgoing_queue.push_back(
1352 new ChatEventChat("", utf8_to_wide(text)));
1354 std::cout << text << std::endl;
1358 void Server::Send(NetworkPacket *pkt)
1360 Send(pkt->getPeerId(), pkt);
1363 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1365 m_clients.send(peer_id,
1366 clientCommandFactoryTable[pkt->getCommand()].channel,
1368 clientCommandFactoryTable[pkt->getCommand()].reliable);
1371 void Server::SendMovement(session_t peer_id)
1373 std::ostringstream os(std::ios_base::binary);
1375 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1377 pkt << g_settings->getFloat("movement_acceleration_default");
1378 pkt << g_settings->getFloat("movement_acceleration_air");
1379 pkt << g_settings->getFloat("movement_acceleration_fast");
1380 pkt << g_settings->getFloat("movement_speed_walk");
1381 pkt << g_settings->getFloat("movement_speed_crouch");
1382 pkt << g_settings->getFloat("movement_speed_fast");
1383 pkt << g_settings->getFloat("movement_speed_climb");
1384 pkt << g_settings->getFloat("movement_speed_jump");
1385 pkt << g_settings->getFloat("movement_liquid_fluidity");
1386 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1387 pkt << g_settings->getFloat("movement_liquid_sink");
1388 pkt << g_settings->getFloat("movement_gravity");
1393 void Server::SendPlayerHPOrDie(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1395 if (!g_settings->getBool("enable_damage"))
1398 session_t peer_id = playersao->getPeerID();
1399 bool is_alive = playersao->getHP() > 0;
1402 SendPlayerHP(peer_id);
1404 DiePlayer(peer_id, reason);
1407 void Server::SendHP(session_t peer_id, u16 hp)
1409 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1414 void Server::SendBreath(session_t peer_id, u16 breath)
1416 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1417 pkt << (u16) breath;
1421 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1422 const std::string &custom_reason, bool reconnect)
1424 assert(reason < SERVER_ACCESSDENIED_MAX);
1426 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1428 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1429 pkt << custom_reason;
1430 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1431 reason == SERVER_ACCESSDENIED_CRASH)
1432 pkt << custom_reason << (u8)reconnect;
1436 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1438 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1443 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1444 v3f camera_point_target)
1446 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1447 pkt << set_camera_point_target << camera_point_target;
1451 void Server::SendItemDef(session_t peer_id,
1452 IItemDefManager *itemdef, u16 protocol_version)
1454 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1458 u32 length of the next item
1459 zlib-compressed serialized ItemDefManager
1461 std::ostringstream tmp_os(std::ios::binary);
1462 itemdef->serialize(tmp_os, protocol_version);
1463 std::ostringstream tmp_os2(std::ios::binary);
1464 compressZlib(tmp_os.str(), tmp_os2);
1465 pkt.putLongString(tmp_os2.str());
1468 verbosestream << "Server: Sending item definitions to id(" << peer_id
1469 << "): size=" << pkt.getSize() << std::endl;
1474 void Server::SendNodeDef(session_t peer_id,
1475 const NodeDefManager *nodedef, u16 protocol_version)
1477 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1481 u32 length of the next item
1482 zlib-compressed serialized NodeDefManager
1484 std::ostringstream tmp_os(std::ios::binary);
1485 nodedef->serialize(tmp_os, protocol_version);
1486 std::ostringstream tmp_os2(std::ios::binary);
1487 compressZlib(tmp_os.str(), tmp_os2);
1489 pkt.putLongString(tmp_os2.str());
1492 verbosestream << "Server: Sending node definitions to id(" << peer_id
1493 << "): size=" << pkt.getSize() << std::endl;
1499 Non-static send methods
1502 void Server::SendInventory(PlayerSAO* playerSAO)
1504 UpdateCrafting(playerSAO->getPlayer());
1510 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1512 std::ostringstream os;
1513 playerSAO->getInventory()->serialize(os);
1515 std::string s = os.str();
1517 pkt.putRawString(s.c_str(), s.size());
1521 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1523 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1525 u8 type = message.type;
1526 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1528 if (peer_id != PEER_ID_INEXISTENT) {
1529 RemotePlayer *player = m_env->getPlayer(peer_id);
1535 m_clients.sendToAll(&pkt);
1539 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1540 const std::string &formname)
1542 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1543 if (formspec.empty()){
1544 //the client should close the formspec
1545 m_formspec_state_data.erase(peer_id);
1546 pkt.putLongString("");
1548 m_formspec_state_data[peer_id] = formname;
1549 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1556 // Spawns a particle on peer with peer_id
1557 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1558 v3f pos, v3f velocity, v3f acceleration,
1559 float expirationtime, float size, bool collisiondetection,
1560 bool collision_removal,
1561 bool vertical, const std::string &texture,
1562 const struct TileAnimationParams &animation, u8 glow)
1564 static thread_local const float radius =
1565 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1567 if (peer_id == PEER_ID_INEXISTENT) {
1568 std::vector<session_t> clients = m_clients.getClientIDs();
1570 for (const session_t client_id : clients) {
1571 RemotePlayer *player = m_env->getPlayer(client_id);
1575 PlayerSAO *sao = player->getPlayerSAO();
1579 // Do not send to distant clients
1580 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1583 SendSpawnParticle(client_id, player->protocol_version,
1584 pos, velocity, acceleration,
1585 expirationtime, size, collisiondetection,
1586 collision_removal, vertical, texture, animation, glow);
1591 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1593 pkt << pos << velocity << acceleration << expirationtime
1594 << size << collisiondetection;
1595 pkt.putLongString(texture);
1597 pkt << collision_removal;
1598 // This is horrible but required (why are there two ways to serialize pkts?)
1599 std::ostringstream os(std::ios_base::binary);
1600 animation.serialize(os, protocol_version);
1601 pkt.putRawString(os.str());
1607 // Adds a ParticleSpawner on peer with peer_id
1608 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1609 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1610 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1611 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1612 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1613 const struct TileAnimationParams &animation, u8 glow)
1615 if (peer_id == PEER_ID_INEXISTENT) {
1616 // This sucks and should be replaced:
1617 std::vector<session_t> clients = m_clients.getClientIDs();
1618 for (const session_t client_id : clients) {
1619 RemotePlayer *player = m_env->getPlayer(client_id);
1622 SendAddParticleSpawner(client_id, player->protocol_version,
1623 amount, spawntime, minpos, maxpos,
1624 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1625 minsize, maxsize, collisiondetection, collision_removal,
1626 attached_id, vertical, texture, id, animation, glow);
1631 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1633 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1634 << minacc << maxacc << minexptime << maxexptime << minsize
1635 << maxsize << collisiondetection;
1637 pkt.putLongString(texture);
1639 pkt << id << vertical;
1640 pkt << collision_removal;
1642 // This is horrible but required
1643 std::ostringstream os(std::ios_base::binary);
1644 animation.serialize(os, protocol_version);
1645 pkt.putRawString(os.str());
1651 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1653 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1655 // Ugly error in this packet
1658 if (peer_id != PEER_ID_INEXISTENT)
1661 m_clients.sendToAll(&pkt);
1665 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1667 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1669 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1670 << form->text << form->number << form->item << form->dir
1671 << form->align << form->offset << form->world_pos << form->size;
1676 void Server::SendHUDRemove(session_t peer_id, u32 id)
1678 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1683 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1685 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1686 pkt << id << (u8) stat;
1690 case HUD_STAT_SCALE:
1691 case HUD_STAT_ALIGN:
1692 case HUD_STAT_OFFSET:
1693 pkt << *(v2f *) value;
1697 pkt << *(std::string *) value;
1699 case HUD_STAT_WORLD_POS:
1700 pkt << *(v3f *) value;
1703 pkt << *(v2s32 *) value;
1705 case HUD_STAT_NUMBER:
1709 pkt << *(u32 *) value;
1716 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1718 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1720 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1722 pkt << flags << mask;
1727 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1729 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1730 pkt << param << value;
1734 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1735 const std::string &type, const std::vector<std::string> ¶ms,
1738 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1739 pkt << bgcolor << type << (u16) params.size();
1741 for (const std::string ¶m : params)
1749 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1751 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1752 pkt << params.density << params.color_bright << params.color_ambient
1753 << params.height << params.thickness << params.speed;
1757 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1760 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1763 pkt << do_override << (u16) (ratio * 65535);
1768 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1770 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1771 pkt << time << time_speed;
1773 if (peer_id == PEER_ID_INEXISTENT) {
1774 m_clients.sendToAll(&pkt);
1781 void Server::SendPlayerHP(session_t peer_id)
1783 PlayerSAO *playersao = getPlayerSAO(peer_id);
1784 // In some rare case if the player is disconnected
1785 // while Lua call l_punch, for example, this can be NULL
1789 SendHP(peer_id, playersao->getHP());
1790 m_script->player_event(playersao,"health_changed");
1792 // Send to other clients
1793 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1794 ActiveObjectMessage aom(playersao->getId(), true, str);
1795 playersao->m_messages_out.push(aom);
1798 void Server::SendPlayerBreath(PlayerSAO *sao)
1802 m_script->player_event(sao, "breath_changed");
1803 SendBreath(sao->getPeerID(), sao->getBreath());
1806 void Server::SendMovePlayer(session_t peer_id)
1808 RemotePlayer *player = m_env->getPlayer(peer_id);
1810 PlayerSAO *sao = player->getPlayerSAO();
1813 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1814 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1817 v3f pos = sao->getBasePosition();
1818 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1819 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1820 << " pitch=" << sao->getPitch()
1821 << " yaw=" << sao->getYaw()
1828 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1829 f32 animation_speed)
1831 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1834 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1835 << animation_frames[3] << animation_speed;
1840 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1842 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1843 pkt << first << third;
1847 void Server::SendPlayerPrivileges(session_t peer_id)
1849 RemotePlayer *player = m_env->getPlayer(peer_id);
1851 if(player->getPeerId() == PEER_ID_INEXISTENT)
1854 std::set<std::string> privs;
1855 m_script->getAuth(player->getName(), NULL, &privs);
1857 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1858 pkt << (u16) privs.size();
1860 for (const std::string &priv : privs) {
1867 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1869 RemotePlayer *player = m_env->getPlayer(peer_id);
1871 if (player->getPeerId() == PEER_ID_INEXISTENT)
1874 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1875 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1879 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1881 RemotePlayer *player = m_env->getPlayer(peer_id);
1883 if (player->getPeerId() == PEER_ID_INEXISTENT)
1886 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1887 pkt << FORMSPEC_VERSION_STRING + player->formspec_prepend;
1891 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1893 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1894 pkt.putRawString(datas.c_str(), datas.size());
1896 return pkt.getSize();
1899 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1902 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1903 datas.size(), peer_id);
1905 pkt.putRawString(datas.c_str(), datas.size());
1907 m_clients.send(pkt.getPeerId(),
1908 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1912 void Server::SendCSMFlavourLimits(session_t peer_id)
1914 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1915 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1916 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1920 s32 Server::playSound(const SimpleSoundSpec &spec,
1921 const ServerSoundParams ¶ms)
1923 // Find out initial position of sound
1924 bool pos_exists = false;
1925 v3f pos = params.getPos(m_env, &pos_exists);
1926 // If position is not found while it should be, cancel sound
1927 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1930 // Filter destination clients
1931 std::vector<session_t> dst_clients;
1932 if(!params.to_player.empty()) {
1933 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1935 infostream<<"Server::playSound: Player \""<<params.to_player
1936 <<"\" not found"<<std::endl;
1939 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1940 infostream<<"Server::playSound: Player \""<<params.to_player
1941 <<"\" not connected"<<std::endl;
1944 dst_clients.push_back(player->getPeerId());
1946 std::vector<session_t> clients = m_clients.getClientIDs();
1948 for (const session_t client_id : clients) {
1949 RemotePlayer *player = m_env->getPlayer(client_id);
1953 PlayerSAO *sao = player->getPlayerSAO();
1958 if(sao->getBasePosition().getDistanceFrom(pos) >
1959 params.max_hear_distance)
1962 dst_clients.push_back(client_id);
1966 if(dst_clients.empty())
1970 s32 id = m_next_sound_id++;
1971 // The sound will exist as a reference in m_playing_sounds
1972 m_playing_sounds[id] = ServerPlayingSound();
1973 ServerPlayingSound &psound = m_playing_sounds[id];
1974 psound.params = params;
1977 float gain = params.gain * spec.gain;
1978 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1979 pkt << id << spec.name << gain
1980 << (u8) params.type << pos << params.object
1981 << params.loop << params.fade << params.pitch;
1983 // Backwards compability
1984 bool play_sound = gain > 0;
1986 for (const u16 dst_client : dst_clients) {
1987 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
1988 psound.clients.insert(dst_client);
1989 m_clients.send(dst_client, 0, &pkt, true);
1994 void Server::stopSound(s32 handle)
1996 // Get sound reference
1997 std::unordered_map<s32, ServerPlayingSound>::iterator i =
1998 m_playing_sounds.find(handle);
1999 if (i == m_playing_sounds.end())
2001 ServerPlayingSound &psound = i->second;
2003 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2006 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2007 si != psound.clients.end(); ++si) {
2009 m_clients.send(*si, 0, &pkt, true);
2011 // Remove sound reference
2012 m_playing_sounds.erase(i);
2015 void Server::fadeSound(s32 handle, float step, float gain)
2017 // Get sound reference
2018 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2019 m_playing_sounds.find(handle);
2020 if (i == m_playing_sounds.end())
2023 ServerPlayingSound &psound = i->second;
2024 psound.params.gain = gain;
2026 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2027 pkt << handle << step << gain;
2029 // Backwards compability
2030 bool play_sound = gain > 0;
2031 ServerPlayingSound compat_psound = psound;
2032 compat_psound.clients.clear();
2034 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2035 compat_pkt << handle;
2037 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2038 it != psound.clients.end();) {
2039 if (m_clients.getProtocolVersion(*it) >= 32) {
2041 m_clients.send(*it, 0, &pkt, true);
2044 compat_psound.clients.insert(*it);
2046 m_clients.send(*it, 0, &compat_pkt, true);
2047 psound.clients.erase(it++);
2051 // Remove sound reference
2052 if (!play_sound || psound.clients.empty())
2053 m_playing_sounds.erase(i);
2055 if (play_sound && !compat_psound.clients.empty()) {
2056 // Play new sound volume on older clients
2057 playSound(compat_psound.spec, compat_psound.params);
2061 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2062 std::vector<u16> *far_players, float far_d_nodes)
2064 float maxd = far_d_nodes*BS;
2065 v3f p_f = intToFloat(p, BS);
2067 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2070 std::vector<session_t> clients = m_clients.getClientIDs();
2071 for (session_t client_id : clients) {
2074 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2075 PlayerSAO *sao = player->getPlayerSAO();
2079 // If player is far away, only set modified blocks not sent
2080 v3f player_pos = sao->getBasePosition();
2081 if (player_pos.getDistanceFrom(p_f) > maxd) {
2082 far_players->push_back(client_id);
2089 m_clients.send(client_id, 0, &pkt, true);
2093 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2094 std::vector<u16> *far_players, float far_d_nodes,
2095 bool remove_metadata)
2097 float maxd = far_d_nodes*BS;
2098 v3f p_f = intToFloat(p, BS);
2100 std::vector<session_t> clients = m_clients.getClientIDs();
2101 for (const session_t client_id : clients) {
2104 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2105 PlayerSAO *sao = player->getPlayerSAO();
2109 // If player is far away, only set modified blocks not sent
2110 v3f player_pos = sao->getBasePosition();
2111 if(player_pos.getDistanceFrom(p_f) > maxd) {
2112 far_players->push_back(client_id);
2118 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2120 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2122 pkt << p << n.param0 << n.param1 << n.param2
2123 << (u8) (remove_metadata ? 0 : 1);
2128 if (pkt.getSize() > 0)
2129 m_clients.send(client_id, 0, &pkt, true);
2133 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2134 u16 net_proto_version)
2137 Create a packet with the block in the right format
2140 std::ostringstream os(std::ios_base::binary);
2141 block->serialize(os, ver, false);
2142 block->serializeNetworkSpecific(os);
2143 std::string s = os.str();
2145 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2147 pkt << block->getPos();
2148 pkt.putRawString(s.c_str(), s.size());
2152 void Server::SendBlocks(float dtime)
2154 MutexAutoLock envlock(m_env_mutex);
2155 //TODO check if one big lock could be faster then multiple small ones
2157 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2159 std::vector<PrioritySortedBlockTransfer> queue;
2161 u32 total_sending = 0;
2164 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2166 std::vector<session_t> clients = m_clients.getClientIDs();
2169 for (const session_t client_id : clients) {
2170 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2175 total_sending += client->getSendingCount();
2176 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2182 // Lowest priority number comes first.
2183 // Lowest is most important.
2184 std::sort(queue.begin(), queue.end());
2188 // Maximal total count calculation
2189 // The per-client block sends is halved with the maximal online users
2190 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2191 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2193 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2194 if (total_sending >= max_blocks_to_send)
2197 MapBlock *block = nullptr;
2199 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2200 } catch (const InvalidPositionException &e) {
2204 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2209 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2210 client->net_proto_version);
2212 client->SentBlock(block_to_send.pos);
2218 void Server::fillMediaCache()
2220 infostream<<"Server: Calculating media file checksums"<<std::endl;
2222 // Collect all media file paths
2223 std::vector<std::string> paths;
2224 m_modmgr->getModsMediaPaths(paths);
2225 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2226 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2228 // Collect media file information from paths into cache
2229 for (const std::string &mediapath : paths) {
2230 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2231 for (const fs::DirListNode &dln : dirlist) {
2232 if (dln.dir) // Ignode dirs
2234 std::string filename = dln.name;
2235 // If name contains illegal characters, ignore the file
2236 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2237 infostream<<"Server: ignoring illegal file name: \""
2238 << filename << "\"" << std::endl;
2241 // If name is not in a supported format, ignore it
2242 const char *supported_ext[] = {
2243 ".png", ".jpg", ".bmp", ".tga",
2244 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2246 ".x", ".b3d", ".md2", ".obj",
2247 // Custom translation file format
2251 if (removeStringEnd(filename, supported_ext).empty()){
2252 infostream << "Server: ignoring unsupported file extension: \""
2253 << filename << "\"" << std::endl;
2256 // Ok, attempt to load the file and add to cache
2257 std::string filepath;
2258 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2261 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2263 errorstream << "Server::fillMediaCache(): Could not open \""
2264 << filename << "\" for reading" << std::endl;
2267 std::ostringstream tmp_os(std::ios_base::binary);
2271 fis.read(buf, 1024);
2272 std::streamsize len = fis.gcount();
2273 tmp_os.write(buf, len);
2282 errorstream<<"Server::fillMediaCache(): Failed to read \""
2283 << filename << "\"" << std::endl;
2286 if(tmp_os.str().length() == 0) {
2287 errorstream << "Server::fillMediaCache(): Empty file \""
2288 << filepath << "\"" << std::endl;
2293 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2295 unsigned char *digest = sha1.getDigest();
2296 std::string sha1_base64 = base64_encode(digest, 20);
2297 std::string sha1_hex = hex_encode((char*)digest, 20);
2301 m_media[filename] = MediaInfo(filepath, sha1_base64);
2302 verbosestream << "Server: " << sha1_hex << " is " << filename
2308 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2310 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2314 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2317 std::string lang_suffix;
2318 lang_suffix.append(".").append(lang_code).append(".tr");
2319 for (const auto &i : m_media) {
2320 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2327 for (const auto &i : m_media) {
2328 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2330 pkt << i.first << i.second.sha1_digest;
2333 pkt << g_settings->get("remote_media");
2337 struct SendableMedia
2343 SendableMedia(const std::string &name_="", const std::string &path_="",
2344 const std::string &data_=""):
2351 void Server::sendRequestedMedia(session_t peer_id,
2352 const std::vector<std::string> &tosend)
2354 verbosestream<<"Server::sendRequestedMedia(): "
2355 <<"Sending files to client"<<std::endl;
2359 // Put 5kB in one bunch (this is not accurate)
2360 u32 bytes_per_bunch = 5000;
2362 std::vector< std::vector<SendableMedia> > file_bunches;
2363 file_bunches.emplace_back();
2365 u32 file_size_bunch_total = 0;
2367 for (const std::string &name : tosend) {
2368 if (m_media.find(name) == m_media.end()) {
2369 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2370 <<"unknown file \""<<(name)<<"\""<<std::endl;
2374 //TODO get path + name
2375 std::string tpath = m_media[name].path;
2378 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2380 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2381 <<tpath<<"\" for reading"<<std::endl;
2384 std::ostringstream tmp_os(std::ios_base::binary);
2388 fis.read(buf, 1024);
2389 std::streamsize len = fis.gcount();
2390 tmp_os.write(buf, len);
2391 file_size_bunch_total += len;
2400 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2401 <<name<<"\""<<std::endl;
2404 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2405 <<tname<<"\""<<std::endl;*/
2407 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2409 // Start next bunch if got enough data
2410 if(file_size_bunch_total >= bytes_per_bunch) {
2411 file_bunches.emplace_back();
2412 file_size_bunch_total = 0;
2417 /* Create and send packets */
2419 u16 num_bunches = file_bunches.size();
2420 for (u16 i = 0; i < num_bunches; i++) {
2423 u16 total number of texture bunches
2424 u16 index of this bunch
2425 u32 number of files in this bunch
2434 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2435 pkt << num_bunches << i << (u32) file_bunches[i].size();
2437 for (const SendableMedia &j : file_bunches[i]) {
2439 pkt.putLongString(j.data);
2442 verbosestream << "Server::sendRequestedMedia(): bunch "
2443 << i << "/" << num_bunches
2444 << " files=" << file_bunches[i].size()
2445 << " size=" << pkt.getSize() << std::endl;
2450 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2452 if(m_detached_inventories.count(name) == 0) {
2453 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2456 Inventory *inv = m_detached_inventories[name];
2457 std::ostringstream os(std::ios_base::binary);
2459 os << serializeString(name);
2463 std::string s = os.str();
2465 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2466 pkt.putRawString(s.c_str(), s.size());
2468 const std::string &check = m_detached_inventories_player[name];
2469 if (peer_id == PEER_ID_INEXISTENT) {
2471 return m_clients.sendToAll(&pkt);
2472 RemotePlayer *p = m_env->getPlayer(check.c_str());
2474 m_clients.send(p->getPeerId(), 0, &pkt, true);
2476 if (check.empty() || getPlayerName(peer_id) == check)
2481 void Server::sendDetachedInventories(session_t peer_id)
2483 for (const auto &detached_inventory : m_detached_inventories) {
2484 const std::string &name = detached_inventory.first;
2485 //Inventory *inv = i->second;
2486 sendDetachedInventory(name, peer_id);
2494 void Server::DiePlayer(session_t peer_id, const PlayerHPChangeReason &reason)
2496 PlayerSAO *playersao = getPlayerSAO(peer_id);
2497 // In some rare cases this can be NULL -- if the player is disconnected
2498 // when a Lua function modifies l_punch, for example
2502 infostream << "Server::DiePlayer(): Player "
2503 << playersao->getPlayer()->getName()
2504 << " dies" << std::endl;
2506 playersao->setHP(0, reason);
2507 playersao->clearParentAttachment();
2509 // Trigger scripted stuff
2510 m_script->on_dieplayer(playersao, reason);
2512 SendPlayerHP(peer_id);
2513 SendDeathscreen(peer_id, false, v3f(0,0,0));
2516 void Server::RespawnPlayer(session_t peer_id)
2518 PlayerSAO *playersao = getPlayerSAO(peer_id);
2521 infostream << "Server::RespawnPlayer(): Player "
2522 << playersao->getPlayer()->getName()
2523 << " respawns" << std::endl;
2525 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2526 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2527 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2529 bool repositioned = m_script->on_respawnplayer(playersao);
2530 if (!repositioned) {
2531 // setPos will send the new position to client
2532 playersao->setPos(findSpawnPos());
2535 SendPlayerHP(peer_id);
2539 void Server::DenySudoAccess(session_t peer_id)
2541 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2546 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2547 const std::string &str_reason, bool reconnect)
2549 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2551 m_clients.event(peer_id, CSE_SetDenied);
2552 DisconnectPeer(peer_id);
2556 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2557 const std::string &custom_reason)
2559 SendAccessDenied(peer_id, reason, custom_reason);
2560 m_clients.event(peer_id, CSE_SetDenied);
2561 DisconnectPeer(peer_id);
2564 // 13/03/15: remove this function when protocol version 25 will become
2565 // the minimum version for MT users, maybe in 1 year
2566 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2568 SendAccessDenied_Legacy(peer_id, reason);
2569 m_clients.event(peer_id, CSE_SetDenied);
2570 DisconnectPeer(peer_id);
2573 void Server::DisconnectPeer(session_t peer_id)
2575 m_modchannel_mgr->leaveAllChannels(peer_id);
2576 m_con->DisconnectPeer(peer_id);
2579 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2582 RemoteClient* client = getClient(peer_id, CS_Invalid);
2584 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2586 // Right now, the auth mechs don't change between login and sudo mode.
2587 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2588 client->allowed_sudo_mechs = sudo_auth_mechs;
2590 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2591 << g_settings->getFloat("dedicated_server_step")
2595 m_clients.event(peer_id, CSE_AuthAccept);
2597 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2599 // We only support SRP right now
2600 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2602 resp_pkt << sudo_auth_mechs;
2604 m_clients.event(peer_id, CSE_SudoSuccess);
2608 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2610 std::wstring message;
2613 Clear references to playing sounds
2615 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2616 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2617 ServerPlayingSound &psound = i->second;
2618 psound.clients.erase(peer_id);
2619 if (psound.clients.empty())
2620 m_playing_sounds.erase(i++);
2625 // clear formspec info so the next client can't abuse the current state
2626 m_formspec_state_data.erase(peer_id);
2628 RemotePlayer *player = m_env->getPlayer(peer_id);
2630 /* Run scripts and remove from environment */
2632 PlayerSAO *playersao = player->getPlayerSAO();
2635 // inform connected clients
2636 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2637 // (u16) 1 + std::string represents a vector serialization representation
2638 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2639 m_clients.sendToAll(¬ice);
2641 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2643 playersao->disconnected();
2650 if (player && reason != CDR_DENY) {
2651 std::ostringstream os(std::ios_base::binary);
2652 std::vector<session_t> clients = m_clients.getClientIDs();
2654 for (const session_t client_id : clients) {
2656 RemotePlayer *player = m_env->getPlayer(client_id);
2660 // Get name of player
2661 os << player->getName() << " ";
2664 std::string name = player->getName();
2665 actionstream << name << " "
2666 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2667 << " List of players: " << os.str() << std::endl;
2669 m_admin_chat->outgoing_queue.push_back(
2670 new ChatEventNick(CET_NICK_REMOVE, name));
2674 MutexAutoLock env_lock(m_env_mutex);
2675 m_clients.DeleteClient(peer_id);
2679 // Send leave chat message to all remaining clients
2680 if (!message.empty()) {
2681 SendChatMessage(PEER_ID_INEXISTENT,
2682 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2686 void Server::UpdateCrafting(RemotePlayer *player)
2688 InventoryList *clist = player->inventory.getList("craft");
2689 if (!clist || clist->getSize() == 0)
2692 // Get a preview for crafting
2694 InventoryLocation loc;
2695 loc.setPlayer(player->getName());
2696 std::vector<ItemStack> output_replacements;
2697 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2698 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2701 InventoryList *plist = player->inventory.getList("craftpreview");
2702 if (plist && plist->getSize() >= 1) {
2703 // Put the new preview in
2704 plist->changeItem(0, preview);
2708 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2710 if (evt->type == CET_NICK_ADD) {
2711 // The terminal informed us of its nick choice
2712 m_admin_nick = ((ChatEventNick *)evt)->nick;
2713 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2714 errorstream << "You haven't set up an account." << std::endl
2715 << "Please log in using the client as '"
2716 << m_admin_nick << "' with a secure password." << std::endl
2717 << "Until then, you can't execute admin tasks via the console," << std::endl
2718 << "and everybody can claim the user account instead of you," << std::endl
2719 << "giving them full control over this server." << std::endl;
2722 assert(evt->type == CET_CHAT);
2723 handleAdminChat((ChatEventChat *)evt);
2727 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2728 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2730 // If something goes wrong, this player is to blame
2731 RollbackScopeActor rollback_scope(m_rollback,
2732 std::string("player:") + name);
2734 if (g_settings->getBool("strip_color_codes"))
2735 wmessage = unescape_enriched(wmessage);
2738 switch (player->canSendChatMessage()) {
2739 case RPLAYER_CHATRESULT_FLOODING: {
2740 std::wstringstream ws;
2741 ws << L"You cannot send more messages. You are limited to "
2742 << g_settings->getFloat("chat_message_limit_per_10sec")
2743 << L" messages per 10 seconds.";
2746 case RPLAYER_CHATRESULT_KICK:
2747 DenyAccess_Legacy(player->getPeerId(),
2748 L"You have been kicked due to message flooding.");
2750 case RPLAYER_CHATRESULT_OK:
2753 FATAL_ERROR("Unhandled chat filtering result found.");
2757 if (m_max_chatmessage_length > 0
2758 && wmessage.length() > m_max_chatmessage_length) {
2759 return L"Your message exceed the maximum chat message limit set on the server. "
2760 L"It was refused. Send a shorter message";
2763 // Run script hook, exit if script ate the chat message
2764 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2769 // Whether to send line to the player that sent the message, or to all players
2770 bool broadcast_line = true;
2772 if (check_shout_priv && !checkPriv(name, "shout")) {
2773 line += L"-!- You don't have permission to shout.";
2774 broadcast_line = false;
2783 Tell calling method to send the message to sender
2785 if (!broadcast_line)
2789 Send the message to others
2791 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2793 std::vector<session_t> clients = m_clients.getClientIDs();
2796 Send the message back to the inital sender
2797 if they are using protocol version >= 29
2800 session_t peer_id_to_avoid_sending =
2801 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2803 if (player && player->protocol_version >= 29)
2804 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2806 for (u16 cid : clients) {
2807 if (cid != peer_id_to_avoid_sending)
2808 SendChatMessage(cid, ChatMessage(line));
2813 void Server::handleAdminChat(const ChatEventChat *evt)
2815 std::string name = evt->nick;
2816 std::wstring wname = utf8_to_wide(name);
2817 std::wstring wmessage = evt->evt_msg;
2819 std::wstring answer = handleChat(name, wname, wmessage);
2821 // If asked to send answer to sender
2822 if (!answer.empty()) {
2823 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2827 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2829 RemoteClient *client = getClientNoEx(peer_id,state_min);
2831 throw ClientNotFoundException("Client not found");
2835 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2837 return m_clients.getClientNoEx(peer_id, state_min);
2840 std::string Server::getPlayerName(session_t peer_id)
2842 RemotePlayer *player = m_env->getPlayer(peer_id);
2844 return "[id="+itos(peer_id)+"]";
2845 return player->getName();
2848 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2850 RemotePlayer *player = m_env->getPlayer(peer_id);
2853 return player->getPlayerSAO();
2856 std::wstring Server::getStatusString()
2858 std::wostringstream os(std::ios_base::binary);
2861 os<<L"version="<<narrow_to_wide(g_version_string);
2863 os<<L", uptime="<<m_uptime.get();
2865 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2866 // Information about clients
2869 std::vector<session_t> clients = m_clients.getClientIDs();
2870 for (session_t client_id : clients) {
2872 RemotePlayer *player = m_env->getPlayer(client_id);
2873 // Get name of player
2874 std::wstring name = L"unknown";
2876 name = narrow_to_wide(player->getName());
2877 // Add name to information string
2886 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2887 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2889 if (!g_settings->get("motd").empty())
2890 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2894 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2896 std::set<std::string> privs;
2897 m_script->getAuth(name, NULL, &privs);
2901 bool Server::checkPriv(const std::string &name, const std::string &priv)
2903 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2904 return (privs.count(priv) != 0);
2907 void Server::reportPrivsModified(const std::string &name)
2910 std::vector<session_t> clients = m_clients.getClientIDs();
2911 for (const session_t client_id : clients) {
2912 RemotePlayer *player = m_env->getPlayer(client_id);
2913 reportPrivsModified(player->getName());
2916 RemotePlayer *player = m_env->getPlayer(name.c_str());
2919 SendPlayerPrivileges(player->getPeerId());
2920 PlayerSAO *sao = player->getPlayerSAO();
2923 sao->updatePrivileges(
2924 getPlayerEffectivePrivs(name),
2929 void Server::reportInventoryFormspecModified(const std::string &name)
2931 RemotePlayer *player = m_env->getPlayer(name.c_str());
2934 SendPlayerInventoryFormspec(player->getPeerId());
2937 void Server::reportFormspecPrependModified(const std::string &name)
2939 RemotePlayer *player = m_env->getPlayer(name.c_str());
2942 SendPlayerFormspecPrepend(player->getPeerId());
2945 void Server::setIpBanned(const std::string &ip, const std::string &name)
2947 m_banmanager->add(ip, name);
2950 void Server::unsetIpBanned(const std::string &ip_or_name)
2952 m_banmanager->remove(ip_or_name);
2955 std::string Server::getBanDescription(const std::string &ip_or_name)
2957 return m_banmanager->getBanDescription(ip_or_name);
2960 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2962 // m_env will be NULL if the server is initializing
2966 if (m_admin_nick == name && !m_admin_nick.empty()) {
2967 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2970 RemotePlayer *player = m_env->getPlayer(name);
2975 if (player->getPeerId() == PEER_ID_INEXISTENT)
2978 SendChatMessage(player->getPeerId(), ChatMessage(msg));
2981 bool Server::showFormspec(const char *playername, const std::string &formspec,
2982 const std::string &formname)
2984 // m_env will be NULL if the server is initializing
2988 RemotePlayer *player = m_env->getPlayer(playername);
2992 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
2996 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3001 u32 id = player->addHud(form);
3003 SendHUDAdd(player->getPeerId(), id, form);
3008 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3012 HudElement* todel = player->removeHud(id);
3019 SendHUDRemove(player->getPeerId(), id);
3023 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3028 SendHUDChange(player->getPeerId(), id, stat, data);
3032 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3037 SendHUDSetFlags(player->getPeerId(), flags, mask);
3038 player->hud_flags &= ~mask;
3039 player->hud_flags |= flags;
3041 PlayerSAO* playersao = player->getPlayerSAO();
3046 m_script->player_event(playersao, "hud_changed");
3050 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3055 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3058 player->setHotbarItemcount(hotbar_itemcount);
3059 std::ostringstream os(std::ios::binary);
3060 writeS32(os, hotbar_itemcount);
3061 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3065 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3070 player->setHotbarImage(name);
3071 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3074 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3079 player->setHotbarSelectedImage(name);
3080 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3083 Address Server::getPeerAddress(session_t peer_id)
3085 return m_con->GetPeerAddress(peer_id);
3088 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3089 v2s32 animation_frames[4], f32 frame_speed)
3091 sanity_check(player);
3092 player->setLocalAnimations(animation_frames, frame_speed);
3093 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3096 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3098 sanity_check(player);
3099 player->eye_offset_first = first;
3100 player->eye_offset_third = third;
3101 SendEyeOffset(player->getPeerId(), first, third);
3104 void Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3105 const std::string &type, const std::vector<std::string> ¶ms,
3108 sanity_check(player);
3109 player->setSky(bgcolor, type, params, clouds);
3110 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3113 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3115 sanity_check(player);
3116 player->setCloudParams(params);
3117 SendCloudParams(player->getPeerId(), params);
3120 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3126 player->overrideDayNightRatio(do_override, ratio);
3127 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3131 void Server::notifyPlayers(const std::wstring &msg)
3133 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3136 void Server::spawnParticle(const std::string &playername, v3f pos,
3137 v3f velocity, v3f acceleration,
3138 float expirationtime, float size, bool
3139 collisiondetection, bool collision_removal,
3140 bool vertical, const std::string &texture,
3141 const struct TileAnimationParams &animation, u8 glow)
3143 // m_env will be NULL if the server is initializing
3147 session_t peer_id = PEER_ID_INEXISTENT;
3149 if (!playername.empty()) {
3150 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3153 peer_id = player->getPeerId();
3154 proto_ver = player->protocol_version;
3157 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3158 expirationtime, size, collisiondetection,
3159 collision_removal, vertical, texture, animation, glow);
3162 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3163 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3164 float minexptime, float maxexptime, float minsize, float maxsize,
3165 bool collisiondetection, bool collision_removal,
3166 ServerActiveObject *attached, bool vertical, const std::string &texture,
3167 const std::string &playername, const struct TileAnimationParams &animation,
3170 // m_env will be NULL if the server is initializing
3174 session_t peer_id = PEER_ID_INEXISTENT;
3176 if (!playername.empty()) {
3177 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3180 peer_id = player->getPeerId();
3181 proto_ver = player->protocol_version;
3184 u16 attached_id = attached ? attached->getId() : 0;
3187 if (attached_id == 0)
3188 id = m_env->addParticleSpawner(spawntime);
3190 id = m_env->addParticleSpawner(spawntime, attached_id);
3192 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3193 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3194 minexptime, maxexptime, minsize, maxsize,
3195 collisiondetection, collision_removal, attached_id, vertical,
3196 texture, id, animation, glow);
3201 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3203 // m_env will be NULL if the server is initializing
3205 throw ServerError("Can't delete particle spawners during initialisation!");
3207 session_t peer_id = PEER_ID_INEXISTENT;
3208 if (!playername.empty()) {
3209 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3212 peer_id = player->getPeerId();
3215 m_env->deleteParticleSpawner(id);
3216 SendDeleteParticleSpawner(peer_id, id);
3219 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3221 if(m_detached_inventories.count(name) > 0){
3222 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3223 delete m_detached_inventories[name];
3225 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3227 Inventory *inv = new Inventory(m_itemdef);
3229 m_detached_inventories[name] = inv;
3230 m_detached_inventories_player[name] = player;
3231 //TODO find a better way to do this
3232 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3236 // actions: time-reversed list
3237 // Return value: success/failure
3238 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3239 std::list<std::string> *log)
3241 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3242 ServerMap *map = (ServerMap*)(&m_env->getMap());
3244 // Fail if no actions to handle
3245 if (actions.empty()) {
3247 log->push_back("Nothing to do.");
3254 for (const RollbackAction &action : actions) {
3256 bool success = action.applyRevert(map, this, this);
3259 std::ostringstream os;
3260 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3261 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3263 log->push_back(os.str());
3265 std::ostringstream os;
3266 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3267 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3269 log->push_back(os.str());
3273 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3274 <<" failed"<<std::endl;
3276 // Call it done if less than half failed
3277 return num_failed <= num_tried/2;
3280 // IGameDef interface
3282 IItemDefManager *Server::getItemDefManager()
3287 const NodeDefManager *Server::getNodeDefManager()
3292 ICraftDefManager *Server::getCraftDefManager()
3297 u16 Server::allocateUnknownNodeId(const std::string &name)
3299 return m_nodedef->allocateDummy(name);
3302 IWritableItemDefManager *Server::getWritableItemDefManager()
3307 NodeDefManager *Server::getWritableNodeDefManager()
3312 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3317 const std::vector<ModSpec> & Server::getMods() const
3319 return m_modmgr->getMods();
3322 const ModSpec *Server::getModSpec(const std::string &modname) const
3324 return m_modmgr->getModSpec(modname);
3327 void Server::getModNames(std::vector<std::string> &modlist)
3329 m_modmgr->getModNames(modlist);
3332 std::string Server::getBuiltinLuaPath()
3334 return porting::path_share + DIR_DELIM + "builtin";
3337 std::string Server::getModStoragePath() const
3339 return m_path_world + DIR_DELIM + "mod_storage";
3342 v3f Server::findSpawnPos()
3344 ServerMap &map = m_env->getServerMap();
3346 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3347 return nodeposf * BS;
3350 bool is_good = false;
3351 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3352 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3354 // Try to find a good place a few times
3355 for(s32 i = 0; i < 4000 && !is_good; i++) {
3356 s32 range = MYMIN(1 + i, range_max);
3357 // We're going to try to throw the player to this position
3358 v2s16 nodepos2d = v2s16(
3359 -range + (myrand() % (range * 2)),
3360 -range + (myrand() % (range * 2)));
3362 // Get spawn level at point
3363 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3364 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3365 // the mapgen to signify an unsuitable spawn position
3366 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3369 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3372 for (s32 i = 0; i < 10; i++) {
3373 v3s16 blockpos = getNodeBlockPos(nodepos);
3374 map.emergeBlock(blockpos, true);
3375 content_t c = map.getNodeNoEx(nodepos).getContent();
3376 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3378 if (air_count >= 2) {
3379 nodeposf = intToFloat(nodepos, BS);
3380 // Don't spawn the player outside map boundaries
3381 if (objectpos_over_limit(nodeposf))
3394 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3396 m_shutdown_timer = delay;
3397 m_shutdown_msg = msg;
3398 m_shutdown_ask_reconnect = reconnect;
3400 if (delay == 0.0f) {
3401 // No delay, shutdown immediately
3402 m_shutdown_requested = true;
3403 // only print to the infostream, a chat message saying
3404 // "Server Shutting Down" is sent when the server destructs.
3405 infostream << "*** Immediate Server shutdown requested." << std::endl;
3406 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3407 // Negative delay, cancel shutdown if requested
3408 m_shutdown_timer = 0.0f;
3409 m_shutdown_msg = "";
3410 m_shutdown_ask_reconnect = false;
3411 m_shutdown_requested = false;
3412 std::wstringstream ws;
3414 ws << L"*** Server shutdown canceled.";
3416 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3417 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3418 } else if (delay > 0.0f) {
3419 // Positive delay, tell the clients when the server will shut down
3420 std::wstringstream ws;
3422 ws << L"*** Server shutting down in "
3423 << duration_to_string(myround(m_shutdown_timer)).c_str()
3426 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3427 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3431 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3434 Try to get an existing player
3436 RemotePlayer *player = m_env->getPlayer(name);
3438 // If player is already connected, cancel
3439 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3440 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3445 If player with the wanted peer_id already exists, cancel.
3447 if (m_env->getPlayer(peer_id)) {
3448 infostream<<"emergePlayer(): Player with wrong name but same"
3449 " peer_id already exists"<<std::endl;
3454 player = new RemotePlayer(name, idef());
3457 bool newplayer = false;
3460 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3462 // Complete init with server parts
3463 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3464 player->protocol_version = proto_version;
3468 m_script->on_newplayer(playersao);
3474 bool Server::registerModStorage(ModMetadata *storage)
3476 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3477 errorstream << "Unable to register same mod storage twice. Storage name: "
3478 << storage->getModName() << std::endl;
3482 m_mod_storages[storage->getModName()] = storage;
3486 void Server::unregisterModStorage(const std::string &name)
3488 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3489 if (it != m_mod_storages.end()) {
3490 // Save unconditionaly on unregistration
3491 it->second->save(getModStoragePath());
3492 m_mod_storages.erase(name);
3496 void dedicated_server_loop(Server &server, bool &kill)
3498 verbosestream<<"dedicated_server_loop()"<<std::endl;
3500 IntervalLimiter m_profiler_interval;
3502 static thread_local const float steplen =
3503 g_settings->getFloat("dedicated_server_step");
3504 static thread_local const float profiler_print_interval =
3505 g_settings->getFloat("profiler_print_interval");
3508 // This is kind of a hack but can be done like this
3509 // because server.step() is very light
3511 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3512 sleep_ms((int)(steplen*1000.0));
3514 server.step(steplen);
3516 if (server.getShutdownRequested() || kill)
3522 if (profiler_print_interval != 0) {
3523 if(m_profiler_interval.step(steplen, profiler_print_interval))
3525 infostream<<"Profiler:"<<std::endl;
3526 g_profiler->print(infostream);
3527 g_profiler->clear();
3532 infostream << "Dedicated server quitting" << std::endl;
3534 if (g_settings->getBool("server_announce"))
3535 ServerList::sendAnnounce(ServerList::AA_DELETE,
3536 server.m_bind_addr.getPort());
3545 bool Server::joinModChannel(const std::string &channel)
3547 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3548 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3551 bool Server::leaveModChannel(const std::string &channel)
3553 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3556 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3558 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3561 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3565 ModChannel* Server::getModChannel(const std::string &channel)
3567 return m_modchannel_mgr->getModChannel(channel);
3570 void Server::broadcastModChannelMessage(const std::string &channel,
3571 const std::string &message, session_t from_peer)
3573 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3577 if (message.size() > STRING_MAX_LEN) {
3578 warningstream << "ModChannel message too long, dropping before sending "
3579 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3580 << channel << ")" << std::endl;
3585 if (from_peer != PEER_ID_SERVER) {
3586 sender = getPlayerName(from_peer);
3589 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3590 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3591 resp_pkt << channel << sender << message;
3592 for (session_t peer_id : peers) {
3594 if (peer_id == from_peer)
3597 Send(peer_id, &resp_pkt);
3600 if (from_peer != PEER_ID_SERVER) {
3601 m_script->on_modchannel_message(channel, sender, message);