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 const auto &inv_it = m_detached_inventories.find(name);
2482 const auto &player_it = m_detached_inventories_player.find(name);
2484 if (player_it == m_detached_inventories_player.end() ||
2485 player_it->second.empty()) {
2486 // OK. Send to everyone
2488 RemotePlayer *p = m_env->getPlayer(player_it->second.c_str());
2490 return; // Player is offline
2492 if (peer_id != PEER_ID_INEXISTENT && peer_id != p->getPeerId())
2493 return; // Caller requested send to a different player, so don't send.
2495 peer_id = p->getPeerId();
2498 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2501 if (inv_it == m_detached_inventories.end()) {
2502 pkt << false; // Remove inventory
2504 pkt << true; // Update inventory
2506 // Serialization & NetworkPacket isn't a love story
2507 std::ostringstream os(std::ios_base::binary);
2508 inv_it->second->serialize(os);
2512 if (peer_id == PEER_ID_INEXISTENT)
2513 m_clients.sendToAll(&pkt);
2518 void Server::sendDetachedInventories(session_t peer_id)
2520 for (const auto &detached_inventory : m_detached_inventories) {
2521 const std::string &name = detached_inventory.first;
2522 //Inventory *inv = i->second;
2523 sendDetachedInventory(name, peer_id);
2531 void Server::DiePlayer(session_t peer_id, const PlayerHPChangeReason &reason)
2533 PlayerSAO *playersao = getPlayerSAO(peer_id);
2534 // In some rare cases this can be NULL -- if the player is disconnected
2535 // when a Lua function modifies l_punch, for example
2539 infostream << "Server::DiePlayer(): Player "
2540 << playersao->getPlayer()->getName()
2541 << " dies" << std::endl;
2543 playersao->setHP(0, reason);
2544 playersao->clearParentAttachment();
2546 // Trigger scripted stuff
2547 m_script->on_dieplayer(playersao, reason);
2549 SendPlayerHP(peer_id);
2550 SendDeathscreen(peer_id, false, v3f(0,0,0));
2553 void Server::RespawnPlayer(session_t peer_id)
2555 PlayerSAO *playersao = getPlayerSAO(peer_id);
2558 infostream << "Server::RespawnPlayer(): Player "
2559 << playersao->getPlayer()->getName()
2560 << " respawns" << std::endl;
2562 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2563 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2564 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2566 bool repositioned = m_script->on_respawnplayer(playersao);
2567 if (!repositioned) {
2568 // setPos will send the new position to client
2569 playersao->setPos(findSpawnPos());
2572 SendPlayerHP(peer_id);
2576 void Server::DenySudoAccess(session_t peer_id)
2578 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2583 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2584 const std::string &str_reason, bool reconnect)
2586 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2588 m_clients.event(peer_id, CSE_SetDenied);
2589 DisconnectPeer(peer_id);
2593 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2594 const std::string &custom_reason)
2596 SendAccessDenied(peer_id, reason, custom_reason);
2597 m_clients.event(peer_id, CSE_SetDenied);
2598 DisconnectPeer(peer_id);
2601 // 13/03/15: remove this function when protocol version 25 will become
2602 // the minimum version for MT users, maybe in 1 year
2603 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2605 SendAccessDenied_Legacy(peer_id, reason);
2606 m_clients.event(peer_id, CSE_SetDenied);
2607 DisconnectPeer(peer_id);
2610 void Server::DisconnectPeer(session_t peer_id)
2612 m_modchannel_mgr->leaveAllChannels(peer_id);
2613 m_con->DisconnectPeer(peer_id);
2616 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2619 RemoteClient* client = getClient(peer_id, CS_Invalid);
2621 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2623 // Right now, the auth mechs don't change between login and sudo mode.
2624 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2625 client->allowed_sudo_mechs = sudo_auth_mechs;
2627 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2628 << g_settings->getFloat("dedicated_server_step")
2632 m_clients.event(peer_id, CSE_AuthAccept);
2634 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2636 // We only support SRP right now
2637 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2639 resp_pkt << sudo_auth_mechs;
2641 m_clients.event(peer_id, CSE_SudoSuccess);
2645 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2647 std::wstring message;
2650 Clear references to playing sounds
2652 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2653 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2654 ServerPlayingSound &psound = i->second;
2655 psound.clients.erase(peer_id);
2656 if (psound.clients.empty())
2657 m_playing_sounds.erase(i++);
2662 // clear formspec info so the next client can't abuse the current state
2663 m_formspec_state_data.erase(peer_id);
2665 RemotePlayer *player = m_env->getPlayer(peer_id);
2667 /* Run scripts and remove from environment */
2669 PlayerSAO *playersao = player->getPlayerSAO();
2672 playersao->clearChildAttachments();
2673 playersao->clearParentAttachment();
2675 // inform connected clients
2676 const std::string &player_name = player->getName();
2677 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2678 // (u16) 1 + std::string represents a vector serialization representation
2679 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << player_name;
2680 m_clients.sendToAll(¬ice);
2682 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2684 playersao->disconnected();
2691 if (player && reason != CDR_DENY) {
2692 std::ostringstream os(std::ios_base::binary);
2693 std::vector<session_t> clients = m_clients.getClientIDs();
2695 for (const session_t client_id : clients) {
2697 RemotePlayer *player = m_env->getPlayer(client_id);
2701 // Get name of player
2702 os << player->getName() << " ";
2705 std::string name = player->getName();
2706 actionstream << name << " "
2707 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2708 << " List of players: " << os.str() << std::endl;
2710 m_admin_chat->outgoing_queue.push_back(
2711 new ChatEventNick(CET_NICK_REMOVE, name));
2715 MutexAutoLock env_lock(m_env_mutex);
2716 m_clients.DeleteClient(peer_id);
2720 // Send leave chat message to all remaining clients
2721 if (!message.empty()) {
2722 SendChatMessage(PEER_ID_INEXISTENT,
2723 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2727 void Server::UpdateCrafting(RemotePlayer *player)
2729 InventoryList *clist = player->inventory.getList("craft");
2730 if (!clist || clist->getSize() == 0)
2733 // Get a preview for crafting
2735 InventoryLocation loc;
2736 loc.setPlayer(player->getName());
2737 std::vector<ItemStack> output_replacements;
2738 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2739 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2742 InventoryList *plist = player->inventory.getList("craftpreview");
2743 if (plist && plist->getSize() >= 1) {
2744 // Put the new preview in
2745 plist->changeItem(0, preview);
2749 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2751 if (evt->type == CET_NICK_ADD) {
2752 // The terminal informed us of its nick choice
2753 m_admin_nick = ((ChatEventNick *)evt)->nick;
2754 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2755 errorstream << "You haven't set up an account." << std::endl
2756 << "Please log in using the client as '"
2757 << m_admin_nick << "' with a secure password." << std::endl
2758 << "Until then, you can't execute admin tasks via the console," << std::endl
2759 << "and everybody can claim the user account instead of you," << std::endl
2760 << "giving them full control over this server." << std::endl;
2763 assert(evt->type == CET_CHAT);
2764 handleAdminChat((ChatEventChat *)evt);
2768 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2769 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2771 // If something goes wrong, this player is to blame
2772 RollbackScopeActor rollback_scope(m_rollback,
2773 std::string("player:") + name);
2775 if (g_settings->getBool("strip_color_codes"))
2776 wmessage = unescape_enriched(wmessage);
2779 switch (player->canSendChatMessage()) {
2780 case RPLAYER_CHATRESULT_FLOODING: {
2781 std::wstringstream ws;
2782 ws << L"You cannot send more messages. You are limited to "
2783 << g_settings->getFloat("chat_message_limit_per_10sec")
2784 << L" messages per 10 seconds.";
2787 case RPLAYER_CHATRESULT_KICK:
2788 DenyAccess_Legacy(player->getPeerId(),
2789 L"You have been kicked due to message flooding.");
2791 case RPLAYER_CHATRESULT_OK:
2794 FATAL_ERROR("Unhandled chat filtering result found.");
2798 if (m_max_chatmessage_length > 0
2799 && wmessage.length() > m_max_chatmessage_length) {
2800 return L"Your message exceed the maximum chat message limit set on the server. "
2801 L"It was refused. Send a shorter message";
2804 // Run script hook, exit if script ate the chat message
2805 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2810 // Whether to send line to the player that sent the message, or to all players
2811 bool broadcast_line = true;
2813 if (check_shout_priv && !checkPriv(name, "shout")) {
2814 line += L"-!- You don't have permission to shout.";
2815 broadcast_line = false;
2824 Tell calling method to send the message to sender
2826 if (!broadcast_line)
2830 Send the message to others
2832 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2834 std::vector<session_t> clients = m_clients.getClientIDs();
2837 Send the message back to the inital sender
2838 if they are using protocol version >= 29
2841 session_t peer_id_to_avoid_sending =
2842 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2844 if (player && player->protocol_version >= 29)
2845 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2847 for (u16 cid : clients) {
2848 if (cid != peer_id_to_avoid_sending)
2849 SendChatMessage(cid, ChatMessage(line));
2854 void Server::handleAdminChat(const ChatEventChat *evt)
2856 std::string name = evt->nick;
2857 std::wstring wname = utf8_to_wide(name);
2858 std::wstring wmessage = evt->evt_msg;
2860 std::wstring answer = handleChat(name, wname, wmessage);
2862 // If asked to send answer to sender
2863 if (!answer.empty()) {
2864 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2868 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2870 RemoteClient *client = getClientNoEx(peer_id,state_min);
2872 throw ClientNotFoundException("Client not found");
2876 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2878 return m_clients.getClientNoEx(peer_id, state_min);
2881 std::string Server::getPlayerName(session_t peer_id)
2883 RemotePlayer *player = m_env->getPlayer(peer_id);
2885 return "[id="+itos(peer_id)+"]";
2886 return player->getName();
2889 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2891 RemotePlayer *player = m_env->getPlayer(peer_id);
2894 return player->getPlayerSAO();
2897 std::wstring Server::getStatusString()
2899 std::wostringstream os(std::ios_base::binary);
2902 os<<L"version="<<narrow_to_wide(g_version_string);
2904 os<<L", uptime="<<m_uptime.get();
2906 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2907 // Information about clients
2910 std::vector<session_t> clients = m_clients.getClientIDs();
2911 for (session_t client_id : clients) {
2913 RemotePlayer *player = m_env->getPlayer(client_id);
2914 // Get name of player
2915 std::wstring name = L"unknown";
2917 name = narrow_to_wide(player->getName());
2918 // Add name to information string
2927 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2928 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2930 if (!g_settings->get("motd").empty())
2931 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2935 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2937 std::set<std::string> privs;
2938 m_script->getAuth(name, NULL, &privs);
2942 bool Server::checkPriv(const std::string &name, const std::string &priv)
2944 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2945 return (privs.count(priv) != 0);
2948 void Server::reportPrivsModified(const std::string &name)
2951 std::vector<session_t> clients = m_clients.getClientIDs();
2952 for (const session_t client_id : clients) {
2953 RemotePlayer *player = m_env->getPlayer(client_id);
2954 reportPrivsModified(player->getName());
2957 RemotePlayer *player = m_env->getPlayer(name.c_str());
2960 SendPlayerPrivileges(player->getPeerId());
2961 PlayerSAO *sao = player->getPlayerSAO();
2964 sao->updatePrivileges(
2965 getPlayerEffectivePrivs(name),
2970 void Server::reportInventoryFormspecModified(const std::string &name)
2972 RemotePlayer *player = m_env->getPlayer(name.c_str());
2975 SendPlayerInventoryFormspec(player->getPeerId());
2978 void Server::reportFormspecPrependModified(const std::string &name)
2980 RemotePlayer *player = m_env->getPlayer(name.c_str());
2983 SendPlayerFormspecPrepend(player->getPeerId());
2986 void Server::setIpBanned(const std::string &ip, const std::string &name)
2988 m_banmanager->add(ip, name);
2991 void Server::unsetIpBanned(const std::string &ip_or_name)
2993 m_banmanager->remove(ip_or_name);
2996 std::string Server::getBanDescription(const std::string &ip_or_name)
2998 return m_banmanager->getBanDescription(ip_or_name);
3001 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3003 // m_env will be NULL if the server is initializing
3007 if (m_admin_nick == name && !m_admin_nick.empty()) {
3008 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3011 RemotePlayer *player = m_env->getPlayer(name);
3016 if (player->getPeerId() == PEER_ID_INEXISTENT)
3019 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3022 bool Server::showFormspec(const char *playername, const std::string &formspec,
3023 const std::string &formname)
3025 // m_env will be NULL if the server is initializing
3029 RemotePlayer *player = m_env->getPlayer(playername);
3033 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3037 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3042 u32 id = player->addHud(form);
3044 SendHUDAdd(player->getPeerId(), id, form);
3049 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3053 HudElement* todel = player->removeHud(id);
3060 SendHUDRemove(player->getPeerId(), id);
3064 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3069 SendHUDChange(player->getPeerId(), id, stat, data);
3073 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3078 SendHUDSetFlags(player->getPeerId(), flags, mask);
3079 player->hud_flags &= ~mask;
3080 player->hud_flags |= flags;
3082 PlayerSAO* playersao = player->getPlayerSAO();
3087 m_script->player_event(playersao, "hud_changed");
3091 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3096 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3099 player->setHotbarItemcount(hotbar_itemcount);
3100 std::ostringstream os(std::ios::binary);
3101 writeS32(os, hotbar_itemcount);
3102 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3106 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3111 player->setHotbarImage(name);
3112 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3115 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3120 player->setHotbarSelectedImage(name);
3121 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3124 Address Server::getPeerAddress(session_t peer_id)
3126 return m_con->GetPeerAddress(peer_id);
3129 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3130 v2s32 animation_frames[4], f32 frame_speed)
3132 sanity_check(player);
3133 player->setLocalAnimations(animation_frames, frame_speed);
3134 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3137 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3139 sanity_check(player);
3140 player->eye_offset_first = first;
3141 player->eye_offset_third = third;
3142 SendEyeOffset(player->getPeerId(), first, third);
3145 void Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3146 const std::string &type, const std::vector<std::string> ¶ms,
3149 sanity_check(player);
3150 player->setSky(bgcolor, type, params, clouds);
3151 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3154 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3156 sanity_check(player);
3157 player->setCloudParams(params);
3158 SendCloudParams(player->getPeerId(), params);
3161 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3167 player->overrideDayNightRatio(do_override, ratio);
3168 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3172 void Server::notifyPlayers(const std::wstring &msg)
3174 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3177 void Server::spawnParticle(const std::string &playername, v3f pos,
3178 v3f velocity, v3f acceleration,
3179 float expirationtime, float size, bool
3180 collisiondetection, bool collision_removal, bool object_collision,
3181 bool vertical, const std::string &texture,
3182 const struct TileAnimationParams &animation, u8 glow)
3184 // m_env will be NULL if the server is initializing
3188 session_t peer_id = PEER_ID_INEXISTENT;
3190 if (!playername.empty()) {
3191 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3194 peer_id = player->getPeerId();
3195 proto_ver = player->protocol_version;
3198 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3199 expirationtime, size, collisiondetection, collision_removal,
3200 object_collision, vertical, texture, animation, glow);
3203 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3204 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3205 float minexptime, float maxexptime, float minsize, float maxsize,
3206 bool collisiondetection, bool collision_removal, bool object_collision,
3207 ServerActiveObject *attached, bool vertical, const std::string &texture,
3208 const std::string &playername, const struct TileAnimationParams &animation,
3211 // m_env will be NULL if the server is initializing
3215 session_t peer_id = PEER_ID_INEXISTENT;
3217 if (!playername.empty()) {
3218 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3221 peer_id = player->getPeerId();
3222 proto_ver = player->protocol_version;
3225 u16 attached_id = attached ? attached->getId() : 0;
3228 if (attached_id == 0)
3229 id = m_env->addParticleSpawner(spawntime);
3231 id = m_env->addParticleSpawner(spawntime, attached_id);
3233 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3234 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3235 minexptime, maxexptime, minsize, maxsize, collisiondetection,
3236 collision_removal, object_collision, attached_id, vertical,
3237 texture, id, animation, glow);
3242 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3244 // m_env will be NULL if the server is initializing
3246 throw ServerError("Can't delete particle spawners during initialisation!");
3248 session_t peer_id = PEER_ID_INEXISTENT;
3249 if (!playername.empty()) {
3250 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3253 peer_id = player->getPeerId();
3256 m_env->deleteParticleSpawner(id);
3257 SendDeleteParticleSpawner(peer_id, id);
3260 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3262 if(m_detached_inventories.count(name) > 0){
3263 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3264 delete m_detached_inventories[name];
3266 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3268 Inventory *inv = new Inventory(m_itemdef);
3270 m_detached_inventories[name] = inv;
3271 m_detached_inventories_player[name] = player;
3272 //TODO find a better way to do this
3273 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3277 bool Server::removeDetachedInventory(const std::string &name)
3279 const auto &inv_it = m_detached_inventories.find(name);
3280 if (inv_it == m_detached_inventories.end())
3283 delete inv_it->second;
3284 m_detached_inventories.erase(inv_it);
3286 const auto &player_it = m_detached_inventories_player.find(name);
3287 if (player_it != m_detached_inventories_player.end()) {
3288 RemotePlayer *player = m_env->getPlayer(player_it->second.c_str());
3290 if (player && player->getPeerId() != PEER_ID_INEXISTENT)
3291 sendDetachedInventory(name, player->getPeerId());
3293 m_detached_inventories_player.erase(player_it);
3295 // Notify all players about the change
3296 sendDetachedInventory(name, PEER_ID_INEXISTENT);
3301 // actions: time-reversed list
3302 // Return value: success/failure
3303 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3304 std::list<std::string> *log)
3306 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3307 ServerMap *map = (ServerMap*)(&m_env->getMap());
3309 // Fail if no actions to handle
3310 if (actions.empty()) {
3312 log->push_back("Nothing to do.");
3319 for (const RollbackAction &action : actions) {
3321 bool success = action.applyRevert(map, this, this);
3324 std::ostringstream os;
3325 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3326 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3328 log->push_back(os.str());
3330 std::ostringstream os;
3331 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3332 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3334 log->push_back(os.str());
3338 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3339 <<" failed"<<std::endl;
3341 // Call it done if less than half failed
3342 return num_failed <= num_tried/2;
3345 // IGameDef interface
3347 IItemDefManager *Server::getItemDefManager()
3352 const NodeDefManager *Server::getNodeDefManager()
3357 ICraftDefManager *Server::getCraftDefManager()
3362 u16 Server::allocateUnknownNodeId(const std::string &name)
3364 return m_nodedef->allocateDummy(name);
3367 IWritableItemDefManager *Server::getWritableItemDefManager()
3372 NodeDefManager *Server::getWritableNodeDefManager()
3377 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3382 const std::vector<ModSpec> & Server::getMods() const
3384 return m_modmgr->getMods();
3387 const ModSpec *Server::getModSpec(const std::string &modname) const
3389 return m_modmgr->getModSpec(modname);
3392 void Server::getModNames(std::vector<std::string> &modlist)
3394 m_modmgr->getModNames(modlist);
3397 std::string Server::getBuiltinLuaPath()
3399 return porting::path_share + DIR_DELIM + "builtin";
3402 std::string Server::getModStoragePath() const
3404 return m_path_world + DIR_DELIM + "mod_storage";
3407 v3f Server::findSpawnPos()
3409 ServerMap &map = m_env->getServerMap();
3411 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3412 return nodeposf * BS;
3415 bool is_good = false;
3416 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3417 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3419 // Try to find a good place a few times
3420 for(s32 i = 0; i < 4000 && !is_good; i++) {
3421 s32 range = MYMIN(1 + i, range_max);
3422 // We're going to try to throw the player to this position
3423 v2s16 nodepos2d = v2s16(
3424 -range + (myrand() % (range * 2)),
3425 -range + (myrand() % (range * 2)));
3427 // Get spawn level at point
3428 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3429 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3430 // the mapgen to signify an unsuitable spawn position
3431 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3434 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3437 for (s32 i = 0; i < 10; i++) {
3438 v3s16 blockpos = getNodeBlockPos(nodepos);
3439 map.emergeBlock(blockpos, true);
3440 content_t c = map.getNodeNoEx(nodepos).getContent();
3441 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3443 if (air_count >= 2) {
3444 nodeposf = intToFloat(nodepos, BS);
3445 // Don't spawn the player outside map boundaries
3446 if (objectpos_over_limit(nodeposf))
3459 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3461 if (delay == 0.0f) {
3462 // No delay, shutdown immediately
3463 m_shutdown_state.is_requested = true;
3464 // only print to the infostream, a chat message saying
3465 // "Server Shutting Down" is sent when the server destructs.
3466 infostream << "*** Immediate Server shutdown requested." << std::endl;
3467 } else if (delay < 0.0f && m_shutdown_state.isTimerRunning()) {
3468 // Negative delay, cancel shutdown if requested
3469 m_shutdown_state.reset();
3470 std::wstringstream ws;
3472 ws << L"*** Server shutdown canceled.";
3474 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3475 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3476 // m_shutdown_* are already handled, skip.
3478 } else if (delay > 0.0f) {
3479 // Positive delay, tell the clients when the server will shut down
3480 std::wstringstream ws;
3482 ws << L"*** Server shutting down in "
3483 << duration_to_string(myround(delay)).c_str()
3486 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3487 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3490 m_shutdown_state.trigger(delay, msg, reconnect);
3493 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3496 Try to get an existing player
3498 RemotePlayer *player = m_env->getPlayer(name);
3500 // If player is already connected, cancel
3501 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3502 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3507 If player with the wanted peer_id already exists, cancel.
3509 if (m_env->getPlayer(peer_id)) {
3510 infostream<<"emergePlayer(): Player with wrong name but same"
3511 " peer_id already exists"<<std::endl;
3516 player = new RemotePlayer(name, idef());
3519 bool newplayer = false;
3522 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3524 // Complete init with server parts
3525 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3526 player->protocol_version = proto_version;
3530 m_script->on_newplayer(playersao);
3536 bool Server::registerModStorage(ModMetadata *storage)
3538 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3539 errorstream << "Unable to register same mod storage twice. Storage name: "
3540 << storage->getModName() << std::endl;
3544 m_mod_storages[storage->getModName()] = storage;
3548 void Server::unregisterModStorage(const std::string &name)
3550 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3551 if (it != m_mod_storages.end()) {
3552 // Save unconditionaly on unregistration
3553 it->second->save(getModStoragePath());
3554 m_mod_storages.erase(name);
3558 void dedicated_server_loop(Server &server, bool &kill)
3560 verbosestream<<"dedicated_server_loop()"<<std::endl;
3562 IntervalLimiter m_profiler_interval;
3564 static thread_local const float steplen =
3565 g_settings->getFloat("dedicated_server_step");
3566 static thread_local const float profiler_print_interval =
3567 g_settings->getFloat("profiler_print_interval");
3570 // This is kind of a hack but can be done like this
3571 // because server.step() is very light
3573 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3574 sleep_ms((int)(steplen*1000.0));
3576 server.step(steplen);
3578 if (server.isShutdownRequested() || kill)
3584 if (profiler_print_interval != 0) {
3585 if(m_profiler_interval.step(steplen, profiler_print_interval))
3587 infostream<<"Profiler:"<<std::endl;
3588 g_profiler->print(infostream);
3589 g_profiler->clear();
3594 infostream << "Dedicated server quitting" << std::endl;
3596 if (g_settings->getBool("server_announce"))
3597 ServerList::sendAnnounce(ServerList::AA_DELETE,
3598 server.m_bind_addr.getPort());
3607 bool Server::joinModChannel(const std::string &channel)
3609 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3610 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3613 bool Server::leaveModChannel(const std::string &channel)
3615 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3618 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3620 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3623 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3627 ModChannel* Server::getModChannel(const std::string &channel)
3629 return m_modchannel_mgr->getModChannel(channel);
3632 void Server::broadcastModChannelMessage(const std::string &channel,
3633 const std::string &message, session_t from_peer)
3635 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3639 if (message.size() > STRING_MAX_LEN) {
3640 warningstream << "ModChannel message too long, dropping before sending "
3641 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3642 << channel << ")" << std::endl;
3647 if (from_peer != PEER_ID_SERVER) {
3648 sender = getPlayerName(from_peer);
3651 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3652 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3653 resp_pkt << channel << sender << message;
3654 for (session_t peer_id : peers) {
3656 if (peer_id == from_peer)
3659 Send(peer_id, &resp_pkt);
3662 if (from_peer != PEER_ID_SERVER) {
3663 m_script->on_modchannel_message(channel, sender, message);