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(); }
142 void Server::ShutdownState::reset()
146 should_reconnect = false;
147 is_requested = false;
150 void Server::ShutdownState::trigger(float delay, const std::string &msg, bool reconnect)
154 should_reconnect = reconnect;
157 void Server::ShutdownState::tick(float dtime, Server *server)
163 static const float shutdown_msg_times[] =
165 1, 2, 3, 4, 5, 10, 20, 40, 60, 120, 180, 300, 600, 1200, 1800, 3600
168 // Automated messages
169 if (m_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
170 for (float t : shutdown_msg_times) {
171 // If shutdown timer matches an automessage, shot it
172 if (m_timer > t && m_timer - dtime < t) {
173 std::wstring periodicMsg = getShutdownTimerMessage();
175 infostream << wide_to_utf8(periodicMsg).c_str() << std::endl;
176 server->SendChatMessage(PEER_ID_INEXISTENT, periodicMsg);
183 if (m_timer < 0.0f) {
189 std::wstring Server::ShutdownState::getShutdownTimerMessage() const
191 std::wstringstream ws;
192 ws << L"*** Server shutting down in "
193 << duration_to_string(myround(m_timer)).c_str() << ".";
202 const std::string &path_world,
203 const SubgameSpec &gamespec,
204 bool simple_singleplayer_mode,
209 m_bind_addr(bind_addr),
210 m_path_world(path_world),
211 m_gamespec(gamespec),
212 m_simple_singleplayer_mode(simple_singleplayer_mode),
213 m_dedicated(dedicated),
214 m_async_fatal_error(""),
215 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
218 m_bind_addr.isIPv6(),
220 m_itemdef(createItemDefManager()),
221 m_nodedef(createNodeDefManager()),
222 m_craftdef(createCraftDefManager()),
226 m_modchannel_mgr(new ModChannelMgr())
228 m_lag = g_settings->getFloat("dedicated_server_step");
230 if (m_path_world.empty())
231 throw ServerError("Supplied empty world path");
233 if (!gamespec.isValid())
234 throw ServerError("Supplied invalid gamespec");
239 infostream << "Server destructing" << std::endl;
241 // Send shutdown message
242 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
243 L"*** Server shutting down"));
246 MutexAutoLock envlock(m_env_mutex);
248 infostream << "Server: Saving players" << std::endl;
249 m_env->saveLoadedPlayers();
251 infostream << "Server: Kicking players" << std::endl;
252 std::string kick_msg;
253 bool reconnect = false;
254 if (isShutdownRequested()) {
255 reconnect = m_shutdown_state.should_reconnect;
256 kick_msg = m_shutdown_state.message;
258 if (kick_msg.empty()) {
259 kick_msg = g_settings->get("kick_msg_shutdown");
261 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
262 kick_msg, reconnect);
265 // Do this before stopping the server in case mapgen callbacks need to access
266 // server-controlled resources (like ModStorages). Also do them before
267 // shutdown callbacks since they may modify state that is finalized in a
270 m_emerge->stopThreads();
273 MutexAutoLock envlock(m_env_mutex);
275 // Execute script shutdown hooks
276 infostream << "Executing shutdown hooks" << std::endl;
277 m_script->on_shutdown();
279 infostream << "Server: Saving environment metadata" << std::endl;
289 // Delete things in the reverse order of creation
298 // Deinitialize scripting
299 infostream << "Server: Deinitializing scripting" << std::endl;
302 // Delete detached inventories
303 for (auto &detached_inventory : m_detached_inventories) {
304 delete detached_inventory.second;
310 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
311 if (m_simple_singleplayer_mode)
312 infostream << " in simple singleplayer mode" << std::endl;
314 infostream << std::endl;
315 infostream << "- world: " << m_path_world << std::endl;
316 infostream << "- game: " << m_gamespec.path << std::endl;
318 // Create world if it doesn't exist
319 if (!loadGameConfAndInitWorld(m_path_world, m_gamespec))
320 throw ServerError("Failed to initialize world");
322 // Create server thread
323 m_thread = new ServerThread(this);
325 // Create emerge manager
326 m_emerge = new EmergeManager(this);
328 // Create ban manager
329 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
330 m_banmanager = new BanManager(ban_path);
332 m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager(m_path_world));
333 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
334 // complain about mods with unsatisfied dependencies
335 if (!m_modmgr->isConsistent()) {
336 m_modmgr->printUnsatisfiedModsError();
340 MutexAutoLock envlock(m_env_mutex);
342 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
343 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge);
345 // Initialize scripting
346 infostream << "Server: Initializing Lua" << std::endl;
348 m_script = new ServerScripting(this);
350 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
352 m_modmgr->loadMods(m_script);
354 // Read Textures and calculate sha1 sums
357 // Apply item aliases in the node definition manager
358 m_nodedef->updateAliases(m_itemdef);
360 // Apply texture overrides from texturepack/override.txt
361 std::vector<std::string> paths;
362 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
363 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
364 for (const std::string &path : paths)
365 m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
367 m_nodedef->setNodeRegistrationStatus(true);
369 // Perform pending node name resolutions
370 m_nodedef->runNodeResolveCallbacks();
372 // unmap node names for connected nodeboxes
373 m_nodedef->mapNodeboxConnections();
375 // init the recipe hashes to speed up crafting
376 m_craftdef->initHashes(this);
378 // Initialize Environment
379 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
381 m_clients.setEnv(m_env);
383 if (!servermap->settings_mgr.makeMapgenParams())
384 FATAL_ERROR("Couldn't create any mapgen type");
386 // Initialize mapgens
387 m_emerge->initMapgens(servermap->getMapgenParams());
389 if (g_settings->getBool("enable_rollback_recording")) {
390 // Create rollback manager
391 m_rollback = new RollbackManager(m_path_world, this);
394 // Give environment reference to scripting api
395 m_script->initializeEnvironment(m_env);
397 // Register us to receive map edit events
398 servermap->addEventReceiver(this);
402 m_liquid_transform_every = g_settings->getFloat("liquid_update");
403 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
404 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
405 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
410 infostream << "Starting server on " << m_bind_addr.serializeString()
411 << "..." << std::endl;
413 // Stop thread if already running
416 // Initialize connection
417 m_con->SetTimeoutMs(30);
418 m_con->Serve(m_bind_addr);
423 // ASCII art for the win!
425 << " .__ __ __ " << std::endl
426 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
427 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
428 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
429 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
430 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
431 actionstream << "World at [" << m_path_world << "]" << std::endl;
432 actionstream << "Server for gameid=\"" << m_gamespec.id
433 << "\" listening on " << m_bind_addr.serializeString() << ":"
434 << m_bind_addr.getPort() << "." << std::endl;
439 infostream<<"Server: Stopping and waiting threads"<<std::endl;
441 // Stop threads (set run=false first so both start stopping)
443 //m_emergethread.setRun(false);
445 //m_emergethread.stop();
447 infostream<<"Server: Threads stopped"<<std::endl;
450 void Server::step(float dtime)
456 MutexAutoLock lock(m_step_dtime_mutex);
457 m_step_dtime += dtime;
459 // Throw if fatal error occurred in thread
460 std::string async_err = m_async_fatal_error.get();
461 if (!async_err.empty()) {
462 if (!m_simple_singleplayer_mode) {
463 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
464 g_settings->get("kick_msg_crash"),
465 g_settings->getBool("ask_reconnect_on_crash"));
467 throw ServerError("AsyncErr: " + async_err);
471 void Server::AsyncRunStep(bool initial_step)
473 g_profiler->add("Server::AsyncRunStep (num)", 1);
477 MutexAutoLock lock1(m_step_dtime_mutex);
478 dtime = m_step_dtime;
482 // Send blocks to clients
486 if((dtime < 0.001) && !initial_step)
489 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
491 //infostream<<"Server steps "<<dtime<<std::endl;
492 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
495 MutexAutoLock lock1(m_step_dtime_mutex);
496 m_step_dtime -= dtime;
503 m_uptime.set(m_uptime.get() + dtime);
509 Update time of day and overall game time
511 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
514 Send to clients at constant intervals
517 m_time_of_day_send_timer -= dtime;
518 if(m_time_of_day_send_timer < 0.0) {
519 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
520 u16 time = m_env->getTimeOfDay();
521 float time_speed = g_settings->getFloat("time_speed");
522 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
526 MutexAutoLock lock(m_env_mutex);
527 // Figure out and report maximum lag to environment
528 float max_lag = m_env->getMaxLagEstimate();
529 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
531 if(dtime > 0.1 && dtime > max_lag * 2.0)
532 infostream<<"Server: Maximum lag peaked to "<<dtime
536 m_env->reportMaxLagEstimate(max_lag);
538 ScopeProfiler sp(g_profiler, "SEnv step");
539 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
543 static const float map_timer_and_unload_dtime = 2.92;
544 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
546 MutexAutoLock lock(m_env_mutex);
547 // Run Map's timers and unload unused data
548 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
549 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
550 g_settings->getFloat("server_unload_unused_data_timeout"),
555 Listen to the admin chat, if available
558 if (!m_admin_chat->command_queue.empty()) {
559 MutexAutoLock lock(m_env_mutex);
560 while (!m_admin_chat->command_queue.empty()) {
561 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
562 handleChatInterfaceEvent(evt);
566 m_admin_chat->outgoing_queue.push_back(
567 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
574 /* Transform liquids */
575 m_liquid_transform_timer += dtime;
576 if(m_liquid_transform_timer >= m_liquid_transform_every)
578 m_liquid_transform_timer -= m_liquid_transform_every;
580 MutexAutoLock lock(m_env_mutex);
582 ScopeProfiler sp(g_profiler, "Server: liquid transform");
584 std::map<v3s16, MapBlock*> modified_blocks;
585 m_env->getMap().transformLiquids(modified_blocks, m_env);
588 Set the modified blocks unsent for all the clients
590 if (!modified_blocks.empty()) {
591 SetBlocksNotSent(modified_blocks);
594 m_clients.step(dtime);
596 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
598 // send masterserver announce
600 float &counter = m_masterserver_timer;
601 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
602 g_settings->getBool("server_announce")) {
603 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
604 ServerList::AA_START,
605 m_bind_addr.getPort(),
606 m_clients.getPlayerNames(),
608 m_env->getGameTime(),
611 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
621 Check added and deleted active objects
624 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
625 MutexAutoLock envlock(m_env_mutex);
628 const RemoteClientMap &clients = m_clients.getClientList();
629 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
631 // Radius inside which objects are active
632 static thread_local const s16 radius =
633 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
635 // Radius inside which players are active
636 static thread_local const bool is_transfer_limited =
637 g_settings->exists("unlimited_player_transfer_distance") &&
638 !g_settings->getBool("unlimited_player_transfer_distance");
639 static thread_local const s16 player_transfer_dist =
640 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
641 s16 player_radius = player_transfer_dist;
642 if (player_radius == 0 && is_transfer_limited)
643 player_radius = radius;
645 for (const auto &client_it : clients) {
646 RemoteClient *client = client_it.second;
648 // If definitions and textures have not been sent, don't
649 // send objects either
650 if (client->getState() < CS_DefinitionsSent)
653 RemotePlayer *player = m_env->getPlayer(client->peer_id);
655 // This can happen if the client timeouts somehow
659 PlayerSAO *playersao = player->getPlayerSAO();
663 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
664 if (my_radius <= 0) my_radius = radius;
665 //infostream << "Server: Active Radius " << my_radius << std::endl;
667 std::queue<u16> removed_objects;
668 std::queue<u16> added_objects;
669 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
670 client->m_known_objects, removed_objects);
671 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
672 client->m_known_objects, added_objects);
674 // Ignore if nothing happened
675 if (removed_objects.empty() && added_objects.empty()) {
679 std::string data_buffer;
683 // Handle removed objects
684 writeU16((u8*)buf, removed_objects.size());
685 data_buffer.append(buf, 2);
686 while (!removed_objects.empty()) {
688 u16 id = removed_objects.front();
689 ServerActiveObject* obj = m_env->getActiveObject(id);
691 // Add to data buffer for sending
692 writeU16((u8*)buf, id);
693 data_buffer.append(buf, 2);
695 // Remove from known objects
696 client->m_known_objects.erase(id);
698 if(obj && obj->m_known_by_count > 0)
699 obj->m_known_by_count--;
700 removed_objects.pop();
703 // Handle added objects
704 writeU16((u8*)buf, added_objects.size());
705 data_buffer.append(buf, 2);
706 while (!added_objects.empty()) {
708 u16 id = added_objects.front();
709 ServerActiveObject* obj = m_env->getActiveObject(id);
712 u8 type = ACTIVEOBJECT_TYPE_INVALID;
714 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
716 type = obj->getSendType();
718 // Add to data buffer for sending
719 writeU16((u8*)buf, id);
720 data_buffer.append(buf, 2);
721 writeU8((u8*)buf, type);
722 data_buffer.append(buf, 1);
725 data_buffer.append(serializeLongString(
726 obj->getClientInitializationData(client->net_proto_version)));
728 data_buffer.append(serializeLongString(""));
730 // Add to known objects
731 client->m_known_objects.insert(id);
734 obj->m_known_by_count++;
739 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
740 verbosestream << "Server: Sent object remove/add: "
741 << removed_objects.size() << " removed, "
742 << added_objects.size() << " added, "
743 << "packet size is " << pktSize << std::endl;
747 m_mod_storage_save_timer -= dtime;
748 if (m_mod_storage_save_timer <= 0.0f) {
749 infostream << "Saving registered mod storages." << std::endl;
750 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
751 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
752 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
753 if (it->second->isModified()) {
754 it->second->save(getModStoragePath());
764 MutexAutoLock envlock(m_env_mutex);
765 ScopeProfiler sp(g_profiler, "Server: sending object messages");
768 // Value = data sent by object
769 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
771 // Get active object messages from environment
773 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
777 std::vector<ActiveObjectMessage>* message_list = nullptr;
778 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
779 n = buffered_messages.find(aom.id);
780 if (n == buffered_messages.end()) {
781 message_list = new std::vector<ActiveObjectMessage>;
782 buffered_messages[aom.id] = message_list;
785 message_list = n->second;
787 message_list->push_back(aom);
791 const RemoteClientMap &clients = m_clients.getClientList();
792 // Route data to every client
793 for (const auto &client_it : clients) {
794 RemoteClient *client = client_it.second;
795 std::string reliable_data;
796 std::string unreliable_data;
797 // Go through all objects in message buffer
798 for (const auto &buffered_message : buffered_messages) {
799 // If object is not known by client, skip it
800 u16 id = buffered_message.first;
801 if (client->m_known_objects.find(id) == client->m_known_objects.end())
804 // Get message list of object
805 std::vector<ActiveObjectMessage>* list = buffered_message.second;
806 // Go through every message
807 for (const ActiveObjectMessage &aom : *list) {
808 // Compose the full new data with header
809 std::string new_data;
812 writeU16((u8*)&buf[0], aom.id);
813 new_data.append(buf, 2);
815 new_data += serializeString(aom.datastring);
816 // Add data to buffer
818 reliable_data += new_data;
820 unreliable_data += new_data;
824 reliable_data and unreliable_data are now ready.
827 if (!reliable_data.empty()) {
828 SendActiveObjectMessages(client->peer_id, reliable_data);
831 if (!unreliable_data.empty()) {
832 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
837 // Clear buffered_messages
838 for (auto &buffered_message : buffered_messages) {
839 delete buffered_message.second;
844 Send queued-for-sending map edit events.
847 // We will be accessing the environment
848 MutexAutoLock lock(m_env_mutex);
850 // Don't send too many at a time
853 // Single change sending is disabled if queue size is not small
854 bool disable_single_change_sending = false;
855 if(m_unsent_map_edit_queue.size() >= 4)
856 disable_single_change_sending = true;
858 int event_count = m_unsent_map_edit_queue.size();
860 // We'll log the amount of each
863 while (!m_unsent_map_edit_queue.empty()) {
864 MapEditEvent* event = m_unsent_map_edit_queue.front();
865 m_unsent_map_edit_queue.pop();
867 // Players far away from the change are stored here.
868 // Instead of sending the changes, MapBlocks are set not sent
870 std::vector<u16> far_players;
872 switch (event->type) {
875 prof.add("MEET_ADDNODE", 1);
876 sendAddNode(event->p, event->n, event->already_known_by_peer,
877 &far_players, disable_single_change_sending ? 5 : 30,
878 event->type == MEET_ADDNODE);
880 case MEET_REMOVENODE:
881 prof.add("MEET_REMOVENODE", 1);
882 sendRemoveNode(event->p, event->already_known_by_peer,
883 &far_players, disable_single_change_sending ? 5 : 30);
885 case MEET_BLOCK_NODE_METADATA_CHANGED:
886 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
887 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
888 m_clients.markBlockposAsNotSent(event->p);
891 infostream << "Server: MEET_OTHER" << std::endl;
892 prof.add("MEET_OTHER", 1);
893 for (const v3s16 &modified_block : event->modified_blocks) {
894 m_clients.markBlockposAsNotSent(modified_block);
898 prof.add("unknown", 1);
899 warningstream << "Server: Unknown MapEditEvent "
900 << ((u32)event->type) << std::endl;
905 Set blocks not sent to far players
907 if (!far_players.empty()) {
908 // Convert list format to that wanted by SetBlocksNotSent
909 std::map<v3s16, MapBlock*> modified_blocks2;
910 for (const v3s16 &modified_block : event->modified_blocks) {
911 modified_blocks2[modified_block] =
912 m_env->getMap().getBlockNoCreateNoEx(modified_block);
915 // Set blocks not sent
916 for (const u16 far_player : far_players) {
917 if (RemoteClient *client = getClient(far_player))
918 client->SetBlocksNotSent(modified_blocks2);
925 if (event_count >= 5) {
926 infostream << "Server: MapEditEvents:" << std::endl;
927 prof.print(infostream);
928 } else if (event_count != 0) {
929 verbosestream << "Server: MapEditEvents:" << std::endl;
930 prof.print(verbosestream);
936 Trigger emergethread (it somehow gets to a non-triggered but
937 bysy state sometimes)
940 float &counter = m_emergethread_trigger_timer;
942 if (counter >= 2.0) {
945 m_emerge->startThreads();
949 // Save map, players and auth stuff
951 float &counter = m_savemap_timer;
953 static thread_local const float save_interval =
954 g_settings->getFloat("server_map_save_interval");
955 if (counter >= save_interval) {
957 MutexAutoLock lock(m_env_mutex);
959 ScopeProfiler sp(g_profiler, "Server: saving stuff");
962 if (m_banmanager->isModified()) {
963 m_banmanager->save();
966 // Save changed parts of map
967 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
970 m_env->saveLoadedPlayers();
972 // Save environment metadata
977 m_shutdown_state.tick(dtime, this);
980 void Server::Receive()
985 m_con->Receive(&pkt);
986 peer_id = pkt.getPeerId();
988 } catch (const con::InvalidIncomingDataException &e) {
989 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
990 << e.what() << std::endl;
991 } catch (const SerializationError &e) {
992 infostream << "Server::Receive(): SerializationError: what()="
993 << e.what() << std::endl;
994 } catch (const ClientStateError &e) {
995 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
996 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
997 L"Try reconnecting or updating your client");
998 } catch (const con::PeerNotFoundException &e) {
1003 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1005 std::string playername;
1006 PlayerSAO *playersao = NULL;
1009 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1011 playername = client->getName();
1012 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1014 } catch (std::exception &e) {
1020 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1022 // If failed, cancel
1023 if (!playersao || !player) {
1024 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1025 actionstream << "Server: Failed to emerge player \"" << playername
1026 << "\" (player allocated to an another client)" << std::endl;
1027 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1028 L"name. If your client closed unexpectedly, try again in "
1031 errorstream << "Server: " << playername << ": Failed to emerge player"
1033 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1039 Send complete position information
1041 SendMovePlayer(peer_id);
1044 SendPlayerPrivileges(peer_id);
1046 // Send inventory formspec
1047 SendPlayerInventoryFormspec(peer_id);
1050 SendInventory(playersao);
1052 // Send HP or death screen
1053 if (playersao->isDead())
1054 SendDeathscreen(peer_id, false, v3f(0,0,0));
1056 SendPlayerHPOrDie(playersao,
1057 PlayerHPChangeReason(PlayerHPChangeReason::SET_HP));
1060 SendPlayerBreath(playersao);
1062 Address addr = getPeerAddress(player->getPeerId());
1063 std::string ip_str = addr.serializeString();
1064 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1069 const std::vector<std::string> &names = m_clients.getPlayerNames();
1071 actionstream << player->getName() << " joins game. List of players: ";
1073 for (const std::string &name : names) {
1074 actionstream << name << " ";
1077 actionstream << player->getName() <<std::endl;
1082 inline void Server::handleCommand(NetworkPacket* pkt)
1084 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1085 (this->*opHandle.handler)(pkt);
1088 void Server::ProcessData(NetworkPacket *pkt)
1090 // Environment is locked first.
1091 MutexAutoLock envlock(m_env_mutex);
1093 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1094 u32 peer_id = pkt->getPeerId();
1097 Address address = getPeerAddress(peer_id);
1098 std::string addr_s = address.serializeString();
1100 if(m_banmanager->isIpBanned(addr_s)) {
1101 std::string ban_name = m_banmanager->getBanName(addr_s);
1102 infostream << "Server: A banned client tried to connect from "
1103 << addr_s << "; banned name was "
1104 << ban_name << std::endl;
1105 // This actually doesn't seem to transfer to the client
1106 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1107 + utf8_to_wide(ban_name));
1111 catch(con::PeerNotFoundException &e) {
1113 * no peer for this packet found
1114 * most common reason is peer timeout, e.g. peer didn't
1115 * respond for some time, your server was overloaded or
1118 infostream << "Server::ProcessData(): Canceling: peer "
1119 << peer_id << " not found" << std::endl;
1124 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1126 // Command must be handled into ToServerCommandHandler
1127 if (command >= TOSERVER_NUM_MSG_TYPES) {
1128 infostream << "Server: Ignoring unknown command "
1129 << command << std::endl;
1133 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1138 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1140 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1141 errorstream << "Server::ProcessData(): Cancelling: Peer"
1142 " serialization format invalid or not initialized."
1143 " Skipping incoming command=" << command << std::endl;
1147 /* Handle commands related to client startup */
1148 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1153 if (m_clients.getClientState(peer_id) < CS_Active) {
1154 if (command == TOSERVER_PLAYERPOS) return;
1156 errorstream << "Got packet command: " << command << " for peer id "
1157 << peer_id << " but client isn't active yet. Dropping packet "
1163 } catch (SendFailedException &e) {
1164 errorstream << "Server::ProcessData(): SendFailedException: "
1165 << "what=" << e.what()
1167 } catch (PacketError &e) {
1168 actionstream << "Server::ProcessData(): PacketError: "
1169 << "what=" << e.what()
1174 void Server::setTimeOfDay(u32 time)
1176 m_env->setTimeOfDay(time);
1177 m_time_of_day_send_timer = 0;
1180 void Server::onMapEditEvent(MapEditEvent *event)
1182 if (m_ignore_map_edit_events_area.contains(event->getArea()))
1184 MapEditEvent *e = event->clone();
1185 m_unsent_map_edit_queue.push(e);
1188 Inventory* Server::getInventory(const InventoryLocation &loc)
1191 case InventoryLocation::UNDEFINED:
1192 case InventoryLocation::CURRENT_PLAYER:
1194 case InventoryLocation::PLAYER:
1196 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1199 PlayerSAO *playersao = player->getPlayerSAO();
1202 return playersao->getInventory();
1205 case InventoryLocation::NODEMETA:
1207 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1210 return meta->getInventory();
1213 case InventoryLocation::DETACHED:
1215 if(m_detached_inventories.count(loc.name) == 0)
1217 return m_detached_inventories[loc.name];
1221 sanity_check(false); // abort
1226 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1229 case InventoryLocation::UNDEFINED:
1231 case InventoryLocation::PLAYER:
1236 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1241 PlayerSAO *playersao = player->getPlayerSAO();
1245 SendInventory(playersao);
1248 case InventoryLocation::NODEMETA:
1250 v3s16 blockpos = getNodeBlockPos(loc.p);
1252 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1254 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1256 m_clients.markBlockposAsNotSent(blockpos);
1259 case InventoryLocation::DETACHED:
1261 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1265 sanity_check(false); // abort
1270 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1272 std::vector<session_t> clients = m_clients.getClientIDs();
1274 // Set the modified blocks unsent for all the clients
1275 for (const session_t client_id : clients) {
1276 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1277 client->SetBlocksNotSent(block);
1282 void Server::peerAdded(con::Peer *peer)
1284 verbosestream<<"Server::peerAdded(): peer->id="
1285 <<peer->id<<std::endl;
1287 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1290 void Server::deletingPeer(con::Peer *peer, bool timeout)
1292 verbosestream<<"Server::deletingPeer(): peer->id="
1293 <<peer->id<<", timeout="<<timeout<<std::endl;
1295 m_clients.event(peer->id, CSE_Disconnect);
1296 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1299 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1301 *retval = m_con->getPeerStat(peer_id,type);
1302 return *retval != -1;
1305 bool Server::getClientInfo(
1314 std::string* vers_string
1317 *state = m_clients.getClientState(peer_id);
1319 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1326 *uptime = client->uptime();
1327 *ser_vers = client->serialization_version;
1328 *prot_vers = client->net_proto_version;
1330 *major = client->getMajor();
1331 *minor = client->getMinor();
1332 *patch = client->getPatch();
1333 *vers_string = client->getPatch();
1340 void Server::handlePeerChanges()
1342 while(!m_peer_change_queue.empty())
1344 con::PeerChange c = m_peer_change_queue.front();
1345 m_peer_change_queue.pop();
1347 verbosestream<<"Server: Handling peer change: "
1348 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1353 case con::PEER_ADDED:
1354 m_clients.CreateClient(c.peer_id);
1357 case con::PEER_REMOVED:
1358 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1362 FATAL_ERROR("Invalid peer change event received!");
1368 void Server::printToConsoleOnly(const std::string &text)
1371 m_admin_chat->outgoing_queue.push_back(
1372 new ChatEventChat("", utf8_to_wide(text)));
1374 std::cout << text << std::endl;
1378 void Server::Send(NetworkPacket *pkt)
1380 Send(pkt->getPeerId(), pkt);
1383 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1385 m_clients.send(peer_id,
1386 clientCommandFactoryTable[pkt->getCommand()].channel,
1388 clientCommandFactoryTable[pkt->getCommand()].reliable);
1391 void Server::SendMovement(session_t peer_id)
1393 std::ostringstream os(std::ios_base::binary);
1395 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1397 pkt << g_settings->getFloat("movement_acceleration_default");
1398 pkt << g_settings->getFloat("movement_acceleration_air");
1399 pkt << g_settings->getFloat("movement_acceleration_fast");
1400 pkt << g_settings->getFloat("movement_speed_walk");
1401 pkt << g_settings->getFloat("movement_speed_crouch");
1402 pkt << g_settings->getFloat("movement_speed_fast");
1403 pkt << g_settings->getFloat("movement_speed_climb");
1404 pkt << g_settings->getFloat("movement_speed_jump");
1405 pkt << g_settings->getFloat("movement_liquid_fluidity");
1406 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1407 pkt << g_settings->getFloat("movement_liquid_sink");
1408 pkt << g_settings->getFloat("movement_gravity");
1413 void Server::SendPlayerHPOrDie(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1415 if (!g_settings->getBool("enable_damage"))
1418 session_t peer_id = playersao->getPeerID();
1419 bool is_alive = playersao->getHP() > 0;
1422 SendPlayerHP(peer_id);
1424 DiePlayer(peer_id, reason);
1427 void Server::SendHP(session_t peer_id, u16 hp)
1429 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1434 void Server::SendBreath(session_t peer_id, u16 breath)
1436 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1437 pkt << (u16) breath;
1441 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1442 const std::string &custom_reason, bool reconnect)
1444 assert(reason < SERVER_ACCESSDENIED_MAX);
1446 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1448 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1449 pkt << custom_reason;
1450 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1451 reason == SERVER_ACCESSDENIED_CRASH)
1452 pkt << custom_reason << (u8)reconnect;
1456 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1458 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1463 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1464 v3f camera_point_target)
1466 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1467 pkt << set_camera_point_target << camera_point_target;
1471 void Server::SendItemDef(session_t peer_id,
1472 IItemDefManager *itemdef, u16 protocol_version)
1474 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1478 u32 length of the next item
1479 zlib-compressed serialized ItemDefManager
1481 std::ostringstream tmp_os(std::ios::binary);
1482 itemdef->serialize(tmp_os, protocol_version);
1483 std::ostringstream tmp_os2(std::ios::binary);
1484 compressZlib(tmp_os.str(), tmp_os2);
1485 pkt.putLongString(tmp_os2.str());
1488 verbosestream << "Server: Sending item definitions to id(" << peer_id
1489 << "): size=" << pkt.getSize() << std::endl;
1494 void Server::SendNodeDef(session_t peer_id,
1495 const NodeDefManager *nodedef, u16 protocol_version)
1497 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1501 u32 length of the next item
1502 zlib-compressed serialized NodeDefManager
1504 std::ostringstream tmp_os(std::ios::binary);
1505 nodedef->serialize(tmp_os, protocol_version);
1506 std::ostringstream tmp_os2(std::ios::binary);
1507 compressZlib(tmp_os.str(), tmp_os2);
1509 pkt.putLongString(tmp_os2.str());
1512 verbosestream << "Server: Sending node definitions to id(" << peer_id
1513 << "): size=" << pkt.getSize() << std::endl;
1519 Non-static send methods
1522 void Server::SendInventory(PlayerSAO* playerSAO)
1524 UpdateCrafting(playerSAO->getPlayer());
1530 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1532 std::ostringstream os;
1533 playerSAO->getInventory()->serialize(os);
1535 std::string s = os.str();
1537 pkt.putRawString(s.c_str(), s.size());
1541 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1543 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1545 u8 type = message.type;
1546 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1548 if (peer_id != PEER_ID_INEXISTENT) {
1549 RemotePlayer *player = m_env->getPlayer(peer_id);
1555 m_clients.sendToAll(&pkt);
1559 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1560 const std::string &formname)
1562 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1563 if (formspec.empty()){
1564 //the client should close the formspec
1565 m_formspec_state_data.erase(peer_id);
1566 pkt.putLongString("");
1568 m_formspec_state_data[peer_id] = formname;
1569 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1576 // Spawns a particle on peer with peer_id
1577 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1578 v3f pos, v3f velocity, v3f acceleration,
1579 float expirationtime, float size, bool collisiondetection,
1580 bool collision_removal,
1581 bool vertical, const std::string &texture,
1582 const struct TileAnimationParams &animation, u8 glow)
1584 static thread_local const float radius =
1585 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1587 if (peer_id == PEER_ID_INEXISTENT) {
1588 std::vector<session_t> clients = m_clients.getClientIDs();
1590 for (const session_t client_id : clients) {
1591 RemotePlayer *player = m_env->getPlayer(client_id);
1595 PlayerSAO *sao = player->getPlayerSAO();
1599 // Do not send to distant clients
1600 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1603 SendSpawnParticle(client_id, player->protocol_version,
1604 pos, velocity, acceleration,
1605 expirationtime, size, collisiondetection,
1606 collision_removal, vertical, texture, animation, glow);
1611 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1613 pkt << pos << velocity << acceleration << expirationtime
1614 << size << collisiondetection;
1615 pkt.putLongString(texture);
1617 pkt << collision_removal;
1618 // This is horrible but required (why are there two ways to serialize pkts?)
1619 std::ostringstream os(std::ios_base::binary);
1620 animation.serialize(os, protocol_version);
1621 pkt.putRawString(os.str());
1627 // Adds a ParticleSpawner on peer with peer_id
1628 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1629 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1630 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1631 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1632 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1633 const struct TileAnimationParams &animation, u8 glow)
1635 if (peer_id == PEER_ID_INEXISTENT) {
1636 // This sucks and should be replaced:
1637 std::vector<session_t> clients = m_clients.getClientIDs();
1638 for (const session_t client_id : clients) {
1639 RemotePlayer *player = m_env->getPlayer(client_id);
1642 SendAddParticleSpawner(client_id, player->protocol_version,
1643 amount, spawntime, minpos, maxpos,
1644 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1645 minsize, maxsize, collisiondetection, collision_removal,
1646 attached_id, vertical, texture, id, animation, glow);
1651 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1653 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1654 << minacc << maxacc << minexptime << maxexptime << minsize
1655 << maxsize << collisiondetection;
1657 pkt.putLongString(texture);
1659 pkt << id << vertical;
1660 pkt << collision_removal;
1662 // This is horrible but required
1663 std::ostringstream os(std::ios_base::binary);
1664 animation.serialize(os, protocol_version);
1665 pkt.putRawString(os.str());
1671 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1673 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1675 // Ugly error in this packet
1678 if (peer_id != PEER_ID_INEXISTENT)
1681 m_clients.sendToAll(&pkt);
1685 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1687 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1689 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1690 << form->text << form->number << form->item << form->dir
1691 << form->align << form->offset << form->world_pos << form->size;
1696 void Server::SendHUDRemove(session_t peer_id, u32 id)
1698 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1703 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1705 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1706 pkt << id << (u8) stat;
1710 case HUD_STAT_SCALE:
1711 case HUD_STAT_ALIGN:
1712 case HUD_STAT_OFFSET:
1713 pkt << *(v2f *) value;
1717 pkt << *(std::string *) value;
1719 case HUD_STAT_WORLD_POS:
1720 pkt << *(v3f *) value;
1723 pkt << *(v2s32 *) value;
1725 case HUD_STAT_NUMBER:
1729 pkt << *(u32 *) value;
1736 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1738 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1740 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1742 pkt << flags << mask;
1747 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1749 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1750 pkt << param << value;
1754 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1755 const std::string &type, const std::vector<std::string> ¶ms,
1758 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1759 pkt << bgcolor << type << (u16) params.size();
1761 for (const std::string ¶m : params)
1769 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1771 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1772 pkt << params.density << params.color_bright << params.color_ambient
1773 << params.height << params.thickness << params.speed;
1777 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1780 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1783 pkt << do_override << (u16) (ratio * 65535);
1788 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1790 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1791 pkt << time << time_speed;
1793 if (peer_id == PEER_ID_INEXISTENT) {
1794 m_clients.sendToAll(&pkt);
1801 void Server::SendPlayerHP(session_t peer_id)
1803 PlayerSAO *playersao = getPlayerSAO(peer_id);
1804 // In some rare case if the player is disconnected
1805 // while Lua call l_punch, for example, this can be NULL
1809 SendHP(peer_id, playersao->getHP());
1810 m_script->player_event(playersao,"health_changed");
1812 // Send to other clients
1813 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1814 ActiveObjectMessage aom(playersao->getId(), true, str);
1815 playersao->m_messages_out.push(aom);
1818 void Server::SendPlayerBreath(PlayerSAO *sao)
1822 m_script->player_event(sao, "breath_changed");
1823 SendBreath(sao->getPeerID(), sao->getBreath());
1826 void Server::SendMovePlayer(session_t peer_id)
1828 RemotePlayer *player = m_env->getPlayer(peer_id);
1830 PlayerSAO *sao = player->getPlayerSAO();
1833 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1834 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1837 v3f pos = sao->getBasePosition();
1838 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1839 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1840 << " pitch=" << sao->getPitch()
1841 << " yaw=" << sao->getYaw()
1848 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1849 f32 animation_speed)
1851 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1854 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1855 << animation_frames[3] << animation_speed;
1860 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1862 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1863 pkt << first << third;
1867 void Server::SendPlayerPrivileges(session_t peer_id)
1869 RemotePlayer *player = m_env->getPlayer(peer_id);
1871 if(player->getPeerId() == PEER_ID_INEXISTENT)
1874 std::set<std::string> privs;
1875 m_script->getAuth(player->getName(), NULL, &privs);
1877 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1878 pkt << (u16) privs.size();
1880 for (const std::string &priv : privs) {
1887 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1889 RemotePlayer *player = m_env->getPlayer(peer_id);
1891 if (player->getPeerId() == PEER_ID_INEXISTENT)
1894 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1895 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1899 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1901 RemotePlayer *player = m_env->getPlayer(peer_id);
1903 if (player->getPeerId() == PEER_ID_INEXISTENT)
1906 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1907 pkt << FORMSPEC_VERSION_STRING + player->formspec_prepend;
1911 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1913 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1914 pkt.putRawString(datas.c_str(), datas.size());
1916 return pkt.getSize();
1919 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1922 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1923 datas.size(), peer_id);
1925 pkt.putRawString(datas.c_str(), datas.size());
1927 m_clients.send(pkt.getPeerId(),
1928 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1932 void Server::SendCSMRestrictionFlags(session_t peer_id)
1934 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
1935 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
1936 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
1940 s32 Server::playSound(const SimpleSoundSpec &spec,
1941 const ServerSoundParams ¶ms)
1943 // Find out initial position of sound
1944 bool pos_exists = false;
1945 v3f pos = params.getPos(m_env, &pos_exists);
1946 // If position is not found while it should be, cancel sound
1947 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1950 // Filter destination clients
1951 std::vector<session_t> dst_clients;
1952 if(!params.to_player.empty()) {
1953 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1955 infostream<<"Server::playSound: Player \""<<params.to_player
1956 <<"\" not found"<<std::endl;
1959 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1960 infostream<<"Server::playSound: Player \""<<params.to_player
1961 <<"\" not connected"<<std::endl;
1964 dst_clients.push_back(player->getPeerId());
1966 std::vector<session_t> clients = m_clients.getClientIDs();
1968 for (const session_t client_id : clients) {
1969 RemotePlayer *player = m_env->getPlayer(client_id);
1973 PlayerSAO *sao = player->getPlayerSAO();
1978 if(sao->getBasePosition().getDistanceFrom(pos) >
1979 params.max_hear_distance)
1982 dst_clients.push_back(client_id);
1986 if(dst_clients.empty())
1990 s32 id = m_next_sound_id++;
1991 // The sound will exist as a reference in m_playing_sounds
1992 m_playing_sounds[id] = ServerPlayingSound();
1993 ServerPlayingSound &psound = m_playing_sounds[id];
1994 psound.params = params;
1997 float gain = params.gain * spec.gain;
1998 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1999 pkt << id << spec.name << gain
2000 << (u8) params.type << pos << params.object
2001 << params.loop << params.fade << params.pitch;
2003 // Backwards compability
2004 bool play_sound = gain > 0;
2006 for (const u16 dst_client : dst_clients) {
2007 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2008 psound.clients.insert(dst_client);
2009 m_clients.send(dst_client, 0, &pkt, true);
2014 void Server::stopSound(s32 handle)
2016 // Get sound reference
2017 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2018 m_playing_sounds.find(handle);
2019 if (i == m_playing_sounds.end())
2021 ServerPlayingSound &psound = i->second;
2023 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2026 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2027 si != psound.clients.end(); ++si) {
2029 m_clients.send(*si, 0, &pkt, true);
2031 // Remove sound reference
2032 m_playing_sounds.erase(i);
2035 void Server::fadeSound(s32 handle, float step, float gain)
2037 // Get sound reference
2038 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2039 m_playing_sounds.find(handle);
2040 if (i == m_playing_sounds.end())
2043 ServerPlayingSound &psound = i->second;
2044 psound.params.gain = gain;
2046 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2047 pkt << handle << step << gain;
2049 // Backwards compability
2050 bool play_sound = gain > 0;
2051 ServerPlayingSound compat_psound = psound;
2052 compat_psound.clients.clear();
2054 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2055 compat_pkt << handle;
2057 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2058 it != psound.clients.end();) {
2059 if (m_clients.getProtocolVersion(*it) >= 32) {
2061 m_clients.send(*it, 0, &pkt, true);
2064 compat_psound.clients.insert(*it);
2066 m_clients.send(*it, 0, &compat_pkt, true);
2067 psound.clients.erase(it++);
2071 // Remove sound reference
2072 if (!play_sound || psound.clients.empty())
2073 m_playing_sounds.erase(i);
2075 if (play_sound && !compat_psound.clients.empty()) {
2076 // Play new sound volume on older clients
2077 playSound(compat_psound.spec, compat_psound.params);
2081 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2082 std::vector<u16> *far_players, float far_d_nodes)
2084 float maxd = far_d_nodes*BS;
2085 v3f p_f = intToFloat(p, BS);
2087 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2090 std::vector<session_t> clients = m_clients.getClientIDs();
2091 for (session_t client_id : clients) {
2094 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2095 PlayerSAO *sao = player->getPlayerSAO();
2099 // If player is far away, only set modified blocks not sent
2100 v3f player_pos = sao->getBasePosition();
2101 if (player_pos.getDistanceFrom(p_f) > maxd) {
2102 far_players->push_back(client_id);
2109 m_clients.send(client_id, 0, &pkt, true);
2113 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2114 std::vector<u16> *far_players, float far_d_nodes,
2115 bool remove_metadata)
2117 float maxd = far_d_nodes*BS;
2118 v3f p_f = intToFloat(p, BS);
2120 std::vector<session_t> clients = m_clients.getClientIDs();
2121 for (const session_t client_id : clients) {
2124 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2125 PlayerSAO *sao = player->getPlayerSAO();
2129 // If player is far away, only set modified blocks not sent
2130 v3f player_pos = sao->getBasePosition();
2131 if(player_pos.getDistanceFrom(p_f) > maxd) {
2132 far_players->push_back(client_id);
2138 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2140 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2142 pkt << p << n.param0 << n.param1 << n.param2
2143 << (u8) (remove_metadata ? 0 : 1);
2148 if (pkt.getSize() > 0)
2149 m_clients.send(client_id, 0, &pkt, true);
2153 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2154 u16 net_proto_version)
2157 Create a packet with the block in the right format
2160 std::ostringstream os(std::ios_base::binary);
2161 block->serialize(os, ver, false);
2162 block->serializeNetworkSpecific(os);
2163 std::string s = os.str();
2165 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2167 pkt << block->getPos();
2168 pkt.putRawString(s.c_str(), s.size());
2172 void Server::SendBlocks(float dtime)
2174 MutexAutoLock envlock(m_env_mutex);
2175 //TODO check if one big lock could be faster then multiple small ones
2177 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2179 std::vector<PrioritySortedBlockTransfer> queue;
2181 u32 total_sending = 0;
2184 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2186 std::vector<session_t> clients = m_clients.getClientIDs();
2189 for (const session_t client_id : clients) {
2190 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2195 total_sending += client->getSendingCount();
2196 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2202 // Lowest priority number comes first.
2203 // Lowest is most important.
2204 std::sort(queue.begin(), queue.end());
2208 // Maximal total count calculation
2209 // The per-client block sends is halved with the maximal online users
2210 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2211 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2213 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2214 if (total_sending >= max_blocks_to_send)
2217 MapBlock *block = nullptr;
2219 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2220 } catch (const InvalidPositionException &e) {
2224 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2229 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2230 client->net_proto_version);
2232 client->SentBlock(block_to_send.pos);
2238 void Server::fillMediaCache()
2240 infostream<<"Server: Calculating media file checksums"<<std::endl;
2242 // Collect all media file paths
2243 std::vector<std::string> paths;
2244 m_modmgr->getModsMediaPaths(paths);
2245 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2246 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2248 // Collect media file information from paths into cache
2249 for (const std::string &mediapath : paths) {
2250 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2251 for (const fs::DirListNode &dln : dirlist) {
2252 if (dln.dir) // Ignode dirs
2254 std::string filename = dln.name;
2255 // If name contains illegal characters, ignore the file
2256 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2257 infostream<<"Server: ignoring illegal file name: \""
2258 << filename << "\"" << std::endl;
2261 // If name is not in a supported format, ignore it
2262 const char *supported_ext[] = {
2263 ".png", ".jpg", ".bmp", ".tga",
2264 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2266 ".x", ".b3d", ".md2", ".obj",
2267 // Custom translation file format
2271 if (removeStringEnd(filename, supported_ext).empty()){
2272 infostream << "Server: ignoring unsupported file extension: \""
2273 << filename << "\"" << std::endl;
2276 // Ok, attempt to load the file and add to cache
2277 std::string filepath;
2278 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2281 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2283 errorstream << "Server::fillMediaCache(): Could not open \""
2284 << filename << "\" for reading" << std::endl;
2287 std::ostringstream tmp_os(std::ios_base::binary);
2291 fis.read(buf, 1024);
2292 std::streamsize len = fis.gcount();
2293 tmp_os.write(buf, len);
2302 errorstream<<"Server::fillMediaCache(): Failed to read \""
2303 << filename << "\"" << std::endl;
2306 if(tmp_os.str().length() == 0) {
2307 errorstream << "Server::fillMediaCache(): Empty file \""
2308 << filepath << "\"" << std::endl;
2313 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2315 unsigned char *digest = sha1.getDigest();
2316 std::string sha1_base64 = base64_encode(digest, 20);
2317 std::string sha1_hex = hex_encode((char*)digest, 20);
2321 m_media[filename] = MediaInfo(filepath, sha1_base64);
2322 verbosestream << "Server: " << sha1_hex << " is " << filename
2328 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2330 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2334 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2337 std::string lang_suffix;
2338 lang_suffix.append(".").append(lang_code).append(".tr");
2339 for (const auto &i : m_media) {
2340 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2347 for (const auto &i : m_media) {
2348 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2350 pkt << i.first << i.second.sha1_digest;
2353 pkt << g_settings->get("remote_media");
2357 struct SendableMedia
2363 SendableMedia(const std::string &name_="", const std::string &path_="",
2364 const std::string &data_=""):
2371 void Server::sendRequestedMedia(session_t peer_id,
2372 const std::vector<std::string> &tosend)
2374 verbosestream<<"Server::sendRequestedMedia(): "
2375 <<"Sending files to client"<<std::endl;
2379 // Put 5kB in one bunch (this is not accurate)
2380 u32 bytes_per_bunch = 5000;
2382 std::vector< std::vector<SendableMedia> > file_bunches;
2383 file_bunches.emplace_back();
2385 u32 file_size_bunch_total = 0;
2387 for (const std::string &name : tosend) {
2388 if (m_media.find(name) == m_media.end()) {
2389 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2390 <<"unknown file \""<<(name)<<"\""<<std::endl;
2394 //TODO get path + name
2395 std::string tpath = m_media[name].path;
2398 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2400 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2401 <<tpath<<"\" for reading"<<std::endl;
2404 std::ostringstream tmp_os(std::ios_base::binary);
2408 fis.read(buf, 1024);
2409 std::streamsize len = fis.gcount();
2410 tmp_os.write(buf, len);
2411 file_size_bunch_total += len;
2420 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2421 <<name<<"\""<<std::endl;
2424 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2425 <<tname<<"\""<<std::endl;*/
2427 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2429 // Start next bunch if got enough data
2430 if(file_size_bunch_total >= bytes_per_bunch) {
2431 file_bunches.emplace_back();
2432 file_size_bunch_total = 0;
2437 /* Create and send packets */
2439 u16 num_bunches = file_bunches.size();
2440 for (u16 i = 0; i < num_bunches; i++) {
2443 u16 total number of texture bunches
2444 u16 index of this bunch
2445 u32 number of files in this bunch
2454 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2455 pkt << num_bunches << i << (u32) file_bunches[i].size();
2457 for (const SendableMedia &j : file_bunches[i]) {
2459 pkt.putLongString(j.data);
2462 verbosestream << "Server::sendRequestedMedia(): bunch "
2463 << i << "/" << num_bunches
2464 << " files=" << file_bunches[i].size()
2465 << " size=" << pkt.getSize() << std::endl;
2470 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2472 if(m_detached_inventories.count(name) == 0) {
2473 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2476 Inventory *inv = m_detached_inventories[name];
2477 std::ostringstream os(std::ios_base::binary);
2479 os << serializeString(name);
2483 std::string s = os.str();
2485 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2486 pkt.putRawString(s.c_str(), s.size());
2488 const std::string &check = m_detached_inventories_player[name];
2489 if (peer_id == PEER_ID_INEXISTENT) {
2491 return m_clients.sendToAll(&pkt);
2492 RemotePlayer *p = m_env->getPlayer(check.c_str());
2494 m_clients.send(p->getPeerId(), 0, &pkt, true);
2496 if (check.empty() || getPlayerName(peer_id) == check)
2501 void Server::sendDetachedInventories(session_t peer_id)
2503 for (const auto &detached_inventory : m_detached_inventories) {
2504 const std::string &name = detached_inventory.first;
2505 //Inventory *inv = i->second;
2506 sendDetachedInventory(name, peer_id);
2514 void Server::DiePlayer(session_t peer_id, const PlayerHPChangeReason &reason)
2516 PlayerSAO *playersao = getPlayerSAO(peer_id);
2517 // In some rare cases this can be NULL -- if the player is disconnected
2518 // when a Lua function modifies l_punch, for example
2522 infostream << "Server::DiePlayer(): Player "
2523 << playersao->getPlayer()->getName()
2524 << " dies" << std::endl;
2526 playersao->setHP(0, reason);
2527 playersao->clearParentAttachment();
2529 // Trigger scripted stuff
2530 m_script->on_dieplayer(playersao, reason);
2532 SendPlayerHP(peer_id);
2533 SendDeathscreen(peer_id, false, v3f(0,0,0));
2536 void Server::RespawnPlayer(session_t peer_id)
2538 PlayerSAO *playersao = getPlayerSAO(peer_id);
2541 infostream << "Server::RespawnPlayer(): Player "
2542 << playersao->getPlayer()->getName()
2543 << " respawns" << std::endl;
2545 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2546 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2547 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2549 bool repositioned = m_script->on_respawnplayer(playersao);
2550 if (!repositioned) {
2551 // setPos will send the new position to client
2552 playersao->setPos(findSpawnPos());
2555 SendPlayerHP(peer_id);
2559 void Server::DenySudoAccess(session_t peer_id)
2561 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2566 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2567 const std::string &str_reason, bool reconnect)
2569 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2571 m_clients.event(peer_id, CSE_SetDenied);
2572 DisconnectPeer(peer_id);
2576 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2577 const std::string &custom_reason)
2579 SendAccessDenied(peer_id, reason, custom_reason);
2580 m_clients.event(peer_id, CSE_SetDenied);
2581 DisconnectPeer(peer_id);
2584 // 13/03/15: remove this function when protocol version 25 will become
2585 // the minimum version for MT users, maybe in 1 year
2586 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2588 SendAccessDenied_Legacy(peer_id, reason);
2589 m_clients.event(peer_id, CSE_SetDenied);
2590 DisconnectPeer(peer_id);
2593 void Server::DisconnectPeer(session_t peer_id)
2595 m_modchannel_mgr->leaveAllChannels(peer_id);
2596 m_con->DisconnectPeer(peer_id);
2599 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2602 RemoteClient* client = getClient(peer_id, CS_Invalid);
2604 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2606 // Right now, the auth mechs don't change between login and sudo mode.
2607 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2608 client->allowed_sudo_mechs = sudo_auth_mechs;
2610 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2611 << g_settings->getFloat("dedicated_server_step")
2615 m_clients.event(peer_id, CSE_AuthAccept);
2617 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2619 // We only support SRP right now
2620 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2622 resp_pkt << sudo_auth_mechs;
2624 m_clients.event(peer_id, CSE_SudoSuccess);
2628 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2630 std::wstring message;
2633 Clear references to playing sounds
2635 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2636 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2637 ServerPlayingSound &psound = i->second;
2638 psound.clients.erase(peer_id);
2639 if (psound.clients.empty())
2640 m_playing_sounds.erase(i++);
2645 // clear formspec info so the next client can't abuse the current state
2646 m_formspec_state_data.erase(peer_id);
2648 RemotePlayer *player = m_env->getPlayer(peer_id);
2650 /* Run scripts and remove from environment */
2652 PlayerSAO *playersao = player->getPlayerSAO();
2655 playersao->clearChildAttachments();
2656 playersao->clearParentAttachment();
2658 // inform connected clients
2659 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2660 // (u16) 1 + std::string represents a vector serialization representation
2661 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2662 m_clients.sendToAll(¬ice);
2664 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2666 playersao->disconnected();
2673 if (player && reason != CDR_DENY) {
2674 std::ostringstream os(std::ios_base::binary);
2675 std::vector<session_t> clients = m_clients.getClientIDs();
2677 for (const session_t client_id : clients) {
2679 RemotePlayer *player = m_env->getPlayer(client_id);
2683 // Get name of player
2684 os << player->getName() << " ";
2687 std::string name = player->getName();
2688 actionstream << name << " "
2689 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2690 << " List of players: " << os.str() << std::endl;
2692 m_admin_chat->outgoing_queue.push_back(
2693 new ChatEventNick(CET_NICK_REMOVE, name));
2697 MutexAutoLock env_lock(m_env_mutex);
2698 m_clients.DeleteClient(peer_id);
2702 // Send leave chat message to all remaining clients
2703 if (!message.empty()) {
2704 SendChatMessage(PEER_ID_INEXISTENT,
2705 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2709 void Server::UpdateCrafting(RemotePlayer *player)
2711 InventoryList *clist = player->inventory.getList("craft");
2712 if (!clist || clist->getSize() == 0)
2715 // Get a preview for crafting
2717 InventoryLocation loc;
2718 loc.setPlayer(player->getName());
2719 std::vector<ItemStack> output_replacements;
2720 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2721 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2724 InventoryList *plist = player->inventory.getList("craftpreview");
2725 if (plist && plist->getSize() >= 1) {
2726 // Put the new preview in
2727 plist->changeItem(0, preview);
2731 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2733 if (evt->type == CET_NICK_ADD) {
2734 // The terminal informed us of its nick choice
2735 m_admin_nick = ((ChatEventNick *)evt)->nick;
2736 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2737 errorstream << "You haven't set up an account." << std::endl
2738 << "Please log in using the client as '"
2739 << m_admin_nick << "' with a secure password." << std::endl
2740 << "Until then, you can't execute admin tasks via the console," << std::endl
2741 << "and everybody can claim the user account instead of you," << std::endl
2742 << "giving them full control over this server." << std::endl;
2745 assert(evt->type == CET_CHAT);
2746 handleAdminChat((ChatEventChat *)evt);
2750 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2751 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2753 // If something goes wrong, this player is to blame
2754 RollbackScopeActor rollback_scope(m_rollback,
2755 std::string("player:") + name);
2757 if (g_settings->getBool("strip_color_codes"))
2758 wmessage = unescape_enriched(wmessage);
2761 switch (player->canSendChatMessage()) {
2762 case RPLAYER_CHATRESULT_FLOODING: {
2763 std::wstringstream ws;
2764 ws << L"You cannot send more messages. You are limited to "
2765 << g_settings->getFloat("chat_message_limit_per_10sec")
2766 << L" messages per 10 seconds.";
2769 case RPLAYER_CHATRESULT_KICK:
2770 DenyAccess_Legacy(player->getPeerId(),
2771 L"You have been kicked due to message flooding.");
2773 case RPLAYER_CHATRESULT_OK:
2776 FATAL_ERROR("Unhandled chat filtering result found.");
2780 if (m_max_chatmessage_length > 0
2781 && wmessage.length() > m_max_chatmessage_length) {
2782 return L"Your message exceed the maximum chat message limit set on the server. "
2783 L"It was refused. Send a shorter message";
2786 // Run script hook, exit if script ate the chat message
2787 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2792 // Whether to send line to the player that sent the message, or to all players
2793 bool broadcast_line = true;
2795 if (check_shout_priv && !checkPriv(name, "shout")) {
2796 line += L"-!- You don't have permission to shout.";
2797 broadcast_line = false;
2806 Tell calling method to send the message to sender
2808 if (!broadcast_line)
2812 Send the message to others
2814 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2816 std::vector<session_t> clients = m_clients.getClientIDs();
2819 Send the message back to the inital sender
2820 if they are using protocol version >= 29
2823 session_t peer_id_to_avoid_sending =
2824 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2826 if (player && player->protocol_version >= 29)
2827 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2829 for (u16 cid : clients) {
2830 if (cid != peer_id_to_avoid_sending)
2831 SendChatMessage(cid, ChatMessage(line));
2836 void Server::handleAdminChat(const ChatEventChat *evt)
2838 std::string name = evt->nick;
2839 std::wstring wname = utf8_to_wide(name);
2840 std::wstring wmessage = evt->evt_msg;
2842 std::wstring answer = handleChat(name, wname, wmessage);
2844 // If asked to send answer to sender
2845 if (!answer.empty()) {
2846 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2850 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2852 RemoteClient *client = getClientNoEx(peer_id,state_min);
2854 throw ClientNotFoundException("Client not found");
2858 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2860 return m_clients.getClientNoEx(peer_id, state_min);
2863 std::string Server::getPlayerName(session_t peer_id)
2865 RemotePlayer *player = m_env->getPlayer(peer_id);
2867 return "[id="+itos(peer_id)+"]";
2868 return player->getName();
2871 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2873 RemotePlayer *player = m_env->getPlayer(peer_id);
2876 return player->getPlayerSAO();
2879 std::wstring Server::getStatusString()
2881 std::wostringstream os(std::ios_base::binary);
2884 os<<L"version="<<narrow_to_wide(g_version_string);
2886 os<<L", uptime="<<m_uptime.get();
2888 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2889 // Information about clients
2892 std::vector<session_t> clients = m_clients.getClientIDs();
2893 for (session_t client_id : clients) {
2895 RemotePlayer *player = m_env->getPlayer(client_id);
2896 // Get name of player
2897 std::wstring name = L"unknown";
2899 name = narrow_to_wide(player->getName());
2900 // Add name to information string
2909 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2910 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2912 if (!g_settings->get("motd").empty())
2913 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2917 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2919 std::set<std::string> privs;
2920 m_script->getAuth(name, NULL, &privs);
2924 bool Server::checkPriv(const std::string &name, const std::string &priv)
2926 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2927 return (privs.count(priv) != 0);
2930 void Server::reportPrivsModified(const std::string &name)
2933 std::vector<session_t> clients = m_clients.getClientIDs();
2934 for (const session_t client_id : clients) {
2935 RemotePlayer *player = m_env->getPlayer(client_id);
2936 reportPrivsModified(player->getName());
2939 RemotePlayer *player = m_env->getPlayer(name.c_str());
2942 SendPlayerPrivileges(player->getPeerId());
2943 PlayerSAO *sao = player->getPlayerSAO();
2946 sao->updatePrivileges(
2947 getPlayerEffectivePrivs(name),
2952 void Server::reportInventoryFormspecModified(const std::string &name)
2954 RemotePlayer *player = m_env->getPlayer(name.c_str());
2957 SendPlayerInventoryFormspec(player->getPeerId());
2960 void Server::reportFormspecPrependModified(const std::string &name)
2962 RemotePlayer *player = m_env->getPlayer(name.c_str());
2965 SendPlayerFormspecPrepend(player->getPeerId());
2968 void Server::setIpBanned(const std::string &ip, const std::string &name)
2970 m_banmanager->add(ip, name);
2973 void Server::unsetIpBanned(const std::string &ip_or_name)
2975 m_banmanager->remove(ip_or_name);
2978 std::string Server::getBanDescription(const std::string &ip_or_name)
2980 return m_banmanager->getBanDescription(ip_or_name);
2983 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2985 // m_env will be NULL if the server is initializing
2989 if (m_admin_nick == name && !m_admin_nick.empty()) {
2990 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2993 RemotePlayer *player = m_env->getPlayer(name);
2998 if (player->getPeerId() == PEER_ID_INEXISTENT)
3001 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3004 bool Server::showFormspec(const char *playername, const std::string &formspec,
3005 const std::string &formname)
3007 // m_env will be NULL if the server is initializing
3011 RemotePlayer *player = m_env->getPlayer(playername);
3015 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3019 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3024 u32 id = player->addHud(form);
3026 SendHUDAdd(player->getPeerId(), id, form);
3031 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3035 HudElement* todel = player->removeHud(id);
3042 SendHUDRemove(player->getPeerId(), id);
3046 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3051 SendHUDChange(player->getPeerId(), id, stat, data);
3055 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3060 SendHUDSetFlags(player->getPeerId(), flags, mask);
3061 player->hud_flags &= ~mask;
3062 player->hud_flags |= flags;
3064 PlayerSAO* playersao = player->getPlayerSAO();
3069 m_script->player_event(playersao, "hud_changed");
3073 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3078 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3081 player->setHotbarItemcount(hotbar_itemcount);
3082 std::ostringstream os(std::ios::binary);
3083 writeS32(os, hotbar_itemcount);
3084 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3088 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3093 player->setHotbarImage(name);
3094 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3097 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3102 player->setHotbarSelectedImage(name);
3103 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3106 Address Server::getPeerAddress(session_t peer_id)
3108 return m_con->GetPeerAddress(peer_id);
3111 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3112 v2s32 animation_frames[4], f32 frame_speed)
3114 sanity_check(player);
3115 player->setLocalAnimations(animation_frames, frame_speed);
3116 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3119 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3121 sanity_check(player);
3122 player->eye_offset_first = first;
3123 player->eye_offset_third = third;
3124 SendEyeOffset(player->getPeerId(), first, third);
3127 void Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3128 const std::string &type, const std::vector<std::string> ¶ms,
3131 sanity_check(player);
3132 player->setSky(bgcolor, type, params, clouds);
3133 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3136 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3138 sanity_check(player);
3139 player->setCloudParams(params);
3140 SendCloudParams(player->getPeerId(), params);
3143 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3149 player->overrideDayNightRatio(do_override, ratio);
3150 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3154 void Server::notifyPlayers(const std::wstring &msg)
3156 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3159 void Server::spawnParticle(const std::string &playername, v3f pos,
3160 v3f velocity, v3f acceleration,
3161 float expirationtime, float size, bool
3162 collisiondetection, bool collision_removal,
3163 bool vertical, const std::string &texture,
3164 const struct TileAnimationParams &animation, u8 glow)
3166 // m_env will be NULL if the server is initializing
3170 session_t peer_id = PEER_ID_INEXISTENT;
3172 if (!playername.empty()) {
3173 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3176 peer_id = player->getPeerId();
3177 proto_ver = player->protocol_version;
3180 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3181 expirationtime, size, collisiondetection,
3182 collision_removal, vertical, texture, animation, glow);
3185 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3186 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3187 float minexptime, float maxexptime, float minsize, float maxsize,
3188 bool collisiondetection, bool collision_removal,
3189 ServerActiveObject *attached, bool vertical, const std::string &texture,
3190 const std::string &playername, const struct TileAnimationParams &animation,
3193 // m_env will be NULL if the server is initializing
3197 session_t peer_id = PEER_ID_INEXISTENT;
3199 if (!playername.empty()) {
3200 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3203 peer_id = player->getPeerId();
3204 proto_ver = player->protocol_version;
3207 u16 attached_id = attached ? attached->getId() : 0;
3210 if (attached_id == 0)
3211 id = m_env->addParticleSpawner(spawntime);
3213 id = m_env->addParticleSpawner(spawntime, attached_id);
3215 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3216 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3217 minexptime, maxexptime, minsize, maxsize,
3218 collisiondetection, collision_removal, attached_id, vertical,
3219 texture, id, animation, glow);
3224 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3226 // m_env will be NULL if the server is initializing
3228 throw ServerError("Can't delete particle spawners during initialisation!");
3230 session_t peer_id = PEER_ID_INEXISTENT;
3231 if (!playername.empty()) {
3232 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3235 peer_id = player->getPeerId();
3238 m_env->deleteParticleSpawner(id);
3239 SendDeleteParticleSpawner(peer_id, id);
3242 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3244 if(m_detached_inventories.count(name) > 0){
3245 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3246 delete m_detached_inventories[name];
3248 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3250 Inventory *inv = new Inventory(m_itemdef);
3252 m_detached_inventories[name] = inv;
3253 m_detached_inventories_player[name] = player;
3254 //TODO find a better way to do this
3255 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3259 // actions: time-reversed list
3260 // Return value: success/failure
3261 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3262 std::list<std::string> *log)
3264 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3265 ServerMap *map = (ServerMap*)(&m_env->getMap());
3267 // Fail if no actions to handle
3268 if (actions.empty()) {
3270 log->push_back("Nothing to do.");
3277 for (const RollbackAction &action : actions) {
3279 bool success = action.applyRevert(map, this, this);
3282 std::ostringstream os;
3283 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3284 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3286 log->push_back(os.str());
3288 std::ostringstream os;
3289 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3290 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3292 log->push_back(os.str());
3296 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3297 <<" failed"<<std::endl;
3299 // Call it done if less than half failed
3300 return num_failed <= num_tried/2;
3303 // IGameDef interface
3305 IItemDefManager *Server::getItemDefManager()
3310 const NodeDefManager *Server::getNodeDefManager()
3315 ICraftDefManager *Server::getCraftDefManager()
3320 u16 Server::allocateUnknownNodeId(const std::string &name)
3322 return m_nodedef->allocateDummy(name);
3325 IWritableItemDefManager *Server::getWritableItemDefManager()
3330 NodeDefManager *Server::getWritableNodeDefManager()
3335 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3340 const std::vector<ModSpec> & Server::getMods() const
3342 return m_modmgr->getMods();
3345 const ModSpec *Server::getModSpec(const std::string &modname) const
3347 return m_modmgr->getModSpec(modname);
3350 void Server::getModNames(std::vector<std::string> &modlist)
3352 m_modmgr->getModNames(modlist);
3355 std::string Server::getBuiltinLuaPath()
3357 return porting::path_share + DIR_DELIM + "builtin";
3360 std::string Server::getModStoragePath() const
3362 return m_path_world + DIR_DELIM + "mod_storage";
3365 v3f Server::findSpawnPos()
3367 ServerMap &map = m_env->getServerMap();
3369 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3370 return nodeposf * BS;
3373 bool is_good = false;
3374 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3375 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3377 // Try to find a good place a few times
3378 for(s32 i = 0; i < 4000 && !is_good; i++) {
3379 s32 range = MYMIN(1 + i, range_max);
3380 // We're going to try to throw the player to this position
3381 v2s16 nodepos2d = v2s16(
3382 -range + (myrand() % (range * 2)),
3383 -range + (myrand() % (range * 2)));
3385 // Get spawn level at point
3386 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3387 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3388 // the mapgen to signify an unsuitable spawn position
3389 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3392 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3395 for (s32 i = 0; i < 10; i++) {
3396 v3s16 blockpos = getNodeBlockPos(nodepos);
3397 map.emergeBlock(blockpos, true);
3398 content_t c = map.getNodeNoEx(nodepos).getContent();
3399 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3401 if (air_count >= 2) {
3402 nodeposf = intToFloat(nodepos, BS);
3403 // Don't spawn the player outside map boundaries
3404 if (objectpos_over_limit(nodeposf))
3417 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3419 if (delay == 0.0f) {
3420 // No delay, shutdown immediately
3421 m_shutdown_state.is_requested = true;
3422 // only print to the infostream, a chat message saying
3423 // "Server Shutting Down" is sent when the server destructs.
3424 infostream << "*** Immediate Server shutdown requested." << std::endl;
3425 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3426 // Negative delay, cancel shutdown if requested
3427 m_shutdown_state.reset();
3428 std::wstringstream ws;
3430 ws << L"*** Server shutdown canceled.";
3432 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3433 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3434 // m_shutdown_* are already handled, skip.
3436 } else if (delay > 0.0f) {
3437 // Positive delay, tell the clients when the server will shut down
3438 std::wstringstream ws;
3440 ws << L"*** Server shutting down in "
3441 << duration_to_string(myround(delay)).c_str()
3444 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3445 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3448 m_shutdown_state.trigger(delay, msg, reconnect);
3451 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3454 Try to get an existing player
3456 RemotePlayer *player = m_env->getPlayer(name);
3458 // If player is already connected, cancel
3459 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3460 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3465 If player with the wanted peer_id already exists, cancel.
3467 if (m_env->getPlayer(peer_id)) {
3468 infostream<<"emergePlayer(): Player with wrong name but same"
3469 " peer_id already exists"<<std::endl;
3474 player = new RemotePlayer(name, idef());
3477 bool newplayer = false;
3480 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3482 // Complete init with server parts
3483 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3484 player->protocol_version = proto_version;
3488 m_script->on_newplayer(playersao);
3494 bool Server::registerModStorage(ModMetadata *storage)
3496 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3497 errorstream << "Unable to register same mod storage twice. Storage name: "
3498 << storage->getModName() << std::endl;
3502 m_mod_storages[storage->getModName()] = storage;
3506 void Server::unregisterModStorage(const std::string &name)
3508 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3509 if (it != m_mod_storages.end()) {
3510 // Save unconditionaly on unregistration
3511 it->second->save(getModStoragePath());
3512 m_mod_storages.erase(name);
3516 void dedicated_server_loop(Server &server, bool &kill)
3518 verbosestream<<"dedicated_server_loop()"<<std::endl;
3520 IntervalLimiter m_profiler_interval;
3522 static thread_local const float steplen =
3523 g_settings->getFloat("dedicated_server_step");
3524 static thread_local const float profiler_print_interval =
3525 g_settings->getFloat("profiler_print_interval");
3528 // This is kind of a hack but can be done like this
3529 // because server.step() is very light
3531 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3532 sleep_ms((int)(steplen*1000.0));
3534 server.step(steplen);
3536 if (server.isShutdownRequested() || kill)
3542 if (profiler_print_interval != 0) {
3543 if(m_profiler_interval.step(steplen, profiler_print_interval))
3545 infostream<<"Profiler:"<<std::endl;
3546 g_profiler->print(infostream);
3547 g_profiler->clear();
3552 infostream << "Dedicated server quitting" << std::endl;
3554 if (g_settings->getBool("server_announce"))
3555 ServerList::sendAnnounce(ServerList::AA_DELETE,
3556 server.m_bind_addr.getPort());
3565 bool Server::joinModChannel(const std::string &channel)
3567 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3568 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3571 bool Server::leaveModChannel(const std::string &channel)
3573 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3576 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3578 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3581 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3585 ModChannel* Server::getModChannel(const std::string &channel)
3587 return m_modchannel_mgr->getModChannel(channel);
3590 void Server::broadcastModChannelMessage(const std::string &channel,
3591 const std::string &message, session_t from_peer)
3593 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3597 if (message.size() > STRING_MAX_LEN) {
3598 warningstream << "ModChannel message too long, dropping before sending "
3599 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3600 << channel << ")" << std::endl;
3605 if (from_peer != PEER_ID_SERVER) {
3606 sender = getPlayerName(from_peer);
3609 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3610 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3611 resp_pkt << channel << sender << message;
3612 for (session_t peer_id : peers) {
3614 if (peer_id == from_peer)
3617 Send(peer_id, &resp_pkt);
3620 if (from_peer != PEER_ID_SERVER) {
3621 m_script->on_modchannel_message(channel, sender, message);