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");
240 // Send shutdown message
241 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
242 L"*** Server shutting down"));
245 MutexAutoLock envlock(m_env_mutex);
247 infostream << "Server: Saving players" << std::endl;
248 m_env->saveLoadedPlayers();
250 infostream << "Server: Kicking players" << std::endl;
251 std::string kick_msg;
252 bool reconnect = false;
253 if (isShutdownRequested()) {
254 reconnect = m_shutdown_state.should_reconnect;
255 kick_msg = m_shutdown_state.message;
257 if (kick_msg.empty()) {
258 kick_msg = g_settings->get("kick_msg_shutdown");
260 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
261 kick_msg, reconnect);
264 actionstream << "Server: Shutting down" << std::endl;
266 // Do this before stopping the server in case mapgen callbacks need to access
267 // server-controlled resources (like ModStorages). Also do them before
268 // shutdown callbacks since they may modify state that is finalized in a
271 m_emerge->stopThreads();
274 MutexAutoLock envlock(m_env_mutex);
276 // Execute script shutdown hooks
277 infostream << "Executing shutdown hooks" << std::endl;
278 m_script->on_shutdown();
280 infostream << "Server: Saving environment metadata" << std::endl;
290 // Delete things in the reverse order of creation
299 // Deinitialize scripting
300 infostream << "Server: Deinitializing scripting" << std::endl;
303 // Delete detached inventories
304 for (auto &detached_inventory : m_detached_inventories) {
305 delete detached_inventory.second;
311 infostream << "Server created for gameid \"" << m_gamespec.id << "\"";
312 if (m_simple_singleplayer_mode)
313 infostream << " in simple singleplayer mode" << std::endl;
315 infostream << std::endl;
316 infostream << "- world: " << m_path_world << std::endl;
317 infostream << "- game: " << m_gamespec.path << std::endl;
319 // Create world if it doesn't exist
320 if (!loadGameConfAndInitWorld(m_path_world, m_gamespec))
321 throw ServerError("Failed to initialize world");
323 // Create server thread
324 m_thread = new ServerThread(this);
326 // Create emerge manager
327 m_emerge = new EmergeManager(this);
329 // Create ban manager
330 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
331 m_banmanager = new BanManager(ban_path);
333 m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager(m_path_world));
334 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
335 // complain about mods with unsatisfied dependencies
336 if (!m_modmgr->isConsistent()) {
337 m_modmgr->printUnsatisfiedModsError();
341 MutexAutoLock envlock(m_env_mutex);
343 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
344 ServerMap *servermap = new ServerMap(m_path_world, this, m_emerge);
346 // Initialize scripting
347 infostream << "Server: Initializing Lua" << std::endl;
349 m_script = new ServerScripting(this);
351 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
353 m_modmgr->loadMods(m_script);
355 // Read Textures and calculate sha1 sums
358 // Apply item aliases in the node definition manager
359 m_nodedef->updateAliases(m_itemdef);
361 // Apply texture overrides from texturepack/override.txt
362 std::vector<std::string> paths;
363 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
364 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
365 for (const std::string &path : paths)
366 m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
368 m_nodedef->setNodeRegistrationStatus(true);
370 // Perform pending node name resolutions
371 m_nodedef->runNodeResolveCallbacks();
373 // unmap node names for connected nodeboxes
374 m_nodedef->mapNodeboxConnections();
376 // init the recipe hashes to speed up crafting
377 m_craftdef->initHashes(this);
379 // Initialize Environment
380 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
382 m_clients.setEnv(m_env);
384 if (!servermap->settings_mgr.makeMapgenParams())
385 FATAL_ERROR("Couldn't create any mapgen type");
387 // Initialize mapgens
388 m_emerge->initMapgens(servermap->getMapgenParams());
390 if (g_settings->getBool("enable_rollback_recording")) {
391 // Create rollback manager
392 m_rollback = new RollbackManager(m_path_world, this);
395 // Give environment reference to scripting api
396 m_script->initializeEnvironment(m_env);
398 // Register us to receive map edit events
399 servermap->addEventReceiver(this);
403 m_liquid_transform_every = g_settings->getFloat("liquid_update");
404 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
405 m_csm_restriction_flags = g_settings->getU64("csm_restriction_flags");
406 m_csm_restriction_noderange = g_settings->getU32("csm_restriction_noderange");
411 infostream << "Starting server on " << m_bind_addr.serializeString()
412 << "..." << std::endl;
414 // Stop thread if already running
417 // Initialize connection
418 m_con->SetTimeoutMs(30);
419 m_con->Serve(m_bind_addr);
424 // ASCII art for the win!
426 << " .__ __ __ " << std::endl
427 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
428 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
429 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
430 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
431 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
432 actionstream << "World at [" << m_path_world << "]" << std::endl;
433 actionstream << "Server for gameid=\"" << m_gamespec.id
434 << "\" listening on " << m_bind_addr.serializeString() << ":"
435 << m_bind_addr.getPort() << "." << std::endl;
440 infostream<<"Server: Stopping and waiting threads"<<std::endl;
442 // Stop threads (set run=false first so both start stopping)
444 //m_emergethread.setRun(false);
446 //m_emergethread.stop();
448 infostream<<"Server: Threads stopped"<<std::endl;
451 void Server::step(float dtime)
457 MutexAutoLock lock(m_step_dtime_mutex);
458 m_step_dtime += dtime;
460 // Throw if fatal error occurred in thread
461 std::string async_err = m_async_fatal_error.get();
462 if (!async_err.empty()) {
463 if (!m_simple_singleplayer_mode) {
464 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
465 g_settings->get("kick_msg_crash"),
466 g_settings->getBool("ask_reconnect_on_crash"));
468 throw ServerError("AsyncErr: " + async_err);
472 void Server::AsyncRunStep(bool initial_step)
474 g_profiler->add("Server::AsyncRunStep (num)", 1);
478 MutexAutoLock lock1(m_step_dtime_mutex);
479 dtime = m_step_dtime;
483 // Send blocks to clients
487 if((dtime < 0.001) && !initial_step)
490 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
492 //infostream<<"Server steps "<<dtime<<std::endl;
493 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
496 MutexAutoLock lock1(m_step_dtime_mutex);
497 m_step_dtime -= dtime;
504 m_uptime.set(m_uptime.get() + dtime);
510 Update time of day and overall game time
512 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
515 Send to clients at constant intervals
518 m_time_of_day_send_timer -= dtime;
519 if(m_time_of_day_send_timer < 0.0) {
520 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
521 u16 time = m_env->getTimeOfDay();
522 float time_speed = g_settings->getFloat("time_speed");
523 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
527 MutexAutoLock lock(m_env_mutex);
528 // Figure out and report maximum lag to environment
529 float max_lag = m_env->getMaxLagEstimate();
530 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
532 if(dtime > 0.1 && dtime > max_lag * 2.0)
533 infostream<<"Server: Maximum lag peaked to "<<dtime
537 m_env->reportMaxLagEstimate(max_lag);
539 ScopeProfiler sp(g_profiler, "SEnv step");
540 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
544 static const float map_timer_and_unload_dtime = 2.92;
545 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
547 MutexAutoLock lock(m_env_mutex);
548 // Run Map's timers and unload unused data
549 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
550 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
551 g_settings->getFloat("server_unload_unused_data_timeout"),
556 Listen to the admin chat, if available
559 if (!m_admin_chat->command_queue.empty()) {
560 MutexAutoLock lock(m_env_mutex);
561 while (!m_admin_chat->command_queue.empty()) {
562 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
563 handleChatInterfaceEvent(evt);
567 m_admin_chat->outgoing_queue.push_back(
568 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
575 /* Transform liquids */
576 m_liquid_transform_timer += dtime;
577 if(m_liquid_transform_timer >= m_liquid_transform_every)
579 m_liquid_transform_timer -= m_liquid_transform_every;
581 MutexAutoLock lock(m_env_mutex);
583 ScopeProfiler sp(g_profiler, "Server: liquid transform");
585 std::map<v3s16, MapBlock*> modified_blocks;
586 m_env->getMap().transformLiquids(modified_blocks, m_env);
589 Set the modified blocks unsent for all the clients
591 if (!modified_blocks.empty()) {
592 SetBlocksNotSent(modified_blocks);
595 m_clients.step(dtime);
597 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
599 // send masterserver announce
601 float &counter = m_masterserver_timer;
602 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
603 g_settings->getBool("server_announce")) {
604 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
605 ServerList::AA_START,
606 m_bind_addr.getPort(),
607 m_clients.getPlayerNames(),
609 m_env->getGameTime(),
612 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
622 Check added and deleted active objects
625 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
626 MutexAutoLock envlock(m_env_mutex);
629 const RemoteClientMap &clients = m_clients.getClientList();
630 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
632 // Radius inside which objects are active
633 static thread_local const s16 radius =
634 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
636 // Radius inside which players are active
637 static thread_local const bool is_transfer_limited =
638 g_settings->exists("unlimited_player_transfer_distance") &&
639 !g_settings->getBool("unlimited_player_transfer_distance");
640 static thread_local const s16 player_transfer_dist =
641 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
642 s16 player_radius = player_transfer_dist;
643 if (player_radius == 0 && is_transfer_limited)
644 player_radius = radius;
646 for (const auto &client_it : clients) {
647 RemoteClient *client = client_it.second;
649 // If definitions and textures have not been sent, don't
650 // send objects either
651 if (client->getState() < CS_DefinitionsSent)
654 RemotePlayer *player = m_env->getPlayer(client->peer_id);
656 // This can happen if the client timeouts somehow
660 PlayerSAO *playersao = player->getPlayerSAO();
664 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
665 if (my_radius <= 0) my_radius = radius;
666 //infostream << "Server: Active Radius " << my_radius << std::endl;
668 std::queue<u16> removed_objects;
669 std::queue<u16> added_objects;
670 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
671 client->m_known_objects, removed_objects);
672 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
673 client->m_known_objects, added_objects);
675 // Ignore if nothing happened
676 if (removed_objects.empty() && added_objects.empty()) {
680 std::string data_buffer;
684 // Handle removed objects
685 writeU16((u8*)buf, removed_objects.size());
686 data_buffer.append(buf, 2);
687 while (!removed_objects.empty()) {
689 u16 id = removed_objects.front();
690 ServerActiveObject* obj = m_env->getActiveObject(id);
692 // Add to data buffer for sending
693 writeU16((u8*)buf, id);
694 data_buffer.append(buf, 2);
696 // Remove from known objects
697 client->m_known_objects.erase(id);
699 if(obj && obj->m_known_by_count > 0)
700 obj->m_known_by_count--;
701 removed_objects.pop();
704 // Handle added objects
705 writeU16((u8*)buf, added_objects.size());
706 data_buffer.append(buf, 2);
707 while (!added_objects.empty()) {
709 u16 id = added_objects.front();
710 ServerActiveObject* obj = m_env->getActiveObject(id);
713 u8 type = ACTIVEOBJECT_TYPE_INVALID;
715 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
717 type = obj->getSendType();
719 // Add to data buffer for sending
720 writeU16((u8*)buf, id);
721 data_buffer.append(buf, 2);
722 writeU8((u8*)buf, type);
723 data_buffer.append(buf, 1);
726 data_buffer.append(serializeLongString(
727 obj->getClientInitializationData(client->net_proto_version)));
729 data_buffer.append(serializeLongString(""));
731 // Add to known objects
732 client->m_known_objects.insert(id);
735 obj->m_known_by_count++;
740 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
741 verbosestream << "Server: Sent object remove/add: "
742 << removed_objects.size() << " removed, "
743 << added_objects.size() << " added, "
744 << "packet size is " << pktSize << std::endl;
748 m_mod_storage_save_timer -= dtime;
749 if (m_mod_storage_save_timer <= 0.0f) {
750 infostream << "Saving registered mod storages." << std::endl;
751 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
752 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
753 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
754 if (it->second->isModified()) {
755 it->second->save(getModStoragePath());
765 MutexAutoLock envlock(m_env_mutex);
766 ScopeProfiler sp(g_profiler, "Server: sending object messages");
769 // Value = data sent by object
770 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
772 // Get active object messages from environment
774 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
778 std::vector<ActiveObjectMessage>* message_list = nullptr;
779 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
780 n = buffered_messages.find(aom.id);
781 if (n == buffered_messages.end()) {
782 message_list = new std::vector<ActiveObjectMessage>;
783 buffered_messages[aom.id] = message_list;
786 message_list = n->second;
788 message_list->push_back(aom);
792 const RemoteClientMap &clients = m_clients.getClientList();
793 // Route data to every client
794 for (const auto &client_it : clients) {
795 RemoteClient *client = client_it.second;
796 std::string reliable_data;
797 std::string unreliable_data;
798 // Go through all objects in message buffer
799 for (const auto &buffered_message : buffered_messages) {
800 // If object is not known by client, skip it
801 u16 id = buffered_message.first;
802 if (client->m_known_objects.find(id) == client->m_known_objects.end())
805 // Get message list of object
806 std::vector<ActiveObjectMessage>* list = buffered_message.second;
807 // Go through every message
808 for (const ActiveObjectMessage &aom : *list) {
809 // Compose the full new data with header
810 std::string new_data;
813 writeU16((u8*)&buf[0], aom.id);
814 new_data.append(buf, 2);
816 new_data += serializeString(aom.datastring);
817 // Add data to buffer
819 reliable_data += new_data;
821 unreliable_data += new_data;
825 reliable_data and unreliable_data are now ready.
828 if (!reliable_data.empty()) {
829 SendActiveObjectMessages(client->peer_id, reliable_data);
832 if (!unreliable_data.empty()) {
833 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
838 // Clear buffered_messages
839 for (auto &buffered_message : buffered_messages) {
840 delete buffered_message.second;
845 Send queued-for-sending map edit events.
848 // We will be accessing the environment
849 MutexAutoLock lock(m_env_mutex);
851 // Don't send too many at a time
854 // Single change sending is disabled if queue size is not small
855 bool disable_single_change_sending = false;
856 if(m_unsent_map_edit_queue.size() >= 4)
857 disable_single_change_sending = true;
859 int event_count = m_unsent_map_edit_queue.size();
861 // We'll log the amount of each
864 while (!m_unsent_map_edit_queue.empty()) {
865 MapEditEvent* event = m_unsent_map_edit_queue.front();
866 m_unsent_map_edit_queue.pop();
868 // Players far away from the change are stored here.
869 // Instead of sending the changes, MapBlocks are set not sent
871 std::unordered_set<u16> far_players;
873 switch (event->type) {
876 prof.add("MEET_ADDNODE", 1);
877 sendAddNode(event->p, event->n, &far_players,
878 disable_single_change_sending ? 5 : 30,
879 event->type == MEET_ADDNODE);
881 case MEET_REMOVENODE:
882 prof.add("MEET_REMOVENODE", 1);
883 sendRemoveNode(event->p, &far_players,
884 disable_single_change_sending ? 5 : 30);
886 case MEET_BLOCK_NODE_METADATA_CHANGED:
887 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
888 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
889 m_clients.markBlockposAsNotSent(event->p);
892 infostream << "Server: MEET_OTHER" << std::endl;
893 prof.add("MEET_OTHER", 1);
894 for (const v3s16 &modified_block : event->modified_blocks) {
895 m_clients.markBlockposAsNotSent(modified_block);
899 prof.add("unknown", 1);
900 warningstream << "Server: Unknown MapEditEvent "
901 << ((u32)event->type) << std::endl;
906 Set blocks not sent to far players
908 if (!far_players.empty()) {
909 // Convert list format to that wanted by SetBlocksNotSent
910 std::map<v3s16, MapBlock*> modified_blocks2;
911 for (const v3s16 &modified_block : event->modified_blocks) {
912 modified_blocks2[modified_block] =
913 m_env->getMap().getBlockNoCreateNoEx(modified_block);
916 // Set blocks not sent
917 for (const u16 far_player : far_players) {
918 if (RemoteClient *client = getClient(far_player))
919 client->SetBlocksNotSent(modified_blocks2);
926 if (event_count >= 5) {
927 infostream << "Server: MapEditEvents:" << std::endl;
928 prof.print(infostream);
929 } else if (event_count != 0) {
930 verbosestream << "Server: MapEditEvents:" << std::endl;
931 prof.print(verbosestream);
937 Trigger emergethread (it somehow gets to a non-triggered but
938 bysy state sometimes)
941 float &counter = m_emergethread_trigger_timer;
943 if (counter >= 2.0) {
946 m_emerge->startThreads();
950 // Save map, players and auth stuff
952 float &counter = m_savemap_timer;
954 static thread_local const float save_interval =
955 g_settings->getFloat("server_map_save_interval");
956 if (counter >= save_interval) {
958 MutexAutoLock lock(m_env_mutex);
960 ScopeProfiler sp(g_profiler, "Server: saving stuff");
963 if (m_banmanager->isModified()) {
964 m_banmanager->save();
967 // Save changed parts of map
968 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
971 m_env->saveLoadedPlayers();
973 // Save environment metadata
978 m_shutdown_state.tick(dtime, this);
981 void Server::Receive()
986 m_con->Receive(&pkt);
987 peer_id = pkt.getPeerId();
989 } catch (const con::InvalidIncomingDataException &e) {
990 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
991 << e.what() << std::endl;
992 } catch (const SerializationError &e) {
993 infostream << "Server::Receive(): SerializationError: what()="
994 << e.what() << std::endl;
995 } catch (const ClientStateError &e) {
996 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
997 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
998 L"Try reconnecting or updating your client");
999 } catch (const con::PeerNotFoundException &e) {
1004 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1006 std::string playername;
1007 PlayerSAO *playersao = NULL;
1010 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1012 playername = client->getName();
1013 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1015 } catch (std::exception &e) {
1021 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1023 // If failed, cancel
1024 if (!playersao || !player) {
1025 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1026 actionstream << "Server: Failed to emerge player \"" << playername
1027 << "\" (player allocated to an another client)" << std::endl;
1028 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1029 L"name. If your client closed unexpectedly, try again in "
1032 errorstream << "Server: " << playername << ": Failed to emerge player"
1034 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1040 Send complete position information
1042 SendMovePlayer(peer_id);
1045 SendPlayerPrivileges(peer_id);
1047 // Send inventory formspec
1048 SendPlayerInventoryFormspec(peer_id);
1051 SendInventory(playersao);
1053 // Send HP or death screen
1054 if (playersao->isDead())
1055 SendDeathscreen(peer_id, false, v3f(0,0,0));
1057 SendPlayerHPOrDie(playersao,
1058 PlayerHPChangeReason(PlayerHPChangeReason::SET_HP));
1061 SendPlayerBreath(playersao);
1063 Address addr = getPeerAddress(player->getPeerId());
1064 std::string ip_str = addr.serializeString();
1065 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1070 const std::vector<std::string> &names = m_clients.getPlayerNames();
1072 actionstream << player->getName() << " joins game. List of players: ";
1074 for (const std::string &name : names) {
1075 actionstream << name << " ";
1078 actionstream << player->getName() <<std::endl;
1083 inline void Server::handleCommand(NetworkPacket* pkt)
1085 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1086 (this->*opHandle.handler)(pkt);
1089 void Server::ProcessData(NetworkPacket *pkt)
1091 // Environment is locked first.
1092 MutexAutoLock envlock(m_env_mutex);
1094 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1095 u32 peer_id = pkt->getPeerId();
1098 Address address = getPeerAddress(peer_id);
1099 std::string addr_s = address.serializeString();
1101 if(m_banmanager->isIpBanned(addr_s)) {
1102 std::string ban_name = m_banmanager->getBanName(addr_s);
1103 infostream << "Server: A banned client tried to connect from "
1104 << addr_s << "; banned name was "
1105 << ban_name << std::endl;
1106 // This actually doesn't seem to transfer to the client
1107 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1108 + utf8_to_wide(ban_name));
1112 catch(con::PeerNotFoundException &e) {
1114 * no peer for this packet found
1115 * most common reason is peer timeout, e.g. peer didn't
1116 * respond for some time, your server was overloaded or
1119 infostream << "Server::ProcessData(): Canceling: peer "
1120 << peer_id << " not found" << std::endl;
1125 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1127 // Command must be handled into ToServerCommandHandler
1128 if (command >= TOSERVER_NUM_MSG_TYPES) {
1129 infostream << "Server: Ignoring unknown command "
1130 << command << std::endl;
1134 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1139 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1141 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1142 errorstream << "Server::ProcessData(): Cancelling: Peer"
1143 " serialization format invalid or not initialized."
1144 " Skipping incoming command=" << command << std::endl;
1148 /* Handle commands related to client startup */
1149 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1154 if (m_clients.getClientState(peer_id) < CS_Active) {
1155 if (command == TOSERVER_PLAYERPOS) return;
1157 errorstream << "Got packet command: " << command << " for peer id "
1158 << peer_id << " but client isn't active yet. Dropping packet "
1164 } catch (SendFailedException &e) {
1165 errorstream << "Server::ProcessData(): SendFailedException: "
1166 << "what=" << e.what()
1168 } catch (PacketError &e) {
1169 actionstream << "Server::ProcessData(): PacketError: "
1170 << "what=" << e.what()
1175 void Server::setTimeOfDay(u32 time)
1177 m_env->setTimeOfDay(time);
1178 m_time_of_day_send_timer = 0;
1181 void Server::onMapEditEvent(MapEditEvent *event)
1183 if (m_ignore_map_edit_events_area.contains(event->getArea()))
1185 MapEditEvent *e = event->clone();
1186 m_unsent_map_edit_queue.push(e);
1189 Inventory* Server::getInventory(const InventoryLocation &loc)
1192 case InventoryLocation::UNDEFINED:
1193 case InventoryLocation::CURRENT_PLAYER:
1195 case InventoryLocation::PLAYER:
1197 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1200 PlayerSAO *playersao = player->getPlayerSAO();
1203 return playersao->getInventory();
1206 case InventoryLocation::NODEMETA:
1208 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1211 return meta->getInventory();
1214 case InventoryLocation::DETACHED:
1216 if(m_detached_inventories.count(loc.name) == 0)
1218 return m_detached_inventories[loc.name];
1222 sanity_check(false); // abort
1227 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1230 case InventoryLocation::UNDEFINED:
1232 case InventoryLocation::PLAYER:
1237 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1242 PlayerSAO *playersao = player->getPlayerSAO();
1246 SendInventory(playersao);
1249 case InventoryLocation::NODEMETA:
1251 v3s16 blockpos = getNodeBlockPos(loc.p);
1253 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1255 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1257 m_clients.markBlockposAsNotSent(blockpos);
1260 case InventoryLocation::DETACHED:
1262 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1266 sanity_check(false); // abort
1271 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1273 std::vector<session_t> clients = m_clients.getClientIDs();
1275 // Set the modified blocks unsent for all the clients
1276 for (const session_t client_id : clients) {
1277 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1278 client->SetBlocksNotSent(block);
1283 void Server::peerAdded(con::Peer *peer)
1285 verbosestream<<"Server::peerAdded(): peer->id="
1286 <<peer->id<<std::endl;
1288 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1291 void Server::deletingPeer(con::Peer *peer, bool timeout)
1293 verbosestream<<"Server::deletingPeer(): peer->id="
1294 <<peer->id<<", timeout="<<timeout<<std::endl;
1296 m_clients.event(peer->id, CSE_Disconnect);
1297 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1300 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1302 *retval = m_con->getPeerStat(peer_id,type);
1303 return *retval != -1;
1306 bool Server::getClientInfo(
1315 std::string* vers_string
1318 *state = m_clients.getClientState(peer_id);
1320 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1327 *uptime = client->uptime();
1328 *ser_vers = client->serialization_version;
1329 *prot_vers = client->net_proto_version;
1331 *major = client->getMajor();
1332 *minor = client->getMinor();
1333 *patch = client->getPatch();
1334 *vers_string = client->getPatch();
1341 void Server::handlePeerChanges()
1343 while(!m_peer_change_queue.empty())
1345 con::PeerChange c = m_peer_change_queue.front();
1346 m_peer_change_queue.pop();
1348 verbosestream<<"Server: Handling peer change: "
1349 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1354 case con::PEER_ADDED:
1355 m_clients.CreateClient(c.peer_id);
1358 case con::PEER_REMOVED:
1359 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1363 FATAL_ERROR("Invalid peer change event received!");
1369 void Server::printToConsoleOnly(const std::string &text)
1372 m_admin_chat->outgoing_queue.push_back(
1373 new ChatEventChat("", utf8_to_wide(text)));
1375 std::cout << text << std::endl;
1379 void Server::Send(NetworkPacket *pkt)
1381 Send(pkt->getPeerId(), pkt);
1384 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1386 m_clients.send(peer_id,
1387 clientCommandFactoryTable[pkt->getCommand()].channel,
1389 clientCommandFactoryTable[pkt->getCommand()].reliable);
1392 void Server::SendMovement(session_t peer_id)
1394 std::ostringstream os(std::ios_base::binary);
1396 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1398 pkt << g_settings->getFloat("movement_acceleration_default");
1399 pkt << g_settings->getFloat("movement_acceleration_air");
1400 pkt << g_settings->getFloat("movement_acceleration_fast");
1401 pkt << g_settings->getFloat("movement_speed_walk");
1402 pkt << g_settings->getFloat("movement_speed_crouch");
1403 pkt << g_settings->getFloat("movement_speed_fast");
1404 pkt << g_settings->getFloat("movement_speed_climb");
1405 pkt << g_settings->getFloat("movement_speed_jump");
1406 pkt << g_settings->getFloat("movement_liquid_fluidity");
1407 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1408 pkt << g_settings->getFloat("movement_liquid_sink");
1409 pkt << g_settings->getFloat("movement_gravity");
1414 void Server::SendPlayerHPOrDie(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1416 if (!g_settings->getBool("enable_damage"))
1419 session_t peer_id = playersao->getPeerID();
1420 bool is_alive = playersao->getHP() > 0;
1423 SendPlayerHP(peer_id);
1425 DiePlayer(peer_id, reason);
1428 void Server::SendHP(session_t peer_id, u16 hp)
1430 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1435 void Server::SendBreath(session_t peer_id, u16 breath)
1437 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1438 pkt << (u16) breath;
1442 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1443 const std::string &custom_reason, bool reconnect)
1445 assert(reason < SERVER_ACCESSDENIED_MAX);
1447 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1449 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1450 pkt << custom_reason;
1451 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1452 reason == SERVER_ACCESSDENIED_CRASH)
1453 pkt << custom_reason << (u8)reconnect;
1457 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1459 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1464 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1465 v3f camera_point_target)
1467 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1468 pkt << set_camera_point_target << camera_point_target;
1472 void Server::SendItemDef(session_t peer_id,
1473 IItemDefManager *itemdef, u16 protocol_version)
1475 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1479 u32 length of the next item
1480 zlib-compressed serialized ItemDefManager
1482 std::ostringstream tmp_os(std::ios::binary);
1483 itemdef->serialize(tmp_os, protocol_version);
1484 std::ostringstream tmp_os2(std::ios::binary);
1485 compressZlib(tmp_os.str(), tmp_os2);
1486 pkt.putLongString(tmp_os2.str());
1489 verbosestream << "Server: Sending item definitions to id(" << peer_id
1490 << "): size=" << pkt.getSize() << std::endl;
1495 void Server::SendNodeDef(session_t peer_id,
1496 const NodeDefManager *nodedef, u16 protocol_version)
1498 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1502 u32 length of the next item
1503 zlib-compressed serialized NodeDefManager
1505 std::ostringstream tmp_os(std::ios::binary);
1506 nodedef->serialize(tmp_os, protocol_version);
1507 std::ostringstream tmp_os2(std::ios::binary);
1508 compressZlib(tmp_os.str(), tmp_os2);
1510 pkt.putLongString(tmp_os2.str());
1513 verbosestream << "Server: Sending node definitions to id(" << peer_id
1514 << "): size=" << pkt.getSize() << std::endl;
1520 Non-static send methods
1523 void Server::SendInventory(PlayerSAO* playerSAO)
1525 UpdateCrafting(playerSAO->getPlayer());
1531 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1533 std::ostringstream os;
1534 playerSAO->getInventory()->serialize(os);
1536 std::string s = os.str();
1538 pkt.putRawString(s.c_str(), s.size());
1542 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1544 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1546 u8 type = message.type;
1547 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1549 if (peer_id != PEER_ID_INEXISTENT) {
1550 RemotePlayer *player = m_env->getPlayer(peer_id);
1556 m_clients.sendToAll(&pkt);
1560 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1561 const std::string &formname)
1563 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1564 if (formspec.empty()){
1565 //the client should close the formspec
1566 m_formspec_state_data.erase(peer_id);
1567 pkt.putLongString("");
1569 m_formspec_state_data[peer_id] = formname;
1570 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1577 // Spawns a particle on peer with peer_id
1578 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1579 v3f pos, v3f velocity, v3f acceleration,
1580 float expirationtime, float size, bool collisiondetection,
1581 bool collision_removal, bool object_collision,
1582 bool vertical, const std::string &texture,
1583 const struct TileAnimationParams &animation, u8 glow)
1585 static thread_local const float radius =
1586 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1588 if (peer_id == PEER_ID_INEXISTENT) {
1589 std::vector<session_t> clients = m_clients.getClientIDs();
1591 for (const session_t client_id : clients) {
1592 RemotePlayer *player = m_env->getPlayer(client_id);
1596 PlayerSAO *sao = player->getPlayerSAO();
1600 // Do not send to distant clients
1601 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1604 SendSpawnParticle(client_id, player->protocol_version,
1605 pos, velocity, acceleration,
1606 expirationtime, size, collisiondetection, collision_removal,
1607 object_collision, vertical, texture, animation, glow);
1612 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1614 pkt << pos << velocity << acceleration << expirationtime
1615 << size << collisiondetection;
1616 pkt.putLongString(texture);
1618 pkt << collision_removal;
1619 // This is horrible but required (why are there two ways to serialize pkts?)
1620 std::ostringstream os(std::ios_base::binary);
1621 animation.serialize(os, protocol_version);
1622 pkt.putRawString(os.str());
1624 pkt << object_collision;
1629 // Adds a ParticleSpawner on peer with peer_id
1630 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1631 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1632 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1633 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1634 bool object_collision, u16 attached_id, bool vertical, const std::string &texture, u32 id,
1635 const struct TileAnimationParams &animation, u8 glow)
1637 if (peer_id == PEER_ID_INEXISTENT) {
1638 // This sucks and should be replaced:
1639 std::vector<session_t> clients = m_clients.getClientIDs();
1640 for (const session_t client_id : clients) {
1641 RemotePlayer *player = m_env->getPlayer(client_id);
1644 SendAddParticleSpawner(client_id, player->protocol_version,
1645 amount, spawntime, minpos, maxpos,
1646 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1647 minsize, maxsize, collisiondetection, collision_removal,
1648 object_collision, attached_id, vertical, texture, id,
1654 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1656 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1657 << minacc << maxacc << minexptime << maxexptime << minsize
1658 << maxsize << collisiondetection;
1660 pkt.putLongString(texture);
1662 pkt << id << vertical;
1663 pkt << collision_removal;
1665 // This is horrible but required
1666 std::ostringstream os(std::ios_base::binary);
1667 animation.serialize(os, protocol_version);
1668 pkt.putRawString(os.str());
1670 pkt << object_collision;
1675 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1677 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1679 // Ugly error in this packet
1682 if (peer_id != PEER_ID_INEXISTENT)
1685 m_clients.sendToAll(&pkt);
1689 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1691 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1693 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1694 << form->text << form->number << form->item << form->dir
1695 << form->align << form->offset << form->world_pos << form->size;
1700 void Server::SendHUDRemove(session_t peer_id, u32 id)
1702 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1707 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1709 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1710 pkt << id << (u8) stat;
1714 case HUD_STAT_SCALE:
1715 case HUD_STAT_ALIGN:
1716 case HUD_STAT_OFFSET:
1717 pkt << *(v2f *) value;
1721 pkt << *(std::string *) value;
1723 case HUD_STAT_WORLD_POS:
1724 pkt << *(v3f *) value;
1727 pkt << *(v2s32 *) value;
1729 case HUD_STAT_NUMBER:
1733 pkt << *(u32 *) value;
1740 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1742 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1744 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1746 pkt << flags << mask;
1751 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1753 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1754 pkt << param << value;
1758 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1759 const std::string &type, const std::vector<std::string> ¶ms,
1762 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1763 pkt << bgcolor << type << (u16) params.size();
1765 for (const std::string ¶m : params)
1773 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1775 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1776 pkt << params.density << params.color_bright << params.color_ambient
1777 << params.height << params.thickness << params.speed;
1781 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1784 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1787 pkt << do_override << (u16) (ratio * 65535);
1792 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1794 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1795 pkt << time << time_speed;
1797 if (peer_id == PEER_ID_INEXISTENT) {
1798 m_clients.sendToAll(&pkt);
1805 void Server::SendPlayerHP(session_t peer_id)
1807 PlayerSAO *playersao = getPlayerSAO(peer_id);
1808 // In some rare case if the player is disconnected
1809 // while Lua call l_punch, for example, this can be NULL
1813 SendHP(peer_id, playersao->getHP());
1814 m_script->player_event(playersao,"health_changed");
1816 // Send to other clients
1817 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1818 ActiveObjectMessage aom(playersao->getId(), true, str);
1819 playersao->m_messages_out.push(aom);
1822 void Server::SendPlayerBreath(PlayerSAO *sao)
1826 m_script->player_event(sao, "breath_changed");
1827 SendBreath(sao->getPeerID(), sao->getBreath());
1830 void Server::SendMovePlayer(session_t peer_id)
1832 RemotePlayer *player = m_env->getPlayer(peer_id);
1834 PlayerSAO *sao = player->getPlayerSAO();
1837 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1838 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1841 v3f pos = sao->getBasePosition();
1842 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1843 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1844 << " pitch=" << sao->getPitch()
1845 << " yaw=" << sao->getYaw()
1852 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1853 f32 animation_speed)
1855 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1858 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1859 << animation_frames[3] << animation_speed;
1864 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1866 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1867 pkt << first << third;
1871 void Server::SendPlayerPrivileges(session_t peer_id)
1873 RemotePlayer *player = m_env->getPlayer(peer_id);
1875 if(player->getPeerId() == PEER_ID_INEXISTENT)
1878 std::set<std::string> privs;
1879 m_script->getAuth(player->getName(), NULL, &privs);
1881 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1882 pkt << (u16) privs.size();
1884 for (const std::string &priv : privs) {
1891 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1893 RemotePlayer *player = m_env->getPlayer(peer_id);
1895 if (player->getPeerId() == PEER_ID_INEXISTENT)
1898 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1899 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1903 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1905 RemotePlayer *player = m_env->getPlayer(peer_id);
1907 if (player->getPeerId() == PEER_ID_INEXISTENT)
1910 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1911 pkt << FORMSPEC_VERSION_STRING + player->formspec_prepend;
1915 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1917 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1918 pkt.putRawString(datas.c_str(), datas.size());
1920 return pkt.getSize();
1923 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1926 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1927 datas.size(), peer_id);
1929 pkt.putRawString(datas.c_str(), datas.size());
1931 m_clients.send(pkt.getPeerId(),
1932 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1936 void Server::SendCSMRestrictionFlags(session_t peer_id)
1938 NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS,
1939 sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id);
1940 pkt << m_csm_restriction_flags << m_csm_restriction_noderange;
1944 s32 Server::playSound(const SimpleSoundSpec &spec,
1945 const ServerSoundParams ¶ms)
1947 // Find out initial position of sound
1948 bool pos_exists = false;
1949 v3f pos = params.getPos(m_env, &pos_exists);
1950 // If position is not found while it should be, cancel sound
1951 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1954 // Filter destination clients
1955 std::vector<session_t> dst_clients;
1956 if(!params.to_player.empty()) {
1957 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1959 infostream<<"Server::playSound: Player \""<<params.to_player
1960 <<"\" not found"<<std::endl;
1963 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1964 infostream<<"Server::playSound: Player \""<<params.to_player
1965 <<"\" not connected"<<std::endl;
1968 dst_clients.push_back(player->getPeerId());
1970 std::vector<session_t> clients = m_clients.getClientIDs();
1972 for (const session_t client_id : clients) {
1973 RemotePlayer *player = m_env->getPlayer(client_id);
1977 PlayerSAO *sao = player->getPlayerSAO();
1982 if(sao->getBasePosition().getDistanceFrom(pos) >
1983 params.max_hear_distance)
1986 dst_clients.push_back(client_id);
1990 if(dst_clients.empty())
1994 s32 id = m_next_sound_id++;
1995 // The sound will exist as a reference in m_playing_sounds
1996 m_playing_sounds[id] = ServerPlayingSound();
1997 ServerPlayingSound &psound = m_playing_sounds[id];
1998 psound.params = params;
2001 float gain = params.gain * spec.gain;
2002 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2003 pkt << id << spec.name << gain
2004 << (u8) params.type << pos << params.object
2005 << params.loop << params.fade << params.pitch;
2007 // Backwards compability
2008 bool play_sound = gain > 0;
2010 for (const u16 dst_client : dst_clients) {
2011 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2012 psound.clients.insert(dst_client);
2013 m_clients.send(dst_client, 0, &pkt, true);
2018 void Server::stopSound(s32 handle)
2020 // Get sound reference
2021 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2022 m_playing_sounds.find(handle);
2023 if (i == m_playing_sounds.end())
2025 ServerPlayingSound &psound = i->second;
2027 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2030 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2031 si != psound.clients.end(); ++si) {
2033 m_clients.send(*si, 0, &pkt, true);
2035 // Remove sound reference
2036 m_playing_sounds.erase(i);
2039 void Server::fadeSound(s32 handle, float step, float gain)
2041 // Get sound reference
2042 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2043 m_playing_sounds.find(handle);
2044 if (i == m_playing_sounds.end())
2047 ServerPlayingSound &psound = i->second;
2048 psound.params.gain = gain;
2050 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2051 pkt << handle << step << gain;
2053 // Backwards compability
2054 bool play_sound = gain > 0;
2055 ServerPlayingSound compat_psound = psound;
2056 compat_psound.clients.clear();
2058 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2059 compat_pkt << handle;
2061 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2062 it != psound.clients.end();) {
2063 if (m_clients.getProtocolVersion(*it) >= 32) {
2065 m_clients.send(*it, 0, &pkt, true);
2068 compat_psound.clients.insert(*it);
2070 m_clients.send(*it, 0, &compat_pkt, true);
2071 psound.clients.erase(it++);
2075 // Remove sound reference
2076 if (!play_sound || psound.clients.empty())
2077 m_playing_sounds.erase(i);
2079 if (play_sound && !compat_psound.clients.empty()) {
2080 // Play new sound volume on older clients
2081 playSound(compat_psound.spec, compat_psound.params);
2085 void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
2088 float maxd = far_d_nodes * BS;
2089 v3f p_f = intToFloat(p, BS);
2090 v3s16 block_pos = getNodeBlockPos(p);
2092 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2095 std::vector<session_t> clients = m_clients.getClientIDs();
2098 for (session_t client_id : clients) {
2099 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2103 RemotePlayer *player = m_env->getPlayer(client_id);
2104 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2106 // If player is far away, only set modified blocks not sent
2107 if (!client->isBlockSent(block_pos) || (sao &&
2108 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2110 far_players->emplace(client_id);
2112 client->SetBlockNotSent(block_pos);
2117 m_clients.send(client_id, 0, &pkt, true);
2123 void Server::sendAddNode(v3s16 p, MapNode n, std::unordered_set<u16> *far_players,
2124 float far_d_nodes, bool remove_metadata)
2126 float maxd = far_d_nodes * BS;
2127 v3f p_f = intToFloat(p, BS);
2128 v3s16 block_pos = getNodeBlockPos(p);
2130 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2131 pkt << p << n.param0 << n.param1 << n.param2
2132 << (u8) (remove_metadata ? 0 : 1);
2134 std::vector<session_t> clients = m_clients.getClientIDs();
2137 for (session_t client_id : clients) {
2138 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id);
2142 RemotePlayer *player = m_env->getPlayer(client_id);
2143 PlayerSAO *sao = player ? player->getPlayerSAO() : nullptr;
2145 // If player is far away, only set modified blocks not sent
2146 if (!client->isBlockSent(block_pos) || (sao &&
2147 sao->getBasePosition().getDistanceFrom(p_f) > maxd)) {
2149 far_players->emplace(client_id);
2151 client->SetBlockNotSent(block_pos);
2156 m_clients.send(client_id, 0, &pkt, true);
2162 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2163 u16 net_proto_version)
2166 Create a packet with the block in the right format
2169 std::ostringstream os(std::ios_base::binary);
2170 block->serialize(os, ver, false);
2171 block->serializeNetworkSpecific(os);
2172 std::string s = os.str();
2174 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2176 pkt << block->getPos();
2177 pkt.putRawString(s.c_str(), s.size());
2181 void Server::SendBlocks(float dtime)
2183 MutexAutoLock envlock(m_env_mutex);
2184 //TODO check if one big lock could be faster then multiple small ones
2186 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2188 std::vector<PrioritySortedBlockTransfer> queue;
2190 u32 total_sending = 0;
2193 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2195 std::vector<session_t> clients = m_clients.getClientIDs();
2198 for (const session_t client_id : clients) {
2199 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2204 total_sending += client->getSendingCount();
2205 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2211 // Lowest priority number comes first.
2212 // Lowest is most important.
2213 std::sort(queue.begin(), queue.end());
2217 // Maximal total count calculation
2218 // The per-client block sends is halved with the maximal online users
2219 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2220 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2222 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2223 if (total_sending >= max_blocks_to_send)
2226 MapBlock *block = nullptr;
2228 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2229 } catch (const InvalidPositionException &e) {
2233 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2238 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2239 client->net_proto_version);
2241 client->SentBlock(block_to_send.pos);
2247 void Server::fillMediaCache()
2249 infostream<<"Server: Calculating media file checksums"<<std::endl;
2251 // Collect all media file paths
2252 std::vector<std::string> paths;
2253 m_modmgr->getModsMediaPaths(paths);
2254 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2255 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2257 // Collect media file information from paths into cache
2258 for (const std::string &mediapath : paths) {
2259 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2260 for (const fs::DirListNode &dln : dirlist) {
2261 if (dln.dir) // Ignode dirs
2263 std::string filename = dln.name;
2264 // If name contains illegal characters, ignore the file
2265 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2266 infostream<<"Server: ignoring illegal file name: \""
2267 << filename << "\"" << std::endl;
2270 // If name is not in a supported format, ignore it
2271 const char *supported_ext[] = {
2272 ".png", ".jpg", ".bmp", ".tga",
2273 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2275 ".x", ".b3d", ".md2", ".obj",
2276 // Custom translation file format
2280 if (removeStringEnd(filename, supported_ext).empty()){
2281 infostream << "Server: ignoring unsupported file extension: \""
2282 << filename << "\"" << std::endl;
2285 // Ok, attempt to load the file and add to cache
2286 std::string filepath;
2287 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2290 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2292 errorstream << "Server::fillMediaCache(): Could not open \""
2293 << filename << "\" for reading" << std::endl;
2296 std::ostringstream tmp_os(std::ios_base::binary);
2300 fis.read(buf, 1024);
2301 std::streamsize len = fis.gcount();
2302 tmp_os.write(buf, len);
2311 errorstream<<"Server::fillMediaCache(): Failed to read \""
2312 << filename << "\"" << std::endl;
2315 if(tmp_os.str().length() == 0) {
2316 errorstream << "Server::fillMediaCache(): Empty file \""
2317 << filepath << "\"" << std::endl;
2322 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2324 unsigned char *digest = sha1.getDigest();
2325 std::string sha1_base64 = base64_encode(digest, 20);
2326 std::string sha1_hex = hex_encode((char*)digest, 20);
2330 m_media[filename] = MediaInfo(filepath, sha1_base64);
2331 verbosestream << "Server: " << sha1_hex << " is " << filename
2337 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2339 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2343 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2346 std::string lang_suffix;
2347 lang_suffix.append(".").append(lang_code).append(".tr");
2348 for (const auto &i : m_media) {
2349 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2356 for (const auto &i : m_media) {
2357 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2359 pkt << i.first << i.second.sha1_digest;
2362 pkt << g_settings->get("remote_media");
2366 struct SendableMedia
2372 SendableMedia(const std::string &name_="", const std::string &path_="",
2373 const std::string &data_=""):
2380 void Server::sendRequestedMedia(session_t peer_id,
2381 const std::vector<std::string> &tosend)
2383 verbosestream<<"Server::sendRequestedMedia(): "
2384 <<"Sending files to client"<<std::endl;
2388 // Put 5kB in one bunch (this is not accurate)
2389 u32 bytes_per_bunch = 5000;
2391 std::vector< std::vector<SendableMedia> > file_bunches;
2392 file_bunches.emplace_back();
2394 u32 file_size_bunch_total = 0;
2396 for (const std::string &name : tosend) {
2397 if (m_media.find(name) == m_media.end()) {
2398 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2399 <<"unknown file \""<<(name)<<"\""<<std::endl;
2403 //TODO get path + name
2404 std::string tpath = m_media[name].path;
2407 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2409 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2410 <<tpath<<"\" for reading"<<std::endl;
2413 std::ostringstream tmp_os(std::ios_base::binary);
2417 fis.read(buf, 1024);
2418 std::streamsize len = fis.gcount();
2419 tmp_os.write(buf, len);
2420 file_size_bunch_total += len;
2429 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2430 <<name<<"\""<<std::endl;
2433 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2434 <<tname<<"\""<<std::endl;*/
2436 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2438 // Start next bunch if got enough data
2439 if(file_size_bunch_total >= bytes_per_bunch) {
2440 file_bunches.emplace_back();
2441 file_size_bunch_total = 0;
2446 /* Create and send packets */
2448 u16 num_bunches = file_bunches.size();
2449 for (u16 i = 0; i < num_bunches; i++) {
2452 u16 total number of texture bunches
2453 u16 index of this bunch
2454 u32 number of files in this bunch
2463 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2464 pkt << num_bunches << i << (u32) file_bunches[i].size();
2466 for (const SendableMedia &j : file_bunches[i]) {
2468 pkt.putLongString(j.data);
2471 verbosestream << "Server::sendRequestedMedia(): bunch "
2472 << i << "/" << num_bunches
2473 << " files=" << file_bunches[i].size()
2474 << " size=" << pkt.getSize() << std::endl;
2479 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2481 if(m_detached_inventories.count(name) == 0) {
2482 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2485 Inventory *inv = m_detached_inventories[name];
2486 std::ostringstream os(std::ios_base::binary);
2488 os << serializeString(name);
2492 std::string s = os.str();
2494 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2495 pkt.putRawString(s.c_str(), s.size());
2497 const std::string &check = m_detached_inventories_player[name];
2498 if (peer_id == PEER_ID_INEXISTENT) {
2500 return m_clients.sendToAll(&pkt);
2501 RemotePlayer *p = m_env->getPlayer(check.c_str());
2503 m_clients.send(p->getPeerId(), 0, &pkt, true);
2505 if (check.empty() || getPlayerName(peer_id) == check)
2510 void Server::sendDetachedInventories(session_t peer_id)
2512 for (const auto &detached_inventory : m_detached_inventories) {
2513 const std::string &name = detached_inventory.first;
2514 //Inventory *inv = i->second;
2515 sendDetachedInventory(name, peer_id);
2523 void Server::DiePlayer(session_t peer_id, const PlayerHPChangeReason &reason)
2525 PlayerSAO *playersao = getPlayerSAO(peer_id);
2526 // In some rare cases this can be NULL -- if the player is disconnected
2527 // when a Lua function modifies l_punch, for example
2531 infostream << "Server::DiePlayer(): Player "
2532 << playersao->getPlayer()->getName()
2533 << " dies" << std::endl;
2535 playersao->setHP(0, reason);
2536 playersao->clearParentAttachment();
2538 // Trigger scripted stuff
2539 m_script->on_dieplayer(playersao, reason);
2541 SendPlayerHP(peer_id);
2542 SendDeathscreen(peer_id, false, v3f(0,0,0));
2545 void Server::RespawnPlayer(session_t peer_id)
2547 PlayerSAO *playersao = getPlayerSAO(peer_id);
2550 infostream << "Server::RespawnPlayer(): Player "
2551 << playersao->getPlayer()->getName()
2552 << " respawns" << std::endl;
2554 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2555 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2556 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2558 bool repositioned = m_script->on_respawnplayer(playersao);
2559 if (!repositioned) {
2560 // setPos will send the new position to client
2561 playersao->setPos(findSpawnPos());
2564 SendPlayerHP(peer_id);
2568 void Server::DenySudoAccess(session_t peer_id)
2570 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2575 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2576 const std::string &str_reason, bool reconnect)
2578 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2580 m_clients.event(peer_id, CSE_SetDenied);
2581 DisconnectPeer(peer_id);
2585 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2586 const std::string &custom_reason)
2588 SendAccessDenied(peer_id, reason, custom_reason);
2589 m_clients.event(peer_id, CSE_SetDenied);
2590 DisconnectPeer(peer_id);
2593 // 13/03/15: remove this function when protocol version 25 will become
2594 // the minimum version for MT users, maybe in 1 year
2595 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2597 SendAccessDenied_Legacy(peer_id, reason);
2598 m_clients.event(peer_id, CSE_SetDenied);
2599 DisconnectPeer(peer_id);
2602 void Server::DisconnectPeer(session_t peer_id)
2604 m_modchannel_mgr->leaveAllChannels(peer_id);
2605 m_con->DisconnectPeer(peer_id);
2608 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2611 RemoteClient* client = getClient(peer_id, CS_Invalid);
2613 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2615 // Right now, the auth mechs don't change between login and sudo mode.
2616 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2617 client->allowed_sudo_mechs = sudo_auth_mechs;
2619 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2620 << g_settings->getFloat("dedicated_server_step")
2624 m_clients.event(peer_id, CSE_AuthAccept);
2626 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2628 // We only support SRP right now
2629 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2631 resp_pkt << sudo_auth_mechs;
2633 m_clients.event(peer_id, CSE_SudoSuccess);
2637 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2639 std::wstring message;
2642 Clear references to playing sounds
2644 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2645 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2646 ServerPlayingSound &psound = i->second;
2647 psound.clients.erase(peer_id);
2648 if (psound.clients.empty())
2649 m_playing_sounds.erase(i++);
2654 // clear formspec info so the next client can't abuse the current state
2655 m_formspec_state_data.erase(peer_id);
2657 RemotePlayer *player = m_env->getPlayer(peer_id);
2659 /* Run scripts and remove from environment */
2661 PlayerSAO *playersao = player->getPlayerSAO();
2664 playersao->clearChildAttachments();
2665 playersao->clearParentAttachment();
2667 // inform connected clients
2668 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2669 // (u16) 1 + std::string represents a vector serialization representation
2670 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2671 m_clients.sendToAll(¬ice);
2673 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2675 playersao->disconnected();
2682 if (player && reason != CDR_DENY) {
2683 std::ostringstream os(std::ios_base::binary);
2684 std::vector<session_t> clients = m_clients.getClientIDs();
2686 for (const session_t client_id : clients) {
2688 RemotePlayer *player = m_env->getPlayer(client_id);
2692 // Get name of player
2693 os << player->getName() << " ";
2696 std::string name = player->getName();
2697 actionstream << name << " "
2698 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2699 << " List of players: " << os.str() << std::endl;
2701 m_admin_chat->outgoing_queue.push_back(
2702 new ChatEventNick(CET_NICK_REMOVE, name));
2706 MutexAutoLock env_lock(m_env_mutex);
2707 m_clients.DeleteClient(peer_id);
2711 // Send leave chat message to all remaining clients
2712 if (!message.empty()) {
2713 SendChatMessage(PEER_ID_INEXISTENT,
2714 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2718 void Server::UpdateCrafting(RemotePlayer *player)
2720 InventoryList *clist = player->inventory.getList("craft");
2721 if (!clist || clist->getSize() == 0)
2724 // Get a preview for crafting
2726 InventoryLocation loc;
2727 loc.setPlayer(player->getName());
2728 std::vector<ItemStack> output_replacements;
2729 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2730 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2733 InventoryList *plist = player->inventory.getList("craftpreview");
2734 if (plist && plist->getSize() >= 1) {
2735 // Put the new preview in
2736 plist->changeItem(0, preview);
2740 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2742 if (evt->type == CET_NICK_ADD) {
2743 // The terminal informed us of its nick choice
2744 m_admin_nick = ((ChatEventNick *)evt)->nick;
2745 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2746 errorstream << "You haven't set up an account." << std::endl
2747 << "Please log in using the client as '"
2748 << m_admin_nick << "' with a secure password." << std::endl
2749 << "Until then, you can't execute admin tasks via the console," << std::endl
2750 << "and everybody can claim the user account instead of you," << std::endl
2751 << "giving them full control over this server." << std::endl;
2754 assert(evt->type == CET_CHAT);
2755 handleAdminChat((ChatEventChat *)evt);
2759 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2760 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2762 // If something goes wrong, this player is to blame
2763 RollbackScopeActor rollback_scope(m_rollback,
2764 std::string("player:") + name);
2766 if (g_settings->getBool("strip_color_codes"))
2767 wmessage = unescape_enriched(wmessage);
2770 switch (player->canSendChatMessage()) {
2771 case RPLAYER_CHATRESULT_FLOODING: {
2772 std::wstringstream ws;
2773 ws << L"You cannot send more messages. You are limited to "
2774 << g_settings->getFloat("chat_message_limit_per_10sec")
2775 << L" messages per 10 seconds.";
2778 case RPLAYER_CHATRESULT_KICK:
2779 DenyAccess_Legacy(player->getPeerId(),
2780 L"You have been kicked due to message flooding.");
2782 case RPLAYER_CHATRESULT_OK:
2785 FATAL_ERROR("Unhandled chat filtering result found.");
2789 if (m_max_chatmessage_length > 0
2790 && wmessage.length() > m_max_chatmessage_length) {
2791 return L"Your message exceed the maximum chat message limit set on the server. "
2792 L"It was refused. Send a shorter message";
2795 // Run script hook, exit if script ate the chat message
2796 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2801 // Whether to send line to the player that sent the message, or to all players
2802 bool broadcast_line = true;
2804 if (check_shout_priv && !checkPriv(name, "shout")) {
2805 line += L"-!- You don't have permission to shout.";
2806 broadcast_line = false;
2815 Tell calling method to send the message to sender
2817 if (!broadcast_line)
2821 Send the message to others
2823 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2825 std::vector<session_t> clients = m_clients.getClientIDs();
2828 Send the message back to the inital sender
2829 if they are using protocol version >= 29
2832 session_t peer_id_to_avoid_sending =
2833 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2835 if (player && player->protocol_version >= 29)
2836 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2838 for (u16 cid : clients) {
2839 if (cid != peer_id_to_avoid_sending)
2840 SendChatMessage(cid, ChatMessage(line));
2845 void Server::handleAdminChat(const ChatEventChat *evt)
2847 std::string name = evt->nick;
2848 std::wstring wname = utf8_to_wide(name);
2849 std::wstring wmessage = evt->evt_msg;
2851 std::wstring answer = handleChat(name, wname, wmessage);
2853 // If asked to send answer to sender
2854 if (!answer.empty()) {
2855 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2859 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2861 RemoteClient *client = getClientNoEx(peer_id,state_min);
2863 throw ClientNotFoundException("Client not found");
2867 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2869 return m_clients.getClientNoEx(peer_id, state_min);
2872 std::string Server::getPlayerName(session_t peer_id)
2874 RemotePlayer *player = m_env->getPlayer(peer_id);
2876 return "[id="+itos(peer_id)+"]";
2877 return player->getName();
2880 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2882 RemotePlayer *player = m_env->getPlayer(peer_id);
2885 return player->getPlayerSAO();
2888 std::wstring Server::getStatusString()
2890 std::wostringstream os(std::ios_base::binary);
2893 os<<L"version="<<narrow_to_wide(g_version_string);
2895 os<<L", uptime="<<m_uptime.get();
2897 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2898 // Information about clients
2901 std::vector<session_t> clients = m_clients.getClientIDs();
2902 for (session_t client_id : clients) {
2904 RemotePlayer *player = m_env->getPlayer(client_id);
2905 // Get name of player
2906 std::wstring name = L"unknown";
2908 name = narrow_to_wide(player->getName());
2909 // Add name to information string
2918 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2919 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2921 if (!g_settings->get("motd").empty())
2922 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2926 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2928 std::set<std::string> privs;
2929 m_script->getAuth(name, NULL, &privs);
2933 bool Server::checkPriv(const std::string &name, const std::string &priv)
2935 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2936 return (privs.count(priv) != 0);
2939 void Server::reportPrivsModified(const std::string &name)
2942 std::vector<session_t> clients = m_clients.getClientIDs();
2943 for (const session_t client_id : clients) {
2944 RemotePlayer *player = m_env->getPlayer(client_id);
2945 reportPrivsModified(player->getName());
2948 RemotePlayer *player = m_env->getPlayer(name.c_str());
2951 SendPlayerPrivileges(player->getPeerId());
2952 PlayerSAO *sao = player->getPlayerSAO();
2955 sao->updatePrivileges(
2956 getPlayerEffectivePrivs(name),
2961 void Server::reportInventoryFormspecModified(const std::string &name)
2963 RemotePlayer *player = m_env->getPlayer(name.c_str());
2966 SendPlayerInventoryFormspec(player->getPeerId());
2969 void Server::reportFormspecPrependModified(const std::string &name)
2971 RemotePlayer *player = m_env->getPlayer(name.c_str());
2974 SendPlayerFormspecPrepend(player->getPeerId());
2977 void Server::setIpBanned(const std::string &ip, const std::string &name)
2979 m_banmanager->add(ip, name);
2982 void Server::unsetIpBanned(const std::string &ip_or_name)
2984 m_banmanager->remove(ip_or_name);
2987 std::string Server::getBanDescription(const std::string &ip_or_name)
2989 return m_banmanager->getBanDescription(ip_or_name);
2992 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2994 // m_env will be NULL if the server is initializing
2998 if (m_admin_nick == name && !m_admin_nick.empty()) {
2999 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3002 RemotePlayer *player = m_env->getPlayer(name);
3007 if (player->getPeerId() == PEER_ID_INEXISTENT)
3010 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3013 bool Server::showFormspec(const char *playername, const std::string &formspec,
3014 const std::string &formname)
3016 // m_env will be NULL if the server is initializing
3020 RemotePlayer *player = m_env->getPlayer(playername);
3024 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3028 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3033 u32 id = player->addHud(form);
3035 SendHUDAdd(player->getPeerId(), id, form);
3040 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3044 HudElement* todel = player->removeHud(id);
3051 SendHUDRemove(player->getPeerId(), id);
3055 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3060 SendHUDChange(player->getPeerId(), id, stat, data);
3064 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3069 SendHUDSetFlags(player->getPeerId(), flags, mask);
3070 player->hud_flags &= ~mask;
3071 player->hud_flags |= flags;
3073 PlayerSAO* playersao = player->getPlayerSAO();
3078 m_script->player_event(playersao, "hud_changed");
3082 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3087 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3090 player->setHotbarItemcount(hotbar_itemcount);
3091 std::ostringstream os(std::ios::binary);
3092 writeS32(os, hotbar_itemcount);
3093 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3097 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3102 player->setHotbarImage(name);
3103 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3106 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3111 player->setHotbarSelectedImage(name);
3112 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3115 Address Server::getPeerAddress(session_t peer_id)
3117 return m_con->GetPeerAddress(peer_id);
3120 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3121 v2s32 animation_frames[4], f32 frame_speed)
3123 sanity_check(player);
3124 player->setLocalAnimations(animation_frames, frame_speed);
3125 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3128 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3130 sanity_check(player);
3131 player->eye_offset_first = first;
3132 player->eye_offset_third = third;
3133 SendEyeOffset(player->getPeerId(), first, third);
3136 void Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3137 const std::string &type, const std::vector<std::string> ¶ms,
3140 sanity_check(player);
3141 player->setSky(bgcolor, type, params, clouds);
3142 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3145 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3147 sanity_check(player);
3148 player->setCloudParams(params);
3149 SendCloudParams(player->getPeerId(), params);
3152 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3158 player->overrideDayNightRatio(do_override, ratio);
3159 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3163 void Server::notifyPlayers(const std::wstring &msg)
3165 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3168 void Server::spawnParticle(const std::string &playername, v3f pos,
3169 v3f velocity, v3f acceleration,
3170 float expirationtime, float size, bool
3171 collisiondetection, bool collision_removal, bool object_collision,
3172 bool vertical, const std::string &texture,
3173 const struct TileAnimationParams &animation, u8 glow)
3175 // m_env will be NULL if the server is initializing
3179 session_t peer_id = PEER_ID_INEXISTENT;
3181 if (!playername.empty()) {
3182 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3185 peer_id = player->getPeerId();
3186 proto_ver = player->protocol_version;
3189 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3190 expirationtime, size, collisiondetection, collision_removal,
3191 object_collision, vertical, texture, animation, glow);
3194 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3195 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3196 float minexptime, float maxexptime, float minsize, float maxsize,
3197 bool collisiondetection, bool collision_removal, bool object_collision,
3198 ServerActiveObject *attached, bool vertical, const std::string &texture,
3199 const std::string &playername, const struct TileAnimationParams &animation,
3202 // m_env will be NULL if the server is initializing
3206 session_t peer_id = PEER_ID_INEXISTENT;
3208 if (!playername.empty()) {
3209 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3212 peer_id = player->getPeerId();
3213 proto_ver = player->protocol_version;
3216 u16 attached_id = attached ? attached->getId() : 0;
3219 if (attached_id == 0)
3220 id = m_env->addParticleSpawner(spawntime);
3222 id = m_env->addParticleSpawner(spawntime, attached_id);
3224 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3225 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3226 minexptime, maxexptime, minsize, maxsize, collisiondetection,
3227 collision_removal, object_collision, attached_id, vertical,
3228 texture, id, animation, glow);
3233 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3235 // m_env will be NULL if the server is initializing
3237 throw ServerError("Can't delete particle spawners during initialisation!");
3239 session_t peer_id = PEER_ID_INEXISTENT;
3240 if (!playername.empty()) {
3241 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3244 peer_id = player->getPeerId();
3247 m_env->deleteParticleSpawner(id);
3248 SendDeleteParticleSpawner(peer_id, id);
3251 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3253 if(m_detached_inventories.count(name) > 0){
3254 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3255 delete m_detached_inventories[name];
3257 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3259 Inventory *inv = new Inventory(m_itemdef);
3261 m_detached_inventories[name] = inv;
3262 m_detached_inventories_player[name] = player;
3263 //TODO find a better way to do this
3264 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3268 // actions: time-reversed list
3269 // Return value: success/failure
3270 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3271 std::list<std::string> *log)
3273 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3274 ServerMap *map = (ServerMap*)(&m_env->getMap());
3276 // Fail if no actions to handle
3277 if (actions.empty()) {
3279 log->push_back("Nothing to do.");
3286 for (const RollbackAction &action : actions) {
3288 bool success = action.applyRevert(map, this, this);
3291 std::ostringstream os;
3292 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3293 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3295 log->push_back(os.str());
3297 std::ostringstream os;
3298 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3299 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3301 log->push_back(os.str());
3305 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3306 <<" failed"<<std::endl;
3308 // Call it done if less than half failed
3309 return num_failed <= num_tried/2;
3312 // IGameDef interface
3314 IItemDefManager *Server::getItemDefManager()
3319 const NodeDefManager *Server::getNodeDefManager()
3324 ICraftDefManager *Server::getCraftDefManager()
3329 u16 Server::allocateUnknownNodeId(const std::string &name)
3331 return m_nodedef->allocateDummy(name);
3334 IWritableItemDefManager *Server::getWritableItemDefManager()
3339 NodeDefManager *Server::getWritableNodeDefManager()
3344 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3349 const std::vector<ModSpec> & Server::getMods() const
3351 return m_modmgr->getMods();
3354 const ModSpec *Server::getModSpec(const std::string &modname) const
3356 return m_modmgr->getModSpec(modname);
3359 void Server::getModNames(std::vector<std::string> &modlist)
3361 m_modmgr->getModNames(modlist);
3364 std::string Server::getBuiltinLuaPath()
3366 return porting::path_share + DIR_DELIM + "builtin";
3369 std::string Server::getModStoragePath() const
3371 return m_path_world + DIR_DELIM + "mod_storage";
3374 v3f Server::findSpawnPos()
3376 ServerMap &map = m_env->getServerMap();
3378 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3379 return nodeposf * BS;
3382 bool is_good = false;
3383 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3384 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3386 // Try to find a good place a few times
3387 for(s32 i = 0; i < 4000 && !is_good; i++) {
3388 s32 range = MYMIN(1 + i, range_max);
3389 // We're going to try to throw the player to this position
3390 v2s16 nodepos2d = v2s16(
3391 -range + (myrand() % (range * 2)),
3392 -range + (myrand() % (range * 2)));
3394 // Get spawn level at point
3395 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3396 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3397 // the mapgen to signify an unsuitable spawn position
3398 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3401 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3404 for (s32 i = 0; i < 10; i++) {
3405 v3s16 blockpos = getNodeBlockPos(nodepos);
3406 map.emergeBlock(blockpos, true);
3407 content_t c = map.getNodeNoEx(nodepos).getContent();
3408 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3410 if (air_count >= 2) {
3411 nodeposf = intToFloat(nodepos, BS);
3412 // Don't spawn the player outside map boundaries
3413 if (objectpos_over_limit(nodeposf))
3426 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3428 if (delay == 0.0f) {
3429 // No delay, shutdown immediately
3430 m_shutdown_state.is_requested = true;
3431 // only print to the infostream, a chat message saying
3432 // "Server Shutting Down" is sent when the server destructs.
3433 infostream << "*** Immediate Server shutdown requested." << std::endl;
3434 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3435 // Negative delay, cancel shutdown if requested
3436 m_shutdown_state.reset();
3437 std::wstringstream ws;
3439 ws << L"*** Server shutdown canceled.";
3441 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3442 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3443 // m_shutdown_* are already handled, skip.
3445 } else if (delay > 0.0f) {
3446 // Positive delay, tell the clients when the server will shut down
3447 std::wstringstream ws;
3449 ws << L"*** Server shutting down in "
3450 << duration_to_string(myround(delay)).c_str()
3453 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3454 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3457 m_shutdown_state.trigger(delay, msg, reconnect);
3460 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3463 Try to get an existing player
3465 RemotePlayer *player = m_env->getPlayer(name);
3467 // If player is already connected, cancel
3468 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3469 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3474 If player with the wanted peer_id already exists, cancel.
3476 if (m_env->getPlayer(peer_id)) {
3477 infostream<<"emergePlayer(): Player with wrong name but same"
3478 " peer_id already exists"<<std::endl;
3483 player = new RemotePlayer(name, idef());
3486 bool newplayer = false;
3489 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3491 // Complete init with server parts
3492 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3493 player->protocol_version = proto_version;
3497 m_script->on_newplayer(playersao);
3503 bool Server::registerModStorage(ModMetadata *storage)
3505 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3506 errorstream << "Unable to register same mod storage twice. Storage name: "
3507 << storage->getModName() << std::endl;
3511 m_mod_storages[storage->getModName()] = storage;
3515 void Server::unregisterModStorage(const std::string &name)
3517 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3518 if (it != m_mod_storages.end()) {
3519 // Save unconditionaly on unregistration
3520 it->second->save(getModStoragePath());
3521 m_mod_storages.erase(name);
3525 void dedicated_server_loop(Server &server, bool &kill)
3527 verbosestream<<"dedicated_server_loop()"<<std::endl;
3529 IntervalLimiter m_profiler_interval;
3531 static thread_local const float steplen =
3532 g_settings->getFloat("dedicated_server_step");
3533 static thread_local const float profiler_print_interval =
3534 g_settings->getFloat("profiler_print_interval");
3537 // This is kind of a hack but can be done like this
3538 // because server.step() is very light
3540 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3541 sleep_ms((int)(steplen*1000.0));
3543 server.step(steplen);
3545 if (server.isShutdownRequested() || kill)
3551 if (profiler_print_interval != 0) {
3552 if(m_profiler_interval.step(steplen, profiler_print_interval))
3554 infostream<<"Profiler:"<<std::endl;
3555 g_profiler->print(infostream);
3556 g_profiler->clear();
3561 infostream << "Dedicated server quitting" << std::endl;
3563 if (g_settings->getBool("server_announce"))
3564 ServerList::sendAnnounce(ServerList::AA_DELETE,
3565 server.m_bind_addr.getPort());
3574 bool Server::joinModChannel(const std::string &channel)
3576 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3577 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3580 bool Server::leaveModChannel(const std::string &channel)
3582 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3585 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3587 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3590 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3594 ModChannel* Server::getModChannel(const std::string &channel)
3596 return m_modchannel_mgr->getModChannel(channel);
3599 void Server::broadcastModChannelMessage(const std::string &channel,
3600 const std::string &message, session_t from_peer)
3602 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3606 if (message.size() > STRING_MAX_LEN) {
3607 warningstream << "ModChannel message too long, dropping before sending "
3608 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3609 << channel << ")" << std::endl;
3614 if (from_peer != PEER_ID_SERVER) {
3615 sender = getPlayerName(from_peer);
3618 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3619 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3620 resp_pkt << channel << sender << message;
3621 for (session_t peer_id : peers) {
3623 if (peer_id == from_peer)
3626 Send(peer_id, &resp_pkt);
3629 if (from_peer != PEER_ID_SERVER) {
3630 m_script->on_modchannel_message(channel, sender, message);