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_flavour_limits = g_settings->getU64("csm_flavour_limits");
405 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
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 // Note things in chat if not in simple singleplayer mode
1063 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1064 // Send information about server to player in chat
1065 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1067 Address addr = getPeerAddress(player->getPeerId());
1068 std::string ip_str = addr.serializeString();
1069 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1074 const std::vector<std::string> &names = m_clients.getPlayerNames();
1076 actionstream << player->getName() << " joins game. List of players: ";
1078 for (const std::string &name : names) {
1079 actionstream << name << " ";
1082 actionstream << player->getName() <<std::endl;
1087 inline void Server::handleCommand(NetworkPacket* pkt)
1089 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1090 (this->*opHandle.handler)(pkt);
1093 void Server::ProcessData(NetworkPacket *pkt)
1095 // Environment is locked first.
1096 MutexAutoLock envlock(m_env_mutex);
1098 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1099 u32 peer_id = pkt->getPeerId();
1102 Address address = getPeerAddress(peer_id);
1103 std::string addr_s = address.serializeString();
1105 if(m_banmanager->isIpBanned(addr_s)) {
1106 std::string ban_name = m_banmanager->getBanName(addr_s);
1107 infostream << "Server: A banned client tried to connect from "
1108 << addr_s << "; banned name was "
1109 << ban_name << std::endl;
1110 // This actually doesn't seem to transfer to the client
1111 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1112 + utf8_to_wide(ban_name));
1116 catch(con::PeerNotFoundException &e) {
1118 * no peer for this packet found
1119 * most common reason is peer timeout, e.g. peer didn't
1120 * respond for some time, your server was overloaded or
1123 infostream << "Server::ProcessData(): Canceling: peer "
1124 << peer_id << " not found" << std::endl;
1129 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1131 // Command must be handled into ToServerCommandHandler
1132 if (command >= TOSERVER_NUM_MSG_TYPES) {
1133 infostream << "Server: Ignoring unknown command "
1134 << command << std::endl;
1138 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1143 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1145 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1146 errorstream << "Server::ProcessData(): Cancelling: Peer"
1147 " serialization format invalid or not initialized."
1148 " Skipping incoming command=" << command << std::endl;
1152 /* Handle commands related to client startup */
1153 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1158 if (m_clients.getClientState(peer_id) < CS_Active) {
1159 if (command == TOSERVER_PLAYERPOS) return;
1161 errorstream << "Got packet command: " << command << " for peer id "
1162 << peer_id << " but client isn't active yet. Dropping packet "
1168 } catch (SendFailedException &e) {
1169 errorstream << "Server::ProcessData(): SendFailedException: "
1170 << "what=" << e.what()
1172 } catch (PacketError &e) {
1173 actionstream << "Server::ProcessData(): PacketError: "
1174 << "what=" << e.what()
1179 void Server::setTimeOfDay(u32 time)
1181 m_env->setTimeOfDay(time);
1182 m_time_of_day_send_timer = 0;
1185 void Server::onMapEditEvent(MapEditEvent *event)
1187 if(m_ignore_map_edit_events)
1189 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1191 MapEditEvent *e = event->clone();
1192 m_unsent_map_edit_queue.push(e);
1195 Inventory* Server::getInventory(const InventoryLocation &loc)
1198 case InventoryLocation::UNDEFINED:
1199 case InventoryLocation::CURRENT_PLAYER:
1201 case InventoryLocation::PLAYER:
1203 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1206 PlayerSAO *playersao = player->getPlayerSAO();
1209 return playersao->getInventory();
1212 case InventoryLocation::NODEMETA:
1214 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1217 return meta->getInventory();
1220 case InventoryLocation::DETACHED:
1222 if(m_detached_inventories.count(loc.name) == 0)
1224 return m_detached_inventories[loc.name];
1228 sanity_check(false); // abort
1233 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1236 case InventoryLocation::UNDEFINED:
1238 case InventoryLocation::PLAYER:
1243 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1248 PlayerSAO *playersao = player->getPlayerSAO();
1252 SendInventory(playersao);
1255 case InventoryLocation::NODEMETA:
1257 v3s16 blockpos = getNodeBlockPos(loc.p);
1259 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1261 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1263 m_clients.markBlockposAsNotSent(blockpos);
1266 case InventoryLocation::DETACHED:
1268 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1272 sanity_check(false); // abort
1277 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1279 std::vector<session_t> clients = m_clients.getClientIDs();
1281 // Set the modified blocks unsent for all the clients
1282 for (const session_t client_id : clients) {
1283 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1284 client->SetBlocksNotSent(block);
1289 void Server::peerAdded(con::Peer *peer)
1291 verbosestream<<"Server::peerAdded(): peer->id="
1292 <<peer->id<<std::endl;
1294 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1297 void Server::deletingPeer(con::Peer *peer, bool timeout)
1299 verbosestream<<"Server::deletingPeer(): peer->id="
1300 <<peer->id<<", timeout="<<timeout<<std::endl;
1302 m_clients.event(peer->id, CSE_Disconnect);
1303 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1306 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1308 *retval = m_con->getPeerStat(peer_id,type);
1309 return *retval != -1;
1312 bool Server::getClientInfo(
1321 std::string* vers_string
1324 *state = m_clients.getClientState(peer_id);
1326 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1333 *uptime = client->uptime();
1334 *ser_vers = client->serialization_version;
1335 *prot_vers = client->net_proto_version;
1337 *major = client->getMajor();
1338 *minor = client->getMinor();
1339 *patch = client->getPatch();
1340 *vers_string = client->getPatch();
1347 void Server::handlePeerChanges()
1349 while(!m_peer_change_queue.empty())
1351 con::PeerChange c = m_peer_change_queue.front();
1352 m_peer_change_queue.pop();
1354 verbosestream<<"Server: Handling peer change: "
1355 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1360 case con::PEER_ADDED:
1361 m_clients.CreateClient(c.peer_id);
1364 case con::PEER_REMOVED:
1365 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1369 FATAL_ERROR("Invalid peer change event received!");
1375 void Server::printToConsoleOnly(const std::string &text)
1378 m_admin_chat->outgoing_queue.push_back(
1379 new ChatEventChat("", utf8_to_wide(text)));
1381 std::cout << text << std::endl;
1385 void Server::Send(NetworkPacket *pkt)
1387 Send(pkt->getPeerId(), pkt);
1390 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1392 m_clients.send(peer_id,
1393 clientCommandFactoryTable[pkt->getCommand()].channel,
1395 clientCommandFactoryTable[pkt->getCommand()].reliable);
1398 void Server::SendMovement(session_t peer_id)
1400 std::ostringstream os(std::ios_base::binary);
1402 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1404 pkt << g_settings->getFloat("movement_acceleration_default");
1405 pkt << g_settings->getFloat("movement_acceleration_air");
1406 pkt << g_settings->getFloat("movement_acceleration_fast");
1407 pkt << g_settings->getFloat("movement_speed_walk");
1408 pkt << g_settings->getFloat("movement_speed_crouch");
1409 pkt << g_settings->getFloat("movement_speed_fast");
1410 pkt << g_settings->getFloat("movement_speed_climb");
1411 pkt << g_settings->getFloat("movement_speed_jump");
1412 pkt << g_settings->getFloat("movement_liquid_fluidity");
1413 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1414 pkt << g_settings->getFloat("movement_liquid_sink");
1415 pkt << g_settings->getFloat("movement_gravity");
1420 void Server::SendPlayerHPOrDie(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1422 if (!g_settings->getBool("enable_damage"))
1425 session_t peer_id = playersao->getPeerID();
1426 bool is_alive = playersao->getHP() > 0;
1429 SendPlayerHP(peer_id);
1431 DiePlayer(peer_id, reason);
1434 void Server::SendHP(session_t peer_id, u16 hp)
1436 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1441 void Server::SendBreath(session_t peer_id, u16 breath)
1443 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1444 pkt << (u16) breath;
1448 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1449 const std::string &custom_reason, bool reconnect)
1451 assert(reason < SERVER_ACCESSDENIED_MAX);
1453 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1455 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1456 pkt << custom_reason;
1457 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1458 reason == SERVER_ACCESSDENIED_CRASH)
1459 pkt << custom_reason << (u8)reconnect;
1463 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1465 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1470 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1471 v3f camera_point_target)
1473 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1474 pkt << set_camera_point_target << camera_point_target;
1478 void Server::SendItemDef(session_t peer_id,
1479 IItemDefManager *itemdef, u16 protocol_version)
1481 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1485 u32 length of the next item
1486 zlib-compressed serialized ItemDefManager
1488 std::ostringstream tmp_os(std::ios::binary);
1489 itemdef->serialize(tmp_os, protocol_version);
1490 std::ostringstream tmp_os2(std::ios::binary);
1491 compressZlib(tmp_os.str(), tmp_os2);
1492 pkt.putLongString(tmp_os2.str());
1495 verbosestream << "Server: Sending item definitions to id(" << peer_id
1496 << "): size=" << pkt.getSize() << std::endl;
1501 void Server::SendNodeDef(session_t peer_id,
1502 const NodeDefManager *nodedef, u16 protocol_version)
1504 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1508 u32 length of the next item
1509 zlib-compressed serialized NodeDefManager
1511 std::ostringstream tmp_os(std::ios::binary);
1512 nodedef->serialize(tmp_os, protocol_version);
1513 std::ostringstream tmp_os2(std::ios::binary);
1514 compressZlib(tmp_os.str(), tmp_os2);
1516 pkt.putLongString(tmp_os2.str());
1519 verbosestream << "Server: Sending node definitions to id(" << peer_id
1520 << "): size=" << pkt.getSize() << std::endl;
1526 Non-static send methods
1529 void Server::SendInventory(PlayerSAO* playerSAO)
1531 UpdateCrafting(playerSAO->getPlayer());
1537 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1539 std::ostringstream os;
1540 playerSAO->getInventory()->serialize(os);
1542 std::string s = os.str();
1544 pkt.putRawString(s.c_str(), s.size());
1548 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1550 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1552 u8 type = message.type;
1553 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1555 if (peer_id != PEER_ID_INEXISTENT) {
1556 RemotePlayer *player = m_env->getPlayer(peer_id);
1562 m_clients.sendToAll(&pkt);
1566 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1567 const std::string &formname)
1569 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1570 if (formspec.empty()){
1571 //the client should close the formspec
1572 m_formspec_state_data.erase(peer_id);
1573 pkt.putLongString("");
1575 m_formspec_state_data[peer_id] = formname;
1576 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1583 // Spawns a particle on peer with peer_id
1584 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1585 v3f pos, v3f velocity, v3f acceleration,
1586 float expirationtime, float size, bool collisiondetection,
1587 bool collision_removal,
1588 bool vertical, const std::string &texture,
1589 const struct TileAnimationParams &animation, u8 glow)
1591 static thread_local const float radius =
1592 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1594 if (peer_id == PEER_ID_INEXISTENT) {
1595 std::vector<session_t> clients = m_clients.getClientIDs();
1597 for (const session_t client_id : clients) {
1598 RemotePlayer *player = m_env->getPlayer(client_id);
1602 PlayerSAO *sao = player->getPlayerSAO();
1606 // Do not send to distant clients
1607 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1610 SendSpawnParticle(client_id, player->protocol_version,
1611 pos, velocity, acceleration,
1612 expirationtime, size, collisiondetection,
1613 collision_removal, vertical, texture, animation, glow);
1618 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1620 pkt << pos << velocity << acceleration << expirationtime
1621 << size << collisiondetection;
1622 pkt.putLongString(texture);
1624 pkt << collision_removal;
1625 // This is horrible but required (why are there two ways to serialize pkts?)
1626 std::ostringstream os(std::ios_base::binary);
1627 animation.serialize(os, protocol_version);
1628 pkt.putRawString(os.str());
1634 // Adds a ParticleSpawner on peer with peer_id
1635 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1636 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1637 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1638 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1639 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1640 const struct TileAnimationParams &animation, u8 glow)
1642 if (peer_id == PEER_ID_INEXISTENT) {
1643 // This sucks and should be replaced:
1644 std::vector<session_t> clients = m_clients.getClientIDs();
1645 for (const session_t client_id : clients) {
1646 RemotePlayer *player = m_env->getPlayer(client_id);
1649 SendAddParticleSpawner(client_id, player->protocol_version,
1650 amount, spawntime, minpos, maxpos,
1651 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1652 minsize, maxsize, collisiondetection, collision_removal,
1653 attached_id, vertical, texture, id, animation, glow);
1658 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1660 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1661 << minacc << maxacc << minexptime << maxexptime << minsize
1662 << maxsize << collisiondetection;
1664 pkt.putLongString(texture);
1666 pkt << id << vertical;
1667 pkt << collision_removal;
1669 // This is horrible but required
1670 std::ostringstream os(std::ios_base::binary);
1671 animation.serialize(os, protocol_version);
1672 pkt.putRawString(os.str());
1678 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1680 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1682 // Ugly error in this packet
1685 if (peer_id != PEER_ID_INEXISTENT)
1688 m_clients.sendToAll(&pkt);
1692 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1694 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1696 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1697 << form->text << form->number << form->item << form->dir
1698 << form->align << form->offset << form->world_pos << form->size;
1703 void Server::SendHUDRemove(session_t peer_id, u32 id)
1705 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1710 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1712 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1713 pkt << id << (u8) stat;
1717 case HUD_STAT_SCALE:
1718 case HUD_STAT_ALIGN:
1719 case HUD_STAT_OFFSET:
1720 pkt << *(v2f *) value;
1724 pkt << *(std::string *) value;
1726 case HUD_STAT_WORLD_POS:
1727 pkt << *(v3f *) value;
1730 pkt << *(v2s32 *) value;
1732 case HUD_STAT_NUMBER:
1736 pkt << *(u32 *) value;
1743 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1745 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1747 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1749 pkt << flags << mask;
1754 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1756 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1757 pkt << param << value;
1761 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1762 const std::string &type, const std::vector<std::string> ¶ms,
1765 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1766 pkt << bgcolor << type << (u16) params.size();
1768 for (const std::string ¶m : params)
1776 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1778 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1779 pkt << params.density << params.color_bright << params.color_ambient
1780 << params.height << params.thickness << params.speed;
1784 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1787 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1790 pkt << do_override << (u16) (ratio * 65535);
1795 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1797 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1798 pkt << time << time_speed;
1800 if (peer_id == PEER_ID_INEXISTENT) {
1801 m_clients.sendToAll(&pkt);
1808 void Server::SendPlayerHP(session_t peer_id)
1810 PlayerSAO *playersao = getPlayerSAO(peer_id);
1811 // In some rare case if the player is disconnected
1812 // while Lua call l_punch, for example, this can be NULL
1816 SendHP(peer_id, playersao->getHP());
1817 m_script->player_event(playersao,"health_changed");
1819 // Send to other clients
1820 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1821 ActiveObjectMessage aom(playersao->getId(), true, str);
1822 playersao->m_messages_out.push(aom);
1825 void Server::SendPlayerBreath(PlayerSAO *sao)
1829 m_script->player_event(sao, "breath_changed");
1830 SendBreath(sao->getPeerID(), sao->getBreath());
1833 void Server::SendMovePlayer(session_t peer_id)
1835 RemotePlayer *player = m_env->getPlayer(peer_id);
1837 PlayerSAO *sao = player->getPlayerSAO();
1840 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1841 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1844 v3f pos = sao->getBasePosition();
1845 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1846 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1847 << " pitch=" << sao->getPitch()
1848 << " yaw=" << sao->getYaw()
1855 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1856 f32 animation_speed)
1858 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1861 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1862 << animation_frames[3] << animation_speed;
1867 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1869 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1870 pkt << first << third;
1874 void Server::SendPlayerPrivileges(session_t peer_id)
1876 RemotePlayer *player = m_env->getPlayer(peer_id);
1878 if(player->getPeerId() == PEER_ID_INEXISTENT)
1881 std::set<std::string> privs;
1882 m_script->getAuth(player->getName(), NULL, &privs);
1884 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1885 pkt << (u16) privs.size();
1887 for (const std::string &priv : privs) {
1894 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1896 RemotePlayer *player = m_env->getPlayer(peer_id);
1898 if (player->getPeerId() == PEER_ID_INEXISTENT)
1901 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1902 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1906 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1908 RemotePlayer *player = m_env->getPlayer(peer_id);
1910 if (player->getPeerId() == PEER_ID_INEXISTENT)
1913 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1914 pkt << FORMSPEC_VERSION_STRING + player->formspec_prepend;
1918 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1920 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1921 pkt.putRawString(datas.c_str(), datas.size());
1923 return pkt.getSize();
1926 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1929 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1930 datas.size(), peer_id);
1932 pkt.putRawString(datas.c_str(), datas.size());
1934 m_clients.send(pkt.getPeerId(),
1935 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1939 void Server::SendCSMFlavourLimits(session_t peer_id)
1941 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1942 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1943 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1947 s32 Server::playSound(const SimpleSoundSpec &spec,
1948 const ServerSoundParams ¶ms)
1950 // Find out initial position of sound
1951 bool pos_exists = false;
1952 v3f pos = params.getPos(m_env, &pos_exists);
1953 // If position is not found while it should be, cancel sound
1954 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1957 // Filter destination clients
1958 std::vector<session_t> dst_clients;
1959 if(!params.to_player.empty()) {
1960 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1962 infostream<<"Server::playSound: Player \""<<params.to_player
1963 <<"\" not found"<<std::endl;
1966 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1967 infostream<<"Server::playSound: Player \""<<params.to_player
1968 <<"\" not connected"<<std::endl;
1971 dst_clients.push_back(player->getPeerId());
1973 std::vector<session_t> clients = m_clients.getClientIDs();
1975 for (const session_t client_id : clients) {
1976 RemotePlayer *player = m_env->getPlayer(client_id);
1980 PlayerSAO *sao = player->getPlayerSAO();
1985 if(sao->getBasePosition().getDistanceFrom(pos) >
1986 params.max_hear_distance)
1989 dst_clients.push_back(client_id);
1993 if(dst_clients.empty())
1997 s32 id = m_next_sound_id++;
1998 // The sound will exist as a reference in m_playing_sounds
1999 m_playing_sounds[id] = ServerPlayingSound();
2000 ServerPlayingSound &psound = m_playing_sounds[id];
2001 psound.params = params;
2004 float gain = params.gain * spec.gain;
2005 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2006 pkt << id << spec.name << gain
2007 << (u8) params.type << pos << params.object
2008 << params.loop << params.fade << params.pitch;
2010 // Backwards compability
2011 bool play_sound = gain > 0;
2013 for (const u16 dst_client : dst_clients) {
2014 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2015 psound.clients.insert(dst_client);
2016 m_clients.send(dst_client, 0, &pkt, true);
2021 void Server::stopSound(s32 handle)
2023 // Get sound reference
2024 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2025 m_playing_sounds.find(handle);
2026 if (i == m_playing_sounds.end())
2028 ServerPlayingSound &psound = i->second;
2030 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2033 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2034 si != psound.clients.end(); ++si) {
2036 m_clients.send(*si, 0, &pkt, true);
2038 // Remove sound reference
2039 m_playing_sounds.erase(i);
2042 void Server::fadeSound(s32 handle, float step, float gain)
2044 // Get sound reference
2045 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2046 m_playing_sounds.find(handle);
2047 if (i == m_playing_sounds.end())
2050 ServerPlayingSound &psound = i->second;
2051 psound.params.gain = gain;
2053 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2054 pkt << handle << step << gain;
2056 // Backwards compability
2057 bool play_sound = gain > 0;
2058 ServerPlayingSound compat_psound = psound;
2059 compat_psound.clients.clear();
2061 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2062 compat_pkt << handle;
2064 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2065 it != psound.clients.end();) {
2066 if (m_clients.getProtocolVersion(*it) >= 32) {
2068 m_clients.send(*it, 0, &pkt, true);
2071 compat_psound.clients.insert(*it);
2073 m_clients.send(*it, 0, &compat_pkt, true);
2074 psound.clients.erase(it++);
2078 // Remove sound reference
2079 if (!play_sound || psound.clients.empty())
2080 m_playing_sounds.erase(i);
2082 if (play_sound && !compat_psound.clients.empty()) {
2083 // Play new sound volume on older clients
2084 playSound(compat_psound.spec, compat_psound.params);
2088 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2089 std::vector<u16> *far_players, float far_d_nodes)
2091 float maxd = far_d_nodes*BS;
2092 v3f p_f = intToFloat(p, BS);
2094 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2097 std::vector<session_t> clients = m_clients.getClientIDs();
2098 for (session_t client_id : clients) {
2101 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2102 PlayerSAO *sao = player->getPlayerSAO();
2106 // If player is far away, only set modified blocks not sent
2107 v3f player_pos = sao->getBasePosition();
2108 if (player_pos.getDistanceFrom(p_f) > maxd) {
2109 far_players->push_back(client_id);
2116 m_clients.send(client_id, 0, &pkt, true);
2120 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2121 std::vector<u16> *far_players, float far_d_nodes,
2122 bool remove_metadata)
2124 float maxd = far_d_nodes*BS;
2125 v3f p_f = intToFloat(p, BS);
2127 std::vector<session_t> clients = m_clients.getClientIDs();
2128 for (const session_t client_id : clients) {
2131 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2132 PlayerSAO *sao = player->getPlayerSAO();
2136 // If player is far away, only set modified blocks not sent
2137 v3f player_pos = sao->getBasePosition();
2138 if(player_pos.getDistanceFrom(p_f) > maxd) {
2139 far_players->push_back(client_id);
2145 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2147 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2149 pkt << p << n.param0 << n.param1 << n.param2
2150 << (u8) (remove_metadata ? 0 : 1);
2155 if (pkt.getSize() > 0)
2156 m_clients.send(client_id, 0, &pkt, true);
2160 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2161 u16 net_proto_version)
2164 Create a packet with the block in the right format
2167 std::ostringstream os(std::ios_base::binary);
2168 block->serialize(os, ver, false);
2169 block->serializeNetworkSpecific(os);
2170 std::string s = os.str();
2172 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2174 pkt << block->getPos();
2175 pkt.putRawString(s.c_str(), s.size());
2179 void Server::SendBlocks(float dtime)
2181 MutexAutoLock envlock(m_env_mutex);
2182 //TODO check if one big lock could be faster then multiple small ones
2184 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2186 std::vector<PrioritySortedBlockTransfer> queue;
2188 u32 total_sending = 0;
2191 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2193 std::vector<session_t> clients = m_clients.getClientIDs();
2196 for (const session_t client_id : clients) {
2197 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2202 total_sending += client->getSendingCount();
2203 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2209 // Lowest priority number comes first.
2210 // Lowest is most important.
2211 std::sort(queue.begin(), queue.end());
2215 // Maximal total count calculation
2216 // The per-client block sends is halved with the maximal online users
2217 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2218 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2220 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2221 if (total_sending >= max_blocks_to_send)
2224 MapBlock *block = nullptr;
2226 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2227 } catch (const InvalidPositionException &e) {
2231 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2236 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2237 client->net_proto_version);
2239 client->SentBlock(block_to_send.pos);
2245 void Server::fillMediaCache()
2247 infostream<<"Server: Calculating media file checksums"<<std::endl;
2249 // Collect all media file paths
2250 std::vector<std::string> paths;
2251 m_modmgr->getModsMediaPaths(paths);
2252 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2253 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2255 // Collect media file information from paths into cache
2256 for (const std::string &mediapath : paths) {
2257 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2258 for (const fs::DirListNode &dln : dirlist) {
2259 if (dln.dir) // Ignode dirs
2261 std::string filename = dln.name;
2262 // If name contains illegal characters, ignore the file
2263 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2264 infostream<<"Server: ignoring illegal file name: \""
2265 << filename << "\"" << std::endl;
2268 // If name is not in a supported format, ignore it
2269 const char *supported_ext[] = {
2270 ".png", ".jpg", ".bmp", ".tga",
2271 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2273 ".x", ".b3d", ".md2", ".obj",
2274 // Custom translation file format
2278 if (removeStringEnd(filename, supported_ext).empty()){
2279 infostream << "Server: ignoring unsupported file extension: \""
2280 << filename << "\"" << std::endl;
2283 // Ok, attempt to load the file and add to cache
2284 std::string filepath;
2285 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2288 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2290 errorstream << "Server::fillMediaCache(): Could not open \""
2291 << filename << "\" for reading" << std::endl;
2294 std::ostringstream tmp_os(std::ios_base::binary);
2298 fis.read(buf, 1024);
2299 std::streamsize len = fis.gcount();
2300 tmp_os.write(buf, len);
2309 errorstream<<"Server::fillMediaCache(): Failed to read \""
2310 << filename << "\"" << std::endl;
2313 if(tmp_os.str().length() == 0) {
2314 errorstream << "Server::fillMediaCache(): Empty file \""
2315 << filepath << "\"" << std::endl;
2320 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2322 unsigned char *digest = sha1.getDigest();
2323 std::string sha1_base64 = base64_encode(digest, 20);
2324 std::string sha1_hex = hex_encode((char*)digest, 20);
2328 m_media[filename] = MediaInfo(filepath, sha1_base64);
2329 verbosestream << "Server: " << sha1_hex << " is " << filename
2335 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2337 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2341 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2344 std::string lang_suffix;
2345 lang_suffix.append(".").append(lang_code).append(".tr");
2346 for (const auto &i : m_media) {
2347 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2354 for (const auto &i : m_media) {
2355 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2357 pkt << i.first << i.second.sha1_digest;
2360 pkt << g_settings->get("remote_media");
2364 struct SendableMedia
2370 SendableMedia(const std::string &name_="", const std::string &path_="",
2371 const std::string &data_=""):
2378 void Server::sendRequestedMedia(session_t peer_id,
2379 const std::vector<std::string> &tosend)
2381 verbosestream<<"Server::sendRequestedMedia(): "
2382 <<"Sending files to client"<<std::endl;
2386 // Put 5kB in one bunch (this is not accurate)
2387 u32 bytes_per_bunch = 5000;
2389 std::vector< std::vector<SendableMedia> > file_bunches;
2390 file_bunches.emplace_back();
2392 u32 file_size_bunch_total = 0;
2394 for (const std::string &name : tosend) {
2395 if (m_media.find(name) == m_media.end()) {
2396 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2397 <<"unknown file \""<<(name)<<"\""<<std::endl;
2401 //TODO get path + name
2402 std::string tpath = m_media[name].path;
2405 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2407 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2408 <<tpath<<"\" for reading"<<std::endl;
2411 std::ostringstream tmp_os(std::ios_base::binary);
2415 fis.read(buf, 1024);
2416 std::streamsize len = fis.gcount();
2417 tmp_os.write(buf, len);
2418 file_size_bunch_total += len;
2427 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2428 <<name<<"\""<<std::endl;
2431 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2432 <<tname<<"\""<<std::endl;*/
2434 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2436 // Start next bunch if got enough data
2437 if(file_size_bunch_total >= bytes_per_bunch) {
2438 file_bunches.emplace_back();
2439 file_size_bunch_total = 0;
2444 /* Create and send packets */
2446 u16 num_bunches = file_bunches.size();
2447 for (u16 i = 0; i < num_bunches; i++) {
2450 u16 total number of texture bunches
2451 u16 index of this bunch
2452 u32 number of files in this bunch
2461 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2462 pkt << num_bunches << i << (u32) file_bunches[i].size();
2464 for (const SendableMedia &j : file_bunches[i]) {
2466 pkt.putLongString(j.data);
2469 verbosestream << "Server::sendRequestedMedia(): bunch "
2470 << i << "/" << num_bunches
2471 << " files=" << file_bunches[i].size()
2472 << " size=" << pkt.getSize() << std::endl;
2477 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2479 if(m_detached_inventories.count(name) == 0) {
2480 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2483 Inventory *inv = m_detached_inventories[name];
2484 std::ostringstream os(std::ios_base::binary);
2486 os << serializeString(name);
2490 std::string s = os.str();
2492 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2493 pkt.putRawString(s.c_str(), s.size());
2495 const std::string &check = m_detached_inventories_player[name];
2496 if (peer_id == PEER_ID_INEXISTENT) {
2498 return m_clients.sendToAll(&pkt);
2499 RemotePlayer *p = m_env->getPlayer(check.c_str());
2501 m_clients.send(p->getPeerId(), 0, &pkt, true);
2503 if (check.empty() || getPlayerName(peer_id) == check)
2508 void Server::sendDetachedInventories(session_t peer_id)
2510 for (const auto &detached_inventory : m_detached_inventories) {
2511 const std::string &name = detached_inventory.first;
2512 //Inventory *inv = i->second;
2513 sendDetachedInventory(name, peer_id);
2521 void Server::DiePlayer(session_t peer_id, const PlayerHPChangeReason &reason)
2523 PlayerSAO *playersao = getPlayerSAO(peer_id);
2524 // In some rare cases this can be NULL -- if the player is disconnected
2525 // when a Lua function modifies l_punch, for example
2529 infostream << "Server::DiePlayer(): Player "
2530 << playersao->getPlayer()->getName()
2531 << " dies" << std::endl;
2533 playersao->setHP(0, reason);
2534 playersao->clearParentAttachment();
2536 // Trigger scripted stuff
2537 m_script->on_dieplayer(playersao, reason);
2539 SendPlayerHP(peer_id);
2540 SendDeathscreen(peer_id, false, v3f(0,0,0));
2543 void Server::RespawnPlayer(session_t peer_id)
2545 PlayerSAO *playersao = getPlayerSAO(peer_id);
2548 infostream << "Server::RespawnPlayer(): Player "
2549 << playersao->getPlayer()->getName()
2550 << " respawns" << std::endl;
2552 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2553 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2554 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2556 bool repositioned = m_script->on_respawnplayer(playersao);
2557 if (!repositioned) {
2558 // setPos will send the new position to client
2559 playersao->setPos(findSpawnPos());
2562 SendPlayerHP(peer_id);
2566 void Server::DenySudoAccess(session_t peer_id)
2568 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2573 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2574 const std::string &str_reason, bool reconnect)
2576 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2578 m_clients.event(peer_id, CSE_SetDenied);
2579 DisconnectPeer(peer_id);
2583 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2584 const std::string &custom_reason)
2586 SendAccessDenied(peer_id, reason, custom_reason);
2587 m_clients.event(peer_id, CSE_SetDenied);
2588 DisconnectPeer(peer_id);
2591 // 13/03/15: remove this function when protocol version 25 will become
2592 // the minimum version for MT users, maybe in 1 year
2593 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2595 SendAccessDenied_Legacy(peer_id, reason);
2596 m_clients.event(peer_id, CSE_SetDenied);
2597 DisconnectPeer(peer_id);
2600 void Server::DisconnectPeer(session_t peer_id)
2602 m_modchannel_mgr->leaveAllChannels(peer_id);
2603 m_con->DisconnectPeer(peer_id);
2606 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2609 RemoteClient* client = getClient(peer_id, CS_Invalid);
2611 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2613 // Right now, the auth mechs don't change between login and sudo mode.
2614 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2615 client->allowed_sudo_mechs = sudo_auth_mechs;
2617 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2618 << g_settings->getFloat("dedicated_server_step")
2622 m_clients.event(peer_id, CSE_AuthAccept);
2624 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2626 // We only support SRP right now
2627 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2629 resp_pkt << sudo_auth_mechs;
2631 m_clients.event(peer_id, CSE_SudoSuccess);
2635 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2637 std::wstring message;
2640 Clear references to playing sounds
2642 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2643 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2644 ServerPlayingSound &psound = i->second;
2645 psound.clients.erase(peer_id);
2646 if (psound.clients.empty())
2647 m_playing_sounds.erase(i++);
2652 // clear formspec info so the next client can't abuse the current state
2653 m_formspec_state_data.erase(peer_id);
2655 RemotePlayer *player = m_env->getPlayer(peer_id);
2657 /* Run scripts and remove from environment */
2659 PlayerSAO *playersao = player->getPlayerSAO();
2662 playersao->clearChildAttachments();
2663 playersao->clearParentAttachment();
2665 // inform connected clients
2666 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2667 // (u16) 1 + std::string represents a vector serialization representation
2668 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2669 m_clients.sendToAll(¬ice);
2671 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2673 playersao->disconnected();
2680 if (player && reason != CDR_DENY) {
2681 std::ostringstream os(std::ios_base::binary);
2682 std::vector<session_t> clients = m_clients.getClientIDs();
2684 for (const session_t client_id : clients) {
2686 RemotePlayer *player = m_env->getPlayer(client_id);
2690 // Get name of player
2691 os << player->getName() << " ";
2694 std::string name = player->getName();
2695 actionstream << name << " "
2696 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2697 << " List of players: " << os.str() << std::endl;
2699 m_admin_chat->outgoing_queue.push_back(
2700 new ChatEventNick(CET_NICK_REMOVE, name));
2704 MutexAutoLock env_lock(m_env_mutex);
2705 m_clients.DeleteClient(peer_id);
2709 // Send leave chat message to all remaining clients
2710 if (!message.empty()) {
2711 SendChatMessage(PEER_ID_INEXISTENT,
2712 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2716 void Server::UpdateCrafting(RemotePlayer *player)
2718 InventoryList *clist = player->inventory.getList("craft");
2719 if (!clist || clist->getSize() == 0)
2722 // Get a preview for crafting
2724 InventoryLocation loc;
2725 loc.setPlayer(player->getName());
2726 std::vector<ItemStack> output_replacements;
2727 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2728 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2731 InventoryList *plist = player->inventory.getList("craftpreview");
2732 if (plist && plist->getSize() >= 1) {
2733 // Put the new preview in
2734 plist->changeItem(0, preview);
2738 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2740 if (evt->type == CET_NICK_ADD) {
2741 // The terminal informed us of its nick choice
2742 m_admin_nick = ((ChatEventNick *)evt)->nick;
2743 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2744 errorstream << "You haven't set up an account." << std::endl
2745 << "Please log in using the client as '"
2746 << m_admin_nick << "' with a secure password." << std::endl
2747 << "Until then, you can't execute admin tasks via the console," << std::endl
2748 << "and everybody can claim the user account instead of you," << std::endl
2749 << "giving them full control over this server." << std::endl;
2752 assert(evt->type == CET_CHAT);
2753 handleAdminChat((ChatEventChat *)evt);
2757 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2758 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2760 // If something goes wrong, this player is to blame
2761 RollbackScopeActor rollback_scope(m_rollback,
2762 std::string("player:") + name);
2764 if (g_settings->getBool("strip_color_codes"))
2765 wmessage = unescape_enriched(wmessage);
2768 switch (player->canSendChatMessage()) {
2769 case RPLAYER_CHATRESULT_FLOODING: {
2770 std::wstringstream ws;
2771 ws << L"You cannot send more messages. You are limited to "
2772 << g_settings->getFloat("chat_message_limit_per_10sec")
2773 << L" messages per 10 seconds.";
2776 case RPLAYER_CHATRESULT_KICK:
2777 DenyAccess_Legacy(player->getPeerId(),
2778 L"You have been kicked due to message flooding.");
2780 case RPLAYER_CHATRESULT_OK:
2783 FATAL_ERROR("Unhandled chat filtering result found.");
2787 if (m_max_chatmessage_length > 0
2788 && wmessage.length() > m_max_chatmessage_length) {
2789 return L"Your message exceed the maximum chat message limit set on the server. "
2790 L"It was refused. Send a shorter message";
2793 // Run script hook, exit if script ate the chat message
2794 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2799 // Whether to send line to the player that sent the message, or to all players
2800 bool broadcast_line = true;
2802 if (check_shout_priv && !checkPriv(name, "shout")) {
2803 line += L"-!- You don't have permission to shout.";
2804 broadcast_line = false;
2813 Tell calling method to send the message to sender
2815 if (!broadcast_line)
2819 Send the message to others
2821 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2823 std::vector<session_t> clients = m_clients.getClientIDs();
2826 Send the message back to the inital sender
2827 if they are using protocol version >= 29
2830 session_t peer_id_to_avoid_sending =
2831 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2833 if (player && player->protocol_version >= 29)
2834 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2836 for (u16 cid : clients) {
2837 if (cid != peer_id_to_avoid_sending)
2838 SendChatMessage(cid, ChatMessage(line));
2843 void Server::handleAdminChat(const ChatEventChat *evt)
2845 std::string name = evt->nick;
2846 std::wstring wname = utf8_to_wide(name);
2847 std::wstring wmessage = evt->evt_msg;
2849 std::wstring answer = handleChat(name, wname, wmessage);
2851 // If asked to send answer to sender
2852 if (!answer.empty()) {
2853 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2857 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2859 RemoteClient *client = getClientNoEx(peer_id,state_min);
2861 throw ClientNotFoundException("Client not found");
2865 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2867 return m_clients.getClientNoEx(peer_id, state_min);
2870 std::string Server::getPlayerName(session_t peer_id)
2872 RemotePlayer *player = m_env->getPlayer(peer_id);
2874 return "[id="+itos(peer_id)+"]";
2875 return player->getName();
2878 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2880 RemotePlayer *player = m_env->getPlayer(peer_id);
2883 return player->getPlayerSAO();
2886 std::wstring Server::getStatusString()
2888 std::wostringstream os(std::ios_base::binary);
2891 os<<L"version="<<narrow_to_wide(g_version_string);
2893 os<<L", uptime="<<m_uptime.get();
2895 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2896 // Information about clients
2899 std::vector<session_t> clients = m_clients.getClientIDs();
2900 for (session_t client_id : clients) {
2902 RemotePlayer *player = m_env->getPlayer(client_id);
2903 // Get name of player
2904 std::wstring name = L"unknown";
2906 name = narrow_to_wide(player->getName());
2907 // Add name to information string
2916 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2917 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2919 if (!g_settings->get("motd").empty())
2920 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2924 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2926 std::set<std::string> privs;
2927 m_script->getAuth(name, NULL, &privs);
2931 bool Server::checkPriv(const std::string &name, const std::string &priv)
2933 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2934 return (privs.count(priv) != 0);
2937 void Server::reportPrivsModified(const std::string &name)
2940 std::vector<session_t> clients = m_clients.getClientIDs();
2941 for (const session_t client_id : clients) {
2942 RemotePlayer *player = m_env->getPlayer(client_id);
2943 reportPrivsModified(player->getName());
2946 RemotePlayer *player = m_env->getPlayer(name.c_str());
2949 SendPlayerPrivileges(player->getPeerId());
2950 PlayerSAO *sao = player->getPlayerSAO();
2953 sao->updatePrivileges(
2954 getPlayerEffectivePrivs(name),
2959 void Server::reportInventoryFormspecModified(const std::string &name)
2961 RemotePlayer *player = m_env->getPlayer(name.c_str());
2964 SendPlayerInventoryFormspec(player->getPeerId());
2967 void Server::reportFormspecPrependModified(const std::string &name)
2969 RemotePlayer *player = m_env->getPlayer(name.c_str());
2972 SendPlayerFormspecPrepend(player->getPeerId());
2975 void Server::setIpBanned(const std::string &ip, const std::string &name)
2977 m_banmanager->add(ip, name);
2980 void Server::unsetIpBanned(const std::string &ip_or_name)
2982 m_banmanager->remove(ip_or_name);
2985 std::string Server::getBanDescription(const std::string &ip_or_name)
2987 return m_banmanager->getBanDescription(ip_or_name);
2990 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2992 // m_env will be NULL if the server is initializing
2996 if (m_admin_nick == name && !m_admin_nick.empty()) {
2997 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3000 RemotePlayer *player = m_env->getPlayer(name);
3005 if (player->getPeerId() == PEER_ID_INEXISTENT)
3008 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3011 bool Server::showFormspec(const char *playername, const std::string &formspec,
3012 const std::string &formname)
3014 // m_env will be NULL if the server is initializing
3018 RemotePlayer *player = m_env->getPlayer(playername);
3022 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3026 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3031 u32 id = player->addHud(form);
3033 SendHUDAdd(player->getPeerId(), id, form);
3038 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3042 HudElement* todel = player->removeHud(id);
3049 SendHUDRemove(player->getPeerId(), id);
3053 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3058 SendHUDChange(player->getPeerId(), id, stat, data);
3062 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3067 SendHUDSetFlags(player->getPeerId(), flags, mask);
3068 player->hud_flags &= ~mask;
3069 player->hud_flags |= flags;
3071 PlayerSAO* playersao = player->getPlayerSAO();
3076 m_script->player_event(playersao, "hud_changed");
3080 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3085 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3088 player->setHotbarItemcount(hotbar_itemcount);
3089 std::ostringstream os(std::ios::binary);
3090 writeS32(os, hotbar_itemcount);
3091 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3095 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3100 player->setHotbarImage(name);
3101 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3104 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3109 player->setHotbarSelectedImage(name);
3110 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3113 Address Server::getPeerAddress(session_t peer_id)
3115 return m_con->GetPeerAddress(peer_id);
3118 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3119 v2s32 animation_frames[4], f32 frame_speed)
3121 sanity_check(player);
3122 player->setLocalAnimations(animation_frames, frame_speed);
3123 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3126 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3128 sanity_check(player);
3129 player->eye_offset_first = first;
3130 player->eye_offset_third = third;
3131 SendEyeOffset(player->getPeerId(), first, third);
3134 void Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3135 const std::string &type, const std::vector<std::string> ¶ms,
3138 sanity_check(player);
3139 player->setSky(bgcolor, type, params, clouds);
3140 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3143 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3145 sanity_check(player);
3146 player->setCloudParams(params);
3147 SendCloudParams(player->getPeerId(), params);
3150 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3156 player->overrideDayNightRatio(do_override, ratio);
3157 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3161 void Server::notifyPlayers(const std::wstring &msg)
3163 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3166 void Server::spawnParticle(const std::string &playername, v3f pos,
3167 v3f velocity, v3f acceleration,
3168 float expirationtime, float size, bool
3169 collisiondetection, bool collision_removal,
3170 bool vertical, const std::string &texture,
3171 const struct TileAnimationParams &animation, u8 glow)
3173 // m_env will be NULL if the server is initializing
3177 session_t peer_id = PEER_ID_INEXISTENT;
3179 if (!playername.empty()) {
3180 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3183 peer_id = player->getPeerId();
3184 proto_ver = player->protocol_version;
3187 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3188 expirationtime, size, collisiondetection,
3189 collision_removal, vertical, texture, animation, glow);
3192 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3193 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3194 float minexptime, float maxexptime, float minsize, float maxsize,
3195 bool collisiondetection, bool collision_removal,
3196 ServerActiveObject *attached, bool vertical, const std::string &texture,
3197 const std::string &playername, const struct TileAnimationParams &animation,
3200 // m_env will be NULL if the server is initializing
3204 session_t peer_id = PEER_ID_INEXISTENT;
3206 if (!playername.empty()) {
3207 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3210 peer_id = player->getPeerId();
3211 proto_ver = player->protocol_version;
3214 u16 attached_id = attached ? attached->getId() : 0;
3217 if (attached_id == 0)
3218 id = m_env->addParticleSpawner(spawntime);
3220 id = m_env->addParticleSpawner(spawntime, attached_id);
3222 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3223 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3224 minexptime, maxexptime, minsize, maxsize,
3225 collisiondetection, collision_removal, attached_id, vertical,
3226 texture, id, animation, glow);
3231 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3233 // m_env will be NULL if the server is initializing
3235 throw ServerError("Can't delete particle spawners during initialisation!");
3237 session_t peer_id = PEER_ID_INEXISTENT;
3238 if (!playername.empty()) {
3239 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3242 peer_id = player->getPeerId();
3245 m_env->deleteParticleSpawner(id);
3246 SendDeleteParticleSpawner(peer_id, id);
3249 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3251 if(m_detached_inventories.count(name) > 0){
3252 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3253 delete m_detached_inventories[name];
3255 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3257 Inventory *inv = new Inventory(m_itemdef);
3259 m_detached_inventories[name] = inv;
3260 m_detached_inventories_player[name] = player;
3261 //TODO find a better way to do this
3262 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3266 // actions: time-reversed list
3267 // Return value: success/failure
3268 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3269 std::list<std::string> *log)
3271 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3272 ServerMap *map = (ServerMap*)(&m_env->getMap());
3274 // Fail if no actions to handle
3275 if (actions.empty()) {
3277 log->push_back("Nothing to do.");
3284 for (const RollbackAction &action : actions) {
3286 bool success = action.applyRevert(map, this, this);
3289 std::ostringstream os;
3290 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3291 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3293 log->push_back(os.str());
3295 std::ostringstream os;
3296 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3297 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3299 log->push_back(os.str());
3303 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3304 <<" failed"<<std::endl;
3306 // Call it done if less than half failed
3307 return num_failed <= num_tried/2;
3310 // IGameDef interface
3312 IItemDefManager *Server::getItemDefManager()
3317 const NodeDefManager *Server::getNodeDefManager()
3322 ICraftDefManager *Server::getCraftDefManager()
3327 u16 Server::allocateUnknownNodeId(const std::string &name)
3329 return m_nodedef->allocateDummy(name);
3332 IWritableItemDefManager *Server::getWritableItemDefManager()
3337 NodeDefManager *Server::getWritableNodeDefManager()
3342 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3347 const std::vector<ModSpec> & Server::getMods() const
3349 return m_modmgr->getMods();
3352 const ModSpec *Server::getModSpec(const std::string &modname) const
3354 return m_modmgr->getModSpec(modname);
3357 void Server::getModNames(std::vector<std::string> &modlist)
3359 m_modmgr->getModNames(modlist);
3362 std::string Server::getBuiltinLuaPath()
3364 return porting::path_share + DIR_DELIM + "builtin";
3367 std::string Server::getModStoragePath() const
3369 return m_path_world + DIR_DELIM + "mod_storage";
3372 v3f Server::findSpawnPos()
3374 ServerMap &map = m_env->getServerMap();
3376 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3377 return nodeposf * BS;
3380 bool is_good = false;
3381 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3382 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3384 // Try to find a good place a few times
3385 for(s32 i = 0; i < 4000 && !is_good; i++) {
3386 s32 range = MYMIN(1 + i, range_max);
3387 // We're going to try to throw the player to this position
3388 v2s16 nodepos2d = v2s16(
3389 -range + (myrand() % (range * 2)),
3390 -range + (myrand() % (range * 2)));
3392 // Get spawn level at point
3393 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3394 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3395 // the mapgen to signify an unsuitable spawn position
3396 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3399 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3402 for (s32 i = 0; i < 10; i++) {
3403 v3s16 blockpos = getNodeBlockPos(nodepos);
3404 map.emergeBlock(blockpos, true);
3405 content_t c = map.getNodeNoEx(nodepos).getContent();
3406 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3408 if (air_count >= 2) {
3409 nodeposf = intToFloat(nodepos, BS);
3410 // Don't spawn the player outside map boundaries
3411 if (objectpos_over_limit(nodeposf))
3424 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3426 if (delay == 0.0f) {
3427 // No delay, shutdown immediately
3428 m_shutdown_state.is_requested = true;
3429 // only print to the infostream, a chat message saying
3430 // "Server Shutting Down" is sent when the server destructs.
3431 infostream << "*** Immediate Server shutdown requested." << std::endl;
3432 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3433 // Negative delay, cancel shutdown if requested
3434 m_shutdown_state.reset();
3435 std::wstringstream ws;
3437 ws << L"*** Server shutdown canceled.";
3439 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3440 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3441 // m_shutdown_* are already handled, skip.
3443 } else if (delay > 0.0f) {
3444 // Positive delay, tell the clients when the server will shut down
3445 std::wstringstream ws;
3447 ws << L"*** Server shutting down in "
3448 << duration_to_string(myround(delay)).c_str()
3451 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3452 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3455 m_shutdown_state.trigger(delay, msg, reconnect);
3458 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3461 Try to get an existing player
3463 RemotePlayer *player = m_env->getPlayer(name);
3465 // If player is already connected, cancel
3466 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3467 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3472 If player with the wanted peer_id already exists, cancel.
3474 if (m_env->getPlayer(peer_id)) {
3475 infostream<<"emergePlayer(): Player with wrong name but same"
3476 " peer_id already exists"<<std::endl;
3481 player = new RemotePlayer(name, idef());
3484 bool newplayer = false;
3487 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3489 // Complete init with server parts
3490 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3491 player->protocol_version = proto_version;
3495 m_script->on_newplayer(playersao);
3501 bool Server::registerModStorage(ModMetadata *storage)
3503 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3504 errorstream << "Unable to register same mod storage twice. Storage name: "
3505 << storage->getModName() << std::endl;
3509 m_mod_storages[storage->getModName()] = storage;
3513 void Server::unregisterModStorage(const std::string &name)
3515 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3516 if (it != m_mod_storages.end()) {
3517 // Save unconditionaly on unregistration
3518 it->second->save(getModStoragePath());
3519 m_mod_storages.erase(name);
3523 void dedicated_server_loop(Server &server, bool &kill)
3525 verbosestream<<"dedicated_server_loop()"<<std::endl;
3527 IntervalLimiter m_profiler_interval;
3529 static thread_local const float steplen =
3530 g_settings->getFloat("dedicated_server_step");
3531 static thread_local const float profiler_print_interval =
3532 g_settings->getFloat("profiler_print_interval");
3535 // This is kind of a hack but can be done like this
3536 // because server.step() is very light
3538 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3539 sleep_ms((int)(steplen*1000.0));
3541 server.step(steplen);
3543 if (server.isShutdownRequested() || kill)
3549 if (profiler_print_interval != 0) {
3550 if(m_profiler_interval.step(steplen, profiler_print_interval))
3552 infostream<<"Profiler:"<<std::endl;
3553 g_profiler->print(infostream);
3554 g_profiler->clear();
3559 infostream << "Dedicated server quitting" << std::endl;
3561 if (g_settings->getBool("server_announce"))
3562 ServerList::sendAnnounce(ServerList::AA_DELETE,
3563 server.m_bind_addr.getPort());
3572 bool Server::joinModChannel(const std::string &channel)
3574 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3575 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3578 bool Server::leaveModChannel(const std::string &channel)
3580 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3583 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3585 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3588 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3592 ModChannel* Server::getModChannel(const std::string &channel)
3594 return m_modchannel_mgr->getModChannel(channel);
3597 void Server::broadcastModChannelMessage(const std::string &channel,
3598 const std::string &message, session_t from_peer)
3600 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3604 if (message.size() > STRING_MAX_LEN) {
3605 warningstream << "ModChannel message too long, dropping before sending "
3606 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3607 << channel << ")" << std::endl;
3612 if (from_peer != PEER_ID_SERVER) {
3613 sender = getPlayerName(from_peer);
3616 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3617 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3618 resp_pkt << channel << sender << message;
3619 for (session_t peer_id : peers) {
3621 if (peer_id == from_peer)
3624 Send(peer_id, &resp_pkt);
3627 if (from_peer != PEER_ID_SERVER) {
3628 m_script->on_modchannel_message(channel, sender, message);