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 u32 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->getSendingCount();
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());
2211 // Maximal total count calculation
2212 // The per-client block sends is halved with the maximal online users
2213 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2214 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2216 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2217 if (total_sending >= max_blocks_to_send)
2220 MapBlock *block = nullptr;
2222 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2223 } catch (const InvalidPositionException &e) {
2227 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2232 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2233 client->net_proto_version);
2235 client->SentBlock(block_to_send.pos);
2241 void Server::fillMediaCache()
2243 infostream<<"Server: Calculating media file checksums"<<std::endl;
2245 // Collect all media file paths
2246 std::vector<std::string> paths;
2247 for (const ModSpec &mod : m_mods) {
2248 paths.push_back(mod.path + DIR_DELIM + "textures");
2249 paths.push_back(mod.path + DIR_DELIM + "sounds");
2250 paths.push_back(mod.path + DIR_DELIM + "media");
2251 paths.push_back(mod.path + DIR_DELIM + "models");
2252 paths.push_back(mod.path + DIR_DELIM + "locale");
2254 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2256 // Collect media file information from paths into cache
2257 for (const std::string &mediapath : paths) {
2258 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2259 for (const fs::DirListNode &dln : dirlist) {
2260 if (dln.dir) // Ignode dirs
2262 std::string filename = dln.name;
2263 // If name contains illegal characters, ignore the file
2264 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2265 infostream<<"Server: ignoring illegal file name: \""
2266 << filename << "\"" << std::endl;
2269 // If name is not in a supported format, ignore it
2270 const char *supported_ext[] = {
2271 ".png", ".jpg", ".bmp", ".tga",
2272 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2274 ".x", ".b3d", ".md2", ".obj",
2275 // Custom translation file format
2279 if (removeStringEnd(filename, supported_ext).empty()){
2280 infostream << "Server: ignoring unsupported file extension: \""
2281 << filename << "\"" << std::endl;
2284 // Ok, attempt to load the file and add to cache
2285 std::string filepath;
2286 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2289 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2291 errorstream << "Server::fillMediaCache(): Could not open \""
2292 << filename << "\" for reading" << std::endl;
2295 std::ostringstream tmp_os(std::ios_base::binary);
2299 fis.read(buf, 1024);
2300 std::streamsize len = fis.gcount();
2301 tmp_os.write(buf, len);
2310 errorstream<<"Server::fillMediaCache(): Failed to read \""
2311 << filename << "\"" << std::endl;
2314 if(tmp_os.str().length() == 0) {
2315 errorstream << "Server::fillMediaCache(): Empty file \""
2316 << filepath << "\"" << std::endl;
2321 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2323 unsigned char *digest = sha1.getDigest();
2324 std::string sha1_base64 = base64_encode(digest, 20);
2325 std::string sha1_hex = hex_encode((char*)digest, 20);
2329 m_media[filename] = MediaInfo(filepath, sha1_base64);
2330 verbosestream << "Server: " << sha1_hex << " is " << filename
2336 void Server::sendMediaAnnouncement(u16 peer_id, const std::string &lang_code)
2338 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2342 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2345 std::string lang_suffix;
2346 lang_suffix.append(".").append(lang_code).append(".tr");
2347 for (const auto &i : m_media) {
2348 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2355 for (const auto &i : m_media) {
2356 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2358 pkt << i.first << i.second.sha1_digest;
2361 pkt << g_settings->get("remote_media");
2365 struct SendableMedia
2371 SendableMedia(const std::string &name_="", const std::string &path_="",
2372 const std::string &data_=""):
2379 void Server::sendRequestedMedia(u16 peer_id,
2380 const std::vector<std::string> &tosend)
2382 verbosestream<<"Server::sendRequestedMedia(): "
2383 <<"Sending files to client"<<std::endl;
2387 // Put 5kB in one bunch (this is not accurate)
2388 u32 bytes_per_bunch = 5000;
2390 std::vector< std::vector<SendableMedia> > file_bunches;
2391 file_bunches.emplace_back();
2393 u32 file_size_bunch_total = 0;
2395 for (const std::string &name : tosend) {
2396 if (m_media.find(name) == m_media.end()) {
2397 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2398 <<"unknown file \""<<(name)<<"\""<<std::endl;
2402 //TODO get path + name
2403 std::string tpath = m_media[name].path;
2406 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2408 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2409 <<tpath<<"\" for reading"<<std::endl;
2412 std::ostringstream tmp_os(std::ios_base::binary);
2416 fis.read(buf, 1024);
2417 std::streamsize len = fis.gcount();
2418 tmp_os.write(buf, len);
2419 file_size_bunch_total += len;
2428 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2429 <<name<<"\""<<std::endl;
2432 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2433 <<tname<<"\""<<std::endl;*/
2435 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2437 // Start next bunch if got enough data
2438 if(file_size_bunch_total >= bytes_per_bunch) {
2439 file_bunches.emplace_back();
2440 file_size_bunch_total = 0;
2445 /* Create and send packets */
2447 u16 num_bunches = file_bunches.size();
2448 for (u16 i = 0; i < num_bunches; i++) {
2451 u16 total number of texture bunches
2452 u16 index of this bunch
2453 u32 number of files in this bunch
2462 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2463 pkt << num_bunches << i << (u32) file_bunches[i].size();
2465 for (const SendableMedia &j : file_bunches[i]) {
2467 pkt.putLongString(j.data);
2470 verbosestream << "Server::sendRequestedMedia(): bunch "
2471 << i << "/" << num_bunches
2472 << " files=" << file_bunches[i].size()
2473 << " size=" << pkt.getSize() << std::endl;
2478 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
2480 if(m_detached_inventories.count(name) == 0) {
2481 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2484 Inventory *inv = m_detached_inventories[name];
2485 std::ostringstream os(std::ios_base::binary);
2487 os << serializeString(name);
2491 std::string s = os.str();
2493 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2494 pkt.putRawString(s.c_str(), s.size());
2496 const std::string &check = m_detached_inventories_player[name];
2497 if (peer_id == PEER_ID_INEXISTENT) {
2499 return m_clients.sendToAll(&pkt);
2500 RemotePlayer *p = m_env->getPlayer(check.c_str());
2502 m_clients.send(p->peer_id, 0, &pkt, true);
2504 if (check.empty() || getPlayerName(peer_id) == check)
2509 void Server::sendDetachedInventories(u16 peer_id)
2511 for (const auto &detached_inventory : m_detached_inventories) {
2512 const std::string &name = detached_inventory.first;
2513 //Inventory *inv = i->second;
2514 sendDetachedInventory(name, peer_id);
2522 void Server::DiePlayer(u16 peer_id)
2524 PlayerSAO *playersao = getPlayerSAO(peer_id);
2525 // In some rare cases this can be NULL -- if the player is disconnected
2526 // when a Lua function modifies l_punch, for example
2530 infostream << "Server::DiePlayer(): Player "
2531 << playersao->getPlayer()->getName()
2532 << " dies" << std::endl;
2534 playersao->setHP(0);
2536 // Trigger scripted stuff
2537 m_script->on_dieplayer(playersao);
2539 SendPlayerHP(peer_id);
2540 SendDeathscreen(peer_id, false, v3f(0,0,0));
2543 void Server::RespawnPlayer(u16 peer_id)
2545 PlayerSAO *playersao = getPlayerSAO(peer_id);
2548 infostream << "Server::RespawnPlayer(): Player "
2549 << playersao->getPlayer()->getName()
2550 << " respawns" << std::endl;
2552 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2553 playersao->setBreath(PLAYER_MAX_BREATH);
2555 bool repositioned = m_script->on_respawnplayer(playersao);
2556 if (!repositioned) {
2557 // setPos will send the new position to client
2558 playersao->setPos(findSpawnPos());
2561 SendPlayerHP(peer_id);
2565 void Server::DenySudoAccess(u16 peer_id)
2567 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2572 void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
2573 const std::string &str_reason, bool reconnect)
2575 if (proto_ver >= 25) {
2576 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2578 std::wstring wreason = utf8_to_wide(
2579 reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
2580 accessDeniedStrings[(u8)reason]);
2581 SendAccessDenied_Legacy(peer_id, wreason);
2584 m_clients.event(peer_id, CSE_SetDenied);
2585 m_con->DisconnectPeer(peer_id);
2589 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
2591 SendAccessDenied(peer_id, reason, custom_reason);
2592 m_clients.event(peer_id, CSE_SetDenied);
2593 m_con->DisconnectPeer(peer_id);
2596 // 13/03/15: remove this function when protocol version 25 will become
2597 // the minimum version for MT users, maybe in 1 year
2598 void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
2600 SendAccessDenied_Legacy(peer_id, reason);
2601 m_clients.event(peer_id, CSE_SetDenied);
2602 m_con->DisconnectPeer(peer_id);
2605 void Server::acceptAuth(u16 peer_id, bool forSudoMode)
2608 RemoteClient* client = getClient(peer_id, CS_Invalid);
2610 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2612 // Right now, the auth mechs don't change between login and sudo mode.
2613 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2614 client->allowed_sudo_mechs = sudo_auth_mechs;
2616 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2617 << g_settings->getFloat("dedicated_server_step")
2621 m_clients.event(peer_id, CSE_AuthAccept);
2623 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2625 // We only support SRP right now
2626 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2628 resp_pkt << sudo_auth_mechs;
2630 m_clients.event(peer_id, CSE_SudoSuccess);
2634 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
2636 std::wstring message;
2639 Clear references to playing sounds
2641 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2642 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2643 ServerPlayingSound &psound = i->second;
2644 psound.clients.erase(peer_id);
2645 if (psound.clients.empty())
2646 m_playing_sounds.erase(i++);
2651 RemotePlayer *player = m_env->getPlayer(peer_id);
2653 /* Run scripts and remove from environment */
2655 PlayerSAO *playersao = player->getPlayerSAO();
2658 // inform connected clients
2659 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2660 // (u16) 1 + std::string represents a vector serialization representation
2661 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2662 m_clients.sendToAll(¬ice);
2664 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2666 playersao->disconnected();
2673 if (player && reason != CDR_DENY) {
2674 std::ostringstream os(std::ios_base::binary);
2675 std::vector<u16> clients = m_clients.getClientIDs();
2677 for (const u16 client_id : clients) {
2679 RemotePlayer *player = m_env->getPlayer(client_id);
2683 // Get name of player
2684 os << player->getName() << " ";
2687 std::string name = player->getName();
2688 actionstream << name << " "
2689 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2690 << " List of players: " << os.str() << std::endl;
2692 m_admin_chat->outgoing_queue.push_back(
2693 new ChatEventNick(CET_NICK_REMOVE, name));
2697 MutexAutoLock env_lock(m_env_mutex);
2698 m_clients.DeleteClient(peer_id);
2702 // Send leave chat message to all remaining clients
2703 if (!message.empty()) {
2704 SendChatMessage(PEER_ID_INEXISTENT,
2705 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2709 void Server::UpdateCrafting(RemotePlayer *player)
2711 // Get a preview for crafting
2713 InventoryLocation loc;
2714 loc.setPlayer(player->getName());
2715 std::vector<ItemStack> output_replacements;
2716 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2717 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2718 (&player->inventory)->getList("craft"), loc);
2720 // Put the new preview in
2721 InventoryList *plist = player->inventory.getList("craftpreview");
2722 sanity_check(plist);
2723 sanity_check(plist->getSize() >= 1);
2724 plist->changeItem(0, preview);
2727 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2729 if (evt->type == CET_NICK_ADD) {
2730 // The terminal informed us of its nick choice
2731 m_admin_nick = ((ChatEventNick *)evt)->nick;
2732 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2733 errorstream << "You haven't set up an account." << std::endl
2734 << "Please log in using the client as '"
2735 << m_admin_nick << "' with a secure password." << std::endl
2736 << "Until then, you can't execute admin tasks via the console," << std::endl
2737 << "and everybody can claim the user account instead of you," << std::endl
2738 << "giving them full control over this server." << std::endl;
2741 assert(evt->type == CET_CHAT);
2742 handleAdminChat((ChatEventChat *)evt);
2746 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2747 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2749 // If something goes wrong, this player is to blame
2750 RollbackScopeActor rollback_scope(m_rollback,
2751 std::string("player:") + name);
2753 if (g_settings->getBool("strip_color_codes"))
2754 wmessage = unescape_enriched(wmessage);
2757 switch (player->canSendChatMessage()) {
2758 case RPLAYER_CHATRESULT_FLOODING: {
2759 std::wstringstream ws;
2760 ws << L"You cannot send more messages. You are limited to "
2761 << g_settings->getFloat("chat_message_limit_per_10sec")
2762 << L" messages per 10 seconds.";
2765 case RPLAYER_CHATRESULT_KICK:
2766 DenyAccess_Legacy(player->peer_id,
2767 L"You have been kicked due to message flooding.");
2769 case RPLAYER_CHATRESULT_OK:
2772 FATAL_ERROR("Unhandled chat filtering result found.");
2776 if (m_max_chatmessage_length > 0
2777 && wmessage.length() > m_max_chatmessage_length) {
2778 return L"Your message exceed the maximum chat message limit set on the server. "
2779 L"It was refused. Send a shorter message";
2782 // Run script hook, exit if script ate the chat message
2783 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2788 // Whether to send line to the player that sent the message, or to all players
2789 bool broadcast_line = true;
2791 if (check_shout_priv && !checkPriv(name, "shout")) {
2792 line += L"-!- You don't have permission to shout.";
2793 broadcast_line = false;
2802 Tell calling method to send the message to sender
2804 if (!broadcast_line)
2808 Send the message to others
2810 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2812 std::vector<u16> clients = m_clients.getClientIDs();
2815 Send the message back to the inital sender
2816 if they are using protocol version >= 29
2819 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2820 if (player && player->protocol_version >= 29)
2821 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2823 for (u16 cid : clients) {
2824 if (cid != peer_id_to_avoid_sending)
2825 SendChatMessage(cid, ChatMessage(line));
2830 void Server::handleAdminChat(const ChatEventChat *evt)
2832 std::string name = evt->nick;
2833 std::wstring wname = utf8_to_wide(name);
2834 std::wstring wmessage = evt->evt_msg;
2836 std::wstring answer = handleChat(name, wname, wmessage);
2838 // If asked to send answer to sender
2839 if (!answer.empty()) {
2840 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2844 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2846 RemoteClient *client = getClientNoEx(peer_id,state_min);
2848 throw ClientNotFoundException("Client not found");
2852 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2854 return m_clients.getClientNoEx(peer_id, state_min);
2857 std::string Server::getPlayerName(u16 peer_id)
2859 RemotePlayer *player = m_env->getPlayer(peer_id);
2861 return "[id="+itos(peer_id)+"]";
2862 return player->getName();
2865 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2867 RemotePlayer *player = m_env->getPlayer(peer_id);
2870 return player->getPlayerSAO();
2873 std::wstring Server::getStatusString()
2875 std::wostringstream os(std::ios_base::binary);
2878 os<<L"version="<<narrow_to_wide(g_version_string);
2880 os<<L", uptime="<<m_uptime.get();
2882 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2883 // Information about clients
2886 std::vector<u16> clients = m_clients.getClientIDs();
2887 for (u16 client_id : clients) {
2889 RemotePlayer *player = m_env->getPlayer(client_id);
2890 // Get name of player
2891 std::wstring name = L"unknown";
2893 name = narrow_to_wide(player->getName());
2894 // Add name to information string
2903 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2904 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2906 if (!g_settings->get("motd").empty())
2907 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2911 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2913 std::set<std::string> privs;
2914 m_script->getAuth(name, NULL, &privs);
2918 bool Server::checkPriv(const std::string &name, const std::string &priv)
2920 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2921 return (privs.count(priv) != 0);
2924 void Server::reportPrivsModified(const std::string &name)
2927 std::vector<u16> clients = m_clients.getClientIDs();
2928 for (const u16 client_id : clients) {
2929 RemotePlayer *player = m_env->getPlayer(client_id);
2930 reportPrivsModified(player->getName());
2933 RemotePlayer *player = m_env->getPlayer(name.c_str());
2936 SendPlayerPrivileges(player->peer_id);
2937 PlayerSAO *sao = player->getPlayerSAO();
2940 sao->updatePrivileges(
2941 getPlayerEffectivePrivs(name),
2946 void Server::reportInventoryFormspecModified(const std::string &name)
2948 RemotePlayer *player = m_env->getPlayer(name.c_str());
2951 SendPlayerInventoryFormspec(player->peer_id);
2954 void Server::setIpBanned(const std::string &ip, const std::string &name)
2956 m_banmanager->add(ip, name);
2959 void Server::unsetIpBanned(const std::string &ip_or_name)
2961 m_banmanager->remove(ip_or_name);
2964 std::string Server::getBanDescription(const std::string &ip_or_name)
2966 return m_banmanager->getBanDescription(ip_or_name);
2969 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2971 // m_env will be NULL if the server is initializing
2975 if (m_admin_nick == name && !m_admin_nick.empty()) {
2976 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2979 RemotePlayer *player = m_env->getPlayer(name);
2984 if (player->peer_id == PEER_ID_INEXISTENT)
2987 SendChatMessage(player->peer_id, ChatMessage(msg));
2990 bool Server::showFormspec(const char *playername, const std::string &formspec,
2991 const std::string &formname)
2993 // m_env will be NULL if the server is initializing
2997 RemotePlayer *player = m_env->getPlayer(playername);
3001 SendShowFormspecMessage(player->peer_id, formspec, formname);
3005 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3010 u32 id = player->addHud(form);
3012 SendHUDAdd(player->peer_id, id, form);
3017 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3021 HudElement* todel = player->removeHud(id);
3028 SendHUDRemove(player->peer_id, id);
3032 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3037 SendHUDChange(player->peer_id, id, stat, data);
3041 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3046 SendHUDSetFlags(player->peer_id, flags, mask);
3047 player->hud_flags &= ~mask;
3048 player->hud_flags |= flags;
3050 PlayerSAO* playersao = player->getPlayerSAO();
3055 m_script->player_event(playersao, "hud_changed");
3059 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3064 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3067 player->setHotbarItemcount(hotbar_itemcount);
3068 std::ostringstream os(std::ios::binary);
3069 writeS32(os, hotbar_itemcount);
3070 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3074 s32 Server::hudGetHotbarItemcount(RemotePlayer *player) const
3076 return player->getHotbarItemcount();
3079 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3084 player->setHotbarImage(name);
3085 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3088 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3092 return player->getHotbarImage();
3095 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3100 player->setHotbarSelectedImage(name);
3101 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3104 const std::string& Server::hudGetHotbarSelectedImage(RemotePlayer *player) const
3106 return player->getHotbarSelectedImage();
3109 Address Server::getPeerAddress(u16 peer_id)
3111 return m_con->GetPeerAddress(peer_id);
3114 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3115 v2s32 animation_frames[4], f32 frame_speed)
3120 player->setLocalAnimations(animation_frames, frame_speed);
3121 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3125 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3130 player->eye_offset_first = first;
3131 player->eye_offset_third = third;
3132 SendEyeOffset(player->peer_id, first, third);
3136 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3137 const std::string &type, const std::vector<std::string> ¶ms,
3143 player->setSky(bgcolor, type, params, clouds);
3144 SendSetSky(player->peer_id, bgcolor, type, params, clouds);
3148 bool Server::setClouds(RemotePlayer *player, float density,
3149 const video::SColor &color_bright,
3150 const video::SColor &color_ambient,
3158 SendCloudParams(player->peer_id, density,
3159 color_bright, color_ambient, height,
3164 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3170 player->overrideDayNightRatio(do_override, ratio);
3171 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3175 void Server::notifyPlayers(const std::wstring &msg)
3177 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3180 void Server::spawnParticle(const std::string &playername, v3f pos,
3181 v3f velocity, v3f acceleration,
3182 float expirationtime, float size, bool
3183 collisiondetection, bool collision_removal,
3184 bool vertical, const std::string &texture,
3185 const struct TileAnimationParams &animation, u8 glow)
3187 // m_env will be NULL if the server is initializing
3191 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3192 if (!playername.empty()) {
3193 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3196 peer_id = player->peer_id;
3197 proto_ver = player->protocol_version;
3200 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3201 expirationtime, size, collisiondetection,
3202 collision_removal, vertical, texture, animation, glow);
3205 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3206 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3207 float minexptime, float maxexptime, float minsize, float maxsize,
3208 bool collisiondetection, bool collision_removal,
3209 ServerActiveObject *attached, bool vertical, const std::string &texture,
3210 const std::string &playername, const struct TileAnimationParams &animation,
3213 // m_env will be NULL if the server is initializing
3217 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3218 if (!playername.empty()) {
3219 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3222 peer_id = player->peer_id;
3223 proto_ver = player->protocol_version;
3226 u16 attached_id = attached ? attached->getId() : 0;
3229 if (attached_id == 0)
3230 id = m_env->addParticleSpawner(spawntime);
3232 id = m_env->addParticleSpawner(spawntime, attached_id);
3234 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3235 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3236 minexptime, maxexptime, minsize, maxsize,
3237 collisiondetection, collision_removal, attached_id, vertical,
3238 texture, id, animation, glow);
3243 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3245 // m_env will be NULL if the server is initializing
3247 throw ServerError("Can't delete particle spawners during initialisation!");
3249 u16 peer_id = PEER_ID_INEXISTENT;
3250 if (!playername.empty()) {
3251 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3254 peer_id = player->peer_id;
3257 m_env->deleteParticleSpawner(id);
3258 SendDeleteParticleSpawner(peer_id, id);
3261 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3263 if(m_detached_inventories.count(name) > 0){
3264 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3265 delete m_detached_inventories[name];
3267 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3269 Inventory *inv = new Inventory(m_itemdef);
3271 m_detached_inventories[name] = inv;
3272 m_detached_inventories_player[name] = player;
3273 //TODO find a better way to do this
3274 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3278 // actions: time-reversed list
3279 // Return value: success/failure
3280 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3281 std::list<std::string> *log)
3283 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3284 ServerMap *map = (ServerMap*)(&m_env->getMap());
3286 // Fail if no actions to handle
3287 if(actions.empty()){
3288 log->push_back("Nothing to do.");
3295 for (const RollbackAction &action : actions) {
3297 bool success = action.applyRevert(map, this, this);
3300 std::ostringstream os;
3301 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3302 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3304 log->push_back(os.str());
3306 std::ostringstream os;
3307 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3308 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3310 log->push_back(os.str());
3314 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3315 <<" failed"<<std::endl;
3317 // Call it done if less than half failed
3318 return num_failed <= num_tried/2;
3321 // IGameDef interface
3323 IItemDefManager *Server::getItemDefManager()
3328 INodeDefManager *Server::getNodeDefManager()
3333 ICraftDefManager *Server::getCraftDefManager()
3338 u16 Server::allocateUnknownNodeId(const std::string &name)
3340 return m_nodedef->allocateDummy(name);
3343 MtEventManager *Server::getEventManager()
3348 IWritableItemDefManager *Server::getWritableItemDefManager()
3353 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3358 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3363 const ModSpec *Server::getModSpec(const std::string &modname) const
3365 std::vector<ModSpec>::const_iterator it;
3366 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3367 const ModSpec &mod = *it;
3368 if (mod.name == modname)
3374 void Server::getModNames(std::vector<std::string> &modlist)
3376 std::vector<ModSpec>::iterator it;
3377 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3378 modlist.push_back(it->name);
3381 std::string Server::getBuiltinLuaPath()
3383 return porting::path_share + DIR_DELIM + "builtin";
3386 std::string Server::getModStoragePath() const
3388 return m_path_world + DIR_DELIM + "mod_storage";
3391 v3f Server::findSpawnPos()
3393 ServerMap &map = m_env->getServerMap();
3395 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3396 return nodeposf * BS;
3399 bool is_good = false;
3400 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3401 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3403 // Try to find a good place a few times
3404 for(s32 i = 0; i < 4000 && !is_good; i++) {
3405 s32 range = MYMIN(1 + i, range_max);
3406 // We're going to try to throw the player to this position
3407 v2s16 nodepos2d = v2s16(
3408 -range + (myrand() % (range * 2)),
3409 -range + (myrand() % (range * 2)));
3411 // Get spawn level at point
3412 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3413 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3414 // the mapgen to signify an unsuitable spawn position
3415 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3418 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3421 for (s32 i = 0; i < 10; i++) {
3422 v3s16 blockpos = getNodeBlockPos(nodepos);
3423 map.emergeBlock(blockpos, true);
3424 content_t c = map.getNodeNoEx(nodepos).getContent();
3425 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3427 if (air_count >= 2) {
3428 nodeposf = intToFloat(nodepos, BS);
3429 // Don't spawn the player outside map boundaries
3430 if (objectpos_over_limit(nodeposf))
3443 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3445 m_shutdown_timer = delay;
3446 m_shutdown_msg = msg;
3447 m_shutdown_ask_reconnect = reconnect;
3449 if (delay == 0.0f) {
3450 // No delay, shutdown immediately
3451 m_shutdown_requested = true;
3452 // only print to the infostream, a chat message saying
3453 // "Server Shutting Down" is sent when the server destructs.
3454 infostream << "*** Immediate Server shutdown requested." << std::endl;
3455 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3456 // Negative delay, cancel shutdown if requested
3457 m_shutdown_timer = 0.0f;
3458 m_shutdown_msg = "";
3459 m_shutdown_ask_reconnect = false;
3460 m_shutdown_requested = false;
3461 std::wstringstream ws;
3463 ws << L"*** Server shutdown canceled.";
3465 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3466 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3467 } else if (delay > 0.0f) {
3468 // Positive delay, tell the clients when the server will shut down
3469 std::wstringstream ws;
3471 ws << L"*** Server shutting down in "
3472 << duration_to_string(myround(m_shutdown_timer)).c_str()
3475 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3476 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3480 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3483 Try to get an existing player
3485 RemotePlayer *player = m_env->getPlayer(name);
3487 // If player is already connected, cancel
3488 if (player && player->peer_id != 0) {
3489 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3494 If player with the wanted peer_id already exists, cancel.
3496 if (m_env->getPlayer(peer_id)) {
3497 infostream<<"emergePlayer(): Player with wrong name but same"
3498 " peer_id already exists"<<std::endl;
3503 player = new RemotePlayer(name, idef());
3506 bool newplayer = false;
3509 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3511 // Complete init with server parts
3512 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3513 player->protocol_version = proto_version;
3517 m_script->on_newplayer(playersao);
3523 bool Server::registerModStorage(ModMetadata *storage)
3525 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3526 errorstream << "Unable to register same mod storage twice. Storage name: "
3527 << storage->getModName() << std::endl;
3531 m_mod_storages[storage->getModName()] = storage;
3535 void Server::unregisterModStorage(const std::string &name)
3537 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3538 if (it != m_mod_storages.end()) {
3539 // Save unconditionaly on unregistration
3540 it->second->save(getModStoragePath());
3541 m_mod_storages.erase(name);
3545 void dedicated_server_loop(Server &server, bool &kill)
3547 verbosestream<<"dedicated_server_loop()"<<std::endl;
3549 IntervalLimiter m_profiler_interval;
3551 static thread_local const float steplen =
3552 g_settings->getFloat("dedicated_server_step");
3553 static thread_local const float profiler_print_interval =
3554 g_settings->getFloat("profiler_print_interval");
3557 // This is kind of a hack but can be done like this
3558 // because server.step() is very light
3560 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3561 sleep_ms((int)(steplen*1000.0));
3563 server.step(steplen);
3565 if (server.getShutdownRequested() || kill)
3571 if (profiler_print_interval != 0) {
3572 if(m_profiler_interval.step(steplen, profiler_print_interval))
3574 infostream<<"Profiler:"<<std::endl;
3575 g_profiler->print(infostream);
3576 g_profiler->clear();
3581 infostream << "Dedicated server quitting" << std::endl;
3583 if (g_settings->getBool("server_announce"))
3584 ServerList::sendAnnounce(ServerList::AA_DELETE,
3585 server.m_bind_addr.getPort());