3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "network/connection.h"
25 #include "network/networkprotocol.h"
26 #include "network/serveropcodes.h"
28 #include "environment.h"
30 #include "threading/mutex_auto_lock.h"
31 #include "constants.h"
37 #include "serverobject.h"
38 #include "genericobject.h"
42 #include "scripting_server.h"
49 #include "content_mapnode.h"
50 #include "content_nodemeta.h"
51 #include "content_sao.h"
53 #include "event_manager.h"
54 #include "serverlist.h"
55 #include "util/string.h"
57 #include "util/serialize.h"
58 #include "util/thread.h"
59 #include "defaultsettings.h"
60 #include "util/base64.h"
61 #include "util/sha1.h"
64 #include "chatmessage.h"
65 #include "chat_interface.h"
66 #include "remoteplayer.h"
68 class ClientNotFoundException : public BaseException
71 ClientNotFoundException(const char *s):
76 class ServerThread : public Thread
80 ServerThread(Server *server):
91 void *ServerThread::run()
93 DSTACK(FUNCTION_NAME);
94 BEGIN_DEBUG_EXCEPTION_HANDLER
96 m_server->AsyncRunStep(true);
98 while (!stopRequested()) {
100 m_server->AsyncRunStep();
104 } catch (con::NoIncomingDataException &e) {
105 } catch (con::PeerNotFoundException &e) {
106 infostream<<"Server: PeerNotFoundException"<<std::endl;
107 } catch (ClientNotFoundException &e) {
108 } catch (con::ConnectionBindFailed &e) {
109 m_server->setAsyncFatalError(e.what());
110 } catch (LuaError &e) {
111 m_server->setAsyncFatalError(
112 "ServerThread::run Lua: " + std::string(e.what()));
116 END_DEBUG_EXCEPTION_HANDLER
121 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
123 if(pos_exists) *pos_exists = false;
128 if(pos_exists) *pos_exists = true;
133 ServerActiveObject *sao = env->getActiveObject(object);
136 if(pos_exists) *pos_exists = true;
137 return sao->getBasePosition(); }
149 const std::string &path_world,
150 const SubgameSpec &gamespec,
151 bool simple_singleplayer_mode,
156 m_path_world(path_world),
157 m_gamespec(gamespec),
158 m_simple_singleplayer_mode(simple_singleplayer_mode),
159 m_dedicated(dedicated),
160 m_async_fatal_error(""),
161 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
166 m_itemdef(createItemDefManager()),
167 m_nodedef(createNodeDefManager()),
168 m_craftdef(createCraftDefManager()),
169 m_event(new EventManager()),
174 m_lag = g_settings->getFloat("dedicated_server_step");
176 if (path_world.empty())
177 throw ServerError("Supplied empty world path");
179 if(!gamespec.isValid())
180 throw ServerError("Supplied invalid gamespec");
182 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
183 if(m_simple_singleplayer_mode)
184 infostream<<" in simple singleplayer mode"<<std::endl;
186 infostream<<std::endl;
187 infostream<<"- world: "<<m_path_world<<std::endl;
188 infostream<<"- game: "<<m_gamespec.path<<std::endl;
190 // Create world if it doesn't exist
191 if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
192 throw ServerError("Failed to initialize world");
194 // Create server thread
195 m_thread = new ServerThread(this);
197 // Create emerge manager
198 m_emerge = new EmergeManager(this);
200 // Create ban manager
201 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
202 m_banmanager = new BanManager(ban_path);
204 ServerModConfiguration modconf(m_path_world);
205 m_mods = modconf.getMods();
206 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
207 // complain about mods with unsatisfied dependencies
208 if (!modconf.isConsistent()) {
209 modconf.printUnsatisfiedModsError();
213 MutexAutoLock envlock(m_env_mutex);
215 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
216 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
218 // Initialize scripting
219 infostream<<"Server: Initializing Lua"<<std::endl;
221 m_script = new ServerScripting(this);
223 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
226 infostream << "Server: Loading mods: ";
227 for (std::vector<ModSpec>::const_iterator i = m_mods.begin();
228 i != m_mods.end(); ++i) {
229 infostream << (*i).name << " ";
231 infostream << std::endl;
232 // Load and run "mod" scripts
233 for (std::vector<ModSpec>::const_iterator it = m_mods.begin();
234 it != m_mods.end(); ++it) {
235 const ModSpec &mod = *it;
236 if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
237 throw ModError("Error loading mod \"" + mod.name +
238 "\": Mod name does not follow naming conventions: "
239 "Only characters [a-z0-9_] are allowed.");
241 std::string script_path = mod.path + DIR_DELIM + "init.lua";
242 infostream << " [" << padStringRight(mod.name, 12) << "] [\""
243 << script_path << "\"]" << std::endl;
244 m_script->loadMod(script_path, mod.name);
247 // Read Textures and calculate sha1 sums
250 // Apply item aliases in the node definition manager
251 m_nodedef->updateAliases(m_itemdef);
253 // Apply texture overrides from texturepack/override.txt
254 std::string texture_path = g_settings->get("texture_path");
255 if (!texture_path.empty() && fs::IsDir(texture_path))
256 m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
258 m_nodedef->setNodeRegistrationStatus(true);
260 // Perform pending node name resolutions
261 m_nodedef->runNodeResolveCallbacks();
263 // unmap node names for connected nodeboxes
264 m_nodedef->mapNodeboxConnections();
266 // init the recipe hashes to speed up crafting
267 m_craftdef->initHashes(this);
269 // Initialize Environment
270 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
272 m_clients.setEnv(m_env);
274 if (!servermap->settings_mgr.makeMapgenParams())
275 FATAL_ERROR("Couldn't create any mapgen type");
277 // Initialize mapgens
278 m_emerge->initMapgens(servermap->getMapgenParams());
280 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
281 if (m_enable_rollback_recording) {
282 // Create rollback manager
283 m_rollback = new RollbackManager(m_path_world, this);
286 // Give environment reference to scripting api
287 m_script->initializeEnvironment(m_env);
289 // Register us to receive map edit events
290 servermap->addEventReceiver(this);
292 // If file exists, load environment metadata
293 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
294 infostream << "Server: Loading environment metadata" << std::endl;
297 m_env->loadDefaultMeta();
300 m_liquid_transform_every = g_settings->getFloat("liquid_update");
301 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
302 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
303 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
308 infostream<<"Server destructing"<<std::endl;
310 // Send shutdown message
311 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
312 L"*** Server shutting down"));
315 MutexAutoLock envlock(m_env_mutex);
317 // Execute script shutdown hooks
318 m_script->on_shutdown();
320 infostream << "Server: Saving players" << std::endl;
321 m_env->saveLoadedPlayers();
323 infostream << "Server: Kicking players" << std::endl;
324 std::string kick_msg;
325 bool reconnect = false;
326 if (getShutdownRequested()) {
327 reconnect = m_shutdown_ask_reconnect;
328 kick_msg = m_shutdown_msg;
330 if (kick_msg.empty()) {
331 kick_msg = g_settings->get("kick_msg_shutdown");
333 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
334 kick_msg, reconnect);
336 infostream << "Server: Saving environment metadata" << std::endl;
344 // stop all emerge threads before deleting players that may have
345 // requested blocks to be emerged
346 m_emerge->stopThreads();
348 // Delete things in the reverse order of creation
358 // Deinitialize scripting
359 infostream<<"Server: Deinitializing scripting"<<std::endl;
362 // Delete detached inventories
363 for (auto &detached_inventory : m_detached_inventories) {
364 delete detached_inventory.second;
368 void Server::start(Address bind_addr)
370 DSTACK(FUNCTION_NAME);
372 m_bind_addr = bind_addr;
374 infostream<<"Starting server on "
375 << bind_addr.serializeString() <<"..."<<std::endl;
377 // Stop thread if already running
380 // Initialize connection
381 m_con->SetTimeoutMs(30);
382 m_con->Serve(bind_addr);
387 // ASCII art for the win!
389 <<" .__ __ __ "<<std::endl
390 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
391 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
392 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
393 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
394 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
395 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
396 actionstream<<"Server for gameid=\""<<m_gamespec.id
397 <<"\" listening on "<<bind_addr.serializeString()<<":"
398 <<bind_addr.getPort() << "."<<std::endl;
403 DSTACK(FUNCTION_NAME);
405 infostream<<"Server: Stopping and waiting threads"<<std::endl;
407 // Stop threads (set run=false first so both start stopping)
409 //m_emergethread.setRun(false);
411 //m_emergethread.stop();
413 infostream<<"Server: Threads stopped"<<std::endl;
416 void Server::step(float dtime)
418 DSTACK(FUNCTION_NAME);
423 MutexAutoLock lock(m_step_dtime_mutex);
424 m_step_dtime += dtime;
426 // Throw if fatal error occurred in thread
427 std::string async_err = m_async_fatal_error.get();
428 if (!async_err.empty()) {
429 if (!m_simple_singleplayer_mode) {
430 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
431 g_settings->get("kick_msg_crash"),
432 g_settings->getBool("ask_reconnect_on_crash"));
434 throw ServerError("AsyncErr: " + async_err);
438 void Server::AsyncRunStep(bool initial_step)
440 DSTACK(FUNCTION_NAME);
442 g_profiler->add("Server::AsyncRunStep (num)", 1);
446 MutexAutoLock lock1(m_step_dtime_mutex);
447 dtime = m_step_dtime;
451 // Send blocks to clients
455 if((dtime < 0.001) && !initial_step)
458 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
460 //infostream<<"Server steps "<<dtime<<std::endl;
461 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
464 MutexAutoLock lock1(m_step_dtime_mutex);
465 m_step_dtime -= dtime;
472 m_uptime.set(m_uptime.get() + dtime);
478 Update time of day and overall game time
480 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
483 Send to clients at constant intervals
486 m_time_of_day_send_timer -= dtime;
487 if(m_time_of_day_send_timer < 0.0) {
488 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
489 u16 time = m_env->getTimeOfDay();
490 float time_speed = g_settings->getFloat("time_speed");
491 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
495 MutexAutoLock lock(m_env_mutex);
496 // Figure out and report maximum lag to environment
497 float max_lag = m_env->getMaxLagEstimate();
498 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
500 if(dtime > 0.1 && dtime > max_lag * 2.0)
501 infostream<<"Server: Maximum lag peaked to "<<dtime
505 m_env->reportMaxLagEstimate(max_lag);
507 ScopeProfiler sp(g_profiler, "SEnv step");
508 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
512 static const float map_timer_and_unload_dtime = 2.92;
513 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
515 MutexAutoLock lock(m_env_mutex);
516 // Run Map's timers and unload unused data
517 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
518 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
519 g_settings->getFloat("server_unload_unused_data_timeout"),
524 Listen to the admin chat, if available
527 if (!m_admin_chat->command_queue.empty()) {
528 MutexAutoLock lock(m_env_mutex);
529 while (!m_admin_chat->command_queue.empty()) {
530 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
531 handleChatInterfaceEvent(evt);
535 m_admin_chat->outgoing_queue.push_back(
536 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
543 /* Transform liquids */
544 m_liquid_transform_timer += dtime;
545 if(m_liquid_transform_timer >= m_liquid_transform_every)
547 m_liquid_transform_timer -= m_liquid_transform_every;
549 MutexAutoLock lock(m_env_mutex);
551 ScopeProfiler sp(g_profiler, "Server: liquid transform");
553 std::map<v3s16, MapBlock*> modified_blocks;
554 m_env->getMap().transformLiquids(modified_blocks, m_env);
557 Set the modified blocks unsent for all the clients
559 if(!modified_blocks.empty())
561 SetBlocksNotSent(modified_blocks);
564 m_clients.step(dtime);
566 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
568 // send masterserver announce
570 float &counter = m_masterserver_timer;
571 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
572 g_settings->getBool("server_announce")) {
573 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
574 ServerList::AA_START,
575 m_bind_addr.getPort(),
576 m_clients.getPlayerNames(),
578 m_env->getGameTime(),
581 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
591 Check added and deleted active objects
594 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
595 MutexAutoLock envlock(m_env_mutex);
598 const RemoteClientMap &clients = m_clients.getClientList();
599 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
601 // Radius inside which objects are active
602 static thread_local const s16 radius =
603 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
605 // Radius inside which players are active
606 static thread_local const bool is_transfer_limited =
607 g_settings->exists("unlimited_player_transfer_distance") &&
608 !g_settings->getBool("unlimited_player_transfer_distance");
609 static thread_local const s16 player_transfer_dist =
610 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
611 s16 player_radius = player_transfer_dist;
612 if (player_radius == 0 && is_transfer_limited)
613 player_radius = radius;
615 for (const auto &client_it : clients) {
616 RemoteClient *client = client_it.second;
618 // If definitions and textures have not been sent, don't
619 // send objects either
620 if (client->getState() < CS_DefinitionsSent)
623 RemotePlayer *player = m_env->getPlayer(client->peer_id);
625 // This can happen if the client timeouts somehow
629 PlayerSAO *playersao = player->getPlayerSAO();
633 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
634 if (my_radius <= 0) my_radius = radius;
635 //infostream << "Server: Active Radius " << my_radius << std::endl;
637 std::queue<u16> removed_objects;
638 std::queue<u16> added_objects;
639 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
640 client->m_known_objects, removed_objects);
641 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
642 client->m_known_objects, added_objects);
644 // Ignore if nothing happened
645 if (removed_objects.empty() && added_objects.empty()) {
649 std::string data_buffer;
653 // Handle removed objects
654 writeU16((u8*)buf, removed_objects.size());
655 data_buffer.append(buf, 2);
656 while (!removed_objects.empty()) {
658 u16 id = removed_objects.front();
659 ServerActiveObject* obj = m_env->getActiveObject(id);
661 // Add to data buffer for sending
662 writeU16((u8*)buf, id);
663 data_buffer.append(buf, 2);
665 // Remove from known objects
666 client->m_known_objects.erase(id);
668 if(obj && obj->m_known_by_count > 0)
669 obj->m_known_by_count--;
670 removed_objects.pop();
673 // Handle added objects
674 writeU16((u8*)buf, added_objects.size());
675 data_buffer.append(buf, 2);
676 while (!added_objects.empty()) {
678 u16 id = added_objects.front();
679 ServerActiveObject* obj = m_env->getActiveObject(id);
682 u8 type = ACTIVEOBJECT_TYPE_INVALID;
684 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
686 type = obj->getSendType();
688 // Add to data buffer for sending
689 writeU16((u8*)buf, id);
690 data_buffer.append(buf, 2);
691 writeU8((u8*)buf, type);
692 data_buffer.append(buf, 1);
695 data_buffer.append(serializeLongString(
696 obj->getClientInitializationData(client->net_proto_version)));
698 data_buffer.append(serializeLongString(""));
700 // Add to known objects
701 client->m_known_objects.insert(id);
704 obj->m_known_by_count++;
709 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
710 verbosestream << "Server: Sent object remove/add: "
711 << removed_objects.size() << " removed, "
712 << added_objects.size() << " added, "
713 << "packet size is " << pktSize << std::endl;
717 m_mod_storage_save_timer -= dtime;
718 if (m_mod_storage_save_timer <= 0.0f) {
719 infostream << "Saving registered mod storages." << std::endl;
720 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
721 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
722 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
723 if (it->second->isModified()) {
724 it->second->save(getModStoragePath());
734 MutexAutoLock envlock(m_env_mutex);
735 ScopeProfiler sp(g_profiler, "Server: sending object messages");
738 // Value = data sent by object
739 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
741 // Get active object messages from environment
743 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
747 std::vector<ActiveObjectMessage>* message_list = nullptr;
748 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
749 n = buffered_messages.find(aom.id);
750 if (n == buffered_messages.end()) {
751 message_list = new std::vector<ActiveObjectMessage>;
752 buffered_messages[aom.id] = message_list;
755 message_list = n->second;
757 message_list->push_back(aom);
761 const RemoteClientMap &clients = m_clients.getClientList();
762 // Route data to every client
763 for (const auto &client_it : clients) {
764 RemoteClient *client = client_it.second;
765 std::string reliable_data;
766 std::string unreliable_data;
767 // Go through all objects in message buffer
768 for (const auto &buffered_message : buffered_messages) {
769 // If object is not known by client, skip it
770 u16 id = buffered_message.first;
771 if (client->m_known_objects.find(id) == client->m_known_objects.end())
774 // Get message list of object
775 std::vector<ActiveObjectMessage>* list = buffered_message.second;
776 // Go through every message
777 for (const ActiveObjectMessage &aom : *list) {
778 // Compose the full new data with header
779 std::string new_data;
782 writeU16((u8*)&buf[0], aom.id);
783 new_data.append(buf, 2);
785 new_data += serializeString(aom.datastring);
786 // Add data to buffer
788 reliable_data += new_data;
790 unreliable_data += new_data;
794 reliable_data and unreliable_data are now ready.
797 if (!reliable_data.empty()) {
798 SendActiveObjectMessages(client->peer_id, reliable_data);
801 if (!unreliable_data.empty()) {
802 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
807 // Clear buffered_messages
808 for (auto &buffered_message : buffered_messages) {
809 delete buffered_message.second;
814 Send queued-for-sending map edit events.
817 // We will be accessing the environment
818 MutexAutoLock lock(m_env_mutex);
820 // Don't send too many at a time
823 // Single change sending is disabled if queue size is not small
824 bool disable_single_change_sending = false;
825 if(m_unsent_map_edit_queue.size() >= 4)
826 disable_single_change_sending = true;
828 int event_count = m_unsent_map_edit_queue.size();
830 // We'll log the amount of each
833 while (!m_unsent_map_edit_queue.empty()) {
834 MapEditEvent* event = m_unsent_map_edit_queue.front();
835 m_unsent_map_edit_queue.pop();
837 // Players far away from the change are stored here.
838 // Instead of sending the changes, MapBlocks are set not sent
840 std::vector<u16> far_players;
842 switch (event->type) {
845 prof.add("MEET_ADDNODE", 1);
846 sendAddNode(event->p, event->n, event->already_known_by_peer,
847 &far_players, disable_single_change_sending ? 5 : 30,
848 event->type == MEET_ADDNODE);
850 case MEET_REMOVENODE:
851 prof.add("MEET_REMOVENODE", 1);
852 sendRemoveNode(event->p, event->already_known_by_peer,
853 &far_players, disable_single_change_sending ? 5 : 30);
855 case MEET_BLOCK_NODE_METADATA_CHANGED:
856 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
857 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
858 setBlockNotSent(event->p);
861 infostream << "Server: MEET_OTHER" << std::endl;
862 prof.add("MEET_OTHER", 1);
863 for (const v3s16 &modified_block : event->modified_blocks) {
864 setBlockNotSent(modified_block);
868 prof.add("unknown", 1);
869 warningstream << "Server: Unknown MapEditEvent "
870 << ((u32)event->type) << std::endl;
875 Set blocks not sent to far players
877 if (!far_players.empty()) {
878 // Convert list format to that wanted by SetBlocksNotSent
879 std::map<v3s16, MapBlock*> modified_blocks2;
880 for (const v3s16 &modified_block : event->modified_blocks) {
881 modified_blocks2[modified_block] =
882 m_env->getMap().getBlockNoCreateNoEx(modified_block);
885 // Set blocks not sent
886 for (const u16 far_player : far_players) {
887 if (RemoteClient *client = getClient(far_player))
888 client->SetBlocksNotSent(modified_blocks2);
895 if (event_count >= 5) {
896 infostream << "Server: MapEditEvents:" << std::endl;
897 prof.print(infostream);
898 } else if (event_count != 0) {
899 verbosestream << "Server: MapEditEvents:" << std::endl;
900 prof.print(verbosestream);
906 Trigger emergethread (it somehow gets to a non-triggered but
907 bysy state sometimes)
910 float &counter = m_emergethread_trigger_timer;
912 if (counter >= 2.0) {
915 m_emerge->startThreads();
919 // Save map, players and auth stuff
921 float &counter = m_savemap_timer;
923 static thread_local const float save_interval =
924 g_settings->getFloat("server_map_save_interval");
925 if (counter >= save_interval) {
927 MutexAutoLock lock(m_env_mutex);
929 ScopeProfiler sp(g_profiler, "Server: saving stuff");
932 if (m_banmanager->isModified()) {
933 m_banmanager->save();
936 // Save changed parts of map
937 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
940 m_env->saveLoadedPlayers();
942 // Save environment metadata
948 static const float shutdown_msg_times[] =
950 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
953 if (m_shutdown_timer > 0.0f) {
954 // Automated messages
955 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
956 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
957 // If shutdown timer matches an automessage, shot it
958 if (m_shutdown_timer > shutdown_msg_times[i] &&
959 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
960 std::wstringstream ws;
962 ws << L"*** Server shutting down in "
963 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
966 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
967 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
973 m_shutdown_timer -= dtime;
974 if (m_shutdown_timer < 0.0f) {
975 m_shutdown_timer = 0.0f;
976 m_shutdown_requested = true;
981 void Server::Receive()
983 DSTACK(FUNCTION_NAME);
987 m_con->Receive(&pkt);
988 peer_id = pkt.getPeerId();
990 } catch (const con::InvalidIncomingDataException &e) {
991 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
992 << e.what() << std::endl;
993 } catch (const SerializationError &e) {
994 infostream << "Server::Receive(): SerializationError: what()="
995 << e.what() << std::endl;
996 } catch (const ClientStateError &e) {
997 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
998 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
999 L"Try reconnecting or updating your client");
1000 } catch (const con::PeerNotFoundException &e) {
1005 PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
1007 std::string playername;
1008 PlayerSAO *playersao = NULL;
1011 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1013 playername = client->getName();
1014 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1016 } catch (std::exception &e) {
1022 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1024 // If failed, cancel
1025 if (!playersao || !player) {
1026 if (player && player->peer_id != 0) {
1027 actionstream << "Server: Failed to emerge player \"" << playername
1028 << "\" (player allocated to an another client)" << std::endl;
1029 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1030 L"name. If your client closed unexpectedly, try again in "
1033 errorstream << "Server: " << playername << ": Failed to emerge player"
1035 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1041 Send complete position information
1043 SendMovePlayer(peer_id);
1046 SendPlayerPrivileges(peer_id);
1048 // Send inventory formspec
1049 SendPlayerInventoryFormspec(peer_id);
1052 SendInventory(playersao);
1054 // Send HP or death screen
1055 if (playersao->isDead())
1056 SendDeathscreen(peer_id, false, v3f(0,0,0));
1058 SendPlayerHPOrDie(playersao);
1061 SendPlayerBreath(playersao);
1063 // Note things in chat if not in simple singleplayer mode
1064 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1065 // Send information about server to player in chat
1066 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1068 Address addr = getPeerAddress(player->peer_id);
1069 std::string ip_str = addr.serializeString();
1070 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1075 const std::vector<std::string> &names = m_clients.getPlayerNames();
1077 actionstream << player->getName() << " joins game. List of players: ";
1079 for (const std::string &name : names) {
1080 actionstream << name << " ";
1083 actionstream << player->getName() <<std::endl;
1088 inline void Server::handleCommand(NetworkPacket* pkt)
1090 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1091 (this->*opHandle.handler)(pkt);
1094 void Server::ProcessData(NetworkPacket *pkt)
1096 DSTACK(FUNCTION_NAME);
1097 // Environment is locked first.
1098 MutexAutoLock envlock(m_env_mutex);
1100 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1101 u32 peer_id = pkt->getPeerId();
1104 Address address = getPeerAddress(peer_id);
1105 std::string addr_s = address.serializeString();
1107 if(m_banmanager->isIpBanned(addr_s)) {
1108 std::string ban_name = m_banmanager->getBanName(addr_s);
1109 infostream << "Server: A banned client tried to connect from "
1110 << addr_s << "; banned name was "
1111 << ban_name << std::endl;
1112 // This actually doesn't seem to transfer to the client
1113 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1114 + utf8_to_wide(ban_name));
1118 catch(con::PeerNotFoundException &e) {
1120 * no peer for this packet found
1121 * most common reason is peer timeout, e.g. peer didn't
1122 * respond for some time, your server was overloaded or
1125 infostream << "Server::ProcessData(): Canceling: peer "
1126 << peer_id << " not found" << std::endl;
1131 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1133 // Command must be handled into ToServerCommandHandler
1134 if (command >= TOSERVER_NUM_MSG_TYPES) {
1135 infostream << "Server: Ignoring unknown command "
1136 << command << std::endl;
1140 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1145 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1147 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1148 errorstream << "Server::ProcessData(): Cancelling: Peer"
1149 " serialization format invalid or not initialized."
1150 " Skipping incoming command=" << command << std::endl;
1154 /* Handle commands related to client startup */
1155 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1160 if (m_clients.getClientState(peer_id) < CS_Active) {
1161 if (command == TOSERVER_PLAYERPOS) return;
1163 errorstream << "Got packet command: " << command << " for peer id "
1164 << peer_id << " but client isn't active yet. Dropping packet "
1170 } catch (SendFailedException &e) {
1171 errorstream << "Server::ProcessData(): SendFailedException: "
1172 << "what=" << e.what()
1174 } catch (PacketError &e) {
1175 actionstream << "Server::ProcessData(): PacketError: "
1176 << "what=" << e.what()
1181 void Server::setTimeOfDay(u32 time)
1183 m_env->setTimeOfDay(time);
1184 m_time_of_day_send_timer = 0;
1187 void Server::onMapEditEvent(MapEditEvent *event)
1189 if(m_ignore_map_edit_events)
1191 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1193 MapEditEvent *e = event->clone();
1194 m_unsent_map_edit_queue.push(e);
1197 Inventory* Server::getInventory(const InventoryLocation &loc)
1200 case InventoryLocation::UNDEFINED:
1201 case InventoryLocation::CURRENT_PLAYER:
1203 case InventoryLocation::PLAYER:
1205 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1208 PlayerSAO *playersao = player->getPlayerSAO();
1211 return playersao->getInventory();
1214 case InventoryLocation::NODEMETA:
1216 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1219 return meta->getInventory();
1222 case InventoryLocation::DETACHED:
1224 if(m_detached_inventories.count(loc.name) == 0)
1226 return m_detached_inventories[loc.name];
1230 sanity_check(false); // abort
1235 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1238 case InventoryLocation::UNDEFINED:
1240 case InventoryLocation::PLAYER:
1245 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1250 PlayerSAO *playersao = player->getPlayerSAO();
1254 SendInventory(playersao);
1257 case InventoryLocation::NODEMETA:
1259 v3s16 blockpos = getNodeBlockPos(loc.p);
1261 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1263 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1265 setBlockNotSent(blockpos);
1268 case InventoryLocation::DETACHED:
1270 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1274 sanity_check(false); // abort
1279 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1281 std::vector<u16> clients = m_clients.getClientIDs();
1283 // Set the modified blocks unsent for all the clients
1284 for (const u16 client_id : clients) {
1285 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1286 client->SetBlocksNotSent(block);
1291 void Server::peerAdded(con::Peer *peer)
1293 DSTACK(FUNCTION_NAME);
1294 verbosestream<<"Server::peerAdded(): peer->id="
1295 <<peer->id<<std::endl;
1297 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1300 void Server::deletingPeer(con::Peer *peer, bool timeout)
1302 DSTACK(FUNCTION_NAME);
1303 verbosestream<<"Server::deletingPeer(): peer->id="
1304 <<peer->id<<", timeout="<<timeout<<std::endl;
1306 m_clients.event(peer->id, CSE_Disconnect);
1307 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1310 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
1312 *retval = m_con->getPeerStat(peer_id,type);
1313 return *retval != -1;
1316 bool Server::getClientInfo(
1325 std::string* vers_string
1328 *state = m_clients.getClientState(peer_id);
1330 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1337 *uptime = client->uptime();
1338 *ser_vers = client->serialization_version;
1339 *prot_vers = client->net_proto_version;
1341 *major = client->getMajor();
1342 *minor = client->getMinor();
1343 *patch = client->getPatch();
1344 *vers_string = client->getPatch();
1351 void Server::handlePeerChanges()
1353 while(!m_peer_change_queue.empty())
1355 con::PeerChange c = m_peer_change_queue.front();
1356 m_peer_change_queue.pop();
1358 verbosestream<<"Server: Handling peer change: "
1359 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1364 case con::PEER_ADDED:
1365 m_clients.CreateClient(c.peer_id);
1368 case con::PEER_REMOVED:
1369 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1373 FATAL_ERROR("Invalid peer change event received!");
1379 void Server::printToConsoleOnly(const std::string &text)
1382 m_admin_chat->outgoing_queue.push_back(
1383 new ChatEventChat("", utf8_to_wide(text)));
1385 std::cout << text << std::endl;
1389 void Server::Send(NetworkPacket* pkt)
1391 m_clients.send(pkt->getPeerId(),
1392 clientCommandFactoryTable[pkt->getCommand()].channel,
1394 clientCommandFactoryTable[pkt->getCommand()].reliable);
1397 void Server::SendMovement(u16 peer_id)
1399 DSTACK(FUNCTION_NAME);
1400 std::ostringstream os(std::ios_base::binary);
1402 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1404 pkt << g_settings->getFloat("movement_acceleration_default");
1405 pkt << g_settings->getFloat("movement_acceleration_air");
1406 pkt << g_settings->getFloat("movement_acceleration_fast");
1407 pkt << g_settings->getFloat("movement_speed_walk");
1408 pkt << g_settings->getFloat("movement_speed_crouch");
1409 pkt << g_settings->getFloat("movement_speed_fast");
1410 pkt << g_settings->getFloat("movement_speed_climb");
1411 pkt << g_settings->getFloat("movement_speed_jump");
1412 pkt << g_settings->getFloat("movement_liquid_fluidity");
1413 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1414 pkt << g_settings->getFloat("movement_liquid_sink");
1415 pkt << g_settings->getFloat("movement_gravity");
1420 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1422 if (!g_settings->getBool("enable_damage"))
1425 u16 peer_id = playersao->getPeerID();
1426 bool is_alive = playersao->getHP() > 0;
1429 SendPlayerHP(peer_id);
1434 void Server::SendHP(u16 peer_id, u8 hp)
1436 DSTACK(FUNCTION_NAME);
1438 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1443 void Server::SendBreath(u16 peer_id, u16 breath)
1445 DSTACK(FUNCTION_NAME);
1447 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1448 pkt << (u16) breath;
1452 void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
1453 const std::string &custom_reason, bool reconnect)
1455 assert(reason < SERVER_ACCESSDENIED_MAX);
1457 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1459 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1460 pkt << custom_reason;
1461 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1462 reason == SERVER_ACCESSDENIED_CRASH)
1463 pkt << custom_reason << (u8)reconnect;
1467 void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
1469 DSTACK(FUNCTION_NAME);
1471 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1476 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
1477 v3f camera_point_target)
1479 DSTACK(FUNCTION_NAME);
1481 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1482 pkt << set_camera_point_target << camera_point_target;
1486 void Server::SendItemDef(u16 peer_id,
1487 IItemDefManager *itemdef, u16 protocol_version)
1489 DSTACK(FUNCTION_NAME);
1491 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1495 u32 length of the next item
1496 zlib-compressed serialized ItemDefManager
1498 std::ostringstream tmp_os(std::ios::binary);
1499 itemdef->serialize(tmp_os, protocol_version);
1500 std::ostringstream tmp_os2(std::ios::binary);
1501 compressZlib(tmp_os.str(), tmp_os2);
1502 pkt.putLongString(tmp_os2.str());
1505 verbosestream << "Server: Sending item definitions to id(" << peer_id
1506 << "): size=" << pkt.getSize() << std::endl;
1511 void Server::SendNodeDef(u16 peer_id,
1512 INodeDefManager *nodedef, u16 protocol_version)
1514 DSTACK(FUNCTION_NAME);
1516 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1520 u32 length of the next item
1521 zlib-compressed serialized NodeDefManager
1523 std::ostringstream tmp_os(std::ios::binary);
1524 nodedef->serialize(tmp_os, protocol_version);
1525 std::ostringstream tmp_os2(std::ios::binary);
1526 compressZlib(tmp_os.str(), tmp_os2);
1528 pkt.putLongString(tmp_os2.str());
1531 verbosestream << "Server: Sending node definitions to id(" << peer_id
1532 << "): size=" << pkt.getSize() << std::endl;
1538 Non-static send methods
1541 void Server::SendInventory(PlayerSAO* playerSAO)
1543 DSTACK(FUNCTION_NAME);
1545 UpdateCrafting(playerSAO->getPlayer());
1551 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1553 std::ostringstream os;
1554 playerSAO->getInventory()->serialize(os);
1556 std::string s = os.str();
1558 pkt.putRawString(s.c_str(), s.size());
1562 void Server::SendChatMessage(u16 peer_id, const ChatMessage &message)
1564 DSTACK(FUNCTION_NAME);
1566 NetworkPacket legacypkt(TOCLIENT_CHAT_MESSAGE_OLD, 0, peer_id);
1567 legacypkt << message.message;
1569 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1571 u8 type = message.type;
1572 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1574 if (peer_id != PEER_ID_INEXISTENT) {
1575 RemotePlayer *player = m_env->getPlayer(peer_id);
1579 if (player->protocol_version < 35)
1584 m_clients.sendToAllCompat(&pkt, &legacypkt, 35);
1588 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
1589 const std::string &formname)
1591 DSTACK(FUNCTION_NAME);
1593 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1594 if (formspec.empty()){
1595 //the client should close the formspec
1596 pkt.putLongString("");
1598 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1605 // Spawns a particle on peer with peer_id
1606 void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
1607 v3f pos, v3f velocity, v3f acceleration,
1608 float expirationtime, float size, bool collisiondetection,
1609 bool collision_removal,
1610 bool vertical, const std::string &texture,
1611 const struct TileAnimationParams &animation, u8 glow)
1613 DSTACK(FUNCTION_NAME);
1614 static thread_local const float radius =
1615 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1617 if (peer_id == PEER_ID_INEXISTENT) {
1618 std::vector<u16> clients = m_clients.getClientIDs();
1620 for (const u16 client_id : clients) {
1621 RemotePlayer *player = m_env->getPlayer(client_id);
1625 PlayerSAO *sao = player->getPlayerSAO();
1629 // Do not send to distant clients
1630 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1633 SendSpawnParticle(client_id, player->protocol_version,
1634 pos, velocity, acceleration,
1635 expirationtime, size, collisiondetection,
1636 collision_removal, vertical, texture, animation, glow);
1641 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1643 pkt << pos << velocity << acceleration << expirationtime
1644 << size << collisiondetection;
1645 pkt.putLongString(texture);
1647 pkt << collision_removal;
1648 // This is horrible but required (why are there two ways to serialize pkts?)
1649 std::ostringstream os(std::ios_base::binary);
1650 animation.serialize(os, protocol_version);
1651 pkt.putRawString(os.str());
1657 // Adds a ParticleSpawner on peer with peer_id
1658 void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version,
1659 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1660 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1661 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1662 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1663 const struct TileAnimationParams &animation, u8 glow)
1665 DSTACK(FUNCTION_NAME);
1666 if (peer_id == PEER_ID_INEXISTENT) {
1667 // This sucks and should be replaced:
1668 std::vector<u16> clients = m_clients.getClientIDs();
1669 for (const u16 client_id : clients) {
1670 RemotePlayer *player = m_env->getPlayer(client_id);
1673 SendAddParticleSpawner(client_id, player->protocol_version,
1674 amount, spawntime, minpos, maxpos,
1675 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1676 minsize, maxsize, collisiondetection, collision_removal,
1677 attached_id, vertical, texture, id, animation, glow);
1682 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1684 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1685 << minacc << maxacc << minexptime << maxexptime << minsize
1686 << maxsize << collisiondetection;
1688 pkt.putLongString(texture);
1690 pkt << id << vertical;
1691 pkt << collision_removal;
1693 // This is horrible but required
1694 std::ostringstream os(std::ios_base::binary);
1695 animation.serialize(os, protocol_version);
1696 pkt.putRawString(os.str());
1702 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
1704 DSTACK(FUNCTION_NAME);
1706 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
1708 // Ugly error in this packet
1711 if (peer_id != PEER_ID_INEXISTENT) {
1715 m_clients.sendToAll(&pkt);
1720 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
1722 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1724 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1725 << form->text << form->number << form->item << form->dir
1726 << form->align << form->offset << form->world_pos << form->size;
1731 void Server::SendHUDRemove(u16 peer_id, u32 id)
1733 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1738 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
1740 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1741 pkt << id << (u8) stat;
1745 case HUD_STAT_SCALE:
1746 case HUD_STAT_ALIGN:
1747 case HUD_STAT_OFFSET:
1748 pkt << *(v2f *) value;
1752 pkt << *(std::string *) value;
1754 case HUD_STAT_WORLD_POS:
1755 pkt << *(v3f *) value;
1758 pkt << *(v2s32 *) value;
1760 case HUD_STAT_NUMBER:
1764 pkt << *(u32 *) value;
1771 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
1773 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1775 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1777 pkt << flags << mask;
1782 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
1784 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1785 pkt << param << value;
1789 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
1790 const std::string &type, const std::vector<std::string> ¶ms,
1793 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1794 pkt << bgcolor << type << (u16) params.size();
1796 for (const std::string ¶m : params)
1804 void Server::SendCloudParams(u16 peer_id, float density,
1805 const video::SColor &color_bright,
1806 const video::SColor &color_ambient,
1811 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1812 pkt << density << color_bright << color_ambient
1813 << height << thickness << speed;
1818 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
1821 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1824 pkt << do_override << (u16) (ratio * 65535);
1829 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
1831 DSTACK(FUNCTION_NAME);
1833 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1834 pkt << time << time_speed;
1836 if (peer_id == PEER_ID_INEXISTENT) {
1837 m_clients.sendToAll(&pkt);
1844 void Server::SendPlayerHP(u16 peer_id)
1846 DSTACK(FUNCTION_NAME);
1847 PlayerSAO *playersao = getPlayerSAO(peer_id);
1848 // In some rare case if the player is disconnected
1849 // while Lua call l_punch, for example, this can be NULL
1853 SendHP(peer_id, playersao->getHP());
1854 m_script->player_event(playersao,"health_changed");
1856 // Send to other clients
1857 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1858 ActiveObjectMessage aom(playersao->getId(), true, str);
1859 playersao->m_messages_out.push(aom);
1862 void Server::SendPlayerBreath(PlayerSAO *sao)
1864 DSTACK(FUNCTION_NAME);
1867 m_script->player_event(sao, "breath_changed");
1868 SendBreath(sao->getPeerID(), sao->getBreath());
1871 void Server::SendMovePlayer(u16 peer_id)
1873 DSTACK(FUNCTION_NAME);
1874 RemotePlayer *player = m_env->getPlayer(peer_id);
1876 PlayerSAO *sao = player->getPlayerSAO();
1879 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1880 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1883 v3f pos = sao->getBasePosition();
1884 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1885 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1886 << " pitch=" << sao->getPitch()
1887 << " yaw=" << sao->getYaw()
1894 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
1896 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1899 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1900 << animation_frames[3] << animation_speed;
1905 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
1907 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1908 pkt << first << third;
1911 void Server::SendPlayerPrivileges(u16 peer_id)
1913 RemotePlayer *player = m_env->getPlayer(peer_id);
1915 if(player->peer_id == PEER_ID_INEXISTENT)
1918 std::set<std::string> privs;
1919 m_script->getAuth(player->getName(), NULL, &privs);
1921 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1922 pkt << (u16) privs.size();
1924 for (const std::string &priv : privs) {
1931 void Server::SendPlayerInventoryFormspec(u16 peer_id)
1933 RemotePlayer *player = m_env->getPlayer(peer_id);
1935 if(player->peer_id == PEER_ID_INEXISTENT)
1938 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1939 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1943 u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
1945 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1946 pkt.putRawString(datas.c_str(), datas.size());
1948 return pkt.getSize();
1951 void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
1953 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1954 datas.size(), peer_id);
1956 pkt.putRawString(datas.c_str(), datas.size());
1958 m_clients.send(pkt.getPeerId(),
1959 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1963 void Server::SendCSMFlavourLimits(u16 peer_id)
1965 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1966 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1967 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1971 s32 Server::playSound(const SimpleSoundSpec &spec,
1972 const ServerSoundParams ¶ms)
1974 // Find out initial position of sound
1975 bool pos_exists = false;
1976 v3f pos = params.getPos(m_env, &pos_exists);
1977 // If position is not found while it should be, cancel sound
1978 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1981 // Filter destination clients
1982 std::vector<u16> dst_clients;
1983 if(!params.to_player.empty()) {
1984 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1986 infostream<<"Server::playSound: Player \""<<params.to_player
1987 <<"\" not found"<<std::endl;
1990 if(player->peer_id == PEER_ID_INEXISTENT){
1991 infostream<<"Server::playSound: Player \""<<params.to_player
1992 <<"\" not connected"<<std::endl;
1995 dst_clients.push_back(player->peer_id);
1997 std::vector<u16> clients = m_clients.getClientIDs();
1999 for (const u16 client_id : clients) {
2000 RemotePlayer *player = m_env->getPlayer(client_id);
2004 PlayerSAO *sao = player->getPlayerSAO();
2009 if(sao->getBasePosition().getDistanceFrom(pos) >
2010 params.max_hear_distance)
2013 dst_clients.push_back(client_id);
2017 if(dst_clients.empty())
2021 s32 id = m_next_sound_id++;
2022 // The sound will exist as a reference in m_playing_sounds
2023 m_playing_sounds[id] = ServerPlayingSound();
2024 ServerPlayingSound &psound = m_playing_sounds[id];
2025 psound.params = params;
2028 float gain = params.gain * spec.gain;
2029 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2030 pkt << id << spec.name << gain
2031 << (u8) params.type << pos << params.object
2032 << params.loop << params.fade << params.pitch;
2034 // Backwards compability
2035 bool play_sound = gain > 0;
2037 for (const u16 dst_client : dst_clients) {
2038 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2039 psound.clients.insert(dst_client);
2040 m_clients.send(dst_client, 0, &pkt, true);
2045 void Server::stopSound(s32 handle)
2047 // Get sound reference
2048 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2049 m_playing_sounds.find(handle);
2050 if (i == m_playing_sounds.end())
2052 ServerPlayingSound &psound = i->second;
2054 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2057 for (std::unordered_set<u16>::const_iterator si = psound.clients.begin();
2058 si != psound.clients.end(); ++si) {
2060 m_clients.send(*si, 0, &pkt, true);
2062 // Remove sound reference
2063 m_playing_sounds.erase(i);
2066 void Server::fadeSound(s32 handle, float step, float gain)
2068 // Get sound reference
2069 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2070 m_playing_sounds.find(handle);
2071 if (i == m_playing_sounds.end())
2074 ServerPlayingSound &psound = i->second;
2075 psound.params.gain = gain;
2077 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2078 pkt << handle << step << gain;
2080 // Backwards compability
2081 bool play_sound = gain > 0;
2082 ServerPlayingSound compat_psound = psound;
2083 compat_psound.clients.clear();
2085 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2086 compat_pkt << handle;
2088 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2089 it != psound.clients.end();) {
2090 if (m_clients.getProtocolVersion(*it) >= 32) {
2092 m_clients.send(*it, 0, &pkt, true);
2095 compat_psound.clients.insert(*it);
2097 m_clients.send(*it, 0, &compat_pkt, true);
2098 psound.clients.erase(it++);
2102 // Remove sound reference
2103 if (!play_sound || psound.clients.empty())
2104 m_playing_sounds.erase(i);
2106 if (play_sound && !compat_psound.clients.empty()) {
2107 // Play new sound volume on older clients
2108 playSound(compat_psound.spec, compat_psound.params);
2112 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2113 std::vector<u16> *far_players, float far_d_nodes)
2115 float maxd = far_d_nodes*BS;
2116 v3f p_f = intToFloat(p, BS);
2118 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2121 std::vector<u16> clients = m_clients.getClientIDs();
2122 for (u16 client_id : clients) {
2125 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2126 PlayerSAO *sao = player->getPlayerSAO();
2130 // If player is far away, only set modified blocks not sent
2131 v3f player_pos = sao->getBasePosition();
2132 if (player_pos.getDistanceFrom(p_f) > maxd) {
2133 far_players->push_back(client_id);
2140 m_clients.send(client_id, 0, &pkt, true);
2144 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2145 std::vector<u16> *far_players, float far_d_nodes,
2146 bool remove_metadata)
2148 float maxd = far_d_nodes*BS;
2149 v3f p_f = intToFloat(p, BS);
2151 std::vector<u16> clients = m_clients.getClientIDs();
2152 for (const u16 client_id : clients) {
2155 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2156 PlayerSAO *sao = player->getPlayerSAO();
2160 // If player is far away, only set modified blocks not sent
2161 v3f player_pos = sao->getBasePosition();
2162 if(player_pos.getDistanceFrom(p_f) > maxd) {
2163 far_players->push_back(client_id);
2169 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2171 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2173 pkt << p << n.param0 << n.param1 << n.param2
2174 << (u8) (remove_metadata ? 0 : 1);
2179 if (pkt.getSize() > 0)
2180 m_clients.send(client_id, 0, &pkt, true);
2184 void Server::setBlockNotSent(v3s16 p)
2186 std::vector<u16> clients = m_clients.getClientIDs();
2188 for (const u16 i : clients) {
2189 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2190 client->SetBlockNotSent(p);
2195 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
2197 DSTACK(FUNCTION_NAME);
2199 v3s16 p = block->getPos();
2202 Create a packet with the block in the right format
2205 std::ostringstream os(std::ios_base::binary);
2206 block->serialize(os, ver, false);
2207 block->serializeNetworkSpecific(os);
2208 std::string s = os.str();
2210 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2213 pkt.putRawString(s.c_str(), s.size());
2217 void Server::SendBlocks(float dtime)
2219 DSTACK(FUNCTION_NAME);
2221 MutexAutoLock envlock(m_env_mutex);
2222 //TODO check if one big lock could be faster then multiple small ones
2224 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2226 std::vector<PrioritySortedBlockTransfer> queue;
2228 s32 total_sending = 0;
2231 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2233 std::vector<u16> clients = m_clients.getClientIDs();
2236 for (const u16 client_id : clients) {
2237 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2242 total_sending += client->SendingCount();
2243 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2249 // Lowest priority number comes first.
2250 // Lowest is most important.
2251 std::sort(queue.begin(), queue.end());
2254 s32 max_blocks_to_send =
2255 g_settings->getS32("max_simultaneous_block_sends_server_total");
2257 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2258 //TODO: Calculate limit dynamically
2259 if (total_sending >= max_blocks_to_send)
2262 MapBlock *block = nullptr;
2264 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2265 } catch (const InvalidPositionException &e) {
2269 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2274 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2275 client->net_proto_version);
2277 client->SentBlock(block_to_send.pos);
2283 void Server::fillMediaCache()
2285 DSTACK(FUNCTION_NAME);
2287 infostream<<"Server: Calculating media file checksums"<<std::endl;
2289 // Collect all media file paths
2290 std::vector<std::string> paths;
2291 for (const ModSpec &mod : m_mods) {
2292 paths.push_back(mod.path + DIR_DELIM + "textures");
2293 paths.push_back(mod.path + DIR_DELIM + "sounds");
2294 paths.push_back(mod.path + DIR_DELIM + "media");
2295 paths.push_back(mod.path + DIR_DELIM + "models");
2296 paths.push_back(mod.path + DIR_DELIM + "locale");
2298 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2300 // Collect media file information from paths into cache
2301 for (const std::string &mediapath : paths) {
2302 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2303 for (const fs::DirListNode &dln : dirlist) {
2304 if (dln.dir) // Ignode dirs
2306 std::string filename = dln.name;
2307 // If name contains illegal characters, ignore the file
2308 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2309 infostream<<"Server: ignoring illegal file name: \""
2310 << filename << "\"" << std::endl;
2313 // If name is not in a supported format, ignore it
2314 const char *supported_ext[] = {
2315 ".png", ".jpg", ".bmp", ".tga",
2316 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2318 ".x", ".b3d", ".md2", ".obj",
2319 // Custom translation file format
2323 if (removeStringEnd(filename, supported_ext).empty()){
2324 infostream << "Server: ignoring unsupported file extension: \""
2325 << filename << "\"" << std::endl;
2328 // Ok, attempt to load the file and add to cache
2329 std::string filepath;
2330 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2333 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2335 errorstream << "Server::fillMediaCache(): Could not open \""
2336 << filename << "\" for reading" << std::endl;
2339 std::ostringstream tmp_os(std::ios_base::binary);
2343 fis.read(buf, 1024);
2344 std::streamsize len = fis.gcount();
2345 tmp_os.write(buf, len);
2354 errorstream<<"Server::fillMediaCache(): Failed to read \""
2355 << filename << "\"" << std::endl;
2358 if(tmp_os.str().length() == 0) {
2359 errorstream << "Server::fillMediaCache(): Empty file \""
2360 << filepath << "\"" << std::endl;
2365 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2367 unsigned char *digest = sha1.getDigest();
2368 std::string sha1_base64 = base64_encode(digest, 20);
2369 std::string sha1_hex = hex_encode((char*)digest, 20);
2373 m_media[filename] = MediaInfo(filepath, sha1_base64);
2374 verbosestream << "Server: " << sha1_hex << " is " << filename
2380 void Server::sendMediaAnnouncement(u16 peer_id, const std::string &lang_code)
2382 DSTACK(FUNCTION_NAME);
2384 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2388 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2391 std::string lang_suffix;
2392 lang_suffix.append(".").append(lang_code).append(".tr");
2393 for (const auto &i : m_media) {
2394 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2401 for (const auto &i : m_media) {
2402 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2404 pkt << i.first << i.second.sha1_digest;
2407 pkt << g_settings->get("remote_media");
2411 struct SendableMedia
2417 SendableMedia(const std::string &name_="", const std::string &path_="",
2418 const std::string &data_=""):
2425 void Server::sendRequestedMedia(u16 peer_id,
2426 const std::vector<std::string> &tosend)
2428 DSTACK(FUNCTION_NAME);
2430 verbosestream<<"Server::sendRequestedMedia(): "
2431 <<"Sending files to client"<<std::endl;
2435 // Put 5kB in one bunch (this is not accurate)
2436 u32 bytes_per_bunch = 5000;
2438 std::vector< std::vector<SendableMedia> > file_bunches;
2439 file_bunches.emplace_back();
2441 u32 file_size_bunch_total = 0;
2443 for (const std::string &name : tosend) {
2444 if (m_media.find(name) == m_media.end()) {
2445 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2446 <<"unknown file \""<<(name)<<"\""<<std::endl;
2450 //TODO get path + name
2451 std::string tpath = m_media[name].path;
2454 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2456 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2457 <<tpath<<"\" for reading"<<std::endl;
2460 std::ostringstream tmp_os(std::ios_base::binary);
2464 fis.read(buf, 1024);
2465 std::streamsize len = fis.gcount();
2466 tmp_os.write(buf, len);
2467 file_size_bunch_total += len;
2476 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2477 <<name<<"\""<<std::endl;
2480 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2481 <<tname<<"\""<<std::endl;*/
2483 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2485 // Start next bunch if got enough data
2486 if(file_size_bunch_total >= bytes_per_bunch) {
2487 file_bunches.emplace_back();
2488 file_size_bunch_total = 0;
2493 /* Create and send packets */
2495 u16 num_bunches = file_bunches.size();
2496 for (u16 i = 0; i < num_bunches; i++) {
2499 u16 total number of texture bunches
2500 u16 index of this bunch
2501 u32 number of files in this bunch
2510 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2511 pkt << num_bunches << i << (u32) file_bunches[i].size();
2513 for (const SendableMedia &j : file_bunches[i]) {
2515 pkt.putLongString(j.data);
2518 verbosestream << "Server::sendRequestedMedia(): bunch "
2519 << i << "/" << num_bunches
2520 << " files=" << file_bunches[i].size()
2521 << " size=" << pkt.getSize() << std::endl;
2526 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
2528 if(m_detached_inventories.count(name) == 0) {
2529 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2532 Inventory *inv = m_detached_inventories[name];
2533 std::ostringstream os(std::ios_base::binary);
2535 os << serializeString(name);
2539 std::string s = os.str();
2541 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2542 pkt.putRawString(s.c_str(), s.size());
2544 const std::string &check = m_detached_inventories_player[name];
2545 if (peer_id == PEER_ID_INEXISTENT) {
2547 return m_clients.sendToAll(&pkt);
2548 RemotePlayer *p = m_env->getPlayer(check.c_str());
2550 m_clients.send(p->peer_id, 0, &pkt, true);
2552 if (check.empty() || getPlayerName(peer_id) == check)
2557 void Server::sendDetachedInventories(u16 peer_id)
2559 DSTACK(FUNCTION_NAME);
2561 for (const auto &detached_inventory : m_detached_inventories) {
2562 const std::string &name = detached_inventory.first;
2563 //Inventory *inv = i->second;
2564 sendDetachedInventory(name, peer_id);
2572 void Server::DiePlayer(u16 peer_id)
2574 DSTACK(FUNCTION_NAME);
2575 PlayerSAO *playersao = getPlayerSAO(peer_id);
2576 // In some rare cases this can be NULL -- if the player is disconnected
2577 // when a Lua function modifies l_punch, for example
2581 infostream << "Server::DiePlayer(): Player "
2582 << playersao->getPlayer()->getName()
2583 << " dies" << std::endl;
2585 playersao->setHP(0);
2587 // Trigger scripted stuff
2588 m_script->on_dieplayer(playersao);
2590 SendPlayerHP(peer_id);
2591 SendDeathscreen(peer_id, false, v3f(0,0,0));
2594 void Server::RespawnPlayer(u16 peer_id)
2596 DSTACK(FUNCTION_NAME);
2598 PlayerSAO *playersao = getPlayerSAO(peer_id);
2601 infostream << "Server::RespawnPlayer(): Player "
2602 << playersao->getPlayer()->getName()
2603 << " respawns" << std::endl;
2605 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2606 playersao->setBreath(PLAYER_MAX_BREATH);
2608 bool repositioned = m_script->on_respawnplayer(playersao);
2609 if (!repositioned) {
2610 // setPos will send the new position to client
2611 playersao->setPos(findSpawnPos());
2614 SendPlayerHP(peer_id);
2618 void Server::DenySudoAccess(u16 peer_id)
2620 DSTACK(FUNCTION_NAME);
2622 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2627 void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
2628 const std::string &str_reason, bool reconnect)
2630 if (proto_ver >= 25) {
2631 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2633 std::wstring wreason = utf8_to_wide(
2634 reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
2635 accessDeniedStrings[(u8)reason]);
2636 SendAccessDenied_Legacy(peer_id, wreason);
2639 m_clients.event(peer_id, CSE_SetDenied);
2640 m_con->DisconnectPeer(peer_id);
2644 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
2646 DSTACK(FUNCTION_NAME);
2648 SendAccessDenied(peer_id, reason, custom_reason);
2649 m_clients.event(peer_id, CSE_SetDenied);
2650 m_con->DisconnectPeer(peer_id);
2653 // 13/03/15: remove this function when protocol version 25 will become
2654 // the minimum version for MT users, maybe in 1 year
2655 void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
2657 DSTACK(FUNCTION_NAME);
2659 SendAccessDenied_Legacy(peer_id, reason);
2660 m_clients.event(peer_id, CSE_SetDenied);
2661 m_con->DisconnectPeer(peer_id);
2664 void Server::acceptAuth(u16 peer_id, bool forSudoMode)
2666 DSTACK(FUNCTION_NAME);
2669 RemoteClient* client = getClient(peer_id, CS_Invalid);
2671 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2673 // Right now, the auth mechs don't change between login and sudo mode.
2674 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2675 client->allowed_sudo_mechs = sudo_auth_mechs;
2677 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2678 << g_settings->getFloat("dedicated_server_step")
2682 m_clients.event(peer_id, CSE_AuthAccept);
2684 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2686 // We only support SRP right now
2687 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2689 resp_pkt << sudo_auth_mechs;
2691 m_clients.event(peer_id, CSE_SudoSuccess);
2695 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
2697 DSTACK(FUNCTION_NAME);
2698 std::wstring message;
2701 Clear references to playing sounds
2703 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2704 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2705 ServerPlayingSound &psound = i->second;
2706 psound.clients.erase(peer_id);
2707 if (psound.clients.empty())
2708 m_playing_sounds.erase(i++);
2713 RemotePlayer *player = m_env->getPlayer(peer_id);
2715 /* Run scripts and remove from environment */
2717 PlayerSAO *playersao = player->getPlayerSAO();
2720 // inform connected clients
2721 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2722 // (u16) 1 + std::string represents a vector serialization representation
2723 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2724 m_clients.sendToAll(¬ice);
2726 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2728 playersao->disconnected();
2735 if (player && reason != CDR_DENY) {
2736 std::ostringstream os(std::ios_base::binary);
2737 std::vector<u16> clients = m_clients.getClientIDs();
2739 for (const u16 client_id : clients) {
2741 RemotePlayer *player = m_env->getPlayer(client_id);
2745 // Get name of player
2746 os << player->getName() << " ";
2749 std::string name = player->getName();
2750 actionstream << name << " "
2751 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2752 << " List of players: " << os.str() << std::endl;
2754 m_admin_chat->outgoing_queue.push_back(
2755 new ChatEventNick(CET_NICK_REMOVE, name));
2759 MutexAutoLock env_lock(m_env_mutex);
2760 m_clients.DeleteClient(peer_id);
2764 // Send leave chat message to all remaining clients
2765 if (!message.empty()) {
2766 SendChatMessage(PEER_ID_INEXISTENT,
2767 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2771 void Server::UpdateCrafting(RemotePlayer *player)
2773 DSTACK(FUNCTION_NAME);
2775 // Get a preview for crafting
2777 InventoryLocation loc;
2778 loc.setPlayer(player->getName());
2779 std::vector<ItemStack> output_replacements;
2780 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2781 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2782 (&player->inventory)->getList("craft"), loc);
2784 // Put the new preview in
2785 InventoryList *plist = player->inventory.getList("craftpreview");
2786 sanity_check(plist);
2787 sanity_check(plist->getSize() >= 1);
2788 plist->changeItem(0, preview);
2791 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2793 if (evt->type == CET_NICK_ADD) {
2794 // The terminal informed us of its nick choice
2795 m_admin_nick = ((ChatEventNick *)evt)->nick;
2796 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2797 errorstream << "You haven't set up an account." << std::endl
2798 << "Please log in using the client as '"
2799 << m_admin_nick << "' with a secure password." << std::endl
2800 << "Until then, you can't execute admin tasks via the console," << std::endl
2801 << "and everybody can claim the user account instead of you," << std::endl
2802 << "giving them full control over this server." << std::endl;
2805 assert(evt->type == CET_CHAT);
2806 handleAdminChat((ChatEventChat *)evt);
2810 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2811 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2813 // If something goes wrong, this player is to blame
2814 RollbackScopeActor rollback_scope(m_rollback,
2815 std::string("player:") + name);
2817 if (g_settings->getBool("strip_color_codes"))
2818 wmessage = unescape_enriched(wmessage);
2821 switch (player->canSendChatMessage()) {
2822 case RPLAYER_CHATRESULT_FLOODING: {
2823 std::wstringstream ws;
2824 ws << L"You cannot send more messages. You are limited to "
2825 << g_settings->getFloat("chat_message_limit_per_10sec")
2826 << L" messages per 10 seconds.";
2829 case RPLAYER_CHATRESULT_KICK:
2830 DenyAccess_Legacy(player->peer_id,
2831 L"You have been kicked due to message flooding.");
2833 case RPLAYER_CHATRESULT_OK:
2836 FATAL_ERROR("Unhandled chat filtering result found.");
2840 if (m_max_chatmessage_length > 0
2841 && wmessage.length() > m_max_chatmessage_length) {
2842 return L"Your message exceed the maximum chat message limit set on the server. "
2843 L"It was refused. Send a shorter message";
2846 // Run script hook, exit if script ate the chat message
2847 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2852 // Whether to send line to the player that sent the message, or to all players
2853 bool broadcast_line = true;
2855 if (check_shout_priv && !checkPriv(name, "shout")) {
2856 line += L"-!- You don't have permission to shout.";
2857 broadcast_line = false;
2866 Tell calling method to send the message to sender
2868 if (!broadcast_line)
2872 Send the message to others
2874 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2876 std::vector<u16> clients = m_clients.getClientIDs();
2879 Send the message back to the inital sender
2880 if they are using protocol version >= 29
2883 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2884 if (player && player->protocol_version >= 29)
2885 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2887 for (u16 cid : clients) {
2888 if (cid != peer_id_to_avoid_sending)
2889 SendChatMessage(cid, ChatMessage(line));
2894 void Server::handleAdminChat(const ChatEventChat *evt)
2896 std::string name = evt->nick;
2897 std::wstring wname = utf8_to_wide(name);
2898 std::wstring wmessage = evt->evt_msg;
2900 std::wstring answer = handleChat(name, wname, wmessage);
2902 // If asked to send answer to sender
2903 if (!answer.empty()) {
2904 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2908 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2910 RemoteClient *client = getClientNoEx(peer_id,state_min);
2912 throw ClientNotFoundException("Client not found");
2916 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2918 return m_clients.getClientNoEx(peer_id, state_min);
2921 std::string Server::getPlayerName(u16 peer_id)
2923 RemotePlayer *player = m_env->getPlayer(peer_id);
2925 return "[id="+itos(peer_id)+"]";
2926 return player->getName();
2929 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2931 RemotePlayer *player = m_env->getPlayer(peer_id);
2934 return player->getPlayerSAO();
2937 std::wstring Server::getStatusString()
2939 std::wostringstream os(std::ios_base::binary);
2942 os<<L"version="<<narrow_to_wide(g_version_string);
2944 os<<L", uptime="<<m_uptime.get();
2946 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2947 // Information about clients
2950 std::vector<u16> clients = m_clients.getClientIDs();
2951 for (u16 client_id : clients) {
2953 RemotePlayer *player = m_env->getPlayer(client_id);
2954 // Get name of player
2955 std::wstring name = L"unknown";
2957 name = narrow_to_wide(player->getName());
2958 // Add name to information string
2967 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2968 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2970 if (!g_settings->get("motd").empty())
2971 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2975 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2977 std::set<std::string> privs;
2978 m_script->getAuth(name, NULL, &privs);
2982 bool Server::checkPriv(const std::string &name, const std::string &priv)
2984 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2985 return (privs.count(priv) != 0);
2988 void Server::reportPrivsModified(const std::string &name)
2991 std::vector<u16> clients = m_clients.getClientIDs();
2992 for (const u16 client_id : clients) {
2993 RemotePlayer *player = m_env->getPlayer(client_id);
2994 reportPrivsModified(player->getName());
2997 RemotePlayer *player = m_env->getPlayer(name.c_str());
3000 SendPlayerPrivileges(player->peer_id);
3001 PlayerSAO *sao = player->getPlayerSAO();
3004 sao->updatePrivileges(
3005 getPlayerEffectivePrivs(name),
3010 void Server::reportInventoryFormspecModified(const std::string &name)
3012 RemotePlayer *player = m_env->getPlayer(name.c_str());
3015 SendPlayerInventoryFormspec(player->peer_id);
3018 void Server::setIpBanned(const std::string &ip, const std::string &name)
3020 m_banmanager->add(ip, name);
3023 void Server::unsetIpBanned(const std::string &ip_or_name)
3025 m_banmanager->remove(ip_or_name);
3028 std::string Server::getBanDescription(const std::string &ip_or_name)
3030 return m_banmanager->getBanDescription(ip_or_name);
3033 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3035 // m_env will be NULL if the server is initializing
3039 if (m_admin_nick == name && !m_admin_nick.empty()) {
3040 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3043 RemotePlayer *player = m_env->getPlayer(name);
3048 if (player->peer_id == PEER_ID_INEXISTENT)
3051 SendChatMessage(player->peer_id, ChatMessage(msg));
3054 bool Server::showFormspec(const char *playername, const std::string &formspec,
3055 const std::string &formname)
3057 // m_env will be NULL if the server is initializing
3061 RemotePlayer *player = m_env->getPlayer(playername);
3065 SendShowFormspecMessage(player->peer_id, formspec, formname);
3069 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3074 u32 id = player->addHud(form);
3076 SendHUDAdd(player->peer_id, id, form);
3081 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3085 HudElement* todel = player->removeHud(id);
3092 SendHUDRemove(player->peer_id, id);
3096 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3101 SendHUDChange(player->peer_id, id, stat, data);
3105 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3110 SendHUDSetFlags(player->peer_id, flags, mask);
3111 player->hud_flags &= ~mask;
3112 player->hud_flags |= flags;
3114 PlayerSAO* playersao = player->getPlayerSAO();
3119 m_script->player_event(playersao, "hud_changed");
3123 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3128 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3131 player->setHotbarItemcount(hotbar_itemcount);
3132 std::ostringstream os(std::ios::binary);
3133 writeS32(os, hotbar_itemcount);
3134 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3138 s32 Server::hudGetHotbarItemcount(RemotePlayer *player) const
3140 return player->getHotbarItemcount();
3143 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3148 player->setHotbarImage(name);
3149 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3152 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3156 return player->getHotbarImage();
3159 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3164 player->setHotbarSelectedImage(name);
3165 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3168 const std::string& Server::hudGetHotbarSelectedImage(RemotePlayer *player) const
3170 return player->getHotbarSelectedImage();
3173 Address Server::getPeerAddress(u16 peer_id)
3175 return m_con->GetPeerAddress(peer_id);
3178 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3179 v2s32 animation_frames[4], f32 frame_speed)
3184 player->setLocalAnimations(animation_frames, frame_speed);
3185 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3189 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3194 player->eye_offset_first = first;
3195 player->eye_offset_third = third;
3196 SendEyeOffset(player->peer_id, first, third);
3200 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3201 const std::string &type, const std::vector<std::string> ¶ms,
3207 player->setSky(bgcolor, type, params, clouds);
3208 SendSetSky(player->peer_id, bgcolor, type, params, clouds);
3212 bool Server::setClouds(RemotePlayer *player, float density,
3213 const video::SColor &color_bright,
3214 const video::SColor &color_ambient,
3222 SendCloudParams(player->peer_id, density,
3223 color_bright, color_ambient, height,
3228 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3234 player->overrideDayNightRatio(do_override, ratio);
3235 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3239 void Server::notifyPlayers(const std::wstring &msg)
3241 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3244 void Server::spawnParticle(const std::string &playername, v3f pos,
3245 v3f velocity, v3f acceleration,
3246 float expirationtime, float size, bool
3247 collisiondetection, bool collision_removal,
3248 bool vertical, const std::string &texture,
3249 const struct TileAnimationParams &animation, u8 glow)
3251 // m_env will be NULL if the server is initializing
3255 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3256 if (!playername.empty()) {
3257 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3260 peer_id = player->peer_id;
3261 proto_ver = player->protocol_version;
3264 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3265 expirationtime, size, collisiondetection,
3266 collision_removal, vertical, texture, animation, glow);
3269 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3270 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3271 float minexptime, float maxexptime, float minsize, float maxsize,
3272 bool collisiondetection, bool collision_removal,
3273 ServerActiveObject *attached, bool vertical, const std::string &texture,
3274 const std::string &playername, const struct TileAnimationParams &animation,
3277 // m_env will be NULL if the server is initializing
3281 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3282 if (!playername.empty()) {
3283 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3286 peer_id = player->peer_id;
3287 proto_ver = player->protocol_version;
3290 u16 attached_id = attached ? attached->getId() : 0;
3293 if (attached_id == 0)
3294 id = m_env->addParticleSpawner(spawntime);
3296 id = m_env->addParticleSpawner(spawntime, attached_id);
3298 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3299 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3300 minexptime, maxexptime, minsize, maxsize,
3301 collisiondetection, collision_removal, attached_id, vertical,
3302 texture, id, animation, glow);
3307 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3309 // m_env will be NULL if the server is initializing
3311 throw ServerError("Can't delete particle spawners during initialisation!");
3313 u16 peer_id = PEER_ID_INEXISTENT;
3314 if (!playername.empty()) {
3315 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3318 peer_id = player->peer_id;
3321 m_env->deleteParticleSpawner(id);
3322 SendDeleteParticleSpawner(peer_id, id);
3325 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3327 if(m_detached_inventories.count(name) > 0){
3328 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3329 delete m_detached_inventories[name];
3331 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3333 Inventory *inv = new Inventory(m_itemdef);
3335 m_detached_inventories[name] = inv;
3336 m_detached_inventories_player[name] = player;
3337 //TODO find a better way to do this
3338 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3342 // actions: time-reversed list
3343 // Return value: success/failure
3344 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3345 std::list<std::string> *log)
3347 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3348 ServerMap *map = (ServerMap*)(&m_env->getMap());
3350 // Fail if no actions to handle
3351 if(actions.empty()){
3352 log->push_back("Nothing to do.");
3359 for (const RollbackAction &action : actions) {
3361 bool success = action.applyRevert(map, this, this);
3364 std::ostringstream os;
3365 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3366 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3368 log->push_back(os.str());
3370 std::ostringstream os;
3371 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3372 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3374 log->push_back(os.str());
3378 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3379 <<" failed"<<std::endl;
3381 // Call it done if less than half failed
3382 return num_failed <= num_tried/2;
3385 // IGameDef interface
3387 IItemDefManager *Server::getItemDefManager()
3392 INodeDefManager *Server::getNodeDefManager()
3397 ICraftDefManager *Server::getCraftDefManager()
3402 u16 Server::allocateUnknownNodeId(const std::string &name)
3404 return m_nodedef->allocateDummy(name);
3407 MtEventManager *Server::getEventManager()
3412 IWritableItemDefManager *Server::getWritableItemDefManager()
3417 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3422 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3427 const ModSpec *Server::getModSpec(const std::string &modname) const
3429 std::vector<ModSpec>::const_iterator it;
3430 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3431 const ModSpec &mod = *it;
3432 if (mod.name == modname)
3438 void Server::getModNames(std::vector<std::string> &modlist)
3440 std::vector<ModSpec>::iterator it;
3441 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3442 modlist.push_back(it->name);
3445 std::string Server::getBuiltinLuaPath()
3447 return porting::path_share + DIR_DELIM + "builtin";
3450 std::string Server::getModStoragePath() const
3452 return m_path_world + DIR_DELIM + "mod_storage";
3455 v3f Server::findSpawnPos()
3457 ServerMap &map = m_env->getServerMap();
3459 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3460 return nodeposf * BS;
3463 bool is_good = false;
3464 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3465 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3467 // Try to find a good place a few times
3468 for(s32 i = 0; i < 4000 && !is_good; i++) {
3469 s32 range = MYMIN(1 + i, range_max);
3470 // We're going to try to throw the player to this position
3471 v2s16 nodepos2d = v2s16(
3472 -range + (myrand() % (range * 2)),
3473 -range + (myrand() % (range * 2)));
3475 // Get spawn level at point
3476 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3477 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3478 // the mapgen to signify an unsuitable spawn position
3479 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3482 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3485 for (s32 i = 0; i < 10; i++) {
3486 v3s16 blockpos = getNodeBlockPos(nodepos);
3487 map.emergeBlock(blockpos, true);
3488 content_t c = map.getNodeNoEx(nodepos).getContent();
3489 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3491 if (air_count >= 2) {
3492 nodeposf = intToFloat(nodepos, BS);
3493 // Don't spawn the player outside map boundaries
3494 if (objectpos_over_limit(nodeposf))
3507 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3509 m_shutdown_timer = delay;
3510 m_shutdown_msg = msg;
3511 m_shutdown_ask_reconnect = reconnect;
3513 if (delay == 0.0f) {
3514 // No delay, shutdown immediately
3515 m_shutdown_requested = true;
3516 // only print to the infostream, a chat message saying
3517 // "Server Shutting Down" is sent when the server destructs.
3518 infostream << "*** Immediate Server shutdown requested." << std::endl;
3519 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3520 // Negative delay, cancel shutdown if requested
3521 m_shutdown_timer = 0.0f;
3522 m_shutdown_msg = "";
3523 m_shutdown_ask_reconnect = false;
3524 m_shutdown_requested = false;
3525 std::wstringstream ws;
3527 ws << L"*** Server shutdown canceled.";
3529 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3530 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3531 } else if (delay > 0.0f) {
3532 // Positive delay, tell the clients when the server will shut down
3533 std::wstringstream ws;
3535 ws << L"*** Server shutting down in "
3536 << duration_to_string(myround(m_shutdown_timer)).c_str()
3539 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3540 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3544 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3547 Try to get an existing player
3549 RemotePlayer *player = m_env->getPlayer(name);
3551 // If player is already connected, cancel
3552 if (player && player->peer_id != 0) {
3553 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3558 If player with the wanted peer_id already exists, cancel.
3560 if (m_env->getPlayer(peer_id)) {
3561 infostream<<"emergePlayer(): Player with wrong name but same"
3562 " peer_id already exists"<<std::endl;
3567 player = new RemotePlayer(name, idef());
3570 bool newplayer = false;
3573 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3575 // Complete init with server parts
3576 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3577 player->protocol_version = proto_version;
3581 m_script->on_newplayer(playersao);
3587 bool Server::registerModStorage(ModMetadata *storage)
3589 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3590 errorstream << "Unable to register same mod storage twice. Storage name: "
3591 << storage->getModName() << std::endl;
3595 m_mod_storages[storage->getModName()] = storage;
3599 void Server::unregisterModStorage(const std::string &name)
3601 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3602 if (it != m_mod_storages.end()) {
3603 // Save unconditionaly on unregistration
3604 it->second->save(getModStoragePath());
3605 m_mod_storages.erase(name);
3609 void dedicated_server_loop(Server &server, bool &kill)
3611 DSTACK(FUNCTION_NAME);
3613 verbosestream<<"dedicated_server_loop()"<<std::endl;
3615 IntervalLimiter m_profiler_interval;
3617 static thread_local const float steplen =
3618 g_settings->getFloat("dedicated_server_step");
3619 static thread_local const float profiler_print_interval =
3620 g_settings->getFloat("profiler_print_interval");
3623 // This is kind of a hack but can be done like this
3624 // because server.step() is very light
3626 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3627 sleep_ms((int)(steplen*1000.0));
3629 server.step(steplen);
3631 if (server.getShutdownRequested() || kill)
3637 if (profiler_print_interval != 0) {
3638 if(m_profiler_interval.step(steplen, profiler_print_interval))
3640 infostream<<"Profiler:"<<std::endl;
3641 g_profiler->print(infostream);
3642 g_profiler->clear();
3647 infostream << "Dedicated server quitting" << std::endl;
3649 if (g_settings->getBool("server_announce"))
3650 ServerList::sendAnnounce(ServerList::AA_DELETE,
3651 server.m_bind_addr.getPort());