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 pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1539 u8 type = message.type;
1540 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1542 if (peer_id != PEER_ID_INEXISTENT) {
1543 RemotePlayer *player = m_env->getPlayer(peer_id);
1549 m_clients.sendToAll(&pkt);
1553 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
1554 const std::string &formname)
1556 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1557 if (formspec.empty()){
1558 //the client should close the formspec
1559 pkt.putLongString("");
1561 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1568 // Spawns a particle on peer with peer_id
1569 void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
1570 v3f pos, v3f velocity, v3f acceleration,
1571 float expirationtime, float size, bool collisiondetection,
1572 bool collision_removal,
1573 bool vertical, const std::string &texture,
1574 const struct TileAnimationParams &animation, u8 glow)
1576 static thread_local const float radius =
1577 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1579 if (peer_id == PEER_ID_INEXISTENT) {
1580 std::vector<u16> clients = m_clients.getClientIDs();
1582 for (const u16 client_id : clients) {
1583 RemotePlayer *player = m_env->getPlayer(client_id);
1587 PlayerSAO *sao = player->getPlayerSAO();
1591 // Do not send to distant clients
1592 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1595 SendSpawnParticle(client_id, player->protocol_version,
1596 pos, velocity, acceleration,
1597 expirationtime, size, collisiondetection,
1598 collision_removal, vertical, texture, animation, glow);
1603 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1605 pkt << pos << velocity << acceleration << expirationtime
1606 << size << collisiondetection;
1607 pkt.putLongString(texture);
1609 pkt << collision_removal;
1610 // This is horrible but required (why are there two ways to serialize pkts?)
1611 std::ostringstream os(std::ios_base::binary);
1612 animation.serialize(os, protocol_version);
1613 pkt.putRawString(os.str());
1619 // Adds a ParticleSpawner on peer with peer_id
1620 void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version,
1621 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1622 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1623 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1624 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1625 const struct TileAnimationParams &animation, u8 glow)
1627 if (peer_id == PEER_ID_INEXISTENT) {
1628 // This sucks and should be replaced:
1629 std::vector<u16> clients = m_clients.getClientIDs();
1630 for (const u16 client_id : clients) {
1631 RemotePlayer *player = m_env->getPlayer(client_id);
1634 SendAddParticleSpawner(client_id, player->protocol_version,
1635 amount, spawntime, minpos, maxpos,
1636 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1637 minsize, maxsize, collisiondetection, collision_removal,
1638 attached_id, vertical, texture, id, animation, glow);
1643 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1645 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1646 << minacc << maxacc << minexptime << maxexptime << minsize
1647 << maxsize << collisiondetection;
1649 pkt.putLongString(texture);
1651 pkt << id << vertical;
1652 pkt << collision_removal;
1654 // This is horrible but required
1655 std::ostringstream os(std::ios_base::binary);
1656 animation.serialize(os, protocol_version);
1657 pkt.putRawString(os.str());
1663 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
1665 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1667 // Ugly error in this packet
1670 if (peer_id != PEER_ID_INEXISTENT)
1673 m_clients.sendToAll(&pkt);
1677 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
1679 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1681 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1682 << form->text << form->number << form->item << form->dir
1683 << form->align << form->offset << form->world_pos << form->size;
1688 void Server::SendHUDRemove(u16 peer_id, u32 id)
1690 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1695 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
1697 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1698 pkt << id << (u8) stat;
1702 case HUD_STAT_SCALE:
1703 case HUD_STAT_ALIGN:
1704 case HUD_STAT_OFFSET:
1705 pkt << *(v2f *) value;
1709 pkt << *(std::string *) value;
1711 case HUD_STAT_WORLD_POS:
1712 pkt << *(v3f *) value;
1715 pkt << *(v2s32 *) value;
1717 case HUD_STAT_NUMBER:
1721 pkt << *(u32 *) value;
1728 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
1730 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1732 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1734 pkt << flags << mask;
1739 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
1741 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1742 pkt << param << value;
1746 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
1747 const std::string &type, const std::vector<std::string> ¶ms,
1750 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1751 pkt << bgcolor << type << (u16) params.size();
1753 for (const std::string ¶m : params)
1761 void Server::SendCloudParams(u16 peer_id, float density,
1762 const video::SColor &color_bright,
1763 const video::SColor &color_ambient,
1768 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1769 pkt << density << color_bright << color_ambient
1770 << height << thickness << speed;
1775 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
1778 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1781 pkt << do_override << (u16) (ratio * 65535);
1786 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
1788 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1789 pkt << time << time_speed;
1791 if (peer_id == PEER_ID_INEXISTENT) {
1792 m_clients.sendToAll(&pkt);
1799 void Server::SendPlayerHP(u16 peer_id)
1801 PlayerSAO *playersao = getPlayerSAO(peer_id);
1802 // In some rare case if the player is disconnected
1803 // while Lua call l_punch, for example, this can be NULL
1807 SendHP(peer_id, playersao->getHP());
1808 m_script->player_event(playersao,"health_changed");
1810 // Send to other clients
1811 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1812 ActiveObjectMessage aom(playersao->getId(), true, str);
1813 playersao->m_messages_out.push(aom);
1816 void Server::SendPlayerBreath(PlayerSAO *sao)
1820 m_script->player_event(sao, "breath_changed");
1821 SendBreath(sao->getPeerID(), sao->getBreath());
1824 void Server::SendMovePlayer(u16 peer_id)
1826 RemotePlayer *player = m_env->getPlayer(peer_id);
1828 PlayerSAO *sao = player->getPlayerSAO();
1831 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1832 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1835 v3f pos = sao->getBasePosition();
1836 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1837 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1838 << " pitch=" << sao->getPitch()
1839 << " yaw=" << sao->getYaw()
1846 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
1848 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1851 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1852 << animation_frames[3] << animation_speed;
1857 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
1859 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1860 pkt << first << third;
1863 void Server::SendPlayerPrivileges(u16 peer_id)
1865 RemotePlayer *player = m_env->getPlayer(peer_id);
1867 if(player->peer_id == PEER_ID_INEXISTENT)
1870 std::set<std::string> privs;
1871 m_script->getAuth(player->getName(), NULL, &privs);
1873 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1874 pkt << (u16) privs.size();
1876 for (const std::string &priv : privs) {
1883 void Server::SendPlayerInventoryFormspec(u16 peer_id)
1885 RemotePlayer *player = m_env->getPlayer(peer_id);
1887 if(player->peer_id == PEER_ID_INEXISTENT)
1890 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1891 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1895 u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
1897 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1898 pkt.putRawString(datas.c_str(), datas.size());
1900 return pkt.getSize();
1903 void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
1905 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1906 datas.size(), peer_id);
1908 pkt.putRawString(datas.c_str(), datas.size());
1910 m_clients.send(pkt.getPeerId(),
1911 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1915 void Server::SendCSMFlavourLimits(u16 peer_id)
1917 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1918 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1919 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1923 s32 Server::playSound(const SimpleSoundSpec &spec,
1924 const ServerSoundParams ¶ms)
1926 // Find out initial position of sound
1927 bool pos_exists = false;
1928 v3f pos = params.getPos(m_env, &pos_exists);
1929 // If position is not found while it should be, cancel sound
1930 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1933 // Filter destination clients
1934 std::vector<u16> dst_clients;
1935 if(!params.to_player.empty()) {
1936 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1938 infostream<<"Server::playSound: Player \""<<params.to_player
1939 <<"\" not found"<<std::endl;
1942 if(player->peer_id == PEER_ID_INEXISTENT){
1943 infostream<<"Server::playSound: Player \""<<params.to_player
1944 <<"\" not connected"<<std::endl;
1947 dst_clients.push_back(player->peer_id);
1949 std::vector<u16> clients = m_clients.getClientIDs();
1951 for (const u16 client_id : clients) {
1952 RemotePlayer *player = m_env->getPlayer(client_id);
1956 PlayerSAO *sao = player->getPlayerSAO();
1961 if(sao->getBasePosition().getDistanceFrom(pos) >
1962 params.max_hear_distance)
1965 dst_clients.push_back(client_id);
1969 if(dst_clients.empty())
1973 s32 id = m_next_sound_id++;
1974 // The sound will exist as a reference in m_playing_sounds
1975 m_playing_sounds[id] = ServerPlayingSound();
1976 ServerPlayingSound &psound = m_playing_sounds[id];
1977 psound.params = params;
1980 float gain = params.gain * spec.gain;
1981 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1982 pkt << id << spec.name << gain
1983 << (u8) params.type << pos << params.object
1984 << params.loop << params.fade << params.pitch;
1986 // Backwards compability
1987 bool play_sound = gain > 0;
1989 for (const u16 dst_client : dst_clients) {
1990 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
1991 psound.clients.insert(dst_client);
1992 m_clients.send(dst_client, 0, &pkt, true);
1997 void Server::stopSound(s32 handle)
1999 // Get sound reference
2000 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2001 m_playing_sounds.find(handle);
2002 if (i == m_playing_sounds.end())
2004 ServerPlayingSound &psound = i->second;
2006 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2009 for (std::unordered_set<u16>::const_iterator si = psound.clients.begin();
2010 si != psound.clients.end(); ++si) {
2012 m_clients.send(*si, 0, &pkt, true);
2014 // Remove sound reference
2015 m_playing_sounds.erase(i);
2018 void Server::fadeSound(s32 handle, float step, float gain)
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())
2026 ServerPlayingSound &psound = i->second;
2027 psound.params.gain = gain;
2029 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2030 pkt << handle << step << gain;
2032 // Backwards compability
2033 bool play_sound = gain > 0;
2034 ServerPlayingSound compat_psound = psound;
2035 compat_psound.clients.clear();
2037 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2038 compat_pkt << handle;
2040 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2041 it != psound.clients.end();) {
2042 if (m_clients.getProtocolVersion(*it) >= 32) {
2044 m_clients.send(*it, 0, &pkt, true);
2047 compat_psound.clients.insert(*it);
2049 m_clients.send(*it, 0, &compat_pkt, true);
2050 psound.clients.erase(it++);
2054 // Remove sound reference
2055 if (!play_sound || psound.clients.empty())
2056 m_playing_sounds.erase(i);
2058 if (play_sound && !compat_psound.clients.empty()) {
2059 // Play new sound volume on older clients
2060 playSound(compat_psound.spec, compat_psound.params);
2064 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2065 std::vector<u16> *far_players, float far_d_nodes)
2067 float maxd = far_d_nodes*BS;
2068 v3f p_f = intToFloat(p, BS);
2070 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2073 std::vector<u16> clients = m_clients.getClientIDs();
2074 for (u16 client_id : clients) {
2077 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2078 PlayerSAO *sao = player->getPlayerSAO();
2082 // If player is far away, only set modified blocks not sent
2083 v3f player_pos = sao->getBasePosition();
2084 if (player_pos.getDistanceFrom(p_f) > maxd) {
2085 far_players->push_back(client_id);
2092 m_clients.send(client_id, 0, &pkt, true);
2096 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2097 std::vector<u16> *far_players, float far_d_nodes,
2098 bool remove_metadata)
2100 float maxd = far_d_nodes*BS;
2101 v3f p_f = intToFloat(p, BS);
2103 std::vector<u16> clients = m_clients.getClientIDs();
2104 for (const u16 client_id : clients) {
2107 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2108 PlayerSAO *sao = player->getPlayerSAO();
2112 // If player is far away, only set modified blocks not sent
2113 v3f player_pos = sao->getBasePosition();
2114 if(player_pos.getDistanceFrom(p_f) > maxd) {
2115 far_players->push_back(client_id);
2121 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2123 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2125 pkt << p << n.param0 << n.param1 << n.param2
2126 << (u8) (remove_metadata ? 0 : 1);
2131 if (pkt.getSize() > 0)
2132 m_clients.send(client_id, 0, &pkt, true);
2136 void Server::setBlockNotSent(v3s16 p)
2138 std::vector<u16> clients = m_clients.getClientIDs();
2140 for (const u16 i : clients) {
2141 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2142 client->SetBlockNotSent(p);
2147 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
2149 v3s16 p = block->getPos();
2152 Create a packet with the block in the right format
2155 std::ostringstream os(std::ios_base::binary);
2156 block->serialize(os, ver, false);
2157 block->serializeNetworkSpecific(os);
2158 std::string s = os.str();
2160 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2163 pkt.putRawString(s.c_str(), s.size());
2167 void Server::SendBlocks(float dtime)
2169 MutexAutoLock envlock(m_env_mutex);
2170 //TODO check if one big lock could be faster then multiple small ones
2172 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2174 std::vector<PrioritySortedBlockTransfer> queue;
2176 u32 total_sending = 0;
2179 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2181 std::vector<u16> clients = m_clients.getClientIDs();
2184 for (const u16 client_id : clients) {
2185 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2190 total_sending += client->getSendingCount();
2191 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2197 // Lowest priority number comes first.
2198 // Lowest is most important.
2199 std::sort(queue.begin(), queue.end());
2203 // Maximal total count calculation
2204 // The per-client block sends is halved with the maximal online users
2205 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2206 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2208 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2209 if (total_sending >= max_blocks_to_send)
2212 MapBlock *block = nullptr;
2214 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2215 } catch (const InvalidPositionException &e) {
2219 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2224 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2225 client->net_proto_version);
2227 client->SentBlock(block_to_send.pos);
2233 void Server::fillMediaCache()
2235 infostream<<"Server: Calculating media file checksums"<<std::endl;
2237 // Collect all media file paths
2238 std::vector<std::string> paths;
2239 for (const ModSpec &mod : m_mods) {
2240 paths.push_back(mod.path + DIR_DELIM + "textures");
2241 paths.push_back(mod.path + DIR_DELIM + "sounds");
2242 paths.push_back(mod.path + DIR_DELIM + "media");
2243 paths.push_back(mod.path + DIR_DELIM + "models");
2244 paths.push_back(mod.path + DIR_DELIM + "locale");
2246 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2248 // Collect media file information from paths into cache
2249 for (const std::string &mediapath : paths) {
2250 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2251 for (const fs::DirListNode &dln : dirlist) {
2252 if (dln.dir) // Ignode dirs
2254 std::string filename = dln.name;
2255 // If name contains illegal characters, ignore the file
2256 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2257 infostream<<"Server: ignoring illegal file name: \""
2258 << filename << "\"" << std::endl;
2261 // If name is not in a supported format, ignore it
2262 const char *supported_ext[] = {
2263 ".png", ".jpg", ".bmp", ".tga",
2264 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2266 ".x", ".b3d", ".md2", ".obj",
2267 // Custom translation file format
2271 if (removeStringEnd(filename, supported_ext).empty()){
2272 infostream << "Server: ignoring unsupported file extension: \""
2273 << filename << "\"" << std::endl;
2276 // Ok, attempt to load the file and add to cache
2277 std::string filepath;
2278 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2281 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2283 errorstream << "Server::fillMediaCache(): Could not open \""
2284 << filename << "\" for reading" << std::endl;
2287 std::ostringstream tmp_os(std::ios_base::binary);
2291 fis.read(buf, 1024);
2292 std::streamsize len = fis.gcount();
2293 tmp_os.write(buf, len);
2302 errorstream<<"Server::fillMediaCache(): Failed to read \""
2303 << filename << "\"" << std::endl;
2306 if(tmp_os.str().length() == 0) {
2307 errorstream << "Server::fillMediaCache(): Empty file \""
2308 << filepath << "\"" << std::endl;
2313 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2315 unsigned char *digest = sha1.getDigest();
2316 std::string sha1_base64 = base64_encode(digest, 20);
2317 std::string sha1_hex = hex_encode((char*)digest, 20);
2321 m_media[filename] = MediaInfo(filepath, sha1_base64);
2322 verbosestream << "Server: " << sha1_hex << " is " << filename
2328 void Server::sendMediaAnnouncement(u16 peer_id, const std::string &lang_code)
2330 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2334 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2337 std::string lang_suffix;
2338 lang_suffix.append(".").append(lang_code).append(".tr");
2339 for (const auto &i : m_media) {
2340 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2347 for (const auto &i : m_media) {
2348 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2350 pkt << i.first << i.second.sha1_digest;
2353 pkt << g_settings->get("remote_media");
2357 struct SendableMedia
2363 SendableMedia(const std::string &name_="", const std::string &path_="",
2364 const std::string &data_=""):
2371 void Server::sendRequestedMedia(u16 peer_id,
2372 const std::vector<std::string> &tosend)
2374 verbosestream<<"Server::sendRequestedMedia(): "
2375 <<"Sending files to client"<<std::endl;
2379 // Put 5kB in one bunch (this is not accurate)
2380 u32 bytes_per_bunch = 5000;
2382 std::vector< std::vector<SendableMedia> > file_bunches;
2383 file_bunches.emplace_back();
2385 u32 file_size_bunch_total = 0;
2387 for (const std::string &name : tosend) {
2388 if (m_media.find(name) == m_media.end()) {
2389 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2390 <<"unknown file \""<<(name)<<"\""<<std::endl;
2394 //TODO get path + name
2395 std::string tpath = m_media[name].path;
2398 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2400 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2401 <<tpath<<"\" for reading"<<std::endl;
2404 std::ostringstream tmp_os(std::ios_base::binary);
2408 fis.read(buf, 1024);
2409 std::streamsize len = fis.gcount();
2410 tmp_os.write(buf, len);
2411 file_size_bunch_total += len;
2420 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2421 <<name<<"\""<<std::endl;
2424 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2425 <<tname<<"\""<<std::endl;*/
2427 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2429 // Start next bunch if got enough data
2430 if(file_size_bunch_total >= bytes_per_bunch) {
2431 file_bunches.emplace_back();
2432 file_size_bunch_total = 0;
2437 /* Create and send packets */
2439 u16 num_bunches = file_bunches.size();
2440 for (u16 i = 0; i < num_bunches; i++) {
2443 u16 total number of texture bunches
2444 u16 index of this bunch
2445 u32 number of files in this bunch
2454 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2455 pkt << num_bunches << i << (u32) file_bunches[i].size();
2457 for (const SendableMedia &j : file_bunches[i]) {
2459 pkt.putLongString(j.data);
2462 verbosestream << "Server::sendRequestedMedia(): bunch "
2463 << i << "/" << num_bunches
2464 << " files=" << file_bunches[i].size()
2465 << " size=" << pkt.getSize() << std::endl;
2470 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
2472 if(m_detached_inventories.count(name) == 0) {
2473 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2476 Inventory *inv = m_detached_inventories[name];
2477 std::ostringstream os(std::ios_base::binary);
2479 os << serializeString(name);
2483 std::string s = os.str();
2485 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2486 pkt.putRawString(s.c_str(), s.size());
2488 const std::string &check = m_detached_inventories_player[name];
2489 if (peer_id == PEER_ID_INEXISTENT) {
2491 return m_clients.sendToAll(&pkt);
2492 RemotePlayer *p = m_env->getPlayer(check.c_str());
2494 m_clients.send(p->peer_id, 0, &pkt, true);
2496 if (check.empty() || getPlayerName(peer_id) == check)
2501 void Server::sendDetachedInventories(u16 peer_id)
2503 for (const auto &detached_inventory : m_detached_inventories) {
2504 const std::string &name = detached_inventory.first;
2505 //Inventory *inv = i->second;
2506 sendDetachedInventory(name, peer_id);
2514 void Server::DiePlayer(u16 peer_id)
2516 PlayerSAO *playersao = getPlayerSAO(peer_id);
2517 // In some rare cases this can be NULL -- if the player is disconnected
2518 // when a Lua function modifies l_punch, for example
2522 infostream << "Server::DiePlayer(): Player "
2523 << playersao->getPlayer()->getName()
2524 << " dies" << std::endl;
2526 playersao->setHP(0);
2528 // Trigger scripted stuff
2529 m_script->on_dieplayer(playersao);
2531 SendPlayerHP(peer_id);
2532 SendDeathscreen(peer_id, false, v3f(0,0,0));
2535 void Server::RespawnPlayer(u16 peer_id)
2537 PlayerSAO *playersao = getPlayerSAO(peer_id);
2540 infostream << "Server::RespawnPlayer(): Player "
2541 << playersao->getPlayer()->getName()
2542 << " respawns" << std::endl;
2544 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2545 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2547 bool repositioned = m_script->on_respawnplayer(playersao);
2548 if (!repositioned) {
2549 // setPos will send the new position to client
2550 playersao->setPos(findSpawnPos());
2553 SendPlayerHP(peer_id);
2557 void Server::DenySudoAccess(u16 peer_id)
2559 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2564 void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
2565 const std::string &str_reason, bool reconnect)
2567 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2569 m_clients.event(peer_id, CSE_SetDenied);
2570 m_con->DisconnectPeer(peer_id);
2574 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
2576 SendAccessDenied(peer_id, reason, custom_reason);
2577 m_clients.event(peer_id, CSE_SetDenied);
2578 m_con->DisconnectPeer(peer_id);
2581 // 13/03/15: remove this function when protocol version 25 will become
2582 // the minimum version for MT users, maybe in 1 year
2583 void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
2585 SendAccessDenied_Legacy(peer_id, reason);
2586 m_clients.event(peer_id, CSE_SetDenied);
2587 m_con->DisconnectPeer(peer_id);
2590 void Server::acceptAuth(u16 peer_id, bool forSudoMode)
2593 RemoteClient* client = getClient(peer_id, CS_Invalid);
2595 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2597 // Right now, the auth mechs don't change between login and sudo mode.
2598 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2599 client->allowed_sudo_mechs = sudo_auth_mechs;
2601 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2602 << g_settings->getFloat("dedicated_server_step")
2606 m_clients.event(peer_id, CSE_AuthAccept);
2608 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2610 // We only support SRP right now
2611 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2613 resp_pkt << sudo_auth_mechs;
2615 m_clients.event(peer_id, CSE_SudoSuccess);
2619 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
2621 std::wstring message;
2624 Clear references to playing sounds
2626 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2627 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2628 ServerPlayingSound &psound = i->second;
2629 psound.clients.erase(peer_id);
2630 if (psound.clients.empty())
2631 m_playing_sounds.erase(i++);
2636 RemotePlayer *player = m_env->getPlayer(peer_id);
2638 /* Run scripts and remove from environment */
2640 PlayerSAO *playersao = player->getPlayerSAO();
2643 // inform connected clients
2644 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2645 // (u16) 1 + std::string represents a vector serialization representation
2646 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2647 m_clients.sendToAll(¬ice);
2649 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2651 playersao->disconnected();
2658 if (player && reason != CDR_DENY) {
2659 std::ostringstream os(std::ios_base::binary);
2660 std::vector<u16> clients = m_clients.getClientIDs();
2662 for (const u16 client_id : clients) {
2664 RemotePlayer *player = m_env->getPlayer(client_id);
2668 // Get name of player
2669 os << player->getName() << " ";
2672 std::string name = player->getName();
2673 actionstream << name << " "
2674 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2675 << " List of players: " << os.str() << std::endl;
2677 m_admin_chat->outgoing_queue.push_back(
2678 new ChatEventNick(CET_NICK_REMOVE, name));
2682 MutexAutoLock env_lock(m_env_mutex);
2683 m_clients.DeleteClient(peer_id);
2687 // Send leave chat message to all remaining clients
2688 if (!message.empty()) {
2689 SendChatMessage(PEER_ID_INEXISTENT,
2690 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2694 void Server::UpdateCrafting(RemotePlayer *player)
2696 // Get a preview for crafting
2698 InventoryLocation loc;
2699 loc.setPlayer(player->getName());
2700 std::vector<ItemStack> output_replacements;
2701 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2702 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2703 (&player->inventory)->getList("craft"), loc);
2705 // Put the new preview in
2706 InventoryList *plist = player->inventory.getList("craftpreview");
2707 sanity_check(plist);
2708 sanity_check(plist->getSize() >= 1);
2709 plist->changeItem(0, preview);
2712 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2714 if (evt->type == CET_NICK_ADD) {
2715 // The terminal informed us of its nick choice
2716 m_admin_nick = ((ChatEventNick *)evt)->nick;
2717 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2718 errorstream << "You haven't set up an account." << std::endl
2719 << "Please log in using the client as '"
2720 << m_admin_nick << "' with a secure password." << std::endl
2721 << "Until then, you can't execute admin tasks via the console," << std::endl
2722 << "and everybody can claim the user account instead of you," << std::endl
2723 << "giving them full control over this server." << std::endl;
2726 assert(evt->type == CET_CHAT);
2727 handleAdminChat((ChatEventChat *)evt);
2731 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2732 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2734 // If something goes wrong, this player is to blame
2735 RollbackScopeActor rollback_scope(m_rollback,
2736 std::string("player:") + name);
2738 if (g_settings->getBool("strip_color_codes"))
2739 wmessage = unescape_enriched(wmessage);
2742 switch (player->canSendChatMessage()) {
2743 case RPLAYER_CHATRESULT_FLOODING: {
2744 std::wstringstream ws;
2745 ws << L"You cannot send more messages. You are limited to "
2746 << g_settings->getFloat("chat_message_limit_per_10sec")
2747 << L" messages per 10 seconds.";
2750 case RPLAYER_CHATRESULT_KICK:
2751 DenyAccess_Legacy(player->peer_id,
2752 L"You have been kicked due to message flooding.");
2754 case RPLAYER_CHATRESULT_OK:
2757 FATAL_ERROR("Unhandled chat filtering result found.");
2761 if (m_max_chatmessage_length > 0
2762 && wmessage.length() > m_max_chatmessage_length) {
2763 return L"Your message exceed the maximum chat message limit set on the server. "
2764 L"It was refused. Send a shorter message";
2767 // Run script hook, exit if script ate the chat message
2768 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2773 // Whether to send line to the player that sent the message, or to all players
2774 bool broadcast_line = true;
2776 if (check_shout_priv && !checkPriv(name, "shout")) {
2777 line += L"-!- You don't have permission to shout.";
2778 broadcast_line = false;
2787 Tell calling method to send the message to sender
2789 if (!broadcast_line)
2793 Send the message to others
2795 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2797 std::vector<u16> clients = m_clients.getClientIDs();
2800 Send the message back to the inital sender
2801 if they are using protocol version >= 29
2804 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2805 if (player && player->protocol_version >= 29)
2806 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2808 for (u16 cid : clients) {
2809 if (cid != peer_id_to_avoid_sending)
2810 SendChatMessage(cid, ChatMessage(line));
2815 void Server::handleAdminChat(const ChatEventChat *evt)
2817 std::string name = evt->nick;
2818 std::wstring wname = utf8_to_wide(name);
2819 std::wstring wmessage = evt->evt_msg;
2821 std::wstring answer = handleChat(name, wname, wmessage);
2823 // If asked to send answer to sender
2824 if (!answer.empty()) {
2825 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2829 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2831 RemoteClient *client = getClientNoEx(peer_id,state_min);
2833 throw ClientNotFoundException("Client not found");
2837 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2839 return m_clients.getClientNoEx(peer_id, state_min);
2842 std::string Server::getPlayerName(u16 peer_id)
2844 RemotePlayer *player = m_env->getPlayer(peer_id);
2846 return "[id="+itos(peer_id)+"]";
2847 return player->getName();
2850 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2852 RemotePlayer *player = m_env->getPlayer(peer_id);
2855 return player->getPlayerSAO();
2858 std::wstring Server::getStatusString()
2860 std::wostringstream os(std::ios_base::binary);
2863 os<<L"version="<<narrow_to_wide(g_version_string);
2865 os<<L", uptime="<<m_uptime.get();
2867 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2868 // Information about clients
2871 std::vector<u16> clients = m_clients.getClientIDs();
2872 for (u16 client_id : clients) {
2874 RemotePlayer *player = m_env->getPlayer(client_id);
2875 // Get name of player
2876 std::wstring name = L"unknown";
2878 name = narrow_to_wide(player->getName());
2879 // Add name to information string
2888 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2889 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2891 if (!g_settings->get("motd").empty())
2892 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2896 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2898 std::set<std::string> privs;
2899 m_script->getAuth(name, NULL, &privs);
2903 bool Server::checkPriv(const std::string &name, const std::string &priv)
2905 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2906 return (privs.count(priv) != 0);
2909 void Server::reportPrivsModified(const std::string &name)
2912 std::vector<u16> clients = m_clients.getClientIDs();
2913 for (const u16 client_id : clients) {
2914 RemotePlayer *player = m_env->getPlayer(client_id);
2915 reportPrivsModified(player->getName());
2918 RemotePlayer *player = m_env->getPlayer(name.c_str());
2921 SendPlayerPrivileges(player->peer_id);
2922 PlayerSAO *sao = player->getPlayerSAO();
2925 sao->updatePrivileges(
2926 getPlayerEffectivePrivs(name),
2931 void Server::reportInventoryFormspecModified(const std::string &name)
2933 RemotePlayer *player = m_env->getPlayer(name.c_str());
2936 SendPlayerInventoryFormspec(player->peer_id);
2939 void Server::setIpBanned(const std::string &ip, const std::string &name)
2941 m_banmanager->add(ip, name);
2944 void Server::unsetIpBanned(const std::string &ip_or_name)
2946 m_banmanager->remove(ip_or_name);
2949 std::string Server::getBanDescription(const std::string &ip_or_name)
2951 return m_banmanager->getBanDescription(ip_or_name);
2954 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2956 // m_env will be NULL if the server is initializing
2960 if (m_admin_nick == name && !m_admin_nick.empty()) {
2961 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2964 RemotePlayer *player = m_env->getPlayer(name);
2969 if (player->peer_id == PEER_ID_INEXISTENT)
2972 SendChatMessage(player->peer_id, ChatMessage(msg));
2975 bool Server::showFormspec(const char *playername, const std::string &formspec,
2976 const std::string &formname)
2978 // m_env will be NULL if the server is initializing
2982 RemotePlayer *player = m_env->getPlayer(playername);
2986 SendShowFormspecMessage(player->peer_id, formspec, formname);
2990 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
2995 u32 id = player->addHud(form);
2997 SendHUDAdd(player->peer_id, id, form);
3002 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3006 HudElement* todel = player->removeHud(id);
3013 SendHUDRemove(player->peer_id, id);
3017 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3022 SendHUDChange(player->peer_id, id, stat, data);
3026 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3031 SendHUDSetFlags(player->peer_id, flags, mask);
3032 player->hud_flags &= ~mask;
3033 player->hud_flags |= flags;
3035 PlayerSAO* playersao = player->getPlayerSAO();
3040 m_script->player_event(playersao, "hud_changed");
3044 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3049 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3052 player->setHotbarItemcount(hotbar_itemcount);
3053 std::ostringstream os(std::ios::binary);
3054 writeS32(os, hotbar_itemcount);
3055 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3059 s32 Server::hudGetHotbarItemcount(RemotePlayer *player) const
3061 return player->getHotbarItemcount();
3064 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3069 player->setHotbarImage(name);
3070 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3073 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3077 return player->getHotbarImage();
3080 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3085 player->setHotbarSelectedImage(name);
3086 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3089 const std::string& Server::hudGetHotbarSelectedImage(RemotePlayer *player) const
3091 return player->getHotbarSelectedImage();
3094 Address Server::getPeerAddress(u16 peer_id)
3096 return m_con->GetPeerAddress(peer_id);
3099 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3100 v2s32 animation_frames[4], f32 frame_speed)
3105 player->setLocalAnimations(animation_frames, frame_speed);
3106 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3110 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3115 player->eye_offset_first = first;
3116 player->eye_offset_third = third;
3117 SendEyeOffset(player->peer_id, first, third);
3121 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3122 const std::string &type, const std::vector<std::string> ¶ms,
3128 player->setSky(bgcolor, type, params, clouds);
3129 SendSetSky(player->peer_id, bgcolor, type, params, clouds);
3133 bool Server::setClouds(RemotePlayer *player, float density,
3134 const video::SColor &color_bright,
3135 const video::SColor &color_ambient,
3143 SendCloudParams(player->peer_id, density,
3144 color_bright, color_ambient, height,
3149 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3155 player->overrideDayNightRatio(do_override, ratio);
3156 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3160 void Server::notifyPlayers(const std::wstring &msg)
3162 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3165 void Server::spawnParticle(const std::string &playername, v3f pos,
3166 v3f velocity, v3f acceleration,
3167 float expirationtime, float size, bool
3168 collisiondetection, bool collision_removal,
3169 bool vertical, const std::string &texture,
3170 const struct TileAnimationParams &animation, u8 glow)
3172 // m_env will be NULL if the server is initializing
3176 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3177 if (!playername.empty()) {
3178 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3181 peer_id = player->peer_id;
3182 proto_ver = player->protocol_version;
3185 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3186 expirationtime, size, collisiondetection,
3187 collision_removal, vertical, texture, animation, glow);
3190 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3191 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3192 float minexptime, float maxexptime, float minsize, float maxsize,
3193 bool collisiondetection, bool collision_removal,
3194 ServerActiveObject *attached, bool vertical, const std::string &texture,
3195 const std::string &playername, const struct TileAnimationParams &animation,
3198 // m_env will be NULL if the server is initializing
3202 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3203 if (!playername.empty()) {
3204 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3207 peer_id = player->peer_id;
3208 proto_ver = player->protocol_version;
3211 u16 attached_id = attached ? attached->getId() : 0;
3214 if (attached_id == 0)
3215 id = m_env->addParticleSpawner(spawntime);
3217 id = m_env->addParticleSpawner(spawntime, attached_id);
3219 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3220 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3221 minexptime, maxexptime, minsize, maxsize,
3222 collisiondetection, collision_removal, attached_id, vertical,
3223 texture, id, animation, glow);
3228 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3230 // m_env will be NULL if the server is initializing
3232 throw ServerError("Can't delete particle spawners during initialisation!");
3234 u16 peer_id = PEER_ID_INEXISTENT;
3235 if (!playername.empty()) {
3236 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3239 peer_id = player->peer_id;
3242 m_env->deleteParticleSpawner(id);
3243 SendDeleteParticleSpawner(peer_id, id);
3246 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3248 if(m_detached_inventories.count(name) > 0){
3249 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3250 delete m_detached_inventories[name];
3252 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3254 Inventory *inv = new Inventory(m_itemdef);
3256 m_detached_inventories[name] = inv;
3257 m_detached_inventories_player[name] = player;
3258 //TODO find a better way to do this
3259 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3263 // actions: time-reversed list
3264 // Return value: success/failure
3265 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3266 std::list<std::string> *log)
3268 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3269 ServerMap *map = (ServerMap*)(&m_env->getMap());
3271 // Fail if no actions to handle
3272 if(actions.empty()){
3273 log->push_back("Nothing to do.");
3280 for (const RollbackAction &action : actions) {
3282 bool success = action.applyRevert(map, this, this);
3285 std::ostringstream os;
3286 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3287 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3289 log->push_back(os.str());
3291 std::ostringstream os;
3292 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3293 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3295 log->push_back(os.str());
3299 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3300 <<" failed"<<std::endl;
3302 // Call it done if less than half failed
3303 return num_failed <= num_tried/2;
3306 // IGameDef interface
3308 IItemDefManager *Server::getItemDefManager()
3313 INodeDefManager *Server::getNodeDefManager()
3318 ICraftDefManager *Server::getCraftDefManager()
3323 u16 Server::allocateUnknownNodeId(const std::string &name)
3325 return m_nodedef->allocateDummy(name);
3328 MtEventManager *Server::getEventManager()
3333 IWritableItemDefManager *Server::getWritableItemDefManager()
3338 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3343 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3348 const ModSpec *Server::getModSpec(const std::string &modname) const
3350 std::vector<ModSpec>::const_iterator it;
3351 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3352 const ModSpec &mod = *it;
3353 if (mod.name == modname)
3359 void Server::getModNames(std::vector<std::string> &modlist)
3361 std::vector<ModSpec>::iterator it;
3362 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3363 modlist.push_back(it->name);
3366 std::string Server::getBuiltinLuaPath()
3368 return porting::path_share + DIR_DELIM + "builtin";
3371 std::string Server::getModStoragePath() const
3373 return m_path_world + DIR_DELIM + "mod_storage";
3376 v3f Server::findSpawnPos()
3378 ServerMap &map = m_env->getServerMap();
3380 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3381 return nodeposf * BS;
3384 bool is_good = false;
3385 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3386 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3388 // Try to find a good place a few times
3389 for(s32 i = 0; i < 4000 && !is_good; i++) {
3390 s32 range = MYMIN(1 + i, range_max);
3391 // We're going to try to throw the player to this position
3392 v2s16 nodepos2d = v2s16(
3393 -range + (myrand() % (range * 2)),
3394 -range + (myrand() % (range * 2)));
3396 // Get spawn level at point
3397 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3398 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3399 // the mapgen to signify an unsuitable spawn position
3400 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3403 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3406 for (s32 i = 0; i < 10; i++) {
3407 v3s16 blockpos = getNodeBlockPos(nodepos);
3408 map.emergeBlock(blockpos, true);
3409 content_t c = map.getNodeNoEx(nodepos).getContent();
3410 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3412 if (air_count >= 2) {
3413 nodeposf = intToFloat(nodepos, BS);
3414 // Don't spawn the player outside map boundaries
3415 if (objectpos_over_limit(nodeposf))
3428 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3430 m_shutdown_timer = delay;
3431 m_shutdown_msg = msg;
3432 m_shutdown_ask_reconnect = reconnect;
3434 if (delay == 0.0f) {
3435 // No delay, shutdown immediately
3436 m_shutdown_requested = true;
3437 // only print to the infostream, a chat message saying
3438 // "Server Shutting Down" is sent when the server destructs.
3439 infostream << "*** Immediate Server shutdown requested." << std::endl;
3440 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3441 // Negative delay, cancel shutdown if requested
3442 m_shutdown_timer = 0.0f;
3443 m_shutdown_msg = "";
3444 m_shutdown_ask_reconnect = false;
3445 m_shutdown_requested = false;
3446 std::wstringstream ws;
3448 ws << L"*** Server shutdown canceled.";
3450 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3451 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3452 } else if (delay > 0.0f) {
3453 // Positive delay, tell the clients when the server will shut down
3454 std::wstringstream ws;
3456 ws << L"*** Server shutting down in "
3457 << duration_to_string(myround(m_shutdown_timer)).c_str()
3460 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3461 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3465 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3468 Try to get an existing player
3470 RemotePlayer *player = m_env->getPlayer(name);
3472 // If player is already connected, cancel
3473 if (player && player->peer_id != 0) {
3474 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3479 If player with the wanted peer_id already exists, cancel.
3481 if (m_env->getPlayer(peer_id)) {
3482 infostream<<"emergePlayer(): Player with wrong name but same"
3483 " peer_id already exists"<<std::endl;
3488 player = new RemotePlayer(name, idef());
3491 bool newplayer = false;
3494 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3496 // Complete init with server parts
3497 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3498 player->protocol_version = proto_version;
3502 m_script->on_newplayer(playersao);
3508 bool Server::registerModStorage(ModMetadata *storage)
3510 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3511 errorstream << "Unable to register same mod storage twice. Storage name: "
3512 << storage->getModName() << std::endl;
3516 m_mod_storages[storage->getModName()] = storage;
3520 void Server::unregisterModStorage(const std::string &name)
3522 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3523 if (it != m_mod_storages.end()) {
3524 // Save unconditionaly on unregistration
3525 it->second->save(getModStoragePath());
3526 m_mod_storages.erase(name);
3530 void dedicated_server_loop(Server &server, bool &kill)
3532 verbosestream<<"dedicated_server_loop()"<<std::endl;
3534 IntervalLimiter m_profiler_interval;
3536 static thread_local const float steplen =
3537 g_settings->getFloat("dedicated_server_step");
3538 static thread_local const float profiler_print_interval =
3539 g_settings->getFloat("profiler_print_interval");
3542 // This is kind of a hack but can be done like this
3543 // because server.step() is very light
3545 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3546 sleep_ms((int)(steplen*1000.0));
3548 server.step(steplen);
3550 if (server.getShutdownRequested() || kill)
3556 if (profiler_print_interval != 0) {
3557 if(m_profiler_interval.step(steplen, profiler_print_interval))
3559 infostream<<"Profiler:"<<std::endl;
3560 g_profiler->print(infostream);
3561 g_profiler->clear();
3566 infostream << "Dedicated server quitting" << std::endl;
3568 if (g_settings->getBool("server_announce"))
3569 ServerList::sendAnnounce(ServerList::AA_DELETE,
3570 server.m_bind_addr.getPort());