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");
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 != "" && 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 == "") {
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);
1019 if (client != NULL) {
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 == NULL) || (player == NULL)) {
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;
1305 c.type = con::PEER_ADDED;
1306 c.peer_id = peer->id;
1308 m_peer_change_queue.push(c);
1311 void Server::deletingPeer(con::Peer *peer, bool timeout)
1313 DSTACK(FUNCTION_NAME);
1314 verbosestream<<"Server::deletingPeer(): peer->id="
1315 <<peer->id<<", timeout="<<timeout<<std::endl;
1317 m_clients.event(peer->id, CSE_Disconnect);
1319 c.type = con::PEER_REMOVED;
1320 c.peer_id = peer->id;
1321 c.timeout = timeout;
1322 m_peer_change_queue.push(c);
1325 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
1327 *retval = m_con.getPeerStat(peer_id,type);
1328 return *retval != -1;
1331 bool Server::getClientInfo(
1340 std::string* vers_string
1343 *state = m_clients.getClientState(peer_id);
1345 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1347 if (client == NULL) {
1352 *uptime = client->uptime();
1353 *ser_vers = client->serialization_version;
1354 *prot_vers = client->net_proto_version;
1356 *major = client->getMajor();
1357 *minor = client->getMinor();
1358 *patch = client->getPatch();
1359 *vers_string = client->getPatch();
1366 void Server::handlePeerChanges()
1368 while(!m_peer_change_queue.empty())
1370 con::PeerChange c = m_peer_change_queue.front();
1371 m_peer_change_queue.pop();
1373 verbosestream<<"Server: Handling peer change: "
1374 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1379 case con::PEER_ADDED:
1380 m_clients.CreateClient(c.peer_id);
1383 case con::PEER_REMOVED:
1384 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1388 FATAL_ERROR("Invalid peer change event received!");
1394 void Server::printToConsoleOnly(const std::string &text)
1397 m_admin_chat->outgoing_queue.push_back(
1398 new ChatEventChat("", utf8_to_wide(text)));
1400 std::cout << text << std::endl;
1404 void Server::Send(NetworkPacket* pkt)
1406 m_clients.send(pkt->getPeerId(),
1407 clientCommandFactoryTable[pkt->getCommand()].channel,
1409 clientCommandFactoryTable[pkt->getCommand()].reliable);
1412 void Server::SendMovement(u16 peer_id)
1414 DSTACK(FUNCTION_NAME);
1415 std::ostringstream os(std::ios_base::binary);
1417 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1419 pkt << g_settings->getFloat("movement_acceleration_default");
1420 pkt << g_settings->getFloat("movement_acceleration_air");
1421 pkt << g_settings->getFloat("movement_acceleration_fast");
1422 pkt << g_settings->getFloat("movement_speed_walk");
1423 pkt << g_settings->getFloat("movement_speed_crouch");
1424 pkt << g_settings->getFloat("movement_speed_fast");
1425 pkt << g_settings->getFloat("movement_speed_climb");
1426 pkt << g_settings->getFloat("movement_speed_jump");
1427 pkt << g_settings->getFloat("movement_liquid_fluidity");
1428 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1429 pkt << g_settings->getFloat("movement_liquid_sink");
1430 pkt << g_settings->getFloat("movement_gravity");
1435 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1437 if (!g_settings->getBool("enable_damage"))
1440 u16 peer_id = playersao->getPeerID();
1441 bool is_alive = playersao->getHP() > 0;
1444 SendPlayerHP(peer_id);
1449 void Server::SendHP(u16 peer_id, u8 hp)
1451 DSTACK(FUNCTION_NAME);
1453 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1458 void Server::SendBreath(u16 peer_id, u16 breath)
1460 DSTACK(FUNCTION_NAME);
1462 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1463 pkt << (u16) breath;
1467 void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
1468 const std::string &custom_reason, bool reconnect)
1470 assert(reason < SERVER_ACCESSDENIED_MAX);
1472 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1474 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1475 pkt << custom_reason;
1476 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1477 reason == SERVER_ACCESSDENIED_CRASH)
1478 pkt << custom_reason << (u8)reconnect;
1482 void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
1484 DSTACK(FUNCTION_NAME);
1486 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1491 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
1492 v3f camera_point_target)
1494 DSTACK(FUNCTION_NAME);
1496 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1497 pkt << set_camera_point_target << camera_point_target;
1501 void Server::SendItemDef(u16 peer_id,
1502 IItemDefManager *itemdef, u16 protocol_version)
1504 DSTACK(FUNCTION_NAME);
1506 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1510 u32 length of the next item
1511 zlib-compressed serialized ItemDefManager
1513 std::ostringstream tmp_os(std::ios::binary);
1514 itemdef->serialize(tmp_os, protocol_version);
1515 std::ostringstream tmp_os2(std::ios::binary);
1516 compressZlib(tmp_os.str(), tmp_os2);
1517 pkt.putLongString(tmp_os2.str());
1520 verbosestream << "Server: Sending item definitions to id(" << peer_id
1521 << "): size=" << pkt.getSize() << std::endl;
1526 void Server::SendNodeDef(u16 peer_id,
1527 INodeDefManager *nodedef, u16 protocol_version)
1529 DSTACK(FUNCTION_NAME);
1531 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1535 u32 length of the next item
1536 zlib-compressed serialized NodeDefManager
1538 std::ostringstream tmp_os(std::ios::binary);
1539 nodedef->serialize(tmp_os, protocol_version);
1540 std::ostringstream tmp_os2(std::ios::binary);
1541 compressZlib(tmp_os.str(), tmp_os2);
1543 pkt.putLongString(tmp_os2.str());
1546 verbosestream << "Server: Sending node definitions to id(" << peer_id
1547 << "): size=" << pkt.getSize() << std::endl;
1553 Non-static send methods
1556 void Server::SendInventory(PlayerSAO* playerSAO)
1558 DSTACK(FUNCTION_NAME);
1560 UpdateCrafting(playerSAO->getPlayer());
1566 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1568 std::ostringstream os;
1569 playerSAO->getInventory()->serialize(os);
1571 std::string s = os.str();
1573 pkt.putRawString(s.c_str(), s.size());
1577 void Server::SendChatMessage(u16 peer_id, const ChatMessage &message)
1579 DSTACK(FUNCTION_NAME);
1581 NetworkPacket legacypkt(TOCLIENT_CHAT_MESSAGE_OLD, 0, peer_id);
1582 legacypkt << message.message;
1584 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1586 u8 type = message.type;
1587 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1589 if (peer_id != PEER_ID_INEXISTENT) {
1590 RemotePlayer *player = m_env->getPlayer(peer_id);
1594 if (player->protocol_version < 35)
1599 m_clients.sendToAllCompat(&pkt, &legacypkt, 35);
1603 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
1604 const std::string &formname)
1606 DSTACK(FUNCTION_NAME);
1608 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1609 if (formspec.empty()){
1610 //the client should close the formspec
1611 pkt.putLongString("");
1613 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1620 // Spawns a particle on peer with peer_id
1621 void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
1622 v3f pos, v3f velocity, v3f acceleration,
1623 float expirationtime, float size, bool collisiondetection,
1624 bool collision_removal,
1625 bool vertical, const std::string &texture,
1626 const struct TileAnimationParams &animation, u8 glow)
1628 DSTACK(FUNCTION_NAME);
1629 static thread_local const float radius =
1630 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1632 if (peer_id == PEER_ID_INEXISTENT) {
1633 std::vector<u16> clients = m_clients.getClientIDs();
1635 for (const u16 client_id : clients) {
1636 RemotePlayer *player = m_env->getPlayer(client_id);
1640 PlayerSAO *sao = player->getPlayerSAO();
1644 // Do not send to distant clients
1645 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1648 SendSpawnParticle(client_id, player->protocol_version,
1649 pos, velocity, acceleration,
1650 expirationtime, size, collisiondetection,
1651 collision_removal, vertical, texture, animation, glow);
1656 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1658 pkt << pos << velocity << acceleration << expirationtime
1659 << size << collisiondetection;
1660 pkt.putLongString(texture);
1662 pkt << collision_removal;
1663 // This is horrible but required (why are there two ways to serialize pkts?)
1664 std::ostringstream os(std::ios_base::binary);
1665 animation.serialize(os, protocol_version);
1666 pkt.putRawString(os.str());
1672 // Adds a ParticleSpawner on peer with peer_id
1673 void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version,
1674 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1675 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1676 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1677 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1678 const struct TileAnimationParams &animation, u8 glow)
1680 DSTACK(FUNCTION_NAME);
1681 if (peer_id == PEER_ID_INEXISTENT) {
1682 // This sucks and should be replaced:
1683 std::vector<u16> clients = m_clients.getClientIDs();
1684 for (const u16 client_id : clients) {
1685 RemotePlayer *player = m_env->getPlayer(client_id);
1688 SendAddParticleSpawner(client_id, player->protocol_version,
1689 amount, spawntime, minpos, maxpos,
1690 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1691 minsize, maxsize, collisiondetection, collision_removal,
1692 attached_id, vertical, texture, id, animation, glow);
1697 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1699 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1700 << minacc << maxacc << minexptime << maxexptime << minsize
1701 << maxsize << collisiondetection;
1703 pkt.putLongString(texture);
1705 pkt << id << vertical;
1706 pkt << collision_removal;
1708 // This is horrible but required
1709 std::ostringstream os(std::ios_base::binary);
1710 animation.serialize(os, protocol_version);
1711 pkt.putRawString(os.str());
1717 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
1719 DSTACK(FUNCTION_NAME);
1721 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
1723 // Ugly error in this packet
1726 if (peer_id != PEER_ID_INEXISTENT) {
1730 m_clients.sendToAll(&pkt);
1735 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
1737 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1739 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1740 << form->text << form->number << form->item << form->dir
1741 << form->align << form->offset << form->world_pos << form->size;
1746 void Server::SendHUDRemove(u16 peer_id, u32 id)
1748 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1753 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
1755 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1756 pkt << id << (u8) stat;
1760 case HUD_STAT_SCALE:
1761 case HUD_STAT_ALIGN:
1762 case HUD_STAT_OFFSET:
1763 pkt << *(v2f *) value;
1767 pkt << *(std::string *) value;
1769 case HUD_STAT_WORLD_POS:
1770 pkt << *(v3f *) value;
1773 pkt << *(v2s32 *) value;
1775 case HUD_STAT_NUMBER:
1779 pkt << *(u32 *) value;
1786 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
1788 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1790 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1792 pkt << flags << mask;
1797 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
1799 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1800 pkt << param << value;
1804 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
1805 const std::string &type, const std::vector<std::string> ¶ms,
1808 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1809 pkt << bgcolor << type << (u16) params.size();
1811 for (const std::string ¶m : params)
1819 void Server::SendCloudParams(u16 peer_id, float density,
1820 const video::SColor &color_bright,
1821 const video::SColor &color_ambient,
1826 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1827 pkt << density << color_bright << color_ambient
1828 << height << thickness << speed;
1833 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
1836 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1839 pkt << do_override << (u16) (ratio * 65535);
1844 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
1846 DSTACK(FUNCTION_NAME);
1848 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1849 pkt << time << time_speed;
1851 if (peer_id == PEER_ID_INEXISTENT) {
1852 m_clients.sendToAll(&pkt);
1859 void Server::SendPlayerHP(u16 peer_id)
1861 DSTACK(FUNCTION_NAME);
1862 PlayerSAO *playersao = getPlayerSAO(peer_id);
1863 // In some rare case if the player is disconnected
1864 // while Lua call l_punch, for example, this can be NULL
1868 SendHP(peer_id, playersao->getHP());
1869 m_script->player_event(playersao,"health_changed");
1871 // Send to other clients
1872 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1873 ActiveObjectMessage aom(playersao->getId(), true, str);
1874 playersao->m_messages_out.push(aom);
1877 void Server::SendPlayerBreath(PlayerSAO *sao)
1879 DSTACK(FUNCTION_NAME);
1882 m_script->player_event(sao, "breath_changed");
1883 SendBreath(sao->getPeerID(), sao->getBreath());
1886 void Server::SendMovePlayer(u16 peer_id)
1888 DSTACK(FUNCTION_NAME);
1889 RemotePlayer *player = m_env->getPlayer(peer_id);
1891 PlayerSAO *sao = player->getPlayerSAO();
1894 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1895 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1898 v3f pos = sao->getBasePosition();
1899 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1900 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1901 << " pitch=" << sao->getPitch()
1902 << " yaw=" << sao->getYaw()
1909 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
1911 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1914 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1915 << animation_frames[3] << animation_speed;
1920 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
1922 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1923 pkt << first << third;
1926 void Server::SendPlayerPrivileges(u16 peer_id)
1928 RemotePlayer *player = m_env->getPlayer(peer_id);
1930 if(player->peer_id == PEER_ID_INEXISTENT)
1933 std::set<std::string> privs;
1934 m_script->getAuth(player->getName(), NULL, &privs);
1936 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1937 pkt << (u16) privs.size();
1939 for (const std::string &priv : privs) {
1946 void Server::SendPlayerInventoryFormspec(u16 peer_id)
1948 RemotePlayer *player = m_env->getPlayer(peer_id);
1950 if(player->peer_id == PEER_ID_INEXISTENT)
1953 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1954 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1958 u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
1960 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1961 pkt.putRawString(datas.c_str(), datas.size());
1963 return pkt.getSize();
1966 void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
1968 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1969 datas.size(), peer_id);
1971 pkt.putRawString(datas.c_str(), datas.size());
1973 m_clients.send(pkt.getPeerId(),
1974 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1978 void Server::SendCSMFlavourLimits(u16 peer_id)
1980 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1981 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1982 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1986 s32 Server::playSound(const SimpleSoundSpec &spec,
1987 const ServerSoundParams ¶ms)
1989 // Find out initial position of sound
1990 bool pos_exists = false;
1991 v3f pos = params.getPos(m_env, &pos_exists);
1992 // If position is not found while it should be, cancel sound
1993 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1996 // Filter destination clients
1997 std::vector<u16> dst_clients;
1998 if(!params.to_player.empty()) {
1999 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2001 infostream<<"Server::playSound: Player \""<<params.to_player
2002 <<"\" not found"<<std::endl;
2005 if(player->peer_id == PEER_ID_INEXISTENT){
2006 infostream<<"Server::playSound: Player \""<<params.to_player
2007 <<"\" not connected"<<std::endl;
2010 dst_clients.push_back(player->peer_id);
2012 std::vector<u16> clients = m_clients.getClientIDs();
2014 for (const u16 client_id : clients) {
2015 RemotePlayer *player = m_env->getPlayer(client_id);
2019 PlayerSAO *sao = player->getPlayerSAO();
2024 if(sao->getBasePosition().getDistanceFrom(pos) >
2025 params.max_hear_distance)
2028 dst_clients.push_back(client_id);
2032 if(dst_clients.empty())
2036 s32 id = m_next_sound_id++;
2037 // The sound will exist as a reference in m_playing_sounds
2038 m_playing_sounds[id] = ServerPlayingSound();
2039 ServerPlayingSound &psound = m_playing_sounds[id];
2040 psound.params = params;
2043 float gain = params.gain * spec.gain;
2044 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2045 pkt << id << spec.name << gain
2046 << (u8) params.type << pos << params.object
2047 << params.loop << params.fade << params.pitch;
2049 // Backwards compability
2050 bool play_sound = gain > 0;
2052 for (const u16 dst_client : dst_clients) {
2053 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2054 psound.clients.insert(dst_client);
2055 m_clients.send(dst_client, 0, &pkt, true);
2060 void Server::stopSound(s32 handle)
2062 // Get sound reference
2063 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2064 m_playing_sounds.find(handle);
2065 if (i == m_playing_sounds.end())
2067 ServerPlayingSound &psound = i->second;
2069 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2072 for (std::unordered_set<u16>::const_iterator si = psound.clients.begin();
2073 si != psound.clients.end(); ++si) {
2075 m_clients.send(*si, 0, &pkt, true);
2077 // Remove sound reference
2078 m_playing_sounds.erase(i);
2081 void Server::fadeSound(s32 handle, float step, float gain)
2083 // Get sound reference
2084 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2085 m_playing_sounds.find(handle);
2086 if (i == m_playing_sounds.end())
2089 ServerPlayingSound &psound = i->second;
2090 psound.params.gain = gain;
2092 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2093 pkt << handle << step << gain;
2095 // Backwards compability
2096 bool play_sound = gain > 0;
2097 ServerPlayingSound compat_psound = psound;
2098 compat_psound.clients.clear();
2100 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2101 compat_pkt << handle;
2103 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2104 it != psound.clients.end();) {
2105 if (m_clients.getProtocolVersion(*it) >= 32) {
2107 m_clients.send(*it, 0, &pkt, true);
2110 compat_psound.clients.insert(*it);
2112 m_clients.send(*it, 0, &compat_pkt, true);
2113 psound.clients.erase(it++);
2117 // Remove sound reference
2118 if (!play_sound || psound.clients.empty())
2119 m_playing_sounds.erase(i);
2121 if (play_sound && !compat_psound.clients.empty()) {
2122 // Play new sound volume on older clients
2123 playSound(compat_psound.spec, compat_psound.params);
2127 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2128 std::vector<u16> *far_players, float far_d_nodes)
2130 float maxd = far_d_nodes*BS;
2131 v3f p_f = intToFloat(p, BS);
2133 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2136 std::vector<u16> clients = m_clients.getClientIDs();
2137 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2140 if (RemotePlayer *player = m_env->getPlayer(*i)) {
2141 PlayerSAO *sao = player->getPlayerSAO();
2145 // If player is far away, only set modified blocks not sent
2146 v3f player_pos = sao->getBasePosition();
2147 if (player_pos.getDistanceFrom(p_f) > maxd) {
2148 far_players->push_back(*i);
2155 m_clients.send(*i, 0, &pkt, true);
2159 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2160 std::vector<u16> *far_players, float far_d_nodes,
2161 bool remove_metadata)
2163 float maxd = far_d_nodes*BS;
2164 v3f p_f = intToFloat(p, BS);
2166 std::vector<u16> clients = m_clients.getClientIDs();
2167 for (const u16 client_id : clients) {
2170 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2171 PlayerSAO *sao = player->getPlayerSAO();
2175 // If player is far away, only set modified blocks not sent
2176 v3f player_pos = sao->getBasePosition();
2177 if(player_pos.getDistanceFrom(p_f) > maxd) {
2178 far_players->push_back(client_id);
2184 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2186 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2188 pkt << p << n.param0 << n.param1 << n.param2
2189 << (u8) (remove_metadata ? 0 : 1);
2194 if (pkt.getSize() > 0)
2195 m_clients.send(client_id, 0, &pkt, true);
2199 void Server::setBlockNotSent(v3s16 p)
2201 std::vector<u16> clients = m_clients.getClientIDs();
2203 for (const u16 i : clients) {
2204 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2205 client->SetBlockNotSent(p);
2210 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
2212 DSTACK(FUNCTION_NAME);
2214 v3s16 p = block->getPos();
2217 Create a packet with the block in the right format
2220 std::ostringstream os(std::ios_base::binary);
2221 block->serialize(os, ver, false);
2222 block->serializeNetworkSpecific(os);
2223 std::string s = os.str();
2225 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2228 pkt.putRawString(s.c_str(), s.size());
2232 void Server::SendBlocks(float dtime)
2234 DSTACK(FUNCTION_NAME);
2236 MutexAutoLock envlock(m_env_mutex);
2237 //TODO check if one big lock could be faster then multiple small ones
2239 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2241 std::vector<PrioritySortedBlockTransfer> queue;
2243 s32 total_sending = 0;
2246 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2248 std::vector<u16> clients = m_clients.getClientIDs();
2251 for (const u16 client_id : clients) {
2252 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2257 total_sending += client->SendingCount();
2258 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2264 // Lowest priority number comes first.
2265 // Lowest is most important.
2266 std::sort(queue.begin(), queue.end());
2269 for(u32 i=0; i<queue.size(); i++)
2271 //TODO: Calculate limit dynamically
2272 if(total_sending >= g_settings->getS32
2273 ("max_simultaneous_block_sends_server_total"))
2276 PrioritySortedBlockTransfer q = queue[i];
2278 MapBlock *block = NULL;
2281 block = m_env->getMap().getBlockNoCreate(q.pos);
2283 catch(InvalidPositionException &e)
2288 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id, CS_Active);
2293 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
2295 client->SentBlock(q.pos);
2301 void Server::fillMediaCache()
2303 DSTACK(FUNCTION_NAME);
2305 infostream<<"Server: Calculating media file checksums"<<std::endl;
2307 // Collect all media file paths
2308 std::vector<std::string> paths;
2309 for (const ModSpec &mod : m_mods) {
2310 paths.push_back(mod.path + DIR_DELIM + "textures");
2311 paths.push_back(mod.path + DIR_DELIM + "sounds");
2312 paths.push_back(mod.path + DIR_DELIM + "media");
2313 paths.push_back(mod.path + DIR_DELIM + "models");
2315 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2317 // Collect media file information from paths into cache
2318 for(std::vector<std::string>::iterator i = paths.begin();
2319 i != paths.end(); ++i) {
2320 std::string mediapath = *i;
2321 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2322 for (u32 j = 0; j < dirlist.size(); j++) {
2323 if (dirlist[j].dir) // Ignode dirs
2325 std::string filename = dirlist[j].name;
2326 // If name contains illegal characters, ignore the file
2327 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2328 infostream<<"Server: ignoring illegal file name: \""
2329 << filename << "\"" << std::endl;
2332 // If name is not in a supported format, ignore it
2333 const char *supported_ext[] = {
2334 ".png", ".jpg", ".bmp", ".tga",
2335 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2337 ".x", ".b3d", ".md2", ".obj",
2340 if (removeStringEnd(filename, supported_ext).empty()){
2341 infostream << "Server: ignoring unsupported file extension: \""
2342 << filename << "\"" << std::endl;
2345 // Ok, attempt to load the file and add to cache
2346 std::string filepath = mediapath + DIR_DELIM + filename;
2348 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2350 errorstream << "Server::fillMediaCache(): Could not open \""
2351 << filename << "\" for reading" << std::endl;
2354 std::ostringstream tmp_os(std::ios_base::binary);
2358 fis.read(buf, 1024);
2359 std::streamsize len = fis.gcount();
2360 tmp_os.write(buf, len);
2369 errorstream<<"Server::fillMediaCache(): Failed to read \""
2370 << filename << "\"" << std::endl;
2373 if(tmp_os.str().length() == 0) {
2374 errorstream << "Server::fillMediaCache(): Empty file \""
2375 << filepath << "\"" << std::endl;
2380 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2382 unsigned char *digest = sha1.getDigest();
2383 std::string sha1_base64 = base64_encode(digest, 20);
2384 std::string sha1_hex = hex_encode((char*)digest, 20);
2388 m_media[filename] = MediaInfo(filepath, sha1_base64);
2389 verbosestream << "Server: " << sha1_hex << " is " << filename
2395 void Server::sendMediaAnnouncement(u16 peer_id)
2397 DSTACK(FUNCTION_NAME);
2399 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2403 std::ostringstream os(std::ios_base::binary);
2405 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2406 pkt << (u16) m_media.size();
2408 for (const auto &i : m_media) {
2409 pkt << i.first << i.second.sha1_digest;
2412 pkt << g_settings->get("remote_media");
2416 struct SendableMedia
2422 SendableMedia(const std::string &name_="", const std::string &path_="",
2423 const std::string &data_=""):
2430 void Server::sendRequestedMedia(u16 peer_id,
2431 const std::vector<std::string> &tosend)
2433 DSTACK(FUNCTION_NAME);
2435 verbosestream<<"Server::sendRequestedMedia(): "
2436 <<"Sending files to client"<<std::endl;
2440 // Put 5kB in one bunch (this is not accurate)
2441 u32 bytes_per_bunch = 5000;
2443 std::vector< std::vector<SendableMedia> > file_bunches;
2444 file_bunches.emplace_back();
2446 u32 file_size_bunch_total = 0;
2448 for (const std::string &name : tosend) {
2449 if (m_media.find(name) == m_media.end()) {
2450 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2451 <<"unknown file \""<<(name)<<"\""<<std::endl;
2455 //TODO get path + name
2456 std::string tpath = m_media[name].path;
2459 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2461 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2462 <<tpath<<"\" for reading"<<std::endl;
2465 std::ostringstream tmp_os(std::ios_base::binary);
2469 fis.read(buf, 1024);
2470 std::streamsize len = fis.gcount();
2471 tmp_os.write(buf, len);
2472 file_size_bunch_total += len;
2481 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2482 <<name<<"\""<<std::endl;
2485 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2486 <<tname<<"\""<<std::endl;*/
2488 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2490 // Start next bunch if got enough data
2491 if(file_size_bunch_total >= bytes_per_bunch) {
2492 file_bunches.emplace_back();
2493 file_size_bunch_total = 0;
2498 /* Create and send packets */
2500 u16 num_bunches = file_bunches.size();
2501 for (u16 i = 0; i < num_bunches; i++) {
2504 u16 total number of texture bunches
2505 u16 index of this bunch
2506 u32 number of files in this bunch
2515 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2516 pkt << num_bunches << i << (u32) file_bunches[i].size();
2518 for (const SendableMedia &j : file_bunches[i]) {
2520 pkt.putLongString(j.data);
2523 verbosestream << "Server::sendRequestedMedia(): bunch "
2524 << i << "/" << num_bunches
2525 << " files=" << file_bunches[i].size()
2526 << " size=" << pkt.getSize() << std::endl;
2531 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
2533 if(m_detached_inventories.count(name) == 0) {
2534 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2537 Inventory *inv = m_detached_inventories[name];
2538 std::ostringstream os(std::ios_base::binary);
2540 os << serializeString(name);
2544 std::string s = os.str();
2546 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2547 pkt.putRawString(s.c_str(), s.size());
2549 const std::string &check = m_detached_inventories_player[name];
2550 if (peer_id == PEER_ID_INEXISTENT) {
2552 return m_clients.sendToAll(&pkt);
2553 RemotePlayer *p = m_env->getPlayer(check.c_str());
2555 m_clients.send(p->peer_id, 0, &pkt, true);
2557 if (check.empty() || getPlayerName(peer_id) == check)
2562 void Server::sendDetachedInventories(u16 peer_id)
2564 DSTACK(FUNCTION_NAME);
2566 for (const auto &detached_inventory : m_detached_inventories) {
2567 const std::string &name = detached_inventory.first;
2568 //Inventory *inv = i->second;
2569 sendDetachedInventory(name, peer_id);
2577 void Server::DiePlayer(u16 peer_id)
2579 DSTACK(FUNCTION_NAME);
2580 PlayerSAO *playersao = getPlayerSAO(peer_id);
2581 // In some rare cases this can be NULL -- if the player is disconnected
2582 // when a Lua function modifies l_punch, for example
2586 infostream << "Server::DiePlayer(): Player "
2587 << playersao->getPlayer()->getName()
2588 << " dies" << std::endl;
2590 playersao->setHP(0);
2592 // Trigger scripted stuff
2593 m_script->on_dieplayer(playersao);
2595 SendPlayerHP(peer_id);
2596 SendDeathscreen(peer_id, false, v3f(0,0,0));
2599 void Server::RespawnPlayer(u16 peer_id)
2601 DSTACK(FUNCTION_NAME);
2603 PlayerSAO *playersao = getPlayerSAO(peer_id);
2606 infostream << "Server::RespawnPlayer(): Player "
2607 << playersao->getPlayer()->getName()
2608 << " respawns" << std::endl;
2610 playersao->setHP(PLAYER_MAX_HP);
2611 playersao->setBreath(PLAYER_MAX_BREATH);
2613 bool repositioned = m_script->on_respawnplayer(playersao);
2614 if (!repositioned) {
2615 // setPos will send the new position to client
2616 playersao->setPos(findSpawnPos());
2619 SendPlayerHP(peer_id);
2623 void Server::DenySudoAccess(u16 peer_id)
2625 DSTACK(FUNCTION_NAME);
2627 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2632 void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
2633 const std::string &str_reason, bool reconnect)
2635 if (proto_ver >= 25) {
2636 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2638 std::wstring wreason = utf8_to_wide(
2639 reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
2640 accessDeniedStrings[(u8)reason]);
2641 SendAccessDenied_Legacy(peer_id, wreason);
2644 m_clients.event(peer_id, CSE_SetDenied);
2645 m_con.DisconnectPeer(peer_id);
2649 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
2651 DSTACK(FUNCTION_NAME);
2653 SendAccessDenied(peer_id, reason, custom_reason);
2654 m_clients.event(peer_id, CSE_SetDenied);
2655 m_con.DisconnectPeer(peer_id);
2658 // 13/03/15: remove this function when protocol version 25 will become
2659 // the minimum version for MT users, maybe in 1 year
2660 void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
2662 DSTACK(FUNCTION_NAME);
2664 SendAccessDenied_Legacy(peer_id, reason);
2665 m_clients.event(peer_id, CSE_SetDenied);
2666 m_con.DisconnectPeer(peer_id);
2669 void Server::acceptAuth(u16 peer_id, bool forSudoMode)
2671 DSTACK(FUNCTION_NAME);
2674 RemoteClient* client = getClient(peer_id, CS_Invalid);
2676 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2678 // Right now, the auth mechs don't change between login and sudo mode.
2679 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2680 client->allowed_sudo_mechs = sudo_auth_mechs;
2682 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2683 << g_settings->getFloat("dedicated_server_step")
2687 m_clients.event(peer_id, CSE_AuthAccept);
2689 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2691 // We only support SRP right now
2692 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2694 resp_pkt << sudo_auth_mechs;
2696 m_clients.event(peer_id, CSE_SudoSuccess);
2700 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
2702 DSTACK(FUNCTION_NAME);
2703 std::wstring message;
2706 Clear references to playing sounds
2708 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2709 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2710 ServerPlayingSound &psound = i->second;
2711 psound.clients.erase(peer_id);
2712 if (psound.clients.empty())
2713 m_playing_sounds.erase(i++);
2718 RemotePlayer *player = m_env->getPlayer(peer_id);
2720 /* Run scripts and remove from environment */
2721 if (player != NULL) {
2722 PlayerSAO *playersao = player->getPlayerSAO();
2725 // inform connected clients
2726 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2727 // (u16) 1 + std::string represents a vector serialization representation
2728 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2729 m_clients.sendToAll(¬ice);
2731 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2733 playersao->disconnected();
2740 if(player != NULL && reason != CDR_DENY) {
2741 std::ostringstream os(std::ios_base::binary);
2742 std::vector<u16> clients = m_clients.getClientIDs();
2744 for (const u16 client_id : clients) {
2746 RemotePlayer *player = m_env->getPlayer(client_id);
2750 // Get name of player
2751 os << player->getName() << " ";
2754 std::string name = player->getName();
2755 actionstream << name << " "
2756 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2757 << " List of players: " << os.str() << std::endl;
2759 m_admin_chat->outgoing_queue.push_back(
2760 new ChatEventNick(CET_NICK_REMOVE, name));
2764 MutexAutoLock env_lock(m_env_mutex);
2765 m_clients.DeleteClient(peer_id);
2769 // Send leave chat message to all remaining clients
2770 if (!message.empty()) {
2771 SendChatMessage(PEER_ID_INEXISTENT,
2772 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2776 void Server::UpdateCrafting(RemotePlayer *player)
2778 DSTACK(FUNCTION_NAME);
2780 // Get a preview for crafting
2782 InventoryLocation loc;
2783 loc.setPlayer(player->getName());
2784 std::vector<ItemStack> output_replacements;
2785 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2786 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2787 (&player->inventory)->getList("craft"), loc);
2789 // Put the new preview in
2790 InventoryList *plist = player->inventory.getList("craftpreview");
2791 sanity_check(plist);
2792 sanity_check(plist->getSize() >= 1);
2793 plist->changeItem(0, preview);
2796 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2798 if (evt->type == CET_NICK_ADD) {
2799 // The terminal informed us of its nick choice
2800 m_admin_nick = ((ChatEventNick *)evt)->nick;
2801 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2802 errorstream << "You haven't set up an account." << std::endl
2803 << "Please log in using the client as '"
2804 << m_admin_nick << "' with a secure password." << std::endl
2805 << "Until then, you can't execute admin tasks via the console," << std::endl
2806 << "and everybody can claim the user account instead of you," << std::endl
2807 << "giving them full control over this server." << std::endl;
2810 assert(evt->type == CET_CHAT);
2811 handleAdminChat((ChatEventChat *)evt);
2815 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2816 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2818 // If something goes wrong, this player is to blame
2819 RollbackScopeActor rollback_scope(m_rollback,
2820 std::string("player:") + name);
2822 if (g_settings->getBool("strip_color_codes"))
2823 wmessage = unescape_enriched(wmessage);
2826 switch (player->canSendChatMessage()) {
2827 case RPLAYER_CHATRESULT_FLOODING: {
2828 std::wstringstream ws;
2829 ws << L"You cannot send more messages. You are limited to "
2830 << g_settings->getFloat("chat_message_limit_per_10sec")
2831 << L" messages per 10 seconds.";
2834 case RPLAYER_CHATRESULT_KICK:
2835 DenyAccess_Legacy(player->peer_id,
2836 L"You have been kicked due to message flooding.");
2838 case RPLAYER_CHATRESULT_OK:
2841 FATAL_ERROR("Unhandled chat filtering result found.");
2845 if (m_max_chatmessage_length > 0
2846 && wmessage.length() > m_max_chatmessage_length) {
2847 return L"Your message exceed the maximum chat message limit set on the server. "
2848 L"It was refused. Send a shorter message";
2851 // Run script hook, exit if script ate the chat message
2852 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2857 // Whether to send line to the player that sent the message, or to all players
2858 bool broadcast_line = true;
2860 if (check_shout_priv && !checkPriv(name, "shout")) {
2861 line += L"-!- You don't have permission to shout.";
2862 broadcast_line = false;
2871 Tell calling method to send the message to sender
2873 if (!broadcast_line)
2877 Send the message to others
2879 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2881 std::vector<u16> clients = m_clients.getClientIDs();
2884 Send the message back to the inital sender
2885 if they are using protocol version >= 29
2888 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2889 if (player && player->protocol_version >= 29)
2890 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2892 for (u16 i = 0; i < clients.size(); i++) {
2893 u16 cid = clients[i];
2894 if (cid != peer_id_to_avoid_sending)
2895 SendChatMessage(cid, ChatMessage(line));
2900 void Server::handleAdminChat(const ChatEventChat *evt)
2902 std::string name = evt->nick;
2903 std::wstring wname = utf8_to_wide(name);
2904 std::wstring wmessage = evt->evt_msg;
2906 std::wstring answer = handleChat(name, wname, wmessage);
2908 // If asked to send answer to sender
2909 if (!answer.empty()) {
2910 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2914 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2916 RemoteClient *client = getClientNoEx(peer_id,state_min);
2918 throw ClientNotFoundException("Client not found");
2922 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2924 return m_clients.getClientNoEx(peer_id, state_min);
2927 std::string Server::getPlayerName(u16 peer_id)
2929 RemotePlayer *player = m_env->getPlayer(peer_id);
2931 return "[id="+itos(peer_id)+"]";
2932 return player->getName();
2935 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2937 RemotePlayer *player = m_env->getPlayer(peer_id);
2940 return player->getPlayerSAO();
2943 std::wstring Server::getStatusString()
2945 std::wostringstream os(std::ios_base::binary);
2948 os<<L"version="<<narrow_to_wide(g_version_string);
2950 os<<L", uptime="<<m_uptime.get();
2952 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2953 // Information about clients
2956 std::vector<u16> clients = m_clients.getClientIDs();
2957 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2959 RemotePlayer *player = m_env->getPlayer(*i);
2960 // Get name of player
2961 std::wstring name = L"unknown";
2963 name = narrow_to_wide(player->getName());
2964 // Add name to information string
2973 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2974 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2976 if (!g_settings->get("motd").empty())
2977 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2981 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2983 std::set<std::string> privs;
2984 m_script->getAuth(name, NULL, &privs);
2988 bool Server::checkPriv(const std::string &name, const std::string &priv)
2990 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2991 return (privs.count(priv) != 0);
2994 void Server::reportPrivsModified(const std::string &name)
2997 std::vector<u16> clients = m_clients.getClientIDs();
2998 for (const u16 client_id : clients) {
2999 RemotePlayer *player = m_env->getPlayer(client_id);
3000 reportPrivsModified(player->getName());
3003 RemotePlayer *player = m_env->getPlayer(name.c_str());
3006 SendPlayerPrivileges(player->peer_id);
3007 PlayerSAO *sao = player->getPlayerSAO();
3010 sao->updatePrivileges(
3011 getPlayerEffectivePrivs(name),
3016 void Server::reportInventoryFormspecModified(const std::string &name)
3018 RemotePlayer *player = m_env->getPlayer(name.c_str());
3021 SendPlayerInventoryFormspec(player->peer_id);
3024 void Server::setIpBanned(const std::string &ip, const std::string &name)
3026 m_banmanager->add(ip, name);
3029 void Server::unsetIpBanned(const std::string &ip_or_name)
3031 m_banmanager->remove(ip_or_name);
3034 std::string Server::getBanDescription(const std::string &ip_or_name)
3036 return m_banmanager->getBanDescription(ip_or_name);
3039 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3041 // m_env will be NULL if the server is initializing
3045 if (m_admin_nick == name && !m_admin_nick.empty()) {
3046 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3049 RemotePlayer *player = m_env->getPlayer(name);
3054 if (player->peer_id == PEER_ID_INEXISTENT)
3057 SendChatMessage(player->peer_id, ChatMessage(msg));
3060 bool Server::showFormspec(const char *playername, const std::string &formspec,
3061 const std::string &formname)
3063 // m_env will be NULL if the server is initializing
3067 RemotePlayer *player = m_env->getPlayer(playername);
3071 SendShowFormspecMessage(player->peer_id, formspec, formname);
3075 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3080 u32 id = player->addHud(form);
3082 SendHUDAdd(player->peer_id, id, form);
3087 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3091 HudElement* todel = player->removeHud(id);
3098 SendHUDRemove(player->peer_id, id);
3102 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3107 SendHUDChange(player->peer_id, id, stat, data);
3111 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3116 SendHUDSetFlags(player->peer_id, flags, mask);
3117 player->hud_flags &= ~mask;
3118 player->hud_flags |= flags;
3120 PlayerSAO* playersao = player->getPlayerSAO();
3125 m_script->player_event(playersao, "hud_changed");
3129 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3134 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3137 player->setHotbarItemcount(hotbar_itemcount);
3138 std::ostringstream os(std::ios::binary);
3139 writeS32(os, hotbar_itemcount);
3140 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3144 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3149 player->setHotbarImage(name);
3150 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3153 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3157 return player->getHotbarImage();
3160 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3165 player->setHotbarSelectedImage(name);
3166 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3169 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3170 v2s32 animation_frames[4], f32 frame_speed)
3175 player->setLocalAnimations(animation_frames, frame_speed);
3176 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3180 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3185 player->eye_offset_first = first;
3186 player->eye_offset_third = third;
3187 SendEyeOffset(player->peer_id, first, third);
3191 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3192 const std::string &type, const std::vector<std::string> ¶ms,
3198 player->setSky(bgcolor, type, params, clouds);
3199 SendSetSky(player->peer_id, bgcolor, type, params, clouds);
3203 bool Server::setClouds(RemotePlayer *player, float density,
3204 const video::SColor &color_bright,
3205 const video::SColor &color_ambient,
3213 SendCloudParams(player->peer_id, density,
3214 color_bright, color_ambient, height,
3219 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3225 player->overrideDayNightRatio(do_override, ratio);
3226 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3230 void Server::notifyPlayers(const std::wstring &msg)
3232 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3235 void Server::spawnParticle(const std::string &playername, v3f pos,
3236 v3f velocity, v3f acceleration,
3237 float expirationtime, float size, bool
3238 collisiondetection, bool collision_removal,
3239 bool vertical, const std::string &texture,
3240 const struct TileAnimationParams &animation, u8 glow)
3242 // m_env will be NULL if the server is initializing
3246 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3247 if (!playername.empty()) {
3248 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3251 peer_id = player->peer_id;
3252 proto_ver = player->protocol_version;
3255 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3256 expirationtime, size, collisiondetection,
3257 collision_removal, vertical, texture, animation, glow);
3260 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3261 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3262 float minexptime, float maxexptime, float minsize, float maxsize,
3263 bool collisiondetection, bool collision_removal,
3264 ServerActiveObject *attached, bool vertical, const std::string &texture,
3265 const std::string &playername, const struct TileAnimationParams &animation,
3268 // m_env will be NULL if the server is initializing
3272 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3273 if (!playername.empty()) {
3274 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3277 peer_id = player->peer_id;
3278 proto_ver = player->protocol_version;
3281 u16 attached_id = attached ? attached->getId() : 0;
3284 if (attached_id == 0)
3285 id = m_env->addParticleSpawner(spawntime);
3287 id = m_env->addParticleSpawner(spawntime, attached_id);
3289 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3290 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3291 minexptime, maxexptime, minsize, maxsize,
3292 collisiondetection, collision_removal, attached_id, vertical,
3293 texture, id, animation, glow);
3298 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3300 // m_env will be NULL if the server is initializing
3302 throw ServerError("Can't delete particle spawners during initialisation!");
3304 u16 peer_id = PEER_ID_INEXISTENT;
3305 if (!playername.empty()) {
3306 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3309 peer_id = player->peer_id;
3312 m_env->deleteParticleSpawner(id);
3313 SendDeleteParticleSpawner(peer_id, id);
3316 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3318 if(m_detached_inventories.count(name) > 0){
3319 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3320 delete m_detached_inventories[name];
3322 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3324 Inventory *inv = new Inventory(m_itemdef);
3326 m_detached_inventories[name] = inv;
3327 m_detached_inventories_player[name] = player;
3328 //TODO find a better way to do this
3329 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3333 // actions: time-reversed list
3334 // Return value: success/failure
3335 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3336 std::list<std::string> *log)
3338 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3339 ServerMap *map = (ServerMap*)(&m_env->getMap());
3341 // Fail if no actions to handle
3342 if(actions.empty()){
3343 log->push_back("Nothing to do.");
3350 for (const RollbackAction &action : actions) {
3352 bool success = action.applyRevert(map, this, this);
3355 std::ostringstream os;
3356 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3357 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3359 log->push_back(os.str());
3361 std::ostringstream os;
3362 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3363 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3365 log->push_back(os.str());
3369 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3370 <<" failed"<<std::endl;
3372 // Call it done if less than half failed
3373 return num_failed <= num_tried/2;
3376 // IGameDef interface
3378 IItemDefManager *Server::getItemDefManager()
3383 INodeDefManager *Server::getNodeDefManager()
3388 ICraftDefManager *Server::getCraftDefManager()
3393 u16 Server::allocateUnknownNodeId(const std::string &name)
3395 return m_nodedef->allocateDummy(name);
3398 MtEventManager *Server::getEventManager()
3403 IWritableItemDefManager *Server::getWritableItemDefManager()
3408 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3413 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3418 const ModSpec *Server::getModSpec(const std::string &modname) const
3420 std::vector<ModSpec>::const_iterator it;
3421 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3422 const ModSpec &mod = *it;
3423 if (mod.name == modname)
3429 void Server::getModNames(std::vector<std::string> &modlist)
3431 std::vector<ModSpec>::iterator it;
3432 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3433 modlist.push_back(it->name);
3436 std::string Server::getBuiltinLuaPath()
3438 return porting::path_share + DIR_DELIM + "builtin";
3441 std::string Server::getModStoragePath() const
3443 return m_path_world + DIR_DELIM + "mod_storage";
3446 v3f Server::findSpawnPos()
3448 ServerMap &map = m_env->getServerMap();
3450 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3451 return nodeposf * BS;
3454 bool is_good = false;
3455 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3456 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3458 // Try to find a good place a few times
3459 for(s32 i = 0; i < 4000 && !is_good; i++) {
3460 s32 range = MYMIN(1 + i, range_max);
3461 // We're going to try to throw the player to this position
3462 v2s16 nodepos2d = v2s16(
3463 -range + (myrand() % (range * 2)),
3464 -range + (myrand() % (range * 2)));
3466 // Get spawn level at point
3467 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3468 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3469 // the mapgen to signify an unsuitable spawn position
3470 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3473 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3476 for (s32 i = 0; i < 10; i++) {
3477 v3s16 blockpos = getNodeBlockPos(nodepos);
3478 map.emergeBlock(blockpos, true);
3479 content_t c = map.getNodeNoEx(nodepos).getContent();
3480 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3482 if (air_count >= 2) {
3483 nodeposf = intToFloat(nodepos, BS);
3484 // Don't spawn the player outside map boundaries
3485 if (objectpos_over_limit(nodeposf))
3498 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3500 m_shutdown_timer = delay;
3501 m_shutdown_msg = msg;
3502 m_shutdown_ask_reconnect = reconnect;
3504 if (delay == 0.0f) {
3505 // No delay, shutdown immediately
3506 m_shutdown_requested = true;
3507 // only print to the infostream, a chat message saying
3508 // "Server Shutting Down" is sent when the server destructs.
3509 infostream << "*** Immediate Server shutdown requested." << std::endl;
3510 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3511 // Negative delay, cancel shutdown if requested
3512 m_shutdown_timer = 0.0f;
3513 m_shutdown_msg = "";
3514 m_shutdown_ask_reconnect = false;
3515 m_shutdown_requested = false;
3516 std::wstringstream ws;
3518 ws << L"*** Server shutdown canceled.";
3520 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3521 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3522 } else if (delay > 0.0f) {
3523 // Positive delay, tell the clients when the server will shut down
3524 std::wstringstream ws;
3526 ws << L"*** Server shutting down in "
3527 << duration_to_string(myround(m_shutdown_timer)).c_str()
3530 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3531 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3535 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3538 Try to get an existing player
3540 RemotePlayer *player = m_env->getPlayer(name);
3542 // If player is already connected, cancel
3543 if (player != NULL && player->peer_id != 0) {
3544 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3549 If player with the wanted peer_id already exists, cancel.
3551 if (m_env->getPlayer(peer_id) != NULL) {
3552 infostream<<"emergePlayer(): Player with wrong name but same"
3553 " peer_id already exists"<<std::endl;
3558 player = new RemotePlayer(name, idef());
3561 bool newplayer = false;
3564 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3566 // Complete init with server parts
3567 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3568 player->protocol_version = proto_version;
3572 m_script->on_newplayer(playersao);
3578 bool Server::registerModStorage(ModMetadata *storage)
3580 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3581 errorstream << "Unable to register same mod storage twice. Storage name: "
3582 << storage->getModName() << std::endl;
3586 m_mod_storages[storage->getModName()] = storage;
3590 void Server::unregisterModStorage(const std::string &name)
3592 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3593 if (it != m_mod_storages.end()) {
3594 // Save unconditionaly on unregistration
3595 it->second->save(getModStoragePath());
3596 m_mod_storages.erase(name);
3600 void dedicated_server_loop(Server &server, bool &kill)
3602 DSTACK(FUNCTION_NAME);
3604 verbosestream<<"dedicated_server_loop()"<<std::endl;
3606 IntervalLimiter m_profiler_interval;
3608 static thread_local const float steplen =
3609 g_settings->getFloat("dedicated_server_step");
3610 static thread_local const float profiler_print_interval =
3611 g_settings->getFloat("profiler_print_interval");
3614 // This is kind of a hack but can be done like this
3615 // because server.step() is very light
3617 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3618 sleep_ms((int)(steplen*1000.0));
3620 server.step(steplen);
3622 if (server.getShutdownRequested() || kill)
3628 if (profiler_print_interval != 0) {
3629 if(m_profiler_interval.step(steplen, profiler_print_interval))
3631 infostream<<"Profiler:"<<std::endl;
3632 g_profiler->print(infostream);
3633 g_profiler->clear();
3638 infostream << "Dedicated server quitting" << std::endl;
3640 if (g_settings->getBool("server_announce"))
3641 ServerList::sendAnnounce(ServerList::AA_DELETE,
3642 server.m_bind_addr.getPort());