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/networkprotocol.h"
25 #include "network/serveropcodes.h"
27 #include "environment.h"
29 #include "threading/mutex_auto_lock.h"
30 #include "constants.h"
36 #include "serverobject.h"
37 #include "genericobject.h"
41 #include "scripting_server.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content_sao.h"
52 #include "event_manager.h"
53 #include "serverlist.h"
54 #include "util/string.h"
56 #include "util/serialize.h"
57 #include "util/thread.h"
58 #include "defaultsettings.h"
59 #include "util/base64.h"
60 #include "util/sha1.h"
63 #include "chatmessage.h"
65 class ClientNotFoundException : public BaseException
68 ClientNotFoundException(const char *s):
73 class ServerThread : public Thread
77 ServerThread(Server *server):
88 void *ServerThread::run()
90 DSTACK(FUNCTION_NAME);
91 BEGIN_DEBUG_EXCEPTION_HANDLER
93 m_server->AsyncRunStep(true);
95 while (!stopRequested()) {
97 //TimeTaker timer("AsyncRunStep() + Receive()");
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(""),
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 (std::map<std::string, Inventory*>::iterator
363 i = m_detached_inventories.begin();
364 i != m_detached_inventories.end(); ++i) {
369 void Server::start(Address bind_addr)
371 DSTACK(FUNCTION_NAME);
373 m_bind_addr = bind_addr;
375 infostream<<"Starting server on "
376 << bind_addr.serializeString() <<"..."<<std::endl;
378 // Stop thread if already running
381 // Initialize connection
382 m_con.SetTimeoutMs(30);
383 m_con.Serve(bind_addr);
388 // ASCII art for the win!
390 <<" .__ __ __ "<<std::endl
391 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
392 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
393 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
394 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
395 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
396 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
397 actionstream<<"Server for gameid=\""<<m_gamespec.id
398 <<"\" listening on "<<bind_addr.serializeString()<<":"
399 <<bind_addr.getPort() << "."<<std::endl;
404 DSTACK(FUNCTION_NAME);
406 infostream<<"Server: Stopping and waiting threads"<<std::endl;
408 // Stop threads (set run=false first so both start stopping)
410 //m_emergethread.setRun(false);
412 //m_emergethread.stop();
414 infostream<<"Server: Threads stopped"<<std::endl;
417 void Server::step(float dtime)
419 DSTACK(FUNCTION_NAME);
424 MutexAutoLock lock(m_step_dtime_mutex);
425 m_step_dtime += dtime;
427 // Throw if fatal error occurred in thread
428 std::string async_err = m_async_fatal_error.get();
429 if (!async_err.empty()) {
430 if (!m_simple_singleplayer_mode) {
431 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
432 g_settings->get("kick_msg_crash"),
433 g_settings->getBool("ask_reconnect_on_crash"));
435 throw ServerError("AsyncErr: " + async_err);
439 void Server::AsyncRunStep(bool initial_step)
441 DSTACK(FUNCTION_NAME);
443 g_profiler->add("Server::AsyncRunStep (num)", 1);
447 MutexAutoLock lock1(m_step_dtime_mutex);
448 dtime = m_step_dtime;
452 // Send blocks to clients
456 if((dtime < 0.001) && !initial_step)
459 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
461 //infostream<<"Server steps "<<dtime<<std::endl;
462 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
465 MutexAutoLock lock1(m_step_dtime_mutex);
466 m_step_dtime -= dtime;
473 m_uptime.set(m_uptime.get() + dtime);
479 Update time of day and overall game time
481 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
484 Send to clients at constant intervals
487 m_time_of_day_send_timer -= dtime;
488 if(m_time_of_day_send_timer < 0.0) {
489 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
490 u16 time = m_env->getTimeOfDay();
491 float time_speed = g_settings->getFloat("time_speed");
492 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
496 MutexAutoLock lock(m_env_mutex);
497 // Figure out and report maximum lag to environment
498 float max_lag = m_env->getMaxLagEstimate();
499 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
501 if(dtime > 0.1 && dtime > max_lag * 2.0)
502 infostream<<"Server: Maximum lag peaked to "<<dtime
506 m_env->reportMaxLagEstimate(max_lag);
508 ScopeProfiler sp(g_profiler, "SEnv step");
509 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
513 static const float map_timer_and_unload_dtime = 2.92;
514 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
516 MutexAutoLock lock(m_env_mutex);
517 // Run Map's timers and unload unused data
518 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
519 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
520 g_settings->getFloat("server_unload_unused_data_timeout"),
525 Listen to the admin chat, if available
528 if (!m_admin_chat->command_queue.empty()) {
529 MutexAutoLock lock(m_env_mutex);
530 while (!m_admin_chat->command_queue.empty()) {
531 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
532 handleChatInterfaceEvent(evt);
536 m_admin_chat->outgoing_queue.push_back(
537 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
544 /* Transform liquids */
545 m_liquid_transform_timer += dtime;
546 if(m_liquid_transform_timer >= m_liquid_transform_every)
548 m_liquid_transform_timer -= m_liquid_transform_every;
550 MutexAutoLock lock(m_env_mutex);
552 ScopeProfiler sp(g_profiler, "Server: liquid transform");
554 std::map<v3s16, MapBlock*> modified_blocks;
555 m_env->getMap().transformLiquids(modified_blocks, m_env);
558 Set the modified blocks unsent for all the clients
560 if(!modified_blocks.empty())
562 SetBlocksNotSent(modified_blocks);
565 m_clients.step(dtime);
567 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
569 // send masterserver announce
571 float &counter = m_masterserver_timer;
572 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
573 g_settings->getBool("server_announce")) {
574 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
575 ServerList::AA_START,
576 m_bind_addr.getPort(),
577 m_clients.getPlayerNames(),
579 m_env->getGameTime(),
582 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
592 Check added and deleted active objects
595 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
596 MutexAutoLock envlock(m_env_mutex);
599 const RemoteClientMap &clients = m_clients.getClientList();
600 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
602 // Radius inside which objects are active
603 static thread_local const s16 radius =
604 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
606 // Radius inside which players are active
607 static thread_local const bool is_transfer_limited =
608 g_settings->exists("unlimited_player_transfer_distance") &&
609 !g_settings->getBool("unlimited_player_transfer_distance");
610 static thread_local const s16 player_transfer_dist =
611 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
612 s16 player_radius = player_transfer_dist;
613 if (player_radius == 0 && is_transfer_limited)
614 player_radius = radius;
616 for (const auto &client_it : clients) {
617 RemoteClient *client = client_it.second;
619 // If definitions and textures have not been sent, don't
620 // send objects either
621 if (client->getState() < CS_DefinitionsSent)
624 RemotePlayer *player = m_env->getPlayer(client->peer_id);
626 // This can happen if the client timeouts somehow
630 PlayerSAO *playersao = player->getPlayerSAO();
634 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
635 if (my_radius <= 0) my_radius = radius;
636 //infostream << "Server: Active Radius " << my_radius << std::endl;
638 std::queue<u16> removed_objects;
639 std::queue<u16> added_objects;
640 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
641 client->m_known_objects, removed_objects);
642 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
643 client->m_known_objects, added_objects);
645 // Ignore if nothing happened
646 if (removed_objects.empty() && added_objects.empty()) {
650 std::string data_buffer;
654 // Handle removed objects
655 writeU16((u8*)buf, removed_objects.size());
656 data_buffer.append(buf, 2);
657 while (!removed_objects.empty()) {
659 u16 id = removed_objects.front();
660 ServerActiveObject* obj = m_env->getActiveObject(id);
662 // Add to data buffer for sending
663 writeU16((u8*)buf, id);
664 data_buffer.append(buf, 2);
666 // Remove from known objects
667 client->m_known_objects.erase(id);
669 if(obj && obj->m_known_by_count > 0)
670 obj->m_known_by_count--;
671 removed_objects.pop();
674 // Handle added objects
675 writeU16((u8*)buf, added_objects.size());
676 data_buffer.append(buf, 2);
677 while (!added_objects.empty()) {
679 u16 id = added_objects.front();
680 ServerActiveObject* obj = m_env->getActiveObject(id);
683 u8 type = ACTIVEOBJECT_TYPE_INVALID;
685 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
687 type = obj->getSendType();
689 // Add to data buffer for sending
690 writeU16((u8*)buf, id);
691 data_buffer.append(buf, 2);
692 writeU8((u8*)buf, type);
693 data_buffer.append(buf, 1);
696 data_buffer.append(serializeLongString(
697 obj->getClientInitializationData(client->net_proto_version)));
699 data_buffer.append(serializeLongString(""));
701 // Add to known objects
702 client->m_known_objects.insert(id);
705 obj->m_known_by_count++;
710 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
711 verbosestream << "Server: Sent object remove/add: "
712 << removed_objects.size() << " removed, "
713 << added_objects.size() << " added, "
714 << "packet size is " << pktSize << std::endl;
718 m_mod_storage_save_timer -= dtime;
719 if (m_mod_storage_save_timer <= 0.0f) {
720 infostream << "Saving registered mod storages." << std::endl;
721 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
722 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
723 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
724 if (it->second->isModified()) {
725 it->second->save(getModStoragePath());
735 MutexAutoLock envlock(m_env_mutex);
736 ScopeProfiler sp(g_profiler, "Server: sending object messages");
739 // Value = data sent by object
740 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
742 // Get active object messages from environment
744 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
748 std::vector<ActiveObjectMessage>* message_list = nullptr;
749 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
750 n = buffered_messages.find(aom.id);
751 if (n == buffered_messages.end()) {
752 message_list = new std::vector<ActiveObjectMessage>;
753 buffered_messages[aom.id] = message_list;
756 message_list = n->second;
758 message_list->push_back(aom);
762 const RemoteClientMap &clients = m_clients.getClientList();
763 // Route data to every client
764 for (const auto &client_it : clients) {
765 RemoteClient *client = client_it.second;
766 std::string reliable_data;
767 std::string unreliable_data;
768 // Go through all objects in message buffer
769 for (const auto &buffered_message : buffered_messages) {
770 // If object is not known by client, skip it
771 u16 id = buffered_message.first;
772 if (client->m_known_objects.find(id) == client->m_known_objects.end())
775 // Get message list of object
776 std::vector<ActiveObjectMessage>* list = buffered_message.second;
777 // Go through every message
778 for (const ActiveObjectMessage &aom : *list) {
779 // Compose the full new data with header
780 std::string new_data;
783 writeU16((u8*)&buf[0], aom.id);
784 new_data.append(buf, 2);
786 new_data += serializeString(aom.datastring);
787 // Add data to buffer
789 reliable_data += new_data;
791 unreliable_data += new_data;
795 reliable_data and unreliable_data are now ready.
798 if (!reliable_data.empty()) {
799 SendActiveObjectMessages(client->peer_id, reliable_data);
802 if (!unreliable_data.empty()) {
803 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
808 // Clear buffered_messages
809 for (auto &buffered_message : buffered_messages) {
810 delete buffered_message.second;
815 Send queued-for-sending map edit events.
818 // We will be accessing the environment
819 MutexAutoLock lock(m_env_mutex);
821 // Don't send too many at a time
824 // Single change sending is disabled if queue size is not small
825 bool disable_single_change_sending = false;
826 if(m_unsent_map_edit_queue.size() >= 4)
827 disable_single_change_sending = true;
829 int event_count = m_unsent_map_edit_queue.size();
831 // We'll log the amount of each
834 while (!m_unsent_map_edit_queue.empty()) {
835 MapEditEvent* event = m_unsent_map_edit_queue.front();
836 m_unsent_map_edit_queue.pop();
838 // Players far away from the change are stored here.
839 // Instead of sending the changes, MapBlocks are set not sent
841 std::vector<u16> far_players;
843 switch (event->type) {
846 prof.add("MEET_ADDNODE", 1);
847 sendAddNode(event->p, event->n, event->already_known_by_peer,
848 &far_players, disable_single_change_sending ? 5 : 30,
849 event->type == MEET_ADDNODE);
851 case MEET_REMOVENODE:
852 prof.add("MEET_REMOVENODE", 1);
853 sendRemoveNode(event->p, event->already_known_by_peer,
854 &far_players, disable_single_change_sending ? 5 : 30);
856 case MEET_BLOCK_NODE_METADATA_CHANGED:
857 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
858 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
859 setBlockNotSent(event->p);
862 infostream << "Server: MEET_OTHER" << std::endl;
863 prof.add("MEET_OTHER", 1);
864 for (const v3s16 &modified_block : event->modified_blocks) {
865 setBlockNotSent(modified_block);
869 prof.add("unknown", 1);
870 warningstream << "Server: Unknown MapEditEvent "
871 << ((u32)event->type) << std::endl;
876 Set blocks not sent to far players
878 if (!far_players.empty()) {
879 // Convert list format to that wanted by SetBlocksNotSent
880 std::map<v3s16, MapBlock*> modified_blocks2;
881 for (const v3s16 &modified_block : event->modified_blocks) {
882 modified_blocks2[modified_block] =
883 m_env->getMap().getBlockNoCreateNoEx(modified_block);
886 // Set blocks not sent
887 for (const u16 far_player : far_players) {
888 if (RemoteClient *client = getClient(far_player))
889 client->SetBlocksNotSent(modified_blocks2);
896 if (event_count >= 5) {
897 infostream << "Server: MapEditEvents:" << std::endl;
898 prof.print(infostream);
899 } else if (event_count != 0) {
900 verbosestream << "Server: MapEditEvents:" << std::endl;
901 prof.print(verbosestream);
907 Trigger emergethread (it somehow gets to a non-triggered but
908 bysy state sometimes)
911 float &counter = m_emergethread_trigger_timer;
913 if (counter >= 2.0) {
916 m_emerge->startThreads();
920 // Save map, players and auth stuff
922 float &counter = m_savemap_timer;
924 static thread_local const float save_interval =
925 g_settings->getFloat("server_map_save_interval");
926 if (counter >= save_interval) {
928 MutexAutoLock lock(m_env_mutex);
930 ScopeProfiler sp(g_profiler, "Server: saving stuff");
933 if (m_banmanager->isModified()) {
934 m_banmanager->save();
937 // Save changed parts of map
938 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
941 m_env->saveLoadedPlayers();
943 // Save environment metadata
949 static const float shutdown_msg_times[] =
951 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
954 if (m_shutdown_timer > 0.0f) {
955 // Automated messages
956 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
957 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
958 // If shutdown timer matches an automessage, shot it
959 if (m_shutdown_timer > shutdown_msg_times[i] &&
960 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
961 std::wstringstream ws;
963 ws << L"*** Server shutting down in "
964 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
967 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
968 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
974 m_shutdown_timer -= dtime;
975 if (m_shutdown_timer < 0.0f) {
976 m_shutdown_timer = 0.0f;
977 m_shutdown_requested = true;
982 void Server::Receive()
984 DSTACK(FUNCTION_NAME);
989 peer_id = pkt.getPeerId();
992 catch(con::InvalidIncomingDataException &e) {
993 infostream<<"Server::Receive(): "
994 "InvalidIncomingDataException: what()="
995 <<e.what()<<std::endl;
997 catch(SerializationError &e) {
998 infostream<<"Server::Receive(): "
999 "SerializationError: what()="
1000 <<e.what()<<std::endl;
1002 catch(ClientStateError &e) {
1003 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
1004 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
1005 L"Try reconnecting or updating your client");
1007 catch(con::PeerNotFoundException &e) {
1012 PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
1014 std::string playername;
1015 PlayerSAO *playersao = NULL;
1018 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1020 playername = client->getName();
1021 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1023 } catch (std::exception &e) {
1029 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1031 // If failed, cancel
1032 if (!playersao || !player) {
1033 if (player && player->peer_id != 0) {
1034 actionstream << "Server: Failed to emerge player \"" << playername
1035 << "\" (player allocated to an another client)" << std::endl;
1036 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1037 L"name. If your client closed unexpectedly, try again in "
1040 errorstream << "Server: " << playername << ": Failed to emerge player"
1042 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1048 Send complete position information
1050 SendMovePlayer(peer_id);
1053 SendPlayerPrivileges(peer_id);
1055 // Send inventory formspec
1056 SendPlayerInventoryFormspec(peer_id);
1059 SendInventory(playersao);
1061 // Send HP or death screen
1062 if (playersao->isDead())
1063 SendDeathscreen(peer_id, false, v3f(0,0,0));
1065 SendPlayerHPOrDie(playersao);
1068 SendPlayerBreath(playersao);
1070 // Note things in chat if not in simple singleplayer mode
1071 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1072 // Send information about server to player in chat
1073 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1075 Address addr = getPeerAddress(player->peer_id);
1076 std::string ip_str = addr.serializeString();
1077 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1082 const std::vector<std::string> &names = m_clients.getPlayerNames();
1084 actionstream << player->getName() << " joins game. List of players: ";
1086 for (const std::string &name : names) {
1087 actionstream << name << " ";
1090 actionstream << player->getName() <<std::endl;
1095 inline void Server::handleCommand(NetworkPacket* pkt)
1097 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1098 (this->*opHandle.handler)(pkt);
1101 void Server::ProcessData(NetworkPacket *pkt)
1103 DSTACK(FUNCTION_NAME);
1104 // Environment is locked first.
1105 MutexAutoLock envlock(m_env_mutex);
1107 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1108 u32 peer_id = pkt->getPeerId();
1111 Address address = getPeerAddress(peer_id);
1112 std::string addr_s = address.serializeString();
1114 if(m_banmanager->isIpBanned(addr_s)) {
1115 std::string ban_name = m_banmanager->getBanName(addr_s);
1116 infostream << "Server: A banned client tried to connect from "
1117 << addr_s << "; banned name was "
1118 << ban_name << std::endl;
1119 // This actually doesn't seem to transfer to the client
1120 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1121 + utf8_to_wide(ban_name));
1125 catch(con::PeerNotFoundException &e) {
1127 * no peer for this packet found
1128 * most common reason is peer timeout, e.g. peer didn't
1129 * respond for some time, your server was overloaded or
1132 infostream << "Server::ProcessData(): Canceling: peer "
1133 << peer_id << " not found" << std::endl;
1138 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1140 // Command must be handled into ToServerCommandHandler
1141 if (command >= TOSERVER_NUM_MSG_TYPES) {
1142 infostream << "Server: Ignoring unknown command "
1143 << command << std::endl;
1147 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1152 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1154 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1155 errorstream << "Server::ProcessData(): Cancelling: Peer"
1156 " serialization format invalid or not initialized."
1157 " Skipping incoming command=" << command << std::endl;
1161 /* Handle commands related to client startup */
1162 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1167 if (m_clients.getClientState(peer_id) < CS_Active) {
1168 if (command == TOSERVER_PLAYERPOS) return;
1170 errorstream << "Got packet command: " << command << " for peer id "
1171 << peer_id << " but client isn't active yet. Dropping packet "
1177 } catch (SendFailedException &e) {
1178 errorstream << "Server::ProcessData(): SendFailedException: "
1179 << "what=" << e.what()
1181 } catch (PacketError &e) {
1182 actionstream << "Server::ProcessData(): PacketError: "
1183 << "what=" << e.what()
1188 void Server::setTimeOfDay(u32 time)
1190 m_env->setTimeOfDay(time);
1191 m_time_of_day_send_timer = 0;
1194 void Server::onMapEditEvent(MapEditEvent *event)
1196 if(m_ignore_map_edit_events)
1198 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1200 MapEditEvent *e = event->clone();
1201 m_unsent_map_edit_queue.push(e);
1204 Inventory* Server::getInventory(const InventoryLocation &loc)
1207 case InventoryLocation::UNDEFINED:
1208 case InventoryLocation::CURRENT_PLAYER:
1210 case InventoryLocation::PLAYER:
1212 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1215 PlayerSAO *playersao = player->getPlayerSAO();
1218 return playersao->getInventory();
1221 case InventoryLocation::NODEMETA:
1223 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1226 return meta->getInventory();
1229 case InventoryLocation::DETACHED:
1231 if(m_detached_inventories.count(loc.name) == 0)
1233 return m_detached_inventories[loc.name];
1237 sanity_check(false); // abort
1242 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1245 case InventoryLocation::UNDEFINED:
1247 case InventoryLocation::PLAYER:
1252 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1257 PlayerSAO *playersao = player->getPlayerSAO();
1261 SendInventory(playersao);
1264 case InventoryLocation::NODEMETA:
1266 v3s16 blockpos = getNodeBlockPos(loc.p);
1268 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1270 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1272 setBlockNotSent(blockpos);
1275 case InventoryLocation::DETACHED:
1277 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1281 sanity_check(false); // abort
1286 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1288 std::vector<u16> clients = m_clients.getClientIDs();
1290 // Set the modified blocks unsent for all the clients
1291 for (const u16 client_id : clients) {
1292 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1293 client->SetBlocksNotSent(block);
1298 void Server::peerAdded(con::Peer *peer)
1300 DSTACK(FUNCTION_NAME);
1301 verbosestream<<"Server::peerAdded(): peer->id="
1302 <<peer->id<<std::endl;
1304 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1307 void Server::deletingPeer(con::Peer *peer, bool timeout)
1309 DSTACK(FUNCTION_NAME);
1310 verbosestream<<"Server::deletingPeer(): peer->id="
1311 <<peer->id<<", timeout="<<timeout<<std::endl;
1313 m_clients.event(peer->id, CSE_Disconnect);
1314 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1317 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
1319 *retval = m_con.getPeerStat(peer_id,type);
1320 return *retval != -1;
1323 bool Server::getClientInfo(
1332 std::string* vers_string
1335 *state = m_clients.getClientState(peer_id);
1337 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1344 *uptime = client->uptime();
1345 *ser_vers = client->serialization_version;
1346 *prot_vers = client->net_proto_version;
1348 *major = client->getMajor();
1349 *minor = client->getMinor();
1350 *patch = client->getPatch();
1351 *vers_string = client->getPatch();
1358 void Server::handlePeerChanges()
1360 while(!m_peer_change_queue.empty())
1362 con::PeerChange c = m_peer_change_queue.front();
1363 m_peer_change_queue.pop();
1365 verbosestream<<"Server: Handling peer change: "
1366 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1371 case con::PEER_ADDED:
1372 m_clients.CreateClient(c.peer_id);
1375 case con::PEER_REMOVED:
1376 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1380 FATAL_ERROR("Invalid peer change event received!");
1386 void Server::printToConsoleOnly(const std::string &text)
1389 m_admin_chat->outgoing_queue.push_back(
1390 new ChatEventChat("", utf8_to_wide(text)));
1392 std::cout << text << std::endl;
1396 void Server::Send(NetworkPacket* pkt)
1398 m_clients.send(pkt->getPeerId(),
1399 clientCommandFactoryTable[pkt->getCommand()].channel,
1401 clientCommandFactoryTable[pkt->getCommand()].reliable);
1404 void Server::SendMovement(u16 peer_id)
1406 DSTACK(FUNCTION_NAME);
1407 std::ostringstream os(std::ios_base::binary);
1409 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1411 pkt << g_settings->getFloat("movement_acceleration_default");
1412 pkt << g_settings->getFloat("movement_acceleration_air");
1413 pkt << g_settings->getFloat("movement_acceleration_fast");
1414 pkt << g_settings->getFloat("movement_speed_walk");
1415 pkt << g_settings->getFloat("movement_speed_crouch");
1416 pkt << g_settings->getFloat("movement_speed_fast");
1417 pkt << g_settings->getFloat("movement_speed_climb");
1418 pkt << g_settings->getFloat("movement_speed_jump");
1419 pkt << g_settings->getFloat("movement_liquid_fluidity");
1420 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1421 pkt << g_settings->getFloat("movement_liquid_sink");
1422 pkt << g_settings->getFloat("movement_gravity");
1427 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1429 if (!g_settings->getBool("enable_damage"))
1432 u16 peer_id = playersao->getPeerID();
1433 bool is_alive = playersao->getHP() > 0;
1436 SendPlayerHP(peer_id);
1441 void Server::SendHP(u16 peer_id, u8 hp)
1443 DSTACK(FUNCTION_NAME);
1445 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1450 void Server::SendBreath(u16 peer_id, u16 breath)
1452 DSTACK(FUNCTION_NAME);
1454 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1455 pkt << (u16) breath;
1459 void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
1460 const std::string &custom_reason, bool reconnect)
1462 assert(reason < SERVER_ACCESSDENIED_MAX);
1464 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1466 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1467 pkt << custom_reason;
1468 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1469 reason == SERVER_ACCESSDENIED_CRASH)
1470 pkt << custom_reason << (u8)reconnect;
1474 void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
1476 DSTACK(FUNCTION_NAME);
1478 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1483 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
1484 v3f camera_point_target)
1486 DSTACK(FUNCTION_NAME);
1488 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1489 pkt << set_camera_point_target << camera_point_target;
1493 void Server::SendItemDef(u16 peer_id,
1494 IItemDefManager *itemdef, u16 protocol_version)
1496 DSTACK(FUNCTION_NAME);
1498 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1502 u32 length of the next item
1503 zlib-compressed serialized ItemDefManager
1505 std::ostringstream tmp_os(std::ios::binary);
1506 itemdef->serialize(tmp_os, protocol_version);
1507 std::ostringstream tmp_os2(std::ios::binary);
1508 compressZlib(tmp_os.str(), tmp_os2);
1509 pkt.putLongString(tmp_os2.str());
1512 verbosestream << "Server: Sending item definitions to id(" << peer_id
1513 << "): size=" << pkt.getSize() << std::endl;
1518 void Server::SendNodeDef(u16 peer_id,
1519 INodeDefManager *nodedef, u16 protocol_version)
1521 DSTACK(FUNCTION_NAME);
1523 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1527 u32 length of the next item
1528 zlib-compressed serialized NodeDefManager
1530 std::ostringstream tmp_os(std::ios::binary);
1531 nodedef->serialize(tmp_os, protocol_version);
1532 std::ostringstream tmp_os2(std::ios::binary);
1533 compressZlib(tmp_os.str(), tmp_os2);
1535 pkt.putLongString(tmp_os2.str());
1538 verbosestream << "Server: Sending node definitions to id(" << peer_id
1539 << "): size=" << pkt.getSize() << std::endl;
1545 Non-static send methods
1548 void Server::SendInventory(PlayerSAO* playerSAO)
1550 DSTACK(FUNCTION_NAME);
1552 UpdateCrafting(playerSAO->getPlayer());
1558 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1560 std::ostringstream os;
1561 playerSAO->getInventory()->serialize(os);
1563 std::string s = os.str();
1565 pkt.putRawString(s.c_str(), s.size());
1569 void Server::SendChatMessage(u16 peer_id, const ChatMessage &message)
1571 DSTACK(FUNCTION_NAME);
1573 NetworkPacket legacypkt(TOCLIENT_CHAT_MESSAGE_OLD, 0, peer_id);
1574 legacypkt << message.message;
1576 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1578 u8 type = message.type;
1579 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1581 if (peer_id != PEER_ID_INEXISTENT) {
1582 RemotePlayer *player = m_env->getPlayer(peer_id);
1586 if (player->protocol_version < 35)
1591 m_clients.sendToAllCompat(&pkt, &legacypkt, 35);
1595 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
1596 const std::string &formname)
1598 DSTACK(FUNCTION_NAME);
1600 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1601 if (formspec.empty()){
1602 //the client should close the formspec
1603 pkt.putLongString("");
1605 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1612 // Spawns a particle on peer with peer_id
1613 void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
1614 v3f pos, v3f velocity, v3f acceleration,
1615 float expirationtime, float size, bool collisiondetection,
1616 bool collision_removal,
1617 bool vertical, const std::string &texture,
1618 const struct TileAnimationParams &animation, u8 glow)
1620 DSTACK(FUNCTION_NAME);
1621 static thread_local const float radius =
1622 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1624 if (peer_id == PEER_ID_INEXISTENT) {
1625 std::vector<u16> clients = m_clients.getClientIDs();
1627 for (const u16 client_id : clients) {
1628 RemotePlayer *player = m_env->getPlayer(client_id);
1632 PlayerSAO *sao = player->getPlayerSAO();
1636 // Do not send to distant clients
1637 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1640 SendSpawnParticle(client_id, player->protocol_version,
1641 pos, velocity, acceleration,
1642 expirationtime, size, collisiondetection,
1643 collision_removal, vertical, texture, animation, glow);
1648 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1650 pkt << pos << velocity << acceleration << expirationtime
1651 << size << collisiondetection;
1652 pkt.putLongString(texture);
1654 pkt << collision_removal;
1655 // This is horrible but required (why are there two ways to serialize pkts?)
1656 std::ostringstream os(std::ios_base::binary);
1657 animation.serialize(os, protocol_version);
1658 pkt.putRawString(os.str());
1664 // Adds a ParticleSpawner on peer with peer_id
1665 void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version,
1666 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1667 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1668 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1669 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1670 const struct TileAnimationParams &animation, u8 glow)
1672 DSTACK(FUNCTION_NAME);
1673 if (peer_id == PEER_ID_INEXISTENT) {
1674 // This sucks and should be replaced:
1675 std::vector<u16> clients = m_clients.getClientIDs();
1676 for (const u16 client_id : clients) {
1677 RemotePlayer *player = m_env->getPlayer(client_id);
1680 SendAddParticleSpawner(client_id, player->protocol_version,
1681 amount, spawntime, minpos, maxpos,
1682 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1683 minsize, maxsize, collisiondetection, collision_removal,
1684 attached_id, vertical, texture, id, animation, glow);
1689 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1691 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1692 << minacc << maxacc << minexptime << maxexptime << minsize
1693 << maxsize << collisiondetection;
1695 pkt.putLongString(texture);
1697 pkt << id << vertical;
1698 pkt << collision_removal;
1700 // This is horrible but required
1701 std::ostringstream os(std::ios_base::binary);
1702 animation.serialize(os, protocol_version);
1703 pkt.putRawString(os.str());
1709 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
1711 DSTACK(FUNCTION_NAME);
1713 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
1715 // Ugly error in this packet
1718 if (peer_id != PEER_ID_INEXISTENT) {
1722 m_clients.sendToAll(&pkt);
1727 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
1729 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1731 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1732 << form->text << form->number << form->item << form->dir
1733 << form->align << form->offset << form->world_pos << form->size;
1738 void Server::SendHUDRemove(u16 peer_id, u32 id)
1740 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1745 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
1747 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1748 pkt << id << (u8) stat;
1752 case HUD_STAT_SCALE:
1753 case HUD_STAT_ALIGN:
1754 case HUD_STAT_OFFSET:
1755 pkt << *(v2f *) value;
1759 pkt << *(std::string *) value;
1761 case HUD_STAT_WORLD_POS:
1762 pkt << *(v3f *) value;
1765 pkt << *(v2s32 *) value;
1767 case HUD_STAT_NUMBER:
1771 pkt << *(u32 *) value;
1778 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
1780 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1782 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1784 pkt << flags << mask;
1789 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
1791 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1792 pkt << param << value;
1796 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
1797 const std::string &type, const std::vector<std::string> ¶ms,
1800 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1801 pkt << bgcolor << type << (u16) params.size();
1803 for (const std::string ¶m : params)
1811 void Server::SendCloudParams(u16 peer_id, float density,
1812 const video::SColor &color_bright,
1813 const video::SColor &color_ambient,
1818 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1819 pkt << density << color_bright << color_ambient
1820 << height << thickness << speed;
1825 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
1828 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1831 pkt << do_override << (u16) (ratio * 65535);
1836 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
1838 DSTACK(FUNCTION_NAME);
1840 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1841 pkt << time << time_speed;
1843 if (peer_id == PEER_ID_INEXISTENT) {
1844 m_clients.sendToAll(&pkt);
1851 void Server::SendPlayerHP(u16 peer_id)
1853 DSTACK(FUNCTION_NAME);
1854 PlayerSAO *playersao = getPlayerSAO(peer_id);
1855 // In some rare case if the player is disconnected
1856 // while Lua call l_punch, for example, this can be NULL
1860 SendHP(peer_id, playersao->getHP());
1861 m_script->player_event(playersao,"health_changed");
1863 // Send to other clients
1864 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1865 ActiveObjectMessage aom(playersao->getId(), true, str);
1866 playersao->m_messages_out.push(aom);
1869 void Server::SendPlayerBreath(PlayerSAO *sao)
1871 DSTACK(FUNCTION_NAME);
1874 m_script->player_event(sao, "breath_changed");
1875 SendBreath(sao->getPeerID(), sao->getBreath());
1878 void Server::SendMovePlayer(u16 peer_id)
1880 DSTACK(FUNCTION_NAME);
1881 RemotePlayer *player = m_env->getPlayer(peer_id);
1883 PlayerSAO *sao = player->getPlayerSAO();
1886 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1887 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1890 v3f pos = sao->getBasePosition();
1891 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1892 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1893 << " pitch=" << sao->getPitch()
1894 << " yaw=" << sao->getYaw()
1901 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
1903 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1906 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1907 << animation_frames[3] << animation_speed;
1912 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
1914 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1915 pkt << first << third;
1918 void Server::SendPlayerPrivileges(u16 peer_id)
1920 RemotePlayer *player = m_env->getPlayer(peer_id);
1922 if(player->peer_id == PEER_ID_INEXISTENT)
1925 std::set<std::string> privs;
1926 m_script->getAuth(player->getName(), NULL, &privs);
1928 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1929 pkt << (u16) privs.size();
1931 for (const std::string &priv : privs) {
1938 void Server::SendPlayerInventoryFormspec(u16 peer_id)
1940 RemotePlayer *player = m_env->getPlayer(peer_id);
1942 if(player->peer_id == PEER_ID_INEXISTENT)
1945 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1946 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1950 u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
1952 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1953 pkt.putRawString(datas.c_str(), datas.size());
1955 return pkt.getSize();
1958 void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
1960 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1961 datas.size(), peer_id);
1963 pkt.putRawString(datas.c_str(), datas.size());
1965 m_clients.send(pkt.getPeerId(),
1966 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1970 void Server::SendCSMFlavourLimits(u16 peer_id)
1972 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1973 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1974 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1978 s32 Server::playSound(const SimpleSoundSpec &spec,
1979 const ServerSoundParams ¶ms)
1981 // Find out initial position of sound
1982 bool pos_exists = false;
1983 v3f pos = params.getPos(m_env, &pos_exists);
1984 // If position is not found while it should be, cancel sound
1985 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1988 // Filter destination clients
1989 std::vector<u16> dst_clients;
1990 if(!params.to_player.empty()) {
1991 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1993 infostream<<"Server::playSound: Player \""<<params.to_player
1994 <<"\" not found"<<std::endl;
1997 if(player->peer_id == PEER_ID_INEXISTENT){
1998 infostream<<"Server::playSound: Player \""<<params.to_player
1999 <<"\" not connected"<<std::endl;
2002 dst_clients.push_back(player->peer_id);
2004 std::vector<u16> clients = m_clients.getClientIDs();
2006 for (const u16 client_id : clients) {
2007 RemotePlayer *player = m_env->getPlayer(client_id);
2011 PlayerSAO *sao = player->getPlayerSAO();
2016 if(sao->getBasePosition().getDistanceFrom(pos) >
2017 params.max_hear_distance)
2020 dst_clients.push_back(client_id);
2024 if(dst_clients.empty())
2028 s32 id = m_next_sound_id++;
2029 // The sound will exist as a reference in m_playing_sounds
2030 m_playing_sounds[id] = ServerPlayingSound();
2031 ServerPlayingSound &psound = m_playing_sounds[id];
2032 psound.params = params;
2035 float gain = params.gain * spec.gain;
2036 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2037 pkt << id << spec.name << gain
2038 << (u8) params.type << pos << params.object
2039 << params.loop << params.fade << params.pitch;
2041 // Backwards compability
2042 bool play_sound = gain > 0;
2044 for (const u16 dst_client : dst_clients) {
2045 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2046 psound.clients.insert(dst_client);
2047 m_clients.send(dst_client, 0, &pkt, true);
2052 void Server::stopSound(s32 handle)
2054 // Get sound reference
2055 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2056 m_playing_sounds.find(handle);
2057 if (i == m_playing_sounds.end())
2059 ServerPlayingSound &psound = i->second;
2061 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2064 for (std::unordered_set<u16>::const_iterator si = psound.clients.begin();
2065 si != psound.clients.end(); ++si) {
2067 m_clients.send(*si, 0, &pkt, true);
2069 // Remove sound reference
2070 m_playing_sounds.erase(i);
2073 void Server::fadeSound(s32 handle, float step, float gain)
2075 // Get sound reference
2076 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2077 m_playing_sounds.find(handle);
2078 if (i == m_playing_sounds.end())
2081 ServerPlayingSound &psound = i->second;
2082 psound.params.gain = gain;
2084 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2085 pkt << handle << step << gain;
2087 // Backwards compability
2088 bool play_sound = gain > 0;
2089 ServerPlayingSound compat_psound = psound;
2090 compat_psound.clients.clear();
2092 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2093 compat_pkt << handle;
2095 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2096 it != psound.clients.end();) {
2097 if (m_clients.getProtocolVersion(*it) >= 32) {
2099 m_clients.send(*it, 0, &pkt, true);
2102 compat_psound.clients.insert(*it);
2104 m_clients.send(*it, 0, &compat_pkt, true);
2105 psound.clients.erase(it++);
2109 // Remove sound reference
2110 if (!play_sound || psound.clients.empty())
2111 m_playing_sounds.erase(i);
2113 if (play_sound && !compat_psound.clients.empty()) {
2114 // Play new sound volume on older clients
2115 playSound(compat_psound.spec, compat_psound.params);
2119 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2120 std::vector<u16> *far_players, float far_d_nodes)
2122 float maxd = far_d_nodes*BS;
2123 v3f p_f = intToFloat(p, BS);
2125 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2128 std::vector<u16> clients = m_clients.getClientIDs();
2129 for (u16 client_id : clients) {
2132 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2133 PlayerSAO *sao = player->getPlayerSAO();
2137 // If player is far away, only set modified blocks not sent
2138 v3f player_pos = sao->getBasePosition();
2139 if (player_pos.getDistanceFrom(p_f) > maxd) {
2140 far_players->push_back(client_id);
2147 m_clients.send(client_id, 0, &pkt, true);
2151 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2152 std::vector<u16> *far_players, float far_d_nodes,
2153 bool remove_metadata)
2155 float maxd = far_d_nodes*BS;
2156 v3f p_f = intToFloat(p, BS);
2158 std::vector<u16> clients = m_clients.getClientIDs();
2159 for (const u16 client_id : clients) {
2162 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2163 PlayerSAO *sao = player->getPlayerSAO();
2167 // If player is far away, only set modified blocks not sent
2168 v3f player_pos = sao->getBasePosition();
2169 if(player_pos.getDistanceFrom(p_f) > maxd) {
2170 far_players->push_back(client_id);
2176 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2178 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2180 pkt << p << n.param0 << n.param1 << n.param2
2181 << (u8) (remove_metadata ? 0 : 1);
2186 if (pkt.getSize() > 0)
2187 m_clients.send(client_id, 0, &pkt, true);
2191 void Server::setBlockNotSent(v3s16 p)
2193 std::vector<u16> clients = m_clients.getClientIDs();
2195 for (const u16 i : clients) {
2196 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2197 client->SetBlockNotSent(p);
2202 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
2204 DSTACK(FUNCTION_NAME);
2206 v3s16 p = block->getPos();
2209 Create a packet with the block in the right format
2212 std::ostringstream os(std::ios_base::binary);
2213 block->serialize(os, ver, false);
2214 block->serializeNetworkSpecific(os);
2215 std::string s = os.str();
2217 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2220 pkt.putRawString(s.c_str(), s.size());
2224 void Server::SendBlocks(float dtime)
2226 DSTACK(FUNCTION_NAME);
2228 MutexAutoLock envlock(m_env_mutex);
2229 //TODO check if one big lock could be faster then multiple small ones
2231 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2233 std::vector<PrioritySortedBlockTransfer> queue;
2235 s32 total_sending = 0;
2238 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2240 std::vector<u16> clients = m_clients.getClientIDs();
2243 for (const u16 client_id : clients) {
2244 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2249 total_sending += client->SendingCount();
2250 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2256 // Lowest priority number comes first.
2257 // Lowest is most important.
2258 std::sort(queue.begin(), queue.end());
2261 for(u32 i=0; i<queue.size(); i++)
2263 //TODO: Calculate limit dynamically
2264 if(total_sending >= g_settings->getS32
2265 ("max_simultaneous_block_sends_server_total"))
2268 PrioritySortedBlockTransfer q = queue[i];
2270 MapBlock *block = nullptr;
2272 block = m_env->getMap().getBlockNoCreate(q.pos);
2273 } catch(const InvalidPositionException &e) {
2277 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id, CS_Active);
2281 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
2283 client->SentBlock(q.pos);
2289 void Server::fillMediaCache()
2291 DSTACK(FUNCTION_NAME);
2293 infostream<<"Server: Calculating media file checksums"<<std::endl;
2295 // Collect all media file paths
2296 std::vector<std::string> paths;
2297 for (const ModSpec &mod : m_mods) {
2298 paths.push_back(mod.path + DIR_DELIM + "textures");
2299 paths.push_back(mod.path + DIR_DELIM + "sounds");
2300 paths.push_back(mod.path + DIR_DELIM + "media");
2301 paths.push_back(mod.path + DIR_DELIM + "models");
2303 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2305 // Collect media file information from paths into cache
2306 for(std::vector<std::string>::iterator i = paths.begin();
2307 i != paths.end(); ++i) {
2308 std::string mediapath = *i;
2309 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2310 for (u32 j = 0; j < dirlist.size(); j++) {
2311 if (dirlist[j].dir) // Ignode dirs
2313 std::string filename = dirlist[j].name;
2314 // If name contains illegal characters, ignore the file
2315 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2316 infostream<<"Server: ignoring illegal file name: \""
2317 << filename << "\"" << std::endl;
2320 // If name is not in a supported format, ignore it
2321 const char *supported_ext[] = {
2322 ".png", ".jpg", ".bmp", ".tga",
2323 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2325 ".x", ".b3d", ".md2", ".obj",
2328 if (removeStringEnd(filename, supported_ext).empty()){
2329 infostream << "Server: ignoring unsupported file extension: \""
2330 << filename << "\"" << std::endl;
2333 // Ok, attempt to load the file and add to cache
2334 std::string filepath = mediapath + DIR_DELIM + filename;
2336 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2338 errorstream << "Server::fillMediaCache(): Could not open \""
2339 << filename << "\" for reading" << std::endl;
2342 std::ostringstream tmp_os(std::ios_base::binary);
2346 fis.read(buf, 1024);
2347 std::streamsize len = fis.gcount();
2348 tmp_os.write(buf, len);
2357 errorstream<<"Server::fillMediaCache(): Failed to read \""
2358 << filename << "\"" << std::endl;
2361 if(tmp_os.str().length() == 0) {
2362 errorstream << "Server::fillMediaCache(): Empty file \""
2363 << filepath << "\"" << std::endl;
2368 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2370 unsigned char *digest = sha1.getDigest();
2371 std::string sha1_base64 = base64_encode(digest, 20);
2372 std::string sha1_hex = hex_encode((char*)digest, 20);
2376 m_media[filename] = MediaInfo(filepath, sha1_base64);
2377 verbosestream << "Server: " << sha1_hex << " is " << filename
2383 void Server::sendMediaAnnouncement(u16 peer_id)
2385 DSTACK(FUNCTION_NAME);
2387 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2391 std::ostringstream os(std::ios_base::binary);
2393 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2394 pkt << (u16) m_media.size();
2396 for (const auto &i : m_media) {
2397 pkt << i.first << i.second.sha1_digest;
2400 pkt << g_settings->get("remote_media");
2404 struct SendableMedia
2410 SendableMedia(const std::string &name_="", const std::string &path_="",
2411 const std::string &data_=""):
2418 void Server::sendRequestedMedia(u16 peer_id,
2419 const std::vector<std::string> &tosend)
2421 DSTACK(FUNCTION_NAME);
2423 verbosestream<<"Server::sendRequestedMedia(): "
2424 <<"Sending files to client"<<std::endl;
2428 // Put 5kB in one bunch (this is not accurate)
2429 u32 bytes_per_bunch = 5000;
2431 std::vector< std::vector<SendableMedia> > file_bunches;
2432 file_bunches.emplace_back();
2434 u32 file_size_bunch_total = 0;
2436 for (const std::string &name : tosend) {
2437 if (m_media.find(name) == m_media.end()) {
2438 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2439 <<"unknown file \""<<(name)<<"\""<<std::endl;
2443 //TODO get path + name
2444 std::string tpath = m_media[name].path;
2447 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2449 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2450 <<tpath<<"\" for reading"<<std::endl;
2453 std::ostringstream tmp_os(std::ios_base::binary);
2457 fis.read(buf, 1024);
2458 std::streamsize len = fis.gcount();
2459 tmp_os.write(buf, len);
2460 file_size_bunch_total += len;
2469 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2470 <<name<<"\""<<std::endl;
2473 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2474 <<tname<<"\""<<std::endl;*/
2476 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2478 // Start next bunch if got enough data
2479 if(file_size_bunch_total >= bytes_per_bunch) {
2480 file_bunches.emplace_back();
2481 file_size_bunch_total = 0;
2486 /* Create and send packets */
2488 u16 num_bunches = file_bunches.size();
2489 for (u16 i = 0; i < num_bunches; i++) {
2492 u16 total number of texture bunches
2493 u16 index of this bunch
2494 u32 number of files in this bunch
2503 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2504 pkt << num_bunches << i << (u32) file_bunches[i].size();
2506 for (const SendableMedia &j : file_bunches[i]) {
2508 pkt.putLongString(j.data);
2511 verbosestream << "Server::sendRequestedMedia(): bunch "
2512 << i << "/" << num_bunches
2513 << " files=" << file_bunches[i].size()
2514 << " size=" << pkt.getSize() << std::endl;
2519 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
2521 if(m_detached_inventories.count(name) == 0) {
2522 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2525 Inventory *inv = m_detached_inventories[name];
2526 std::ostringstream os(std::ios_base::binary);
2528 os << serializeString(name);
2532 std::string s = os.str();
2534 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2535 pkt.putRawString(s.c_str(), s.size());
2537 const std::string &check = m_detached_inventories_player[name];
2538 if (peer_id == PEER_ID_INEXISTENT) {
2540 return m_clients.sendToAll(&pkt);
2541 RemotePlayer *p = m_env->getPlayer(check.c_str());
2543 m_clients.send(p->peer_id, 0, &pkt, true);
2545 if (check.empty() || getPlayerName(peer_id) == check)
2550 void Server::sendDetachedInventories(u16 peer_id)
2552 DSTACK(FUNCTION_NAME);
2554 for (const auto &detached_inventory : m_detached_inventories) {
2555 const std::string &name = detached_inventory.first;
2556 //Inventory *inv = i->second;
2557 sendDetachedInventory(name, peer_id);
2565 void Server::DiePlayer(u16 peer_id)
2567 DSTACK(FUNCTION_NAME);
2568 PlayerSAO *playersao = getPlayerSAO(peer_id);
2569 // In some rare cases this can be NULL -- if the player is disconnected
2570 // when a Lua function modifies l_punch, for example
2574 infostream << "Server::DiePlayer(): Player "
2575 << playersao->getPlayer()->getName()
2576 << " dies" << std::endl;
2578 playersao->setHP(0);
2580 // Trigger scripted stuff
2581 m_script->on_dieplayer(playersao);
2583 SendPlayerHP(peer_id);
2584 SendDeathscreen(peer_id, false, v3f(0,0,0));
2587 void Server::RespawnPlayer(u16 peer_id)
2589 DSTACK(FUNCTION_NAME);
2591 PlayerSAO *playersao = getPlayerSAO(peer_id);
2594 infostream << "Server::RespawnPlayer(): Player "
2595 << playersao->getPlayer()->getName()
2596 << " respawns" << std::endl;
2598 playersao->setHP(PLAYER_MAX_HP);
2599 playersao->setBreath(PLAYER_MAX_BREATH);
2601 bool repositioned = m_script->on_respawnplayer(playersao);
2602 if (!repositioned) {
2603 // setPos will send the new position to client
2604 playersao->setPos(findSpawnPos());
2607 SendPlayerHP(peer_id);
2611 void Server::DenySudoAccess(u16 peer_id)
2613 DSTACK(FUNCTION_NAME);
2615 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2620 void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
2621 const std::string &str_reason, bool reconnect)
2623 if (proto_ver >= 25) {
2624 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2626 std::wstring wreason = utf8_to_wide(
2627 reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
2628 accessDeniedStrings[(u8)reason]);
2629 SendAccessDenied_Legacy(peer_id, wreason);
2632 m_clients.event(peer_id, CSE_SetDenied);
2633 m_con.DisconnectPeer(peer_id);
2637 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
2639 DSTACK(FUNCTION_NAME);
2641 SendAccessDenied(peer_id, reason, custom_reason);
2642 m_clients.event(peer_id, CSE_SetDenied);
2643 m_con.DisconnectPeer(peer_id);
2646 // 13/03/15: remove this function when protocol version 25 will become
2647 // the minimum version for MT users, maybe in 1 year
2648 void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
2650 DSTACK(FUNCTION_NAME);
2652 SendAccessDenied_Legacy(peer_id, reason);
2653 m_clients.event(peer_id, CSE_SetDenied);
2654 m_con.DisconnectPeer(peer_id);
2657 void Server::acceptAuth(u16 peer_id, bool forSudoMode)
2659 DSTACK(FUNCTION_NAME);
2662 RemoteClient* client = getClient(peer_id, CS_Invalid);
2664 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2666 // Right now, the auth mechs don't change between login and sudo mode.
2667 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2668 client->allowed_sudo_mechs = sudo_auth_mechs;
2670 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2671 << g_settings->getFloat("dedicated_server_step")
2675 m_clients.event(peer_id, CSE_AuthAccept);
2677 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2679 // We only support SRP right now
2680 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2682 resp_pkt << sudo_auth_mechs;
2684 m_clients.event(peer_id, CSE_SudoSuccess);
2688 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
2690 DSTACK(FUNCTION_NAME);
2691 std::wstring message;
2694 Clear references to playing sounds
2696 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2697 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2698 ServerPlayingSound &psound = i->second;
2699 psound.clients.erase(peer_id);
2700 if (psound.clients.empty())
2701 m_playing_sounds.erase(i++);
2706 RemotePlayer *player = m_env->getPlayer(peer_id);
2708 /* Run scripts and remove from environment */
2710 PlayerSAO *playersao = player->getPlayerSAO();
2713 // inform connected clients
2714 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2715 // (u16) 1 + std::string represents a vector serialization representation
2716 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2717 m_clients.sendToAll(¬ice);
2719 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2721 playersao->disconnected();
2728 if (player && reason != CDR_DENY) {
2729 std::ostringstream os(std::ios_base::binary);
2730 std::vector<u16> clients = m_clients.getClientIDs();
2732 for (const u16 client_id : clients) {
2734 RemotePlayer *player = m_env->getPlayer(client_id);
2738 // Get name of player
2739 os << player->getName() << " ";
2742 std::string name = player->getName();
2743 actionstream << name << " "
2744 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2745 << " List of players: " << os.str() << std::endl;
2747 m_admin_chat->outgoing_queue.push_back(
2748 new ChatEventNick(CET_NICK_REMOVE, name));
2752 MutexAutoLock env_lock(m_env_mutex);
2753 m_clients.DeleteClient(peer_id);
2757 // Send leave chat message to all remaining clients
2758 if (!message.empty()) {
2759 SendChatMessage(PEER_ID_INEXISTENT,
2760 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2764 void Server::UpdateCrafting(RemotePlayer *player)
2766 DSTACK(FUNCTION_NAME);
2768 // Get a preview for crafting
2770 InventoryLocation loc;
2771 loc.setPlayer(player->getName());
2772 std::vector<ItemStack> output_replacements;
2773 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2774 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2775 (&player->inventory)->getList("craft"), loc);
2777 // Put the new preview in
2778 InventoryList *plist = player->inventory.getList("craftpreview");
2779 sanity_check(plist);
2780 sanity_check(plist->getSize() >= 1);
2781 plist->changeItem(0, preview);
2784 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2786 if (evt->type == CET_NICK_ADD) {
2787 // The terminal informed us of its nick choice
2788 m_admin_nick = ((ChatEventNick *)evt)->nick;
2789 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2790 errorstream << "You haven't set up an account." << std::endl
2791 << "Please log in using the client as '"
2792 << m_admin_nick << "' with a secure password." << std::endl
2793 << "Until then, you can't execute admin tasks via the console," << std::endl
2794 << "and everybody can claim the user account instead of you," << std::endl
2795 << "giving them full control over this server." << std::endl;
2798 assert(evt->type == CET_CHAT);
2799 handleAdminChat((ChatEventChat *)evt);
2803 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2804 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2806 // If something goes wrong, this player is to blame
2807 RollbackScopeActor rollback_scope(m_rollback,
2808 std::string("player:") + name);
2810 if (g_settings->getBool("strip_color_codes"))
2811 wmessage = unescape_enriched(wmessage);
2814 switch (player->canSendChatMessage()) {
2815 case RPLAYER_CHATRESULT_FLOODING: {
2816 std::wstringstream ws;
2817 ws << L"You cannot send more messages. You are limited to "
2818 << g_settings->getFloat("chat_message_limit_per_10sec")
2819 << L" messages per 10 seconds.";
2822 case RPLAYER_CHATRESULT_KICK:
2823 DenyAccess_Legacy(player->peer_id,
2824 L"You have been kicked due to message flooding.");
2826 case RPLAYER_CHATRESULT_OK:
2829 FATAL_ERROR("Unhandled chat filtering result found.");
2833 if (m_max_chatmessage_length > 0
2834 && wmessage.length() > m_max_chatmessage_length) {
2835 return L"Your message exceed the maximum chat message limit set on the server. "
2836 L"It was refused. Send a shorter message";
2839 // Run script hook, exit if script ate the chat message
2840 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2845 // Whether to send line to the player that sent the message, or to all players
2846 bool broadcast_line = true;
2848 if (check_shout_priv && !checkPriv(name, "shout")) {
2849 line += L"-!- You don't have permission to shout.";
2850 broadcast_line = false;
2859 Tell calling method to send the message to sender
2861 if (!broadcast_line)
2865 Send the message to others
2867 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2869 std::vector<u16> clients = m_clients.getClientIDs();
2872 Send the message back to the inital sender
2873 if they are using protocol version >= 29
2876 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2877 if (player && player->protocol_version >= 29)
2878 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2880 for (u16 cid : clients) {
2881 if (cid != peer_id_to_avoid_sending)
2882 SendChatMessage(cid, ChatMessage(line));
2887 void Server::handleAdminChat(const ChatEventChat *evt)
2889 std::string name = evt->nick;
2890 std::wstring wname = utf8_to_wide(name);
2891 std::wstring wmessage = evt->evt_msg;
2893 std::wstring answer = handleChat(name, wname, wmessage);
2895 // If asked to send answer to sender
2896 if (!answer.empty()) {
2897 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2901 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2903 RemoteClient *client = getClientNoEx(peer_id,state_min);
2905 throw ClientNotFoundException("Client not found");
2909 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2911 return m_clients.getClientNoEx(peer_id, state_min);
2914 std::string Server::getPlayerName(u16 peer_id)
2916 RemotePlayer *player = m_env->getPlayer(peer_id);
2918 return "[id="+itos(peer_id)+"]";
2919 return player->getName();
2922 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2924 RemotePlayer *player = m_env->getPlayer(peer_id);
2927 return player->getPlayerSAO();
2930 std::wstring Server::getStatusString()
2932 std::wostringstream os(std::ios_base::binary);
2935 os<<L"version="<<narrow_to_wide(g_version_string);
2937 os<<L", uptime="<<m_uptime.get();
2939 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2940 // Information about clients
2943 std::vector<u16> clients = m_clients.getClientIDs();
2944 for (u16 client_id : clients) {
2946 RemotePlayer *player = m_env->getPlayer(client_id);
2947 // Get name of player
2948 std::wstring name = L"unknown";
2950 name = narrow_to_wide(player->getName());
2951 // Add name to information string
2960 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2961 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2963 if (!g_settings->get("motd").empty())
2964 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2968 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2970 std::set<std::string> privs;
2971 m_script->getAuth(name, NULL, &privs);
2975 bool Server::checkPriv(const std::string &name, const std::string &priv)
2977 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2978 return (privs.count(priv) != 0);
2981 void Server::reportPrivsModified(const std::string &name)
2984 std::vector<u16> clients = m_clients.getClientIDs();
2985 for (const u16 client_id : clients) {
2986 RemotePlayer *player = m_env->getPlayer(client_id);
2987 reportPrivsModified(player->getName());
2990 RemotePlayer *player = m_env->getPlayer(name.c_str());
2993 SendPlayerPrivileges(player->peer_id);
2994 PlayerSAO *sao = player->getPlayerSAO();
2997 sao->updatePrivileges(
2998 getPlayerEffectivePrivs(name),
3003 void Server::reportInventoryFormspecModified(const std::string &name)
3005 RemotePlayer *player = m_env->getPlayer(name.c_str());
3008 SendPlayerInventoryFormspec(player->peer_id);
3011 void Server::setIpBanned(const std::string &ip, const std::string &name)
3013 m_banmanager->add(ip, name);
3016 void Server::unsetIpBanned(const std::string &ip_or_name)
3018 m_banmanager->remove(ip_or_name);
3021 std::string Server::getBanDescription(const std::string &ip_or_name)
3023 return m_banmanager->getBanDescription(ip_or_name);
3026 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3028 // m_env will be NULL if the server is initializing
3032 if (m_admin_nick == name && !m_admin_nick.empty()) {
3033 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3036 RemotePlayer *player = m_env->getPlayer(name);
3041 if (player->peer_id == PEER_ID_INEXISTENT)
3044 SendChatMessage(player->peer_id, ChatMessage(msg));
3047 bool Server::showFormspec(const char *playername, const std::string &formspec,
3048 const std::string &formname)
3050 // m_env will be NULL if the server is initializing
3054 RemotePlayer *player = m_env->getPlayer(playername);
3058 SendShowFormspecMessage(player->peer_id, formspec, formname);
3062 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3067 u32 id = player->addHud(form);
3069 SendHUDAdd(player->peer_id, id, form);
3074 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3078 HudElement* todel = player->removeHud(id);
3085 SendHUDRemove(player->peer_id, id);
3089 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3094 SendHUDChange(player->peer_id, id, stat, data);
3098 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3103 SendHUDSetFlags(player->peer_id, flags, mask);
3104 player->hud_flags &= ~mask;
3105 player->hud_flags |= flags;
3107 PlayerSAO* playersao = player->getPlayerSAO();
3112 m_script->player_event(playersao, "hud_changed");
3116 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3121 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3124 player->setHotbarItemcount(hotbar_itemcount);
3125 std::ostringstream os(std::ios::binary);
3126 writeS32(os, hotbar_itemcount);
3127 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3131 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3136 player->setHotbarImage(name);
3137 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3140 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3144 return player->getHotbarImage();
3147 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3152 player->setHotbarSelectedImage(name);
3153 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3156 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3157 v2s32 animation_frames[4], f32 frame_speed)
3162 player->setLocalAnimations(animation_frames, frame_speed);
3163 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3167 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3172 player->eye_offset_first = first;
3173 player->eye_offset_third = third;
3174 SendEyeOffset(player->peer_id, first, third);
3178 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3179 const std::string &type, const std::vector<std::string> ¶ms,
3185 player->setSky(bgcolor, type, params, clouds);
3186 SendSetSky(player->peer_id, bgcolor, type, params, clouds);
3190 bool Server::setClouds(RemotePlayer *player, float density,
3191 const video::SColor &color_bright,
3192 const video::SColor &color_ambient,
3200 SendCloudParams(player->peer_id, density,
3201 color_bright, color_ambient, height,
3206 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3212 player->overrideDayNightRatio(do_override, ratio);
3213 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3217 void Server::notifyPlayers(const std::wstring &msg)
3219 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3222 void Server::spawnParticle(const std::string &playername, v3f pos,
3223 v3f velocity, v3f acceleration,
3224 float expirationtime, float size, bool
3225 collisiondetection, bool collision_removal,
3226 bool vertical, const std::string &texture,
3227 const struct TileAnimationParams &animation, u8 glow)
3229 // m_env will be NULL if the server is initializing
3233 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3234 if (!playername.empty()) {
3235 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3238 peer_id = player->peer_id;
3239 proto_ver = player->protocol_version;
3242 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3243 expirationtime, size, collisiondetection,
3244 collision_removal, vertical, texture, animation, glow);
3247 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3248 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3249 float minexptime, float maxexptime, float minsize, float maxsize,
3250 bool collisiondetection, bool collision_removal,
3251 ServerActiveObject *attached, bool vertical, const std::string &texture,
3252 const std::string &playername, const struct TileAnimationParams &animation,
3255 // m_env will be NULL if the server is initializing
3259 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3260 if (!playername.empty()) {
3261 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3264 peer_id = player->peer_id;
3265 proto_ver = player->protocol_version;
3268 u16 attached_id = attached ? attached->getId() : 0;
3271 if (attached_id == 0)
3272 id = m_env->addParticleSpawner(spawntime);
3274 id = m_env->addParticleSpawner(spawntime, attached_id);
3276 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3277 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3278 minexptime, maxexptime, minsize, maxsize,
3279 collisiondetection, collision_removal, attached_id, vertical,
3280 texture, id, animation, glow);
3285 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3287 // m_env will be NULL if the server is initializing
3289 throw ServerError("Can't delete particle spawners during initialisation!");
3291 u16 peer_id = PEER_ID_INEXISTENT;
3292 if (!playername.empty()) {
3293 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3296 peer_id = player->peer_id;
3299 m_env->deleteParticleSpawner(id);
3300 SendDeleteParticleSpawner(peer_id, id);
3303 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3305 if(m_detached_inventories.count(name) > 0){
3306 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3307 delete m_detached_inventories[name];
3309 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3311 Inventory *inv = new Inventory(m_itemdef);
3313 m_detached_inventories[name] = inv;
3314 m_detached_inventories_player[name] = player;
3315 //TODO find a better way to do this
3316 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3320 // actions: time-reversed list
3321 // Return value: success/failure
3322 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3323 std::list<std::string> *log)
3325 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3326 ServerMap *map = (ServerMap*)(&m_env->getMap());
3328 // Fail if no actions to handle
3329 if(actions.empty()){
3330 log->push_back("Nothing to do.");
3337 for (const RollbackAction &action : actions) {
3339 bool success = action.applyRevert(map, this, this);
3342 std::ostringstream os;
3343 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3344 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3346 log->push_back(os.str());
3348 std::ostringstream os;
3349 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3350 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3352 log->push_back(os.str());
3356 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3357 <<" failed"<<std::endl;
3359 // Call it done if less than half failed
3360 return num_failed <= num_tried/2;
3363 // IGameDef interface
3365 IItemDefManager *Server::getItemDefManager()
3370 INodeDefManager *Server::getNodeDefManager()
3375 ICraftDefManager *Server::getCraftDefManager()
3380 u16 Server::allocateUnknownNodeId(const std::string &name)
3382 return m_nodedef->allocateDummy(name);
3385 MtEventManager *Server::getEventManager()
3390 IWritableItemDefManager *Server::getWritableItemDefManager()
3395 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3400 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3405 const ModSpec *Server::getModSpec(const std::string &modname) const
3407 std::vector<ModSpec>::const_iterator it;
3408 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3409 const ModSpec &mod = *it;
3410 if (mod.name == modname)
3416 void Server::getModNames(std::vector<std::string> &modlist)
3418 std::vector<ModSpec>::iterator it;
3419 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3420 modlist.push_back(it->name);
3423 std::string Server::getBuiltinLuaPath()
3425 return porting::path_share + DIR_DELIM + "builtin";
3428 std::string Server::getModStoragePath() const
3430 return m_path_world + DIR_DELIM + "mod_storage";
3433 v3f Server::findSpawnPos()
3435 ServerMap &map = m_env->getServerMap();
3437 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3438 return nodeposf * BS;
3441 bool is_good = false;
3442 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3443 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3445 // Try to find a good place a few times
3446 for(s32 i = 0; i < 4000 && !is_good; i++) {
3447 s32 range = MYMIN(1 + i, range_max);
3448 // We're going to try to throw the player to this position
3449 v2s16 nodepos2d = v2s16(
3450 -range + (myrand() % (range * 2)),
3451 -range + (myrand() % (range * 2)));
3453 // Get spawn level at point
3454 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3455 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3456 // the mapgen to signify an unsuitable spawn position
3457 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3460 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3463 for (s32 i = 0; i < 10; i++) {
3464 v3s16 blockpos = getNodeBlockPos(nodepos);
3465 map.emergeBlock(blockpos, true);
3466 content_t c = map.getNodeNoEx(nodepos).getContent();
3467 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3469 if (air_count >= 2) {
3470 nodeposf = intToFloat(nodepos, BS);
3471 // Don't spawn the player outside map boundaries
3472 if (objectpos_over_limit(nodeposf))
3485 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3487 m_shutdown_timer = delay;
3488 m_shutdown_msg = msg;
3489 m_shutdown_ask_reconnect = reconnect;
3491 if (delay == 0.0f) {
3492 // No delay, shutdown immediately
3493 m_shutdown_requested = true;
3494 // only print to the infostream, a chat message saying
3495 // "Server Shutting Down" is sent when the server destructs.
3496 infostream << "*** Immediate Server shutdown requested." << std::endl;
3497 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3498 // Negative delay, cancel shutdown if requested
3499 m_shutdown_timer = 0.0f;
3500 m_shutdown_msg = "";
3501 m_shutdown_ask_reconnect = false;
3502 m_shutdown_requested = false;
3503 std::wstringstream ws;
3505 ws << L"*** Server shutdown canceled.";
3507 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3508 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3509 } else if (delay > 0.0f) {
3510 // Positive delay, tell the clients when the server will shut down
3511 std::wstringstream ws;
3513 ws << L"*** Server shutting down in "
3514 << duration_to_string(myround(m_shutdown_timer)).c_str()
3517 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3518 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3522 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3525 Try to get an existing player
3527 RemotePlayer *player = m_env->getPlayer(name);
3529 // If player is already connected, cancel
3530 if (player && player->peer_id != 0) {
3531 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3536 If player with the wanted peer_id already exists, cancel.
3538 if (m_env->getPlayer(peer_id)) {
3539 infostream<<"emergePlayer(): Player with wrong name but same"
3540 " peer_id already exists"<<std::endl;
3545 player = new RemotePlayer(name, idef());
3548 bool newplayer = false;
3551 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3553 // Complete init with server parts
3554 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3555 player->protocol_version = proto_version;
3559 m_script->on_newplayer(playersao);
3565 bool Server::registerModStorage(ModMetadata *storage)
3567 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3568 errorstream << "Unable to register same mod storage twice. Storage name: "
3569 << storage->getModName() << std::endl;
3573 m_mod_storages[storage->getModName()] = storage;
3577 void Server::unregisterModStorage(const std::string &name)
3579 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3580 if (it != m_mod_storages.end()) {
3581 // Save unconditionaly on unregistration
3582 it->second->save(getModStoragePath());
3583 m_mod_storages.erase(name);
3587 void dedicated_server_loop(Server &server, bool &kill)
3589 DSTACK(FUNCTION_NAME);
3591 verbosestream<<"dedicated_server_loop()"<<std::endl;
3593 IntervalLimiter m_profiler_interval;
3595 static thread_local const float steplen =
3596 g_settings->getFloat("dedicated_server_step");
3597 static thread_local const float profiler_print_interval =
3598 g_settings->getFloat("profiler_print_interval");
3601 // This is kind of a hack but can be done like this
3602 // because server.step() is very light
3604 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3605 sleep_ms((int)(steplen*1000.0));
3607 server.step(steplen);
3609 if (server.getShutdownRequested() || kill)
3615 if (profiler_print_interval != 0) {
3616 if(m_profiler_interval.step(steplen, profiler_print_interval))
3618 infostream<<"Profiler:"<<std::endl;
3619 g_profiler->print(infostream);
3620 g_profiler->clear();
3625 infostream << "Dedicated server quitting" << std::endl;
3627 if (g_settings->getBool("server_announce"))
3628 ServerList::sendAnnounce(ServerList::AA_DELETE,
3629 server.m_bind_addr.getPort());