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"
49 #include "content_mapnode.h"
50 #include "content_nodemeta.h"
51 #include "content_sao.h"
53 #include "event_manager.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 "util/base64.h"
61 #include "util/sha1.h"
64 #include "chatmessage.h"
65 #include "chat_interface.h"
66 #include "remoteplayer.h"
68 class ClientNotFoundException : public BaseException
71 ClientNotFoundException(const char *s):
76 class ServerThread : public Thread
80 ServerThread(Server *server):
91 void *ServerThread::run()
93 BEGIN_DEBUG_EXCEPTION_HANDLER
95 m_server->AsyncRunStep(true);
97 while (!stopRequested()) {
99 m_server->AsyncRunStep();
103 } catch (con::NoIncomingDataException &e) {
104 } catch (con::PeerNotFoundException &e) {
105 infostream<<"Server: PeerNotFoundException"<<std::endl;
106 } catch (ClientNotFoundException &e) {
107 } catch (con::ConnectionBindFailed &e) {
108 m_server->setAsyncFatalError(e.what());
109 } catch (LuaError &e) {
110 m_server->setAsyncFatalError(
111 "ServerThread::run Lua: " + std::string(e.what()));
115 END_DEBUG_EXCEPTION_HANDLER
120 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
122 if(pos_exists) *pos_exists = false;
127 if(pos_exists) *pos_exists = true;
132 ServerActiveObject *sao = env->getActiveObject(object);
135 if(pos_exists) *pos_exists = true;
136 return sao->getBasePosition(); }
148 const std::string &path_world,
149 const SubgameSpec &gamespec,
150 bool simple_singleplayer_mode,
155 m_path_world(path_world),
156 m_gamespec(gamespec),
157 m_simple_singleplayer_mode(simple_singleplayer_mode),
158 m_dedicated(dedicated),
159 m_async_fatal_error(""),
160 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
165 m_itemdef(createItemDefManager()),
166 m_nodedef(createNodeDefManager()),
167 m_craftdef(createCraftDefManager()),
168 m_event(new EventManager()),
173 m_lag = g_settings->getFloat("dedicated_server_step");
175 if (path_world.empty())
176 throw ServerError("Supplied empty world path");
178 if(!gamespec.isValid())
179 throw ServerError("Supplied invalid gamespec");
181 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
182 if(m_simple_singleplayer_mode)
183 infostream<<" in simple singleplayer mode"<<std::endl;
185 infostream<<std::endl;
186 infostream<<"- world: "<<m_path_world<<std::endl;
187 infostream<<"- game: "<<m_gamespec.path<<std::endl;
189 // Create world if it doesn't exist
190 if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
191 throw ServerError("Failed to initialize world");
193 // Create server thread
194 m_thread = new ServerThread(this);
196 // Create emerge manager
197 m_emerge = new EmergeManager(this);
199 // Create ban manager
200 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
201 m_banmanager = new BanManager(ban_path);
203 ServerModConfiguration modconf(m_path_world);
204 m_mods = modconf.getMods();
205 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
206 // complain about mods with unsatisfied dependencies
207 if (!modconf.isConsistent()) {
208 modconf.printUnsatisfiedModsError();
212 MutexAutoLock envlock(m_env_mutex);
214 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
215 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
217 // Initialize scripting
218 infostream<<"Server: Initializing Lua"<<std::endl;
220 m_script = new ServerScripting(this);
222 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
225 infostream << "Server: Loading mods: ";
226 for (std::vector<ModSpec>::const_iterator i = m_mods.begin();
227 i != m_mods.end(); ++i) {
228 infostream << (*i).name << " ";
230 infostream << std::endl;
231 // Load and run "mod" scripts
232 for (std::vector<ModSpec>::const_iterator it = m_mods.begin();
233 it != m_mods.end(); ++it) {
234 const ModSpec &mod = *it;
235 if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
236 throw ModError("Error loading mod \"" + mod.name +
237 "\": Mod name does not follow naming conventions: "
238 "Only characters [a-z0-9_] are allowed.");
240 std::string script_path = mod.path + DIR_DELIM + "init.lua";
241 infostream << " [" << padStringRight(mod.name, 12) << "] [\""
242 << script_path << "\"]" << std::endl;
243 m_script->loadMod(script_path, mod.name);
246 // Read Textures and calculate sha1 sums
249 // Apply item aliases in the node definition manager
250 m_nodedef->updateAliases(m_itemdef);
252 // Apply texture overrides from texturepack/override.txt
253 std::string texture_path = g_settings->get("texture_path");
254 if (!texture_path.empty() && fs::IsDir(texture_path))
255 m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
257 m_nodedef->setNodeRegistrationStatus(true);
259 // Perform pending node name resolutions
260 m_nodedef->runNodeResolveCallbacks();
262 // unmap node names for connected nodeboxes
263 m_nodedef->mapNodeboxConnections();
265 // init the recipe hashes to speed up crafting
266 m_craftdef->initHashes(this);
268 // Initialize Environment
269 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
271 m_clients.setEnv(m_env);
273 if (!servermap->settings_mgr.makeMapgenParams())
274 FATAL_ERROR("Couldn't create any mapgen type");
276 // Initialize mapgens
277 m_emerge->initMapgens(servermap->getMapgenParams());
279 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
280 if (m_enable_rollback_recording) {
281 // Create rollback manager
282 m_rollback = new RollbackManager(m_path_world, this);
285 // Give environment reference to scripting api
286 m_script->initializeEnvironment(m_env);
288 // Register us to receive map edit events
289 servermap->addEventReceiver(this);
291 // If file exists, load environment metadata
292 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
293 infostream << "Server: Loading environment metadata" << std::endl;
296 m_env->loadDefaultMeta();
299 m_liquid_transform_every = g_settings->getFloat("liquid_update");
300 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
301 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
302 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
307 infostream<<"Server destructing"<<std::endl;
309 // Send shutdown message
310 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
311 L"*** Server shutting down"));
314 MutexAutoLock envlock(m_env_mutex);
316 // Execute script shutdown hooks
317 m_script->on_shutdown();
319 infostream << "Server: Saving players" << std::endl;
320 m_env->saveLoadedPlayers();
322 infostream << "Server: Kicking players" << std::endl;
323 std::string kick_msg;
324 bool reconnect = false;
325 if (getShutdownRequested()) {
326 reconnect = m_shutdown_ask_reconnect;
327 kick_msg = m_shutdown_msg;
329 if (kick_msg.empty()) {
330 kick_msg = g_settings->get("kick_msg_shutdown");
332 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
333 kick_msg, reconnect);
335 infostream << "Server: Saving environment metadata" << std::endl;
343 // stop all emerge threads before deleting players that may have
344 // requested blocks to be emerged
345 m_emerge->stopThreads();
347 // Delete things in the reverse order of creation
357 // Deinitialize scripting
358 infostream<<"Server: Deinitializing scripting"<<std::endl;
361 // Delete detached inventories
362 for (auto &detached_inventory : m_detached_inventories) {
363 delete detached_inventory.second;
367 void Server::start(Address bind_addr)
369 m_bind_addr = bind_addr;
371 infostream<<"Starting server on "
372 << bind_addr.serializeString() <<"..."<<std::endl;
374 // Stop thread if already running
377 // Initialize connection
378 m_con->SetTimeoutMs(30);
379 m_con->Serve(bind_addr);
384 // ASCII art for the win!
386 <<" .__ __ __ "<<std::endl
387 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
388 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
389 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
390 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
391 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
392 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
393 actionstream<<"Server for gameid=\""<<m_gamespec.id
394 <<"\" listening on "<<bind_addr.serializeString()<<":"
395 <<bind_addr.getPort() << "."<<std::endl;
400 infostream<<"Server: Stopping and waiting threads"<<std::endl;
402 // Stop threads (set run=false first so both start stopping)
404 //m_emergethread.setRun(false);
406 //m_emergethread.stop();
408 infostream<<"Server: Threads stopped"<<std::endl;
411 void Server::step(float dtime)
417 MutexAutoLock lock(m_step_dtime_mutex);
418 m_step_dtime += dtime;
420 // Throw if fatal error occurred in thread
421 std::string async_err = m_async_fatal_error.get();
422 if (!async_err.empty()) {
423 if (!m_simple_singleplayer_mode) {
424 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
425 g_settings->get("kick_msg_crash"),
426 g_settings->getBool("ask_reconnect_on_crash"));
428 throw ServerError("AsyncErr: " + async_err);
432 void Server::AsyncRunStep(bool initial_step)
434 g_profiler->add("Server::AsyncRunStep (num)", 1);
438 MutexAutoLock lock1(m_step_dtime_mutex);
439 dtime = m_step_dtime;
443 // Send blocks to clients
447 if((dtime < 0.001) && !initial_step)
450 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
452 //infostream<<"Server steps "<<dtime<<std::endl;
453 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
456 MutexAutoLock lock1(m_step_dtime_mutex);
457 m_step_dtime -= dtime;
464 m_uptime.set(m_uptime.get() + dtime);
470 Update time of day and overall game time
472 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
475 Send to clients at constant intervals
478 m_time_of_day_send_timer -= dtime;
479 if(m_time_of_day_send_timer < 0.0) {
480 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
481 u16 time = m_env->getTimeOfDay();
482 float time_speed = g_settings->getFloat("time_speed");
483 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
487 MutexAutoLock lock(m_env_mutex);
488 // Figure out and report maximum lag to environment
489 float max_lag = m_env->getMaxLagEstimate();
490 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
492 if(dtime > 0.1 && dtime > max_lag * 2.0)
493 infostream<<"Server: Maximum lag peaked to "<<dtime
497 m_env->reportMaxLagEstimate(max_lag);
499 ScopeProfiler sp(g_profiler, "SEnv step");
500 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
504 static const float map_timer_and_unload_dtime = 2.92;
505 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
507 MutexAutoLock lock(m_env_mutex);
508 // Run Map's timers and unload unused data
509 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
510 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
511 g_settings->getFloat("server_unload_unused_data_timeout"),
516 Listen to the admin chat, if available
519 if (!m_admin_chat->command_queue.empty()) {
520 MutexAutoLock lock(m_env_mutex);
521 while (!m_admin_chat->command_queue.empty()) {
522 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
523 handleChatInterfaceEvent(evt);
527 m_admin_chat->outgoing_queue.push_back(
528 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
535 /* Transform liquids */
536 m_liquid_transform_timer += dtime;
537 if(m_liquid_transform_timer >= m_liquid_transform_every)
539 m_liquid_transform_timer -= m_liquid_transform_every;
541 MutexAutoLock lock(m_env_mutex);
543 ScopeProfiler sp(g_profiler, "Server: liquid transform");
545 std::map<v3s16, MapBlock*> modified_blocks;
546 m_env->getMap().transformLiquids(modified_blocks, m_env);
549 Set the modified blocks unsent for all the clients
551 if(!modified_blocks.empty())
553 SetBlocksNotSent(modified_blocks);
556 m_clients.step(dtime);
558 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
560 // send masterserver announce
562 float &counter = m_masterserver_timer;
563 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
564 g_settings->getBool("server_announce")) {
565 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
566 ServerList::AA_START,
567 m_bind_addr.getPort(),
568 m_clients.getPlayerNames(),
570 m_env->getGameTime(),
573 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
583 Check added and deleted active objects
586 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
587 MutexAutoLock envlock(m_env_mutex);
590 const RemoteClientMap &clients = m_clients.getClientList();
591 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
593 // Radius inside which objects are active
594 static thread_local const s16 radius =
595 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
597 // Radius inside which players are active
598 static thread_local const bool is_transfer_limited =
599 g_settings->exists("unlimited_player_transfer_distance") &&
600 !g_settings->getBool("unlimited_player_transfer_distance");
601 static thread_local const s16 player_transfer_dist =
602 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
603 s16 player_radius = player_transfer_dist;
604 if (player_radius == 0 && is_transfer_limited)
605 player_radius = radius;
607 for (const auto &client_it : clients) {
608 RemoteClient *client = client_it.second;
610 // If definitions and textures have not been sent, don't
611 // send objects either
612 if (client->getState() < CS_DefinitionsSent)
615 RemotePlayer *player = m_env->getPlayer(client->peer_id);
617 // This can happen if the client timeouts somehow
621 PlayerSAO *playersao = player->getPlayerSAO();
625 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
626 if (my_radius <= 0) my_radius = radius;
627 //infostream << "Server: Active Radius " << my_radius << std::endl;
629 std::queue<u16> removed_objects;
630 std::queue<u16> added_objects;
631 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
632 client->m_known_objects, removed_objects);
633 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
634 client->m_known_objects, added_objects);
636 // Ignore if nothing happened
637 if (removed_objects.empty() && added_objects.empty()) {
641 std::string data_buffer;
645 // Handle removed objects
646 writeU16((u8*)buf, removed_objects.size());
647 data_buffer.append(buf, 2);
648 while (!removed_objects.empty()) {
650 u16 id = removed_objects.front();
651 ServerActiveObject* obj = m_env->getActiveObject(id);
653 // Add to data buffer for sending
654 writeU16((u8*)buf, id);
655 data_buffer.append(buf, 2);
657 // Remove from known objects
658 client->m_known_objects.erase(id);
660 if(obj && obj->m_known_by_count > 0)
661 obj->m_known_by_count--;
662 removed_objects.pop();
665 // Handle added objects
666 writeU16((u8*)buf, added_objects.size());
667 data_buffer.append(buf, 2);
668 while (!added_objects.empty()) {
670 u16 id = added_objects.front();
671 ServerActiveObject* obj = m_env->getActiveObject(id);
674 u8 type = ACTIVEOBJECT_TYPE_INVALID;
676 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
678 type = obj->getSendType();
680 // Add to data buffer for sending
681 writeU16((u8*)buf, id);
682 data_buffer.append(buf, 2);
683 writeU8((u8*)buf, type);
684 data_buffer.append(buf, 1);
687 data_buffer.append(serializeLongString(
688 obj->getClientInitializationData(client->net_proto_version)));
690 data_buffer.append(serializeLongString(""));
692 // Add to known objects
693 client->m_known_objects.insert(id);
696 obj->m_known_by_count++;
701 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
702 verbosestream << "Server: Sent object remove/add: "
703 << removed_objects.size() << " removed, "
704 << added_objects.size() << " added, "
705 << "packet size is " << pktSize << std::endl;
709 m_mod_storage_save_timer -= dtime;
710 if (m_mod_storage_save_timer <= 0.0f) {
711 infostream << "Saving registered mod storages." << std::endl;
712 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
713 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
714 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
715 if (it->second->isModified()) {
716 it->second->save(getModStoragePath());
726 MutexAutoLock envlock(m_env_mutex);
727 ScopeProfiler sp(g_profiler, "Server: sending object messages");
730 // Value = data sent by object
731 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
733 // Get active object messages from environment
735 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
739 std::vector<ActiveObjectMessage>* message_list = nullptr;
740 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
741 n = buffered_messages.find(aom.id);
742 if (n == buffered_messages.end()) {
743 message_list = new std::vector<ActiveObjectMessage>;
744 buffered_messages[aom.id] = message_list;
747 message_list = n->second;
749 message_list->push_back(aom);
753 const RemoteClientMap &clients = m_clients.getClientList();
754 // Route data to every client
755 for (const auto &client_it : clients) {
756 RemoteClient *client = client_it.second;
757 std::string reliable_data;
758 std::string unreliable_data;
759 // Go through all objects in message buffer
760 for (const auto &buffered_message : buffered_messages) {
761 // If object is not known by client, skip it
762 u16 id = buffered_message.first;
763 if (client->m_known_objects.find(id) == client->m_known_objects.end())
766 // Get message list of object
767 std::vector<ActiveObjectMessage>* list = buffered_message.second;
768 // Go through every message
769 for (const ActiveObjectMessage &aom : *list) {
770 // Compose the full new data with header
771 std::string new_data;
774 writeU16((u8*)&buf[0], aom.id);
775 new_data.append(buf, 2);
777 new_data += serializeString(aom.datastring);
778 // Add data to buffer
780 reliable_data += new_data;
782 unreliable_data += new_data;
786 reliable_data and unreliable_data are now ready.
789 if (!reliable_data.empty()) {
790 SendActiveObjectMessages(client->peer_id, reliable_data);
793 if (!unreliable_data.empty()) {
794 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
799 // Clear buffered_messages
800 for (auto &buffered_message : buffered_messages) {
801 delete buffered_message.second;
806 Send queued-for-sending map edit events.
809 // We will be accessing the environment
810 MutexAutoLock lock(m_env_mutex);
812 // Don't send too many at a time
815 // Single change sending is disabled if queue size is not small
816 bool disable_single_change_sending = false;
817 if(m_unsent_map_edit_queue.size() >= 4)
818 disable_single_change_sending = true;
820 int event_count = m_unsent_map_edit_queue.size();
822 // We'll log the amount of each
825 while (!m_unsent_map_edit_queue.empty()) {
826 MapEditEvent* event = m_unsent_map_edit_queue.front();
827 m_unsent_map_edit_queue.pop();
829 // Players far away from the change are stored here.
830 // Instead of sending the changes, MapBlocks are set not sent
832 std::vector<u16> far_players;
834 switch (event->type) {
837 prof.add("MEET_ADDNODE", 1);
838 sendAddNode(event->p, event->n, event->already_known_by_peer,
839 &far_players, disable_single_change_sending ? 5 : 30,
840 event->type == MEET_ADDNODE);
842 case MEET_REMOVENODE:
843 prof.add("MEET_REMOVENODE", 1);
844 sendRemoveNode(event->p, event->already_known_by_peer,
845 &far_players, disable_single_change_sending ? 5 : 30);
847 case MEET_BLOCK_NODE_METADATA_CHANGED:
848 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
849 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
850 setBlockNotSent(event->p);
853 infostream << "Server: MEET_OTHER" << std::endl;
854 prof.add("MEET_OTHER", 1);
855 for (const v3s16 &modified_block : event->modified_blocks) {
856 setBlockNotSent(modified_block);
860 prof.add("unknown", 1);
861 warningstream << "Server: Unknown MapEditEvent "
862 << ((u32)event->type) << std::endl;
867 Set blocks not sent to far players
869 if (!far_players.empty()) {
870 // Convert list format to that wanted by SetBlocksNotSent
871 std::map<v3s16, MapBlock*> modified_blocks2;
872 for (const v3s16 &modified_block : event->modified_blocks) {
873 modified_blocks2[modified_block] =
874 m_env->getMap().getBlockNoCreateNoEx(modified_block);
877 // Set blocks not sent
878 for (const u16 far_player : far_players) {
879 if (RemoteClient *client = getClient(far_player))
880 client->SetBlocksNotSent(modified_blocks2);
887 if (event_count >= 5) {
888 infostream << "Server: MapEditEvents:" << std::endl;
889 prof.print(infostream);
890 } else if (event_count != 0) {
891 verbosestream << "Server: MapEditEvents:" << std::endl;
892 prof.print(verbosestream);
898 Trigger emergethread (it somehow gets to a non-triggered but
899 bysy state sometimes)
902 float &counter = m_emergethread_trigger_timer;
904 if (counter >= 2.0) {
907 m_emerge->startThreads();
911 // Save map, players and auth stuff
913 float &counter = m_savemap_timer;
915 static thread_local const float save_interval =
916 g_settings->getFloat("server_map_save_interval");
917 if (counter >= save_interval) {
919 MutexAutoLock lock(m_env_mutex);
921 ScopeProfiler sp(g_profiler, "Server: saving stuff");
924 if (m_banmanager->isModified()) {
925 m_banmanager->save();
928 // Save changed parts of map
929 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
932 m_env->saveLoadedPlayers();
934 // Save environment metadata
940 static const float shutdown_msg_times[] =
942 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
945 if (m_shutdown_timer > 0.0f) {
946 // Automated messages
947 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
948 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
949 // If shutdown timer matches an automessage, shot it
950 if (m_shutdown_timer > shutdown_msg_times[i] &&
951 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
952 std::wstringstream ws;
954 ws << L"*** Server shutting down in "
955 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
958 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
959 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
965 m_shutdown_timer -= dtime;
966 if (m_shutdown_timer < 0.0f) {
967 m_shutdown_timer = 0.0f;
968 m_shutdown_requested = true;
973 void Server::Receive()
978 m_con->Receive(&pkt);
979 peer_id = pkt.getPeerId();
981 } catch (const con::InvalidIncomingDataException &e) {
982 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
983 << e.what() << std::endl;
984 } catch (const SerializationError &e) {
985 infostream << "Server::Receive(): SerializationError: what()="
986 << e.what() << std::endl;
987 } catch (const ClientStateError &e) {
988 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
989 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
990 L"Try reconnecting or updating your client");
991 } catch (const con::PeerNotFoundException &e) {
996 PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
998 std::string playername;
999 PlayerSAO *playersao = NULL;
1002 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1004 playername = client->getName();
1005 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1007 } catch (std::exception &e) {
1013 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1015 // If failed, cancel
1016 if (!playersao || !player) {
1017 if (player && player->peer_id != 0) {
1018 actionstream << "Server: Failed to emerge player \"" << playername
1019 << "\" (player allocated to an another client)" << std::endl;
1020 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1021 L"name. If your client closed unexpectedly, try again in "
1024 errorstream << "Server: " << playername << ": Failed to emerge player"
1026 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1032 Send complete position information
1034 SendMovePlayer(peer_id);
1037 SendPlayerPrivileges(peer_id);
1039 // Send inventory formspec
1040 SendPlayerInventoryFormspec(peer_id);
1043 SendInventory(playersao);
1045 // Send HP or death screen
1046 if (playersao->isDead())
1047 SendDeathscreen(peer_id, false, v3f(0,0,0));
1049 SendPlayerHPOrDie(playersao);
1052 SendPlayerBreath(playersao);
1054 // Note things in chat if not in simple singleplayer mode
1055 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1056 // Send information about server to player in chat
1057 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1059 Address addr = getPeerAddress(player->peer_id);
1060 std::string ip_str = addr.serializeString();
1061 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1066 const std::vector<std::string> &names = m_clients.getPlayerNames();
1068 actionstream << player->getName() << " joins game. List of players: ";
1070 for (const std::string &name : names) {
1071 actionstream << name << " ";
1074 actionstream << player->getName() <<std::endl;
1079 inline void Server::handleCommand(NetworkPacket* pkt)
1081 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1082 (this->*opHandle.handler)(pkt);
1085 void Server::ProcessData(NetworkPacket *pkt)
1087 // Environment is locked first.
1088 MutexAutoLock envlock(m_env_mutex);
1090 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1091 u32 peer_id = pkt->getPeerId();
1094 Address address = getPeerAddress(peer_id);
1095 std::string addr_s = address.serializeString();
1097 if(m_banmanager->isIpBanned(addr_s)) {
1098 std::string ban_name = m_banmanager->getBanName(addr_s);
1099 infostream << "Server: A banned client tried to connect from "
1100 << addr_s << "; banned name was "
1101 << ban_name << std::endl;
1102 // This actually doesn't seem to transfer to the client
1103 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1104 + utf8_to_wide(ban_name));
1108 catch(con::PeerNotFoundException &e) {
1110 * no peer for this packet found
1111 * most common reason is peer timeout, e.g. peer didn't
1112 * respond for some time, your server was overloaded or
1115 infostream << "Server::ProcessData(): Canceling: peer "
1116 << peer_id << " not found" << std::endl;
1121 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1123 // Command must be handled into ToServerCommandHandler
1124 if (command >= TOSERVER_NUM_MSG_TYPES) {
1125 infostream << "Server: Ignoring unknown command "
1126 << command << std::endl;
1130 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1135 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1137 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1138 errorstream << "Server::ProcessData(): Cancelling: Peer"
1139 " serialization format invalid or not initialized."
1140 " Skipping incoming command=" << command << std::endl;
1144 /* Handle commands related to client startup */
1145 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1150 if (m_clients.getClientState(peer_id) < CS_Active) {
1151 if (command == TOSERVER_PLAYERPOS) return;
1153 errorstream << "Got packet command: " << command << " for peer id "
1154 << peer_id << " but client isn't active yet. Dropping packet "
1160 } catch (SendFailedException &e) {
1161 errorstream << "Server::ProcessData(): SendFailedException: "
1162 << "what=" << e.what()
1164 } catch (PacketError &e) {
1165 actionstream << "Server::ProcessData(): PacketError: "
1166 << "what=" << e.what()
1171 void Server::setTimeOfDay(u32 time)
1173 m_env->setTimeOfDay(time);
1174 m_time_of_day_send_timer = 0;
1177 void Server::onMapEditEvent(MapEditEvent *event)
1179 if(m_ignore_map_edit_events)
1181 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1183 MapEditEvent *e = event->clone();
1184 m_unsent_map_edit_queue.push(e);
1187 Inventory* Server::getInventory(const InventoryLocation &loc)
1190 case InventoryLocation::UNDEFINED:
1191 case InventoryLocation::CURRENT_PLAYER:
1193 case InventoryLocation::PLAYER:
1195 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1198 PlayerSAO *playersao = player->getPlayerSAO();
1201 return playersao->getInventory();
1204 case InventoryLocation::NODEMETA:
1206 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1209 return meta->getInventory();
1212 case InventoryLocation::DETACHED:
1214 if(m_detached_inventories.count(loc.name) == 0)
1216 return m_detached_inventories[loc.name];
1220 sanity_check(false); // abort
1225 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1228 case InventoryLocation::UNDEFINED:
1230 case InventoryLocation::PLAYER:
1235 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1240 PlayerSAO *playersao = player->getPlayerSAO();
1244 SendInventory(playersao);
1247 case InventoryLocation::NODEMETA:
1249 v3s16 blockpos = getNodeBlockPos(loc.p);
1251 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1253 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1255 setBlockNotSent(blockpos);
1258 case InventoryLocation::DETACHED:
1260 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1264 sanity_check(false); // abort
1269 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1271 std::vector<u16> clients = m_clients.getClientIDs();
1273 // Set the modified blocks unsent for all the clients
1274 for (const u16 client_id : clients) {
1275 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1276 client->SetBlocksNotSent(block);
1281 void Server::peerAdded(con::Peer *peer)
1283 verbosestream<<"Server::peerAdded(): peer->id="
1284 <<peer->id<<std::endl;
1286 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1289 void Server::deletingPeer(con::Peer *peer, bool timeout)
1291 verbosestream<<"Server::deletingPeer(): peer->id="
1292 <<peer->id<<", timeout="<<timeout<<std::endl;
1294 m_clients.event(peer->id, CSE_Disconnect);
1295 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1298 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
1300 *retval = m_con->getPeerStat(peer_id,type);
1301 return *retval != -1;
1304 bool Server::getClientInfo(
1313 std::string* vers_string
1316 *state = m_clients.getClientState(peer_id);
1318 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1325 *uptime = client->uptime();
1326 *ser_vers = client->serialization_version;
1327 *prot_vers = client->net_proto_version;
1329 *major = client->getMajor();
1330 *minor = client->getMinor();
1331 *patch = client->getPatch();
1332 *vers_string = client->getPatch();
1339 void Server::handlePeerChanges()
1341 while(!m_peer_change_queue.empty())
1343 con::PeerChange c = m_peer_change_queue.front();
1344 m_peer_change_queue.pop();
1346 verbosestream<<"Server: Handling peer change: "
1347 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1352 case con::PEER_ADDED:
1353 m_clients.CreateClient(c.peer_id);
1356 case con::PEER_REMOVED:
1357 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1361 FATAL_ERROR("Invalid peer change event received!");
1367 void Server::printToConsoleOnly(const std::string &text)
1370 m_admin_chat->outgoing_queue.push_back(
1371 new ChatEventChat("", utf8_to_wide(text)));
1373 std::cout << text << std::endl;
1377 void Server::Send(NetworkPacket* pkt)
1379 m_clients.send(pkt->getPeerId(),
1380 clientCommandFactoryTable[pkt->getCommand()].channel,
1382 clientCommandFactoryTable[pkt->getCommand()].reliable);
1385 void Server::SendMovement(u16 peer_id)
1387 std::ostringstream os(std::ios_base::binary);
1389 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1391 pkt << g_settings->getFloat("movement_acceleration_default");
1392 pkt << g_settings->getFloat("movement_acceleration_air");
1393 pkt << g_settings->getFloat("movement_acceleration_fast");
1394 pkt << g_settings->getFloat("movement_speed_walk");
1395 pkt << g_settings->getFloat("movement_speed_crouch");
1396 pkt << g_settings->getFloat("movement_speed_fast");
1397 pkt << g_settings->getFloat("movement_speed_climb");
1398 pkt << g_settings->getFloat("movement_speed_jump");
1399 pkt << g_settings->getFloat("movement_liquid_fluidity");
1400 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1401 pkt << g_settings->getFloat("movement_liquid_sink");
1402 pkt << g_settings->getFloat("movement_gravity");
1407 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1409 if (!g_settings->getBool("enable_damage"))
1412 u16 peer_id = playersao->getPeerID();
1413 bool is_alive = playersao->getHP() > 0;
1416 SendPlayerHP(peer_id);
1421 void Server::SendHP(u16 peer_id, u16 hp)
1423 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1428 void Server::SendBreath(u16 peer_id, u16 breath)
1430 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1431 pkt << (u16) breath;
1435 void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
1436 const std::string &custom_reason, bool reconnect)
1438 assert(reason < SERVER_ACCESSDENIED_MAX);
1440 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1442 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1443 pkt << custom_reason;
1444 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1445 reason == SERVER_ACCESSDENIED_CRASH)
1446 pkt << custom_reason << (u8)reconnect;
1450 void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
1452 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1457 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
1458 v3f camera_point_target)
1460 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1461 pkt << set_camera_point_target << camera_point_target;
1465 void Server::SendItemDef(u16 peer_id,
1466 IItemDefManager *itemdef, u16 protocol_version)
1468 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1472 u32 length of the next item
1473 zlib-compressed serialized ItemDefManager
1475 std::ostringstream tmp_os(std::ios::binary);
1476 itemdef->serialize(tmp_os, protocol_version);
1477 std::ostringstream tmp_os2(std::ios::binary);
1478 compressZlib(tmp_os.str(), tmp_os2);
1479 pkt.putLongString(tmp_os2.str());
1482 verbosestream << "Server: Sending item definitions to id(" << peer_id
1483 << "): size=" << pkt.getSize() << std::endl;
1488 void Server::SendNodeDef(u16 peer_id,
1489 INodeDefManager *nodedef, u16 protocol_version)
1491 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1495 u32 length of the next item
1496 zlib-compressed serialized NodeDefManager
1498 std::ostringstream tmp_os(std::ios::binary);
1499 nodedef->serialize(tmp_os, protocol_version);
1500 std::ostringstream tmp_os2(std::ios::binary);
1501 compressZlib(tmp_os.str(), tmp_os2);
1503 pkt.putLongString(tmp_os2.str());
1506 verbosestream << "Server: Sending node definitions to id(" << peer_id
1507 << "): size=" << pkt.getSize() << std::endl;
1513 Non-static send methods
1516 void Server::SendInventory(PlayerSAO* playerSAO)
1518 UpdateCrafting(playerSAO->getPlayer());
1524 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1526 std::ostringstream os;
1527 playerSAO->getInventory()->serialize(os);
1529 std::string s = os.str();
1531 pkt.putRawString(s.c_str(), s.size());
1535 void Server::SendChatMessage(u16 peer_id, const ChatMessage &message)
1537 NetworkPacket legacypkt(TOCLIENT_CHAT_MESSAGE_OLD, 0, peer_id);
1538 legacypkt << message.message;
1540 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1542 u8 type = message.type;
1543 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1545 if (peer_id != PEER_ID_INEXISTENT) {
1546 RemotePlayer *player = m_env->getPlayer(peer_id);
1550 if (player->protocol_version < 35)
1555 m_clients.sendToAllCompat(&pkt, &legacypkt, 35);
1559 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
1560 const std::string &formname)
1562 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1563 if (formspec.empty()){
1564 //the client should close the formspec
1565 pkt.putLongString("");
1567 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1574 // Spawns a particle on peer with peer_id
1575 void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
1576 v3f pos, v3f velocity, v3f acceleration,
1577 float expirationtime, float size, bool collisiondetection,
1578 bool collision_removal,
1579 bool vertical, const std::string &texture,
1580 const struct TileAnimationParams &animation, u8 glow)
1582 static thread_local const float radius =
1583 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1585 if (peer_id == PEER_ID_INEXISTENT) {
1586 std::vector<u16> clients = m_clients.getClientIDs();
1588 for (const u16 client_id : clients) {
1589 RemotePlayer *player = m_env->getPlayer(client_id);
1593 PlayerSAO *sao = player->getPlayerSAO();
1597 // Do not send to distant clients
1598 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1601 SendSpawnParticle(client_id, player->protocol_version,
1602 pos, velocity, acceleration,
1603 expirationtime, size, collisiondetection,
1604 collision_removal, vertical, texture, animation, glow);
1609 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1611 pkt << pos << velocity << acceleration << expirationtime
1612 << size << collisiondetection;
1613 pkt.putLongString(texture);
1615 pkt << collision_removal;
1616 // This is horrible but required (why are there two ways to serialize pkts?)
1617 std::ostringstream os(std::ios_base::binary);
1618 animation.serialize(os, protocol_version);
1619 pkt.putRawString(os.str());
1625 // Adds a ParticleSpawner on peer with peer_id
1626 void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version,
1627 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1628 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1629 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1630 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1631 const struct TileAnimationParams &animation, u8 glow)
1633 if (peer_id == PEER_ID_INEXISTENT) {
1634 // This sucks and should be replaced:
1635 std::vector<u16> clients = m_clients.getClientIDs();
1636 for (const u16 client_id : clients) {
1637 RemotePlayer *player = m_env->getPlayer(client_id);
1640 SendAddParticleSpawner(client_id, player->protocol_version,
1641 amount, spawntime, minpos, maxpos,
1642 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1643 minsize, maxsize, collisiondetection, collision_removal,
1644 attached_id, vertical, texture, id, animation, glow);
1649 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1651 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1652 << minacc << maxacc << minexptime << maxexptime << minsize
1653 << maxsize << collisiondetection;
1655 pkt.putLongString(texture);
1657 pkt << id << vertical;
1658 pkt << collision_removal;
1660 // This is horrible but required
1661 std::ostringstream os(std::ios_base::binary);
1662 animation.serialize(os, protocol_version);
1663 pkt.putRawString(os.str());
1669 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
1671 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
1673 // Ugly error in this packet
1676 if (peer_id != PEER_ID_INEXISTENT) {
1680 m_clients.sendToAll(&pkt);
1685 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
1687 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1689 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1690 << form->text << form->number << form->item << form->dir
1691 << form->align << form->offset << form->world_pos << form->size;
1696 void Server::SendHUDRemove(u16 peer_id, u32 id)
1698 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1703 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
1705 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1706 pkt << id << (u8) stat;
1710 case HUD_STAT_SCALE:
1711 case HUD_STAT_ALIGN:
1712 case HUD_STAT_OFFSET:
1713 pkt << *(v2f *) value;
1717 pkt << *(std::string *) value;
1719 case HUD_STAT_WORLD_POS:
1720 pkt << *(v3f *) value;
1723 pkt << *(v2s32 *) value;
1725 case HUD_STAT_NUMBER:
1729 pkt << *(u32 *) value;
1736 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
1738 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1740 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1742 pkt << flags << mask;
1747 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
1749 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1750 pkt << param << value;
1754 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
1755 const std::string &type, const std::vector<std::string> ¶ms,
1758 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1759 pkt << bgcolor << type << (u16) params.size();
1761 for (const std::string ¶m : params)
1769 void Server::SendCloudParams(u16 peer_id, float density,
1770 const video::SColor &color_bright,
1771 const video::SColor &color_ambient,
1776 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1777 pkt << density << color_bright << color_ambient
1778 << height << thickness << speed;
1783 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
1786 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1789 pkt << do_override << (u16) (ratio * 65535);
1794 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
1796 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1797 pkt << time << time_speed;
1799 if (peer_id == PEER_ID_INEXISTENT) {
1800 m_clients.sendToAll(&pkt);
1807 void Server::SendPlayerHP(u16 peer_id)
1809 PlayerSAO *playersao = getPlayerSAO(peer_id);
1810 // In some rare case if the player is disconnected
1811 // while Lua call l_punch, for example, this can be NULL
1815 SendHP(peer_id, playersao->getHP());
1816 m_script->player_event(playersao,"health_changed");
1818 // Send to other clients
1819 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1820 ActiveObjectMessage aom(playersao->getId(), true, str);
1821 playersao->m_messages_out.push(aom);
1824 void Server::SendPlayerBreath(PlayerSAO *sao)
1828 m_script->player_event(sao, "breath_changed");
1829 SendBreath(sao->getPeerID(), sao->getBreath());
1832 void Server::SendMovePlayer(u16 peer_id)
1834 RemotePlayer *player = m_env->getPlayer(peer_id);
1836 PlayerSAO *sao = player->getPlayerSAO();
1839 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1840 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1843 v3f pos = sao->getBasePosition();
1844 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1845 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1846 << " pitch=" << sao->getPitch()
1847 << " yaw=" << sao->getYaw()
1854 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
1856 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1859 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1860 << animation_frames[3] << animation_speed;
1865 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
1867 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1868 pkt << first << third;
1871 void Server::SendPlayerPrivileges(u16 peer_id)
1873 RemotePlayer *player = m_env->getPlayer(peer_id);
1875 if(player->peer_id == 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(u16 peer_id)
1893 RemotePlayer *player = m_env->getPlayer(peer_id);
1895 if(player->peer_id == PEER_ID_INEXISTENT)
1898 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1899 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1903 u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
1905 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1906 pkt.putRawString(datas.c_str(), datas.size());
1908 return pkt.getSize();
1911 void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
1913 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1914 datas.size(), peer_id);
1916 pkt.putRawString(datas.c_str(), datas.size());
1918 m_clients.send(pkt.getPeerId(),
1919 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1923 void Server::SendCSMFlavourLimits(u16 peer_id)
1925 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1926 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1927 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1931 s32 Server::playSound(const SimpleSoundSpec &spec,
1932 const ServerSoundParams ¶ms)
1934 // Find out initial position of sound
1935 bool pos_exists = false;
1936 v3f pos = params.getPos(m_env, &pos_exists);
1937 // If position is not found while it should be, cancel sound
1938 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1941 // Filter destination clients
1942 std::vector<u16> dst_clients;
1943 if(!params.to_player.empty()) {
1944 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1946 infostream<<"Server::playSound: Player \""<<params.to_player
1947 <<"\" not found"<<std::endl;
1950 if(player->peer_id == PEER_ID_INEXISTENT){
1951 infostream<<"Server::playSound: Player \""<<params.to_player
1952 <<"\" not connected"<<std::endl;
1955 dst_clients.push_back(player->peer_id);
1957 std::vector<u16> clients = m_clients.getClientIDs();
1959 for (const u16 client_id : clients) {
1960 RemotePlayer *player = m_env->getPlayer(client_id);
1964 PlayerSAO *sao = player->getPlayerSAO();
1969 if(sao->getBasePosition().getDistanceFrom(pos) >
1970 params.max_hear_distance)
1973 dst_clients.push_back(client_id);
1977 if(dst_clients.empty())
1981 s32 id = m_next_sound_id++;
1982 // The sound will exist as a reference in m_playing_sounds
1983 m_playing_sounds[id] = ServerPlayingSound();
1984 ServerPlayingSound &psound = m_playing_sounds[id];
1985 psound.params = params;
1988 float gain = params.gain * spec.gain;
1989 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1990 pkt << id << spec.name << gain
1991 << (u8) params.type << pos << params.object
1992 << params.loop << params.fade << params.pitch;
1994 // Backwards compability
1995 bool play_sound = gain > 0;
1997 for (const u16 dst_client : dst_clients) {
1998 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
1999 psound.clients.insert(dst_client);
2000 m_clients.send(dst_client, 0, &pkt, true);
2005 void Server::stopSound(s32 handle)
2007 // Get sound reference
2008 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2009 m_playing_sounds.find(handle);
2010 if (i == m_playing_sounds.end())
2012 ServerPlayingSound &psound = i->second;
2014 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2017 for (std::unordered_set<u16>::const_iterator si = psound.clients.begin();
2018 si != psound.clients.end(); ++si) {
2020 m_clients.send(*si, 0, &pkt, true);
2022 // Remove sound reference
2023 m_playing_sounds.erase(i);
2026 void Server::fadeSound(s32 handle, float step, float gain)
2028 // Get sound reference
2029 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2030 m_playing_sounds.find(handle);
2031 if (i == m_playing_sounds.end())
2034 ServerPlayingSound &psound = i->second;
2035 psound.params.gain = gain;
2037 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2038 pkt << handle << step << gain;
2040 // Backwards compability
2041 bool play_sound = gain > 0;
2042 ServerPlayingSound compat_psound = psound;
2043 compat_psound.clients.clear();
2045 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2046 compat_pkt << handle;
2048 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2049 it != psound.clients.end();) {
2050 if (m_clients.getProtocolVersion(*it) >= 32) {
2052 m_clients.send(*it, 0, &pkt, true);
2055 compat_psound.clients.insert(*it);
2057 m_clients.send(*it, 0, &compat_pkt, true);
2058 psound.clients.erase(it++);
2062 // Remove sound reference
2063 if (!play_sound || psound.clients.empty())
2064 m_playing_sounds.erase(i);
2066 if (play_sound && !compat_psound.clients.empty()) {
2067 // Play new sound volume on older clients
2068 playSound(compat_psound.spec, compat_psound.params);
2072 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2073 std::vector<u16> *far_players, float far_d_nodes)
2075 float maxd = far_d_nodes*BS;
2076 v3f p_f = intToFloat(p, BS);
2078 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2081 std::vector<u16> clients = m_clients.getClientIDs();
2082 for (u16 client_id : clients) {
2085 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2086 PlayerSAO *sao = player->getPlayerSAO();
2090 // If player is far away, only set modified blocks not sent
2091 v3f player_pos = sao->getBasePosition();
2092 if (player_pos.getDistanceFrom(p_f) > maxd) {
2093 far_players->push_back(client_id);
2100 m_clients.send(client_id, 0, &pkt, true);
2104 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2105 std::vector<u16> *far_players, float far_d_nodes,
2106 bool remove_metadata)
2108 float maxd = far_d_nodes*BS;
2109 v3f p_f = intToFloat(p, BS);
2111 std::vector<u16> clients = m_clients.getClientIDs();
2112 for (const u16 client_id : clients) {
2115 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2116 PlayerSAO *sao = player->getPlayerSAO();
2120 // If player is far away, only set modified blocks not sent
2121 v3f player_pos = sao->getBasePosition();
2122 if(player_pos.getDistanceFrom(p_f) > maxd) {
2123 far_players->push_back(client_id);
2129 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2131 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2133 pkt << p << n.param0 << n.param1 << n.param2
2134 << (u8) (remove_metadata ? 0 : 1);
2139 if (pkt.getSize() > 0)
2140 m_clients.send(client_id, 0, &pkt, true);
2144 void Server::setBlockNotSent(v3s16 p)
2146 std::vector<u16> clients = m_clients.getClientIDs();
2148 for (const u16 i : clients) {
2149 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2150 client->SetBlockNotSent(p);
2155 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
2157 v3s16 p = block->getPos();
2160 Create a packet with the block in the right format
2163 std::ostringstream os(std::ios_base::binary);
2164 block->serialize(os, ver, false);
2165 block->serializeNetworkSpecific(os);
2166 std::string s = os.str();
2168 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2171 pkt.putRawString(s.c_str(), s.size());
2175 void Server::SendBlocks(float dtime)
2177 MutexAutoLock envlock(m_env_mutex);
2178 //TODO check if one big lock could be faster then multiple small ones
2180 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2182 std::vector<PrioritySortedBlockTransfer> queue;
2184 s32 total_sending = 0;
2187 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2189 std::vector<u16> clients = m_clients.getClientIDs();
2192 for (const u16 client_id : clients) {
2193 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2198 total_sending += client->SendingCount();
2199 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2205 // Lowest priority number comes first.
2206 // Lowest is most important.
2207 std::sort(queue.begin(), queue.end());
2210 s32 max_blocks_to_send =
2211 g_settings->getS32("max_simultaneous_block_sends_server_total");
2213 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2214 //TODO: Calculate limit dynamically
2215 if (total_sending >= max_blocks_to_send)
2218 MapBlock *block = nullptr;
2220 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2221 } catch (const InvalidPositionException &e) {
2225 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2230 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2231 client->net_proto_version);
2233 client->SentBlock(block_to_send.pos);
2239 void Server::fillMediaCache()
2241 infostream<<"Server: Calculating media file checksums"<<std::endl;
2243 // Collect all media file paths
2244 std::vector<std::string> paths;
2245 for (const ModSpec &mod : m_mods) {
2246 paths.push_back(mod.path + DIR_DELIM + "textures");
2247 paths.push_back(mod.path + DIR_DELIM + "sounds");
2248 paths.push_back(mod.path + DIR_DELIM + "media");
2249 paths.push_back(mod.path + DIR_DELIM + "models");
2250 paths.push_back(mod.path + DIR_DELIM + "locale");
2252 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2254 // Collect media file information from paths into cache
2255 for (const std::string &mediapath : paths) {
2256 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2257 for (const fs::DirListNode &dln : dirlist) {
2258 if (dln.dir) // Ignode dirs
2260 std::string filename = dln.name;
2261 // If name contains illegal characters, ignore the file
2262 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2263 infostream<<"Server: ignoring illegal file name: \""
2264 << filename << "\"" << std::endl;
2267 // If name is not in a supported format, ignore it
2268 const char *supported_ext[] = {
2269 ".png", ".jpg", ".bmp", ".tga",
2270 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2272 ".x", ".b3d", ".md2", ".obj",
2273 // Custom translation file format
2277 if (removeStringEnd(filename, supported_ext).empty()){
2278 infostream << "Server: ignoring unsupported file extension: \""
2279 << filename << "\"" << std::endl;
2282 // Ok, attempt to load the file and add to cache
2283 std::string filepath;
2284 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2287 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2289 errorstream << "Server::fillMediaCache(): Could not open \""
2290 << filename << "\" for reading" << std::endl;
2293 std::ostringstream tmp_os(std::ios_base::binary);
2297 fis.read(buf, 1024);
2298 std::streamsize len = fis.gcount();
2299 tmp_os.write(buf, len);
2308 errorstream<<"Server::fillMediaCache(): Failed to read \""
2309 << filename << "\"" << std::endl;
2312 if(tmp_os.str().length() == 0) {
2313 errorstream << "Server::fillMediaCache(): Empty file \""
2314 << filepath << "\"" << std::endl;
2319 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2321 unsigned char *digest = sha1.getDigest();
2322 std::string sha1_base64 = base64_encode(digest, 20);
2323 std::string sha1_hex = hex_encode((char*)digest, 20);
2327 m_media[filename] = MediaInfo(filepath, sha1_base64);
2328 verbosestream << "Server: " << sha1_hex << " is " << filename
2334 void Server::sendMediaAnnouncement(u16 peer_id, const std::string &lang_code)
2336 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2340 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2343 std::string lang_suffix;
2344 lang_suffix.append(".").append(lang_code).append(".tr");
2345 for (const auto &i : m_media) {
2346 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2353 for (const auto &i : m_media) {
2354 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2356 pkt << i.first << i.second.sha1_digest;
2359 pkt << g_settings->get("remote_media");
2363 struct SendableMedia
2369 SendableMedia(const std::string &name_="", const std::string &path_="",
2370 const std::string &data_=""):
2377 void Server::sendRequestedMedia(u16 peer_id,
2378 const std::vector<std::string> &tosend)
2380 verbosestream<<"Server::sendRequestedMedia(): "
2381 <<"Sending files to client"<<std::endl;
2385 // Put 5kB in one bunch (this is not accurate)
2386 u32 bytes_per_bunch = 5000;
2388 std::vector< std::vector<SendableMedia> > file_bunches;
2389 file_bunches.emplace_back();
2391 u32 file_size_bunch_total = 0;
2393 for (const std::string &name : tosend) {
2394 if (m_media.find(name) == m_media.end()) {
2395 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2396 <<"unknown file \""<<(name)<<"\""<<std::endl;
2400 //TODO get path + name
2401 std::string tpath = m_media[name].path;
2404 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2406 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2407 <<tpath<<"\" for reading"<<std::endl;
2410 std::ostringstream tmp_os(std::ios_base::binary);
2414 fis.read(buf, 1024);
2415 std::streamsize len = fis.gcount();
2416 tmp_os.write(buf, len);
2417 file_size_bunch_total += len;
2426 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2427 <<name<<"\""<<std::endl;
2430 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2431 <<tname<<"\""<<std::endl;*/
2433 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2435 // Start next bunch if got enough data
2436 if(file_size_bunch_total >= bytes_per_bunch) {
2437 file_bunches.emplace_back();
2438 file_size_bunch_total = 0;
2443 /* Create and send packets */
2445 u16 num_bunches = file_bunches.size();
2446 for (u16 i = 0; i < num_bunches; i++) {
2449 u16 total number of texture bunches
2450 u16 index of this bunch
2451 u32 number of files in this bunch
2460 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2461 pkt << num_bunches << i << (u32) file_bunches[i].size();
2463 for (const SendableMedia &j : file_bunches[i]) {
2465 pkt.putLongString(j.data);
2468 verbosestream << "Server::sendRequestedMedia(): bunch "
2469 << i << "/" << num_bunches
2470 << " files=" << file_bunches[i].size()
2471 << " size=" << pkt.getSize() << std::endl;
2476 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
2478 if(m_detached_inventories.count(name) == 0) {
2479 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2482 Inventory *inv = m_detached_inventories[name];
2483 std::ostringstream os(std::ios_base::binary);
2485 os << serializeString(name);
2489 std::string s = os.str();
2491 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2492 pkt.putRawString(s.c_str(), s.size());
2494 const std::string &check = m_detached_inventories_player[name];
2495 if (peer_id == PEER_ID_INEXISTENT) {
2497 return m_clients.sendToAll(&pkt);
2498 RemotePlayer *p = m_env->getPlayer(check.c_str());
2500 m_clients.send(p->peer_id, 0, &pkt, true);
2502 if (check.empty() || getPlayerName(peer_id) == check)
2507 void Server::sendDetachedInventories(u16 peer_id)
2509 for (const auto &detached_inventory : m_detached_inventories) {
2510 const std::string &name = detached_inventory.first;
2511 //Inventory *inv = i->second;
2512 sendDetachedInventory(name, peer_id);
2520 void Server::DiePlayer(u16 peer_id)
2522 PlayerSAO *playersao = getPlayerSAO(peer_id);
2523 // In some rare cases this can be NULL -- if the player is disconnected
2524 // when a Lua function modifies l_punch, for example
2528 infostream << "Server::DiePlayer(): Player "
2529 << playersao->getPlayer()->getName()
2530 << " dies" << std::endl;
2532 playersao->setHP(0);
2534 // Trigger scripted stuff
2535 m_script->on_dieplayer(playersao);
2537 SendPlayerHP(peer_id);
2538 SendDeathscreen(peer_id, false, v3f(0,0,0));
2541 void Server::RespawnPlayer(u16 peer_id)
2543 PlayerSAO *playersao = getPlayerSAO(peer_id);
2546 infostream << "Server::RespawnPlayer(): Player "
2547 << playersao->getPlayer()->getName()
2548 << " respawns" << std::endl;
2550 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2551 playersao->setBreath(PLAYER_MAX_BREATH);
2553 bool repositioned = m_script->on_respawnplayer(playersao);
2554 if (!repositioned) {
2555 // setPos will send the new position to client
2556 playersao->setPos(findSpawnPos());
2559 SendPlayerHP(peer_id);
2563 void Server::DenySudoAccess(u16 peer_id)
2565 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2570 void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
2571 const std::string &str_reason, bool reconnect)
2573 if (proto_ver >= 25) {
2574 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2576 std::wstring wreason = utf8_to_wide(
2577 reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
2578 accessDeniedStrings[(u8)reason]);
2579 SendAccessDenied_Legacy(peer_id, wreason);
2582 m_clients.event(peer_id, CSE_SetDenied);
2583 m_con->DisconnectPeer(peer_id);
2587 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
2589 SendAccessDenied(peer_id, reason, custom_reason);
2590 m_clients.event(peer_id, CSE_SetDenied);
2591 m_con->DisconnectPeer(peer_id);
2594 // 13/03/15: remove this function when protocol version 25 will become
2595 // the minimum version for MT users, maybe in 1 year
2596 void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
2598 SendAccessDenied_Legacy(peer_id, reason);
2599 m_clients.event(peer_id, CSE_SetDenied);
2600 m_con->DisconnectPeer(peer_id);
2603 void Server::acceptAuth(u16 peer_id, bool forSudoMode)
2606 RemoteClient* client = getClient(peer_id, CS_Invalid);
2608 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2610 // Right now, the auth mechs don't change between login and sudo mode.
2611 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2612 client->allowed_sudo_mechs = sudo_auth_mechs;
2614 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2615 << g_settings->getFloat("dedicated_server_step")
2619 m_clients.event(peer_id, CSE_AuthAccept);
2621 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2623 // We only support SRP right now
2624 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2626 resp_pkt << sudo_auth_mechs;
2628 m_clients.event(peer_id, CSE_SudoSuccess);
2632 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
2634 std::wstring message;
2637 Clear references to playing sounds
2639 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2640 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2641 ServerPlayingSound &psound = i->second;
2642 psound.clients.erase(peer_id);
2643 if (psound.clients.empty())
2644 m_playing_sounds.erase(i++);
2649 RemotePlayer *player = m_env->getPlayer(peer_id);
2651 /* Run scripts and remove from environment */
2653 PlayerSAO *playersao = player->getPlayerSAO();
2656 // inform connected clients
2657 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2658 // (u16) 1 + std::string represents a vector serialization representation
2659 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2660 m_clients.sendToAll(¬ice);
2662 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2664 playersao->disconnected();
2671 if (player && reason != CDR_DENY) {
2672 std::ostringstream os(std::ios_base::binary);
2673 std::vector<u16> clients = m_clients.getClientIDs();
2675 for (const u16 client_id : clients) {
2677 RemotePlayer *player = m_env->getPlayer(client_id);
2681 // Get name of player
2682 os << player->getName() << " ";
2685 std::string name = player->getName();
2686 actionstream << name << " "
2687 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2688 << " List of players: " << os.str() << std::endl;
2690 m_admin_chat->outgoing_queue.push_back(
2691 new ChatEventNick(CET_NICK_REMOVE, name));
2695 MutexAutoLock env_lock(m_env_mutex);
2696 m_clients.DeleteClient(peer_id);
2700 // Send leave chat message to all remaining clients
2701 if (!message.empty()) {
2702 SendChatMessage(PEER_ID_INEXISTENT,
2703 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2707 void Server::UpdateCrafting(RemotePlayer *player)
2709 // Get a preview for crafting
2711 InventoryLocation loc;
2712 loc.setPlayer(player->getName());
2713 std::vector<ItemStack> output_replacements;
2714 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2715 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2716 (&player->inventory)->getList("craft"), loc);
2718 // Put the new preview in
2719 InventoryList *plist = player->inventory.getList("craftpreview");
2720 sanity_check(plist);
2721 sanity_check(plist->getSize() >= 1);
2722 plist->changeItem(0, preview);
2725 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2727 if (evt->type == CET_NICK_ADD) {
2728 // The terminal informed us of its nick choice
2729 m_admin_nick = ((ChatEventNick *)evt)->nick;
2730 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2731 errorstream << "You haven't set up an account." << std::endl
2732 << "Please log in using the client as '"
2733 << m_admin_nick << "' with a secure password." << std::endl
2734 << "Until then, you can't execute admin tasks via the console," << std::endl
2735 << "and everybody can claim the user account instead of you," << std::endl
2736 << "giving them full control over this server." << std::endl;
2739 assert(evt->type == CET_CHAT);
2740 handleAdminChat((ChatEventChat *)evt);
2744 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2745 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2747 // If something goes wrong, this player is to blame
2748 RollbackScopeActor rollback_scope(m_rollback,
2749 std::string("player:") + name);
2751 if (g_settings->getBool("strip_color_codes"))
2752 wmessage = unescape_enriched(wmessage);
2755 switch (player->canSendChatMessage()) {
2756 case RPLAYER_CHATRESULT_FLOODING: {
2757 std::wstringstream ws;
2758 ws << L"You cannot send more messages. You are limited to "
2759 << g_settings->getFloat("chat_message_limit_per_10sec")
2760 << L" messages per 10 seconds.";
2763 case RPLAYER_CHATRESULT_KICK:
2764 DenyAccess_Legacy(player->peer_id,
2765 L"You have been kicked due to message flooding.");
2767 case RPLAYER_CHATRESULT_OK:
2770 FATAL_ERROR("Unhandled chat filtering result found.");
2774 if (m_max_chatmessage_length > 0
2775 && wmessage.length() > m_max_chatmessage_length) {
2776 return L"Your message exceed the maximum chat message limit set on the server. "
2777 L"It was refused. Send a shorter message";
2780 // Run script hook, exit if script ate the chat message
2781 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2786 // Whether to send line to the player that sent the message, or to all players
2787 bool broadcast_line = true;
2789 if (check_shout_priv && !checkPriv(name, "shout")) {
2790 line += L"-!- You don't have permission to shout.";
2791 broadcast_line = false;
2800 Tell calling method to send the message to sender
2802 if (!broadcast_line)
2806 Send the message to others
2808 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2810 std::vector<u16> clients = m_clients.getClientIDs();
2813 Send the message back to the inital sender
2814 if they are using protocol version >= 29
2817 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2818 if (player && player->protocol_version >= 29)
2819 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2821 for (u16 cid : clients) {
2822 if (cid != peer_id_to_avoid_sending)
2823 SendChatMessage(cid, ChatMessage(line));
2828 void Server::handleAdminChat(const ChatEventChat *evt)
2830 std::string name = evt->nick;
2831 std::wstring wname = utf8_to_wide(name);
2832 std::wstring wmessage = evt->evt_msg;
2834 std::wstring answer = handleChat(name, wname, wmessage);
2836 // If asked to send answer to sender
2837 if (!answer.empty()) {
2838 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2842 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2844 RemoteClient *client = getClientNoEx(peer_id,state_min);
2846 throw ClientNotFoundException("Client not found");
2850 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2852 return m_clients.getClientNoEx(peer_id, state_min);
2855 std::string Server::getPlayerName(u16 peer_id)
2857 RemotePlayer *player = m_env->getPlayer(peer_id);
2859 return "[id="+itos(peer_id)+"]";
2860 return player->getName();
2863 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2865 RemotePlayer *player = m_env->getPlayer(peer_id);
2868 return player->getPlayerSAO();
2871 std::wstring Server::getStatusString()
2873 std::wostringstream os(std::ios_base::binary);
2876 os<<L"version="<<narrow_to_wide(g_version_string);
2878 os<<L", uptime="<<m_uptime.get();
2880 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2881 // Information about clients
2884 std::vector<u16> clients = m_clients.getClientIDs();
2885 for (u16 client_id : clients) {
2887 RemotePlayer *player = m_env->getPlayer(client_id);
2888 // Get name of player
2889 std::wstring name = L"unknown";
2891 name = narrow_to_wide(player->getName());
2892 // Add name to information string
2901 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2902 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2904 if (!g_settings->get("motd").empty())
2905 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2909 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2911 std::set<std::string> privs;
2912 m_script->getAuth(name, NULL, &privs);
2916 bool Server::checkPriv(const std::string &name, const std::string &priv)
2918 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2919 return (privs.count(priv) != 0);
2922 void Server::reportPrivsModified(const std::string &name)
2925 std::vector<u16> clients = m_clients.getClientIDs();
2926 for (const u16 client_id : clients) {
2927 RemotePlayer *player = m_env->getPlayer(client_id);
2928 reportPrivsModified(player->getName());
2931 RemotePlayer *player = m_env->getPlayer(name.c_str());
2934 SendPlayerPrivileges(player->peer_id);
2935 PlayerSAO *sao = player->getPlayerSAO();
2938 sao->updatePrivileges(
2939 getPlayerEffectivePrivs(name),
2944 void Server::reportInventoryFormspecModified(const std::string &name)
2946 RemotePlayer *player = m_env->getPlayer(name.c_str());
2949 SendPlayerInventoryFormspec(player->peer_id);
2952 void Server::setIpBanned(const std::string &ip, const std::string &name)
2954 m_banmanager->add(ip, name);
2957 void Server::unsetIpBanned(const std::string &ip_or_name)
2959 m_banmanager->remove(ip_or_name);
2962 std::string Server::getBanDescription(const std::string &ip_or_name)
2964 return m_banmanager->getBanDescription(ip_or_name);
2967 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2969 // m_env will be NULL if the server is initializing
2973 if (m_admin_nick == name && !m_admin_nick.empty()) {
2974 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2977 RemotePlayer *player = m_env->getPlayer(name);
2982 if (player->peer_id == PEER_ID_INEXISTENT)
2985 SendChatMessage(player->peer_id, ChatMessage(msg));
2988 bool Server::showFormspec(const char *playername, const std::string &formspec,
2989 const std::string &formname)
2991 // m_env will be NULL if the server is initializing
2995 RemotePlayer *player = m_env->getPlayer(playername);
2999 SendShowFormspecMessage(player->peer_id, formspec, formname);
3003 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3008 u32 id = player->addHud(form);
3010 SendHUDAdd(player->peer_id, id, form);
3015 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3019 HudElement* todel = player->removeHud(id);
3026 SendHUDRemove(player->peer_id, id);
3030 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3035 SendHUDChange(player->peer_id, id, stat, data);
3039 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3044 SendHUDSetFlags(player->peer_id, flags, mask);
3045 player->hud_flags &= ~mask;
3046 player->hud_flags |= flags;
3048 PlayerSAO* playersao = player->getPlayerSAO();
3053 m_script->player_event(playersao, "hud_changed");
3057 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3062 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3065 player->setHotbarItemcount(hotbar_itemcount);
3066 std::ostringstream os(std::ios::binary);
3067 writeS32(os, hotbar_itemcount);
3068 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3072 s32 Server::hudGetHotbarItemcount(RemotePlayer *player) const
3074 return player->getHotbarItemcount();
3077 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3082 player->setHotbarImage(name);
3083 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3086 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3090 return player->getHotbarImage();
3093 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3098 player->setHotbarSelectedImage(name);
3099 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3102 const std::string& Server::hudGetHotbarSelectedImage(RemotePlayer *player) const
3104 return player->getHotbarSelectedImage();
3107 Address Server::getPeerAddress(u16 peer_id)
3109 return m_con->GetPeerAddress(peer_id);
3112 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3113 v2s32 animation_frames[4], f32 frame_speed)
3118 player->setLocalAnimations(animation_frames, frame_speed);
3119 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3123 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3128 player->eye_offset_first = first;
3129 player->eye_offset_third = third;
3130 SendEyeOffset(player->peer_id, first, third);
3134 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3135 const std::string &type, const std::vector<std::string> ¶ms,
3141 player->setSky(bgcolor, type, params, clouds);
3142 SendSetSky(player->peer_id, bgcolor, type, params, clouds);
3146 bool Server::setClouds(RemotePlayer *player, float density,
3147 const video::SColor &color_bright,
3148 const video::SColor &color_ambient,
3156 SendCloudParams(player->peer_id, density,
3157 color_bright, color_ambient, height,
3162 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3168 player->overrideDayNightRatio(do_override, ratio);
3169 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3173 void Server::notifyPlayers(const std::wstring &msg)
3175 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3178 void Server::spawnParticle(const std::string &playername, v3f pos,
3179 v3f velocity, v3f acceleration,
3180 float expirationtime, float size, bool
3181 collisiondetection, bool collision_removal,
3182 bool vertical, const std::string &texture,
3183 const struct TileAnimationParams &animation, u8 glow)
3185 // m_env will be NULL if the server is initializing
3189 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3190 if (!playername.empty()) {
3191 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3194 peer_id = player->peer_id;
3195 proto_ver = player->protocol_version;
3198 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3199 expirationtime, size, collisiondetection,
3200 collision_removal, 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,
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 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3216 if (!playername.empty()) {
3217 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3220 peer_id = player->peer_id;
3221 proto_ver = player->protocol_version;
3224 u16 attached_id = attached ? attached->getId() : 0;
3227 if (attached_id == 0)
3228 id = m_env->addParticleSpawner(spawntime);
3230 id = m_env->addParticleSpawner(spawntime, attached_id);
3232 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3233 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3234 minexptime, maxexptime, minsize, maxsize,
3235 collisiondetection, collision_removal, attached_id, vertical,
3236 texture, id, animation, glow);
3241 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3243 // m_env will be NULL if the server is initializing
3245 throw ServerError("Can't delete particle spawners during initialisation!");
3247 u16 peer_id = PEER_ID_INEXISTENT;
3248 if (!playername.empty()) {
3249 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3252 peer_id = player->peer_id;
3255 m_env->deleteParticleSpawner(id);
3256 SendDeleteParticleSpawner(peer_id, id);
3259 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3261 if(m_detached_inventories.count(name) > 0){
3262 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3263 delete m_detached_inventories[name];
3265 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3267 Inventory *inv = new Inventory(m_itemdef);
3269 m_detached_inventories[name] = inv;
3270 m_detached_inventories_player[name] = player;
3271 //TODO find a better way to do this
3272 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3276 // actions: time-reversed list
3277 // Return value: success/failure
3278 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3279 std::list<std::string> *log)
3281 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3282 ServerMap *map = (ServerMap*)(&m_env->getMap());
3284 // Fail if no actions to handle
3285 if(actions.empty()){
3286 log->push_back("Nothing to do.");
3293 for (const RollbackAction &action : actions) {
3295 bool success = action.applyRevert(map, this, this);
3298 std::ostringstream os;
3299 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3300 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3302 log->push_back(os.str());
3304 std::ostringstream os;
3305 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3306 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3308 log->push_back(os.str());
3312 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3313 <<" failed"<<std::endl;
3315 // Call it done if less than half failed
3316 return num_failed <= num_tried/2;
3319 // IGameDef interface
3321 IItemDefManager *Server::getItemDefManager()
3326 INodeDefManager *Server::getNodeDefManager()
3331 ICraftDefManager *Server::getCraftDefManager()
3336 u16 Server::allocateUnknownNodeId(const std::string &name)
3338 return m_nodedef->allocateDummy(name);
3341 MtEventManager *Server::getEventManager()
3346 IWritableItemDefManager *Server::getWritableItemDefManager()
3351 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3356 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3361 const ModSpec *Server::getModSpec(const std::string &modname) const
3363 std::vector<ModSpec>::const_iterator it;
3364 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3365 const ModSpec &mod = *it;
3366 if (mod.name == modname)
3372 void Server::getModNames(std::vector<std::string> &modlist)
3374 std::vector<ModSpec>::iterator it;
3375 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3376 modlist.push_back(it->name);
3379 std::string Server::getBuiltinLuaPath()
3381 return porting::path_share + DIR_DELIM + "builtin";
3384 std::string Server::getModStoragePath() const
3386 return m_path_world + DIR_DELIM + "mod_storage";
3389 v3f Server::findSpawnPos()
3391 ServerMap &map = m_env->getServerMap();
3393 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3394 return nodeposf * BS;
3397 bool is_good = false;
3398 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3399 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3401 // Try to find a good place a few times
3402 for(s32 i = 0; i < 4000 && !is_good; i++) {
3403 s32 range = MYMIN(1 + i, range_max);
3404 // We're going to try to throw the player to this position
3405 v2s16 nodepos2d = v2s16(
3406 -range + (myrand() % (range * 2)),
3407 -range + (myrand() % (range * 2)));
3409 // Get spawn level at point
3410 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3411 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3412 // the mapgen to signify an unsuitable spawn position
3413 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3416 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3419 for (s32 i = 0; i < 10; i++) {
3420 v3s16 blockpos = getNodeBlockPos(nodepos);
3421 map.emergeBlock(blockpos, true);
3422 content_t c = map.getNodeNoEx(nodepos).getContent();
3423 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3425 if (air_count >= 2) {
3426 nodeposf = intToFloat(nodepos, BS);
3427 // Don't spawn the player outside map boundaries
3428 if (objectpos_over_limit(nodeposf))
3441 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3443 m_shutdown_timer = delay;
3444 m_shutdown_msg = msg;
3445 m_shutdown_ask_reconnect = reconnect;
3447 if (delay == 0.0f) {
3448 // No delay, shutdown immediately
3449 m_shutdown_requested = true;
3450 // only print to the infostream, a chat message saying
3451 // "Server Shutting Down" is sent when the server destructs.
3452 infostream << "*** Immediate Server shutdown requested." << std::endl;
3453 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3454 // Negative delay, cancel shutdown if requested
3455 m_shutdown_timer = 0.0f;
3456 m_shutdown_msg = "";
3457 m_shutdown_ask_reconnect = false;
3458 m_shutdown_requested = false;
3459 std::wstringstream ws;
3461 ws << L"*** Server shutdown canceled.";
3463 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3464 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3465 } else if (delay > 0.0f) {
3466 // Positive delay, tell the clients when the server will shut down
3467 std::wstringstream ws;
3469 ws << L"*** Server shutting down in "
3470 << duration_to_string(myround(m_shutdown_timer)).c_str()
3473 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3474 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3478 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3481 Try to get an existing player
3483 RemotePlayer *player = m_env->getPlayer(name);
3485 // If player is already connected, cancel
3486 if (player && player->peer_id != 0) {
3487 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3492 If player with the wanted peer_id already exists, cancel.
3494 if (m_env->getPlayer(peer_id)) {
3495 infostream<<"emergePlayer(): Player with wrong name but same"
3496 " peer_id already exists"<<std::endl;
3501 player = new RemotePlayer(name, idef());
3504 bool newplayer = false;
3507 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3509 // Complete init with server parts
3510 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3511 player->protocol_version = proto_version;
3515 m_script->on_newplayer(playersao);
3521 bool Server::registerModStorage(ModMetadata *storage)
3523 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3524 errorstream << "Unable to register same mod storage twice. Storage name: "
3525 << storage->getModName() << std::endl;
3529 m_mod_storages[storage->getModName()] = storage;
3533 void Server::unregisterModStorage(const std::string &name)
3535 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3536 if (it != m_mod_storages.end()) {
3537 // Save unconditionaly on unregistration
3538 it->second->save(getModStoragePath());
3539 m_mod_storages.erase(name);
3543 void dedicated_server_loop(Server &server, bool &kill)
3545 verbosestream<<"dedicated_server_loop()"<<std::endl;
3547 IntervalLimiter m_profiler_interval;
3549 static thread_local const float steplen =
3550 g_settings->getFloat("dedicated_server_step");
3551 static thread_local const float profiler_print_interval =
3552 g_settings->getFloat("profiler_print_interval");
3555 // This is kind of a hack but can be done like this
3556 // because server.step() is very light
3558 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3559 sleep_ms((int)(steplen*1000.0));
3561 server.step(steplen);
3563 if (server.getShutdownRequested() || kill)
3569 if (profiler_print_interval != 0) {
3570 if(m_profiler_interval.step(steplen, profiler_print_interval))
3572 infostream<<"Profiler:"<<std::endl;
3573 g_profiler->print(infostream);
3574 g_profiler->clear();
3579 infostream << "Dedicated server quitting" << std::endl;
3581 if (g_settings->getBool("server_announce"))
3582 ServerList::sendAnnounce(ServerList::AA_DELETE,
3583 server.m_bind_addr.getPort());