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"
64 #include "chat_interface.h"
66 class ClientNotFoundException : public BaseException
69 ClientNotFoundException(const char *s):
74 class ServerThread : public Thread
78 ServerThread(Server *server):
89 void *ServerThread::run()
91 DSTACK(FUNCTION_NAME);
92 BEGIN_DEBUG_EXCEPTION_HANDLER
94 m_server->AsyncRunStep(true);
96 while (!stopRequested()) {
98 //TimeTaker timer("AsyncRunStep() + Receive()");
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(""),
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 (std::map<std::string, Inventory*>::iterator
364 i = m_detached_inventories.begin();
365 i != m_detached_inventories.end(); ++i) {
370 void Server::start(Address bind_addr)
372 DSTACK(FUNCTION_NAME);
374 m_bind_addr = bind_addr;
376 infostream<<"Starting server on "
377 << bind_addr.serializeString() <<"..."<<std::endl;
379 // Stop thread if already running
382 // Initialize connection
383 m_con.SetTimeoutMs(30);
384 m_con.Serve(bind_addr);
389 // ASCII art for the win!
391 <<" .__ __ __ "<<std::endl
392 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
393 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
394 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
395 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
396 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
397 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
398 actionstream<<"Server for gameid=\""<<m_gamespec.id
399 <<"\" listening on "<<bind_addr.serializeString()<<":"
400 <<bind_addr.getPort() << "."<<std::endl;
405 DSTACK(FUNCTION_NAME);
407 infostream<<"Server: Stopping and waiting threads"<<std::endl;
409 // Stop threads (set run=false first so both start stopping)
411 //m_emergethread.setRun(false);
413 //m_emergethread.stop();
415 infostream<<"Server: Threads stopped"<<std::endl;
418 void Server::step(float dtime)
420 DSTACK(FUNCTION_NAME);
425 MutexAutoLock lock(m_step_dtime_mutex);
426 m_step_dtime += dtime;
428 // Throw if fatal error occurred in thread
429 std::string async_err = m_async_fatal_error.get();
430 if (!async_err.empty()) {
431 if (!m_simple_singleplayer_mode) {
432 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
433 g_settings->get("kick_msg_crash"),
434 g_settings->getBool("ask_reconnect_on_crash"));
436 throw ServerError("AsyncErr: " + async_err);
440 void Server::AsyncRunStep(bool initial_step)
442 DSTACK(FUNCTION_NAME);
444 g_profiler->add("Server::AsyncRunStep (num)", 1);
448 MutexAutoLock lock1(m_step_dtime_mutex);
449 dtime = m_step_dtime;
453 // Send blocks to clients
457 if((dtime < 0.001) && !initial_step)
460 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
462 //infostream<<"Server steps "<<dtime<<std::endl;
463 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
466 MutexAutoLock lock1(m_step_dtime_mutex);
467 m_step_dtime -= dtime;
474 m_uptime.set(m_uptime.get() + dtime);
480 Update time of day and overall game time
482 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
485 Send to clients at constant intervals
488 m_time_of_day_send_timer -= dtime;
489 if(m_time_of_day_send_timer < 0.0) {
490 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
491 u16 time = m_env->getTimeOfDay();
492 float time_speed = g_settings->getFloat("time_speed");
493 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
497 MutexAutoLock lock(m_env_mutex);
498 // Figure out and report maximum lag to environment
499 float max_lag = m_env->getMaxLagEstimate();
500 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
502 if(dtime > 0.1 && dtime > max_lag * 2.0)
503 infostream<<"Server: Maximum lag peaked to "<<dtime
507 m_env->reportMaxLagEstimate(max_lag);
509 ScopeProfiler sp(g_profiler, "SEnv step");
510 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
514 static const float map_timer_and_unload_dtime = 2.92;
515 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
517 MutexAutoLock lock(m_env_mutex);
518 // Run Map's timers and unload unused data
519 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
520 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
521 g_settings->getFloat("server_unload_unused_data_timeout"),
526 Listen to the admin chat, if available
529 if (!m_admin_chat->command_queue.empty()) {
530 MutexAutoLock lock(m_env_mutex);
531 while (!m_admin_chat->command_queue.empty()) {
532 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
533 handleChatInterfaceEvent(evt);
537 m_admin_chat->outgoing_queue.push_back(
538 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
545 /* Transform liquids */
546 m_liquid_transform_timer += dtime;
547 if(m_liquid_transform_timer >= m_liquid_transform_every)
549 m_liquid_transform_timer -= m_liquid_transform_every;
551 MutexAutoLock lock(m_env_mutex);
553 ScopeProfiler sp(g_profiler, "Server: liquid transform");
555 std::map<v3s16, MapBlock*> modified_blocks;
556 m_env->getMap().transformLiquids(modified_blocks, m_env);
559 Set the modified blocks unsent for all the clients
561 if(!modified_blocks.empty())
563 SetBlocksNotSent(modified_blocks);
566 m_clients.step(dtime);
568 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
570 // send masterserver announce
572 float &counter = m_masterserver_timer;
573 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
574 g_settings->getBool("server_announce")) {
575 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
576 ServerList::AA_START,
577 m_bind_addr.getPort(),
578 m_clients.getPlayerNames(),
580 m_env->getGameTime(),
583 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
593 Check added and deleted active objects
596 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
597 MutexAutoLock envlock(m_env_mutex);
600 const RemoteClientMap &clients = m_clients.getClientList();
601 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
603 // Radius inside which objects are active
604 static thread_local const s16 radius =
605 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
607 // Radius inside which players are active
608 static thread_local const bool is_transfer_limited =
609 g_settings->exists("unlimited_player_transfer_distance") &&
610 !g_settings->getBool("unlimited_player_transfer_distance");
611 static thread_local const s16 player_transfer_dist =
612 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
613 s16 player_radius = player_transfer_dist;
614 if (player_radius == 0 && is_transfer_limited)
615 player_radius = radius;
617 for (const auto &client_it : clients) {
618 RemoteClient *client = client_it.second;
620 // If definitions and textures have not been sent, don't
621 // send objects either
622 if (client->getState() < CS_DefinitionsSent)
625 RemotePlayer *player = m_env->getPlayer(client->peer_id);
627 // This can happen if the client timeouts somehow
631 PlayerSAO *playersao = player->getPlayerSAO();
635 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
636 if (my_radius <= 0) my_radius = radius;
637 //infostream << "Server: Active Radius " << my_radius << std::endl;
639 std::queue<u16> removed_objects;
640 std::queue<u16> added_objects;
641 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
642 client->m_known_objects, removed_objects);
643 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
644 client->m_known_objects, added_objects);
646 // Ignore if nothing happened
647 if (removed_objects.empty() && added_objects.empty()) {
651 std::string data_buffer;
655 // Handle removed objects
656 writeU16((u8*)buf, removed_objects.size());
657 data_buffer.append(buf, 2);
658 while (!removed_objects.empty()) {
660 u16 id = removed_objects.front();
661 ServerActiveObject* obj = m_env->getActiveObject(id);
663 // Add to data buffer for sending
664 writeU16((u8*)buf, id);
665 data_buffer.append(buf, 2);
667 // Remove from known objects
668 client->m_known_objects.erase(id);
670 if(obj && obj->m_known_by_count > 0)
671 obj->m_known_by_count--;
672 removed_objects.pop();
675 // Handle added objects
676 writeU16((u8*)buf, added_objects.size());
677 data_buffer.append(buf, 2);
678 while (!added_objects.empty()) {
680 u16 id = added_objects.front();
681 ServerActiveObject* obj = m_env->getActiveObject(id);
684 u8 type = ACTIVEOBJECT_TYPE_INVALID;
686 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
688 type = obj->getSendType();
690 // Add to data buffer for sending
691 writeU16((u8*)buf, id);
692 data_buffer.append(buf, 2);
693 writeU8((u8*)buf, type);
694 data_buffer.append(buf, 1);
697 data_buffer.append(serializeLongString(
698 obj->getClientInitializationData(client->net_proto_version)));
700 data_buffer.append(serializeLongString(""));
702 // Add to known objects
703 client->m_known_objects.insert(id);
706 obj->m_known_by_count++;
711 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
712 verbosestream << "Server: Sent object remove/add: "
713 << removed_objects.size() << " removed, "
714 << added_objects.size() << " added, "
715 << "packet size is " << pktSize << std::endl;
719 m_mod_storage_save_timer -= dtime;
720 if (m_mod_storage_save_timer <= 0.0f) {
721 infostream << "Saving registered mod storages." << std::endl;
722 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
723 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
724 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
725 if (it->second->isModified()) {
726 it->second->save(getModStoragePath());
736 MutexAutoLock envlock(m_env_mutex);
737 ScopeProfiler sp(g_profiler, "Server: sending object messages");
740 // Value = data sent by object
741 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
743 // Get active object messages from environment
745 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
749 std::vector<ActiveObjectMessage>* message_list = nullptr;
750 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
751 n = buffered_messages.find(aom.id);
752 if (n == buffered_messages.end()) {
753 message_list = new std::vector<ActiveObjectMessage>;
754 buffered_messages[aom.id] = message_list;
757 message_list = n->second;
759 message_list->push_back(aom);
763 const RemoteClientMap &clients = m_clients.getClientList();
764 // Route data to every client
765 for (const auto &client_it : clients) {
766 RemoteClient *client = client_it.second;
767 std::string reliable_data;
768 std::string unreliable_data;
769 // Go through all objects in message buffer
770 for (const auto &buffered_message : buffered_messages) {
771 // If object is not known by client, skip it
772 u16 id = buffered_message.first;
773 if (client->m_known_objects.find(id) == client->m_known_objects.end())
776 // Get message list of object
777 std::vector<ActiveObjectMessage>* list = buffered_message.second;
778 // Go through every message
779 for (const ActiveObjectMessage &aom : *list) {
780 // Compose the full new data with header
781 std::string new_data;
784 writeU16((u8*)&buf[0], aom.id);
785 new_data.append(buf, 2);
787 new_data += serializeString(aom.datastring);
788 // Add data to buffer
790 reliable_data += new_data;
792 unreliable_data += new_data;
796 reliable_data and unreliable_data are now ready.
799 if (!reliable_data.empty()) {
800 SendActiveObjectMessages(client->peer_id, reliable_data);
803 if (!unreliable_data.empty()) {
804 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
809 // Clear buffered_messages
810 for (auto &buffered_message : buffered_messages) {
811 delete buffered_message.second;
816 Send queued-for-sending map edit events.
819 // We will be accessing the environment
820 MutexAutoLock lock(m_env_mutex);
822 // Don't send too many at a time
825 // Single change sending is disabled if queue size is not small
826 bool disable_single_change_sending = false;
827 if(m_unsent_map_edit_queue.size() >= 4)
828 disable_single_change_sending = true;
830 int event_count = m_unsent_map_edit_queue.size();
832 // We'll log the amount of each
835 while (!m_unsent_map_edit_queue.empty()) {
836 MapEditEvent* event = m_unsent_map_edit_queue.front();
837 m_unsent_map_edit_queue.pop();
839 // Players far away from the change are stored here.
840 // Instead of sending the changes, MapBlocks are set not sent
842 std::vector<u16> far_players;
844 switch (event->type) {
847 prof.add("MEET_ADDNODE", 1);
848 sendAddNode(event->p, event->n, event->already_known_by_peer,
849 &far_players, disable_single_change_sending ? 5 : 30,
850 event->type == MEET_ADDNODE);
852 case MEET_REMOVENODE:
853 prof.add("MEET_REMOVENODE", 1);
854 sendRemoveNode(event->p, event->already_known_by_peer,
855 &far_players, disable_single_change_sending ? 5 : 30);
857 case MEET_BLOCK_NODE_METADATA_CHANGED:
858 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
859 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
860 setBlockNotSent(event->p);
863 infostream << "Server: MEET_OTHER" << std::endl;
864 prof.add("MEET_OTHER", 1);
865 for (const v3s16 &modified_block : event->modified_blocks) {
866 setBlockNotSent(modified_block);
870 prof.add("unknown", 1);
871 warningstream << "Server: Unknown MapEditEvent "
872 << ((u32)event->type) << std::endl;
877 Set blocks not sent to far players
879 if (!far_players.empty()) {
880 // Convert list format to that wanted by SetBlocksNotSent
881 std::map<v3s16, MapBlock*> modified_blocks2;
882 for (const v3s16 &modified_block : event->modified_blocks) {
883 modified_blocks2[modified_block] =
884 m_env->getMap().getBlockNoCreateNoEx(modified_block);
887 // Set blocks not sent
888 for (const u16 far_player : far_players) {
889 if (RemoteClient *client = getClient(far_player))
890 client->SetBlocksNotSent(modified_blocks2);
897 if (event_count >= 5) {
898 infostream << "Server: MapEditEvents:" << std::endl;
899 prof.print(infostream);
900 } else if (event_count != 0) {
901 verbosestream << "Server: MapEditEvents:" << std::endl;
902 prof.print(verbosestream);
908 Trigger emergethread (it somehow gets to a non-triggered but
909 bysy state sometimes)
912 float &counter = m_emergethread_trigger_timer;
914 if (counter >= 2.0) {
917 m_emerge->startThreads();
921 // Save map, players and auth stuff
923 float &counter = m_savemap_timer;
925 static thread_local const float save_interval =
926 g_settings->getFloat("server_map_save_interval");
927 if (counter >= save_interval) {
929 MutexAutoLock lock(m_env_mutex);
931 ScopeProfiler sp(g_profiler, "Server: saving stuff");
934 if (m_banmanager->isModified()) {
935 m_banmanager->save();
938 // Save changed parts of map
939 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
942 m_env->saveLoadedPlayers();
944 // Save environment metadata
950 static const float shutdown_msg_times[] =
952 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
955 if (m_shutdown_timer > 0.0f) {
956 // Automated messages
957 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
958 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
959 // If shutdown timer matches an automessage, shot it
960 if (m_shutdown_timer > shutdown_msg_times[i] &&
961 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
962 std::wstringstream ws;
964 ws << L"*** Server shutting down in "
965 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
968 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
969 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
975 m_shutdown_timer -= dtime;
976 if (m_shutdown_timer < 0.0f) {
977 m_shutdown_timer = 0.0f;
978 m_shutdown_requested = true;
983 void Server::Receive()
985 DSTACK(FUNCTION_NAME);
990 peer_id = pkt.getPeerId();
993 catch(con::InvalidIncomingDataException &e) {
994 infostream<<"Server::Receive(): "
995 "InvalidIncomingDataException: what()="
996 <<e.what()<<std::endl;
998 catch(SerializationError &e) {
999 infostream<<"Server::Receive(): "
1000 "SerializationError: what()="
1001 <<e.what()<<std::endl;
1003 catch(ClientStateError &e) {
1004 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
1005 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
1006 L"Try reconnecting or updating your client");
1008 catch(con::PeerNotFoundException &e) {
1013 PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
1015 std::string playername;
1016 PlayerSAO *playersao = NULL;
1019 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1021 playername = client->getName();
1022 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1024 } catch (std::exception &e) {
1030 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1032 // If failed, cancel
1033 if (!playersao || !player) {
1034 if (player && player->peer_id != 0) {
1035 actionstream << "Server: Failed to emerge player \"" << playername
1036 << "\" (player allocated to an another client)" << std::endl;
1037 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1038 L"name. If your client closed unexpectedly, try again in "
1041 errorstream << "Server: " << playername << ": Failed to emerge player"
1043 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1049 Send complete position information
1051 SendMovePlayer(peer_id);
1054 SendPlayerPrivileges(peer_id);
1056 // Send inventory formspec
1057 SendPlayerInventoryFormspec(peer_id);
1060 SendInventory(playersao);
1062 // Send HP or death screen
1063 if (playersao->isDead())
1064 SendDeathscreen(peer_id, false, v3f(0,0,0));
1066 SendPlayerHPOrDie(playersao);
1069 SendPlayerBreath(playersao);
1071 // Note things in chat if not in simple singleplayer mode
1072 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1073 // Send information about server to player in chat
1074 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1076 Address addr = getPeerAddress(player->peer_id);
1077 std::string ip_str = addr.serializeString();
1078 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1083 const std::vector<std::string> &names = m_clients.getPlayerNames();
1085 actionstream << player->getName() << " joins game. List of players: ";
1087 for (const std::string &name : names) {
1088 actionstream << name << " ";
1091 actionstream << player->getName() <<std::endl;
1096 inline void Server::handleCommand(NetworkPacket* pkt)
1098 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1099 (this->*opHandle.handler)(pkt);
1102 void Server::ProcessData(NetworkPacket *pkt)
1104 DSTACK(FUNCTION_NAME);
1105 // Environment is locked first.
1106 MutexAutoLock envlock(m_env_mutex);
1108 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1109 u32 peer_id = pkt->getPeerId();
1112 Address address = getPeerAddress(peer_id);
1113 std::string addr_s = address.serializeString();
1115 if(m_banmanager->isIpBanned(addr_s)) {
1116 std::string ban_name = m_banmanager->getBanName(addr_s);
1117 infostream << "Server: A banned client tried to connect from "
1118 << addr_s << "; banned name was "
1119 << ban_name << std::endl;
1120 // This actually doesn't seem to transfer to the client
1121 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1122 + utf8_to_wide(ban_name));
1126 catch(con::PeerNotFoundException &e) {
1128 * no peer for this packet found
1129 * most common reason is peer timeout, e.g. peer didn't
1130 * respond for some time, your server was overloaded or
1133 infostream << "Server::ProcessData(): Canceling: peer "
1134 << peer_id << " not found" << std::endl;
1139 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1141 // Command must be handled into ToServerCommandHandler
1142 if (command >= TOSERVER_NUM_MSG_TYPES) {
1143 infostream << "Server: Ignoring unknown command "
1144 << command << std::endl;
1148 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1153 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1155 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1156 errorstream << "Server::ProcessData(): Cancelling: Peer"
1157 " serialization format invalid or not initialized."
1158 " Skipping incoming command=" << command << std::endl;
1162 /* Handle commands related to client startup */
1163 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1168 if (m_clients.getClientState(peer_id) < CS_Active) {
1169 if (command == TOSERVER_PLAYERPOS) return;
1171 errorstream << "Got packet command: " << command << " for peer id "
1172 << peer_id << " but client isn't active yet. Dropping packet "
1178 } catch (SendFailedException &e) {
1179 errorstream << "Server::ProcessData(): SendFailedException: "
1180 << "what=" << e.what()
1182 } catch (PacketError &e) {
1183 actionstream << "Server::ProcessData(): PacketError: "
1184 << "what=" << e.what()
1189 void Server::setTimeOfDay(u32 time)
1191 m_env->setTimeOfDay(time);
1192 m_time_of_day_send_timer = 0;
1195 void Server::onMapEditEvent(MapEditEvent *event)
1197 if(m_ignore_map_edit_events)
1199 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1201 MapEditEvent *e = event->clone();
1202 m_unsent_map_edit_queue.push(e);
1205 Inventory* Server::getInventory(const InventoryLocation &loc)
1208 case InventoryLocation::UNDEFINED:
1209 case InventoryLocation::CURRENT_PLAYER:
1211 case InventoryLocation::PLAYER:
1213 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1216 PlayerSAO *playersao = player->getPlayerSAO();
1219 return playersao->getInventory();
1222 case InventoryLocation::NODEMETA:
1224 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1227 return meta->getInventory();
1230 case InventoryLocation::DETACHED:
1232 if(m_detached_inventories.count(loc.name) == 0)
1234 return m_detached_inventories[loc.name];
1238 sanity_check(false); // abort
1243 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1246 case InventoryLocation::UNDEFINED:
1248 case InventoryLocation::PLAYER:
1253 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1258 PlayerSAO *playersao = player->getPlayerSAO();
1262 SendInventory(playersao);
1265 case InventoryLocation::NODEMETA:
1267 v3s16 blockpos = getNodeBlockPos(loc.p);
1269 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1271 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1273 setBlockNotSent(blockpos);
1276 case InventoryLocation::DETACHED:
1278 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1282 sanity_check(false); // abort
1287 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1289 std::vector<u16> clients = m_clients.getClientIDs();
1291 // Set the modified blocks unsent for all the clients
1292 for (const u16 client_id : clients) {
1293 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1294 client->SetBlocksNotSent(block);
1299 void Server::peerAdded(con::Peer *peer)
1301 DSTACK(FUNCTION_NAME);
1302 verbosestream<<"Server::peerAdded(): peer->id="
1303 <<peer->id<<std::endl;
1305 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1308 void Server::deletingPeer(con::Peer *peer, bool timeout)
1310 DSTACK(FUNCTION_NAME);
1311 verbosestream<<"Server::deletingPeer(): peer->id="
1312 <<peer->id<<", timeout="<<timeout<<std::endl;
1314 m_clients.event(peer->id, CSE_Disconnect);
1315 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1318 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
1320 *retval = m_con.getPeerStat(peer_id,type);
1321 return *retval != -1;
1324 bool Server::getClientInfo(
1333 std::string* vers_string
1336 *state = m_clients.getClientState(peer_id);
1338 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1345 *uptime = client->uptime();
1346 *ser_vers = client->serialization_version;
1347 *prot_vers = client->net_proto_version;
1349 *major = client->getMajor();
1350 *minor = client->getMinor();
1351 *patch = client->getPatch();
1352 *vers_string = client->getPatch();
1359 void Server::handlePeerChanges()
1361 while(!m_peer_change_queue.empty())
1363 con::PeerChange c = m_peer_change_queue.front();
1364 m_peer_change_queue.pop();
1366 verbosestream<<"Server: Handling peer change: "
1367 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1372 case con::PEER_ADDED:
1373 m_clients.CreateClient(c.peer_id);
1376 case con::PEER_REMOVED:
1377 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1381 FATAL_ERROR("Invalid peer change event received!");
1387 void Server::printToConsoleOnly(const std::string &text)
1390 m_admin_chat->outgoing_queue.push_back(
1391 new ChatEventChat("", utf8_to_wide(text)));
1393 std::cout << text << std::endl;
1397 void Server::Send(NetworkPacket* pkt)
1399 m_clients.send(pkt->getPeerId(),
1400 clientCommandFactoryTable[pkt->getCommand()].channel,
1402 clientCommandFactoryTable[pkt->getCommand()].reliable);
1405 void Server::SendMovement(u16 peer_id)
1407 DSTACK(FUNCTION_NAME);
1408 std::ostringstream os(std::ios_base::binary);
1410 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1412 pkt << g_settings->getFloat("movement_acceleration_default");
1413 pkt << g_settings->getFloat("movement_acceleration_air");
1414 pkt << g_settings->getFloat("movement_acceleration_fast");
1415 pkt << g_settings->getFloat("movement_speed_walk");
1416 pkt << g_settings->getFloat("movement_speed_crouch");
1417 pkt << g_settings->getFloat("movement_speed_fast");
1418 pkt << g_settings->getFloat("movement_speed_climb");
1419 pkt << g_settings->getFloat("movement_speed_jump");
1420 pkt << g_settings->getFloat("movement_liquid_fluidity");
1421 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1422 pkt << g_settings->getFloat("movement_liquid_sink");
1423 pkt << g_settings->getFloat("movement_gravity");
1428 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1430 if (!g_settings->getBool("enable_damage"))
1433 u16 peer_id = playersao->getPeerID();
1434 bool is_alive = playersao->getHP() > 0;
1437 SendPlayerHP(peer_id);
1442 void Server::SendHP(u16 peer_id, u8 hp)
1444 DSTACK(FUNCTION_NAME);
1446 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1451 void Server::SendBreath(u16 peer_id, u16 breath)
1453 DSTACK(FUNCTION_NAME);
1455 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1456 pkt << (u16) breath;
1460 void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
1461 const std::string &custom_reason, bool reconnect)
1463 assert(reason < SERVER_ACCESSDENIED_MAX);
1465 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1467 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1468 pkt << custom_reason;
1469 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1470 reason == SERVER_ACCESSDENIED_CRASH)
1471 pkt << custom_reason << (u8)reconnect;
1475 void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
1477 DSTACK(FUNCTION_NAME);
1479 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1484 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
1485 v3f camera_point_target)
1487 DSTACK(FUNCTION_NAME);
1489 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1490 pkt << set_camera_point_target << camera_point_target;
1494 void Server::SendItemDef(u16 peer_id,
1495 IItemDefManager *itemdef, u16 protocol_version)
1497 DSTACK(FUNCTION_NAME);
1499 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1503 u32 length of the next item
1504 zlib-compressed serialized ItemDefManager
1506 std::ostringstream tmp_os(std::ios::binary);
1507 itemdef->serialize(tmp_os, protocol_version);
1508 std::ostringstream tmp_os2(std::ios::binary);
1509 compressZlib(tmp_os.str(), tmp_os2);
1510 pkt.putLongString(tmp_os2.str());
1513 verbosestream << "Server: Sending item definitions to id(" << peer_id
1514 << "): size=" << pkt.getSize() << std::endl;
1519 void Server::SendNodeDef(u16 peer_id,
1520 INodeDefManager *nodedef, u16 protocol_version)
1522 DSTACK(FUNCTION_NAME);
1524 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1528 u32 length of the next item
1529 zlib-compressed serialized NodeDefManager
1531 std::ostringstream tmp_os(std::ios::binary);
1532 nodedef->serialize(tmp_os, protocol_version);
1533 std::ostringstream tmp_os2(std::ios::binary);
1534 compressZlib(tmp_os.str(), tmp_os2);
1536 pkt.putLongString(tmp_os2.str());
1539 verbosestream << "Server: Sending node definitions to id(" << peer_id
1540 << "): size=" << pkt.getSize() << std::endl;
1546 Non-static send methods
1549 void Server::SendInventory(PlayerSAO* playerSAO)
1551 DSTACK(FUNCTION_NAME);
1553 UpdateCrafting(playerSAO->getPlayer());
1559 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1561 std::ostringstream os;
1562 playerSAO->getInventory()->serialize(os);
1564 std::string s = os.str();
1566 pkt.putRawString(s.c_str(), s.size());
1570 void Server::SendChatMessage(u16 peer_id, const ChatMessage &message)
1572 DSTACK(FUNCTION_NAME);
1574 NetworkPacket legacypkt(TOCLIENT_CHAT_MESSAGE_OLD, 0, peer_id);
1575 legacypkt << message.message;
1577 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1579 u8 type = message.type;
1580 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1582 if (peer_id != PEER_ID_INEXISTENT) {
1583 RemotePlayer *player = m_env->getPlayer(peer_id);
1587 if (player->protocol_version < 35)
1592 m_clients.sendToAllCompat(&pkt, &legacypkt, 35);
1596 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
1597 const std::string &formname)
1599 DSTACK(FUNCTION_NAME);
1601 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1602 if (formspec.empty()){
1603 //the client should close the formspec
1604 pkt.putLongString("");
1606 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1613 // Spawns a particle on peer with peer_id
1614 void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
1615 v3f pos, v3f velocity, v3f acceleration,
1616 float expirationtime, float size, bool collisiondetection,
1617 bool collision_removal,
1618 bool vertical, const std::string &texture,
1619 const struct TileAnimationParams &animation, u8 glow)
1621 DSTACK(FUNCTION_NAME);
1622 static thread_local const float radius =
1623 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1625 if (peer_id == PEER_ID_INEXISTENT) {
1626 std::vector<u16> clients = m_clients.getClientIDs();
1628 for (const u16 client_id : clients) {
1629 RemotePlayer *player = m_env->getPlayer(client_id);
1633 PlayerSAO *sao = player->getPlayerSAO();
1637 // Do not send to distant clients
1638 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1641 SendSpawnParticle(client_id, player->protocol_version,
1642 pos, velocity, acceleration,
1643 expirationtime, size, collisiondetection,
1644 collision_removal, vertical, texture, animation, glow);
1649 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1651 pkt << pos << velocity << acceleration << expirationtime
1652 << size << collisiondetection;
1653 pkt.putLongString(texture);
1655 pkt << collision_removal;
1656 // This is horrible but required (why are there two ways to serialize pkts?)
1657 std::ostringstream os(std::ios_base::binary);
1658 animation.serialize(os, protocol_version);
1659 pkt.putRawString(os.str());
1665 // Adds a ParticleSpawner on peer with peer_id
1666 void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version,
1667 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1668 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1669 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1670 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1671 const struct TileAnimationParams &animation, u8 glow)
1673 DSTACK(FUNCTION_NAME);
1674 if (peer_id == PEER_ID_INEXISTENT) {
1675 // This sucks and should be replaced:
1676 std::vector<u16> clients = m_clients.getClientIDs();
1677 for (const u16 client_id : clients) {
1678 RemotePlayer *player = m_env->getPlayer(client_id);
1681 SendAddParticleSpawner(client_id, player->protocol_version,
1682 amount, spawntime, minpos, maxpos,
1683 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1684 minsize, maxsize, collisiondetection, collision_removal,
1685 attached_id, vertical, texture, id, animation, glow);
1690 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1692 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1693 << minacc << maxacc << minexptime << maxexptime << minsize
1694 << maxsize << collisiondetection;
1696 pkt.putLongString(texture);
1698 pkt << id << vertical;
1699 pkt << collision_removal;
1701 // This is horrible but required
1702 std::ostringstream os(std::ios_base::binary);
1703 animation.serialize(os, protocol_version);
1704 pkt.putRawString(os.str());
1710 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
1712 DSTACK(FUNCTION_NAME);
1714 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
1716 // Ugly error in this packet
1719 if (peer_id != PEER_ID_INEXISTENT) {
1723 m_clients.sendToAll(&pkt);
1728 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
1730 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1732 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1733 << form->text << form->number << form->item << form->dir
1734 << form->align << form->offset << form->world_pos << form->size;
1739 void Server::SendHUDRemove(u16 peer_id, u32 id)
1741 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1746 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
1748 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1749 pkt << id << (u8) stat;
1753 case HUD_STAT_SCALE:
1754 case HUD_STAT_ALIGN:
1755 case HUD_STAT_OFFSET:
1756 pkt << *(v2f *) value;
1760 pkt << *(std::string *) value;
1762 case HUD_STAT_WORLD_POS:
1763 pkt << *(v3f *) value;
1766 pkt << *(v2s32 *) value;
1768 case HUD_STAT_NUMBER:
1772 pkt << *(u32 *) value;
1779 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
1781 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1783 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1785 pkt << flags << mask;
1790 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
1792 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1793 pkt << param << value;
1797 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
1798 const std::string &type, const std::vector<std::string> ¶ms,
1801 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1802 pkt << bgcolor << type << (u16) params.size();
1804 for (const std::string ¶m : params)
1812 void Server::SendCloudParams(u16 peer_id, float density,
1813 const video::SColor &color_bright,
1814 const video::SColor &color_ambient,
1819 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1820 pkt << density << color_bright << color_ambient
1821 << height << thickness << speed;
1826 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
1829 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1832 pkt << do_override << (u16) (ratio * 65535);
1837 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
1839 DSTACK(FUNCTION_NAME);
1841 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1842 pkt << time << time_speed;
1844 if (peer_id == PEER_ID_INEXISTENT) {
1845 m_clients.sendToAll(&pkt);
1852 void Server::SendPlayerHP(u16 peer_id)
1854 DSTACK(FUNCTION_NAME);
1855 PlayerSAO *playersao = getPlayerSAO(peer_id);
1856 // In some rare case if the player is disconnected
1857 // while Lua call l_punch, for example, this can be NULL
1861 SendHP(peer_id, playersao->getHP());
1862 m_script->player_event(playersao,"health_changed");
1864 // Send to other clients
1865 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1866 ActiveObjectMessage aom(playersao->getId(), true, str);
1867 playersao->m_messages_out.push(aom);
1870 void Server::SendPlayerBreath(PlayerSAO *sao)
1872 DSTACK(FUNCTION_NAME);
1875 m_script->player_event(sao, "breath_changed");
1876 SendBreath(sao->getPeerID(), sao->getBreath());
1879 void Server::SendMovePlayer(u16 peer_id)
1881 DSTACK(FUNCTION_NAME);
1882 RemotePlayer *player = m_env->getPlayer(peer_id);
1884 PlayerSAO *sao = player->getPlayerSAO();
1887 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1888 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1891 v3f pos = sao->getBasePosition();
1892 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1893 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1894 << " pitch=" << sao->getPitch()
1895 << " yaw=" << sao->getYaw()
1902 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
1904 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1907 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1908 << animation_frames[3] << animation_speed;
1913 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
1915 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1916 pkt << first << third;
1919 void Server::SendPlayerPrivileges(u16 peer_id)
1921 RemotePlayer *player = m_env->getPlayer(peer_id);
1923 if(player->peer_id == PEER_ID_INEXISTENT)
1926 std::set<std::string> privs;
1927 m_script->getAuth(player->getName(), NULL, &privs);
1929 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1930 pkt << (u16) privs.size();
1932 for (const std::string &priv : privs) {
1939 void Server::SendPlayerInventoryFormspec(u16 peer_id)
1941 RemotePlayer *player = m_env->getPlayer(peer_id);
1943 if(player->peer_id == PEER_ID_INEXISTENT)
1946 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1947 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1951 u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
1953 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1954 pkt.putRawString(datas.c_str(), datas.size());
1956 return pkt.getSize();
1959 void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
1961 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1962 datas.size(), peer_id);
1964 pkt.putRawString(datas.c_str(), datas.size());
1966 m_clients.send(pkt.getPeerId(),
1967 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1971 void Server::SendCSMFlavourLimits(u16 peer_id)
1973 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1974 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1975 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1979 s32 Server::playSound(const SimpleSoundSpec &spec,
1980 const ServerSoundParams ¶ms)
1982 // Find out initial position of sound
1983 bool pos_exists = false;
1984 v3f pos = params.getPos(m_env, &pos_exists);
1985 // If position is not found while it should be, cancel sound
1986 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1989 // Filter destination clients
1990 std::vector<u16> dst_clients;
1991 if(!params.to_player.empty()) {
1992 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1994 infostream<<"Server::playSound: Player \""<<params.to_player
1995 <<"\" not found"<<std::endl;
1998 if(player->peer_id == PEER_ID_INEXISTENT){
1999 infostream<<"Server::playSound: Player \""<<params.to_player
2000 <<"\" not connected"<<std::endl;
2003 dst_clients.push_back(player->peer_id);
2005 std::vector<u16> clients = m_clients.getClientIDs();
2007 for (const u16 client_id : clients) {
2008 RemotePlayer *player = m_env->getPlayer(client_id);
2012 PlayerSAO *sao = player->getPlayerSAO();
2017 if(sao->getBasePosition().getDistanceFrom(pos) >
2018 params.max_hear_distance)
2021 dst_clients.push_back(client_id);
2025 if(dst_clients.empty())
2029 s32 id = m_next_sound_id++;
2030 // The sound will exist as a reference in m_playing_sounds
2031 m_playing_sounds[id] = ServerPlayingSound();
2032 ServerPlayingSound &psound = m_playing_sounds[id];
2033 psound.params = params;
2036 float gain = params.gain * spec.gain;
2037 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2038 pkt << id << spec.name << gain
2039 << (u8) params.type << pos << params.object
2040 << params.loop << params.fade << params.pitch;
2042 // Backwards compability
2043 bool play_sound = gain > 0;
2045 for (const u16 dst_client : dst_clients) {
2046 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2047 psound.clients.insert(dst_client);
2048 m_clients.send(dst_client, 0, &pkt, true);
2053 void Server::stopSound(s32 handle)
2055 // Get sound reference
2056 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2057 m_playing_sounds.find(handle);
2058 if (i == m_playing_sounds.end())
2060 ServerPlayingSound &psound = i->second;
2062 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2065 for (std::unordered_set<u16>::const_iterator si = psound.clients.begin();
2066 si != psound.clients.end(); ++si) {
2068 m_clients.send(*si, 0, &pkt, true);
2070 // Remove sound reference
2071 m_playing_sounds.erase(i);
2074 void Server::fadeSound(s32 handle, float step, float gain)
2076 // Get sound reference
2077 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2078 m_playing_sounds.find(handle);
2079 if (i == m_playing_sounds.end())
2082 ServerPlayingSound &psound = i->second;
2083 psound.params.gain = gain;
2085 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2086 pkt << handle << step << gain;
2088 // Backwards compability
2089 bool play_sound = gain > 0;
2090 ServerPlayingSound compat_psound = psound;
2091 compat_psound.clients.clear();
2093 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2094 compat_pkt << handle;
2096 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2097 it != psound.clients.end();) {
2098 if (m_clients.getProtocolVersion(*it) >= 32) {
2100 m_clients.send(*it, 0, &pkt, true);
2103 compat_psound.clients.insert(*it);
2105 m_clients.send(*it, 0, &compat_pkt, true);
2106 psound.clients.erase(it++);
2110 // Remove sound reference
2111 if (!play_sound || psound.clients.empty())
2112 m_playing_sounds.erase(i);
2114 if (play_sound && !compat_psound.clients.empty()) {
2115 // Play new sound volume on older clients
2116 playSound(compat_psound.spec, compat_psound.params);
2120 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2121 std::vector<u16> *far_players, float far_d_nodes)
2123 float maxd = far_d_nodes*BS;
2124 v3f p_f = intToFloat(p, BS);
2126 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2129 std::vector<u16> clients = m_clients.getClientIDs();
2130 for (u16 client_id : clients) {
2133 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2134 PlayerSAO *sao = player->getPlayerSAO();
2138 // If player is far away, only set modified blocks not sent
2139 v3f player_pos = sao->getBasePosition();
2140 if (player_pos.getDistanceFrom(p_f) > maxd) {
2141 far_players->push_back(client_id);
2148 m_clients.send(client_id, 0, &pkt, true);
2152 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2153 std::vector<u16> *far_players, float far_d_nodes,
2154 bool remove_metadata)
2156 float maxd = far_d_nodes*BS;
2157 v3f p_f = intToFloat(p, BS);
2159 std::vector<u16> clients = m_clients.getClientIDs();
2160 for (const u16 client_id : clients) {
2163 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2164 PlayerSAO *sao = player->getPlayerSAO();
2168 // If player is far away, only set modified blocks not sent
2169 v3f player_pos = sao->getBasePosition();
2170 if(player_pos.getDistanceFrom(p_f) > maxd) {
2171 far_players->push_back(client_id);
2177 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2179 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2181 pkt << p << n.param0 << n.param1 << n.param2
2182 << (u8) (remove_metadata ? 0 : 1);
2187 if (pkt.getSize() > 0)
2188 m_clients.send(client_id, 0, &pkt, true);
2192 void Server::setBlockNotSent(v3s16 p)
2194 std::vector<u16> clients = m_clients.getClientIDs();
2196 for (const u16 i : clients) {
2197 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2198 client->SetBlockNotSent(p);
2203 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
2205 DSTACK(FUNCTION_NAME);
2207 v3s16 p = block->getPos();
2210 Create a packet with the block in the right format
2213 std::ostringstream os(std::ios_base::binary);
2214 block->serialize(os, ver, false);
2215 block->serializeNetworkSpecific(os);
2216 std::string s = os.str();
2218 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2221 pkt.putRawString(s.c_str(), s.size());
2225 void Server::SendBlocks(float dtime)
2227 DSTACK(FUNCTION_NAME);
2229 MutexAutoLock envlock(m_env_mutex);
2230 //TODO check if one big lock could be faster then multiple small ones
2232 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2234 std::vector<PrioritySortedBlockTransfer> queue;
2236 s32 total_sending = 0;
2239 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2241 std::vector<u16> clients = m_clients.getClientIDs();
2244 for (const u16 client_id : clients) {
2245 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2250 total_sending += client->SendingCount();
2251 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2257 // Lowest priority number comes first.
2258 // Lowest is most important.
2259 std::sort(queue.begin(), queue.end());
2262 for(u32 i=0; i<queue.size(); i++)
2264 //TODO: Calculate limit dynamically
2265 if(total_sending >= g_settings->getS32
2266 ("max_simultaneous_block_sends_server_total"))
2269 PrioritySortedBlockTransfer q = queue[i];
2271 MapBlock *block = nullptr;
2273 block = m_env->getMap().getBlockNoCreate(q.pos);
2274 } catch(const InvalidPositionException &e) {
2278 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id, CS_Active);
2282 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
2284 client->SentBlock(q.pos);
2290 void Server::fillMediaCache()
2292 DSTACK(FUNCTION_NAME);
2294 infostream<<"Server: Calculating media file checksums"<<std::endl;
2296 // Collect all media file paths
2297 std::vector<std::string> paths;
2298 for (const ModSpec &mod : m_mods) {
2299 paths.push_back(mod.path + DIR_DELIM + "textures");
2300 paths.push_back(mod.path + DIR_DELIM + "sounds");
2301 paths.push_back(mod.path + DIR_DELIM + "media");
2302 paths.push_back(mod.path + DIR_DELIM + "models");
2304 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2306 // Collect media file information from paths into cache
2307 for(std::vector<std::string>::iterator i = paths.begin();
2308 i != paths.end(); ++i) {
2309 std::string mediapath = *i;
2310 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2311 for (u32 j = 0; j < dirlist.size(); j++) {
2312 if (dirlist[j].dir) // Ignode dirs
2314 std::string filename = dirlist[j].name;
2315 // If name contains illegal characters, ignore the file
2316 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2317 infostream<<"Server: ignoring illegal file name: \""
2318 << filename << "\"" << std::endl;
2321 // If name is not in a supported format, ignore it
2322 const char *supported_ext[] = {
2323 ".png", ".jpg", ".bmp", ".tga",
2324 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2326 ".x", ".b3d", ".md2", ".obj",
2329 if (removeStringEnd(filename, supported_ext).empty()){
2330 infostream << "Server: ignoring unsupported file extension: \""
2331 << filename << "\"" << std::endl;
2334 // Ok, attempt to load the file and add to cache
2335 std::string filepath = mediapath + DIR_DELIM + filename;
2337 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2339 errorstream << "Server::fillMediaCache(): Could not open \""
2340 << filename << "\" for reading" << std::endl;
2343 std::ostringstream tmp_os(std::ios_base::binary);
2347 fis.read(buf, 1024);
2348 std::streamsize len = fis.gcount();
2349 tmp_os.write(buf, len);
2358 errorstream<<"Server::fillMediaCache(): Failed to read \""
2359 << filename << "\"" << std::endl;
2362 if(tmp_os.str().length() == 0) {
2363 errorstream << "Server::fillMediaCache(): Empty file \""
2364 << filepath << "\"" << std::endl;
2369 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2371 unsigned char *digest = sha1.getDigest();
2372 std::string sha1_base64 = base64_encode(digest, 20);
2373 std::string sha1_hex = hex_encode((char*)digest, 20);
2377 m_media[filename] = MediaInfo(filepath, sha1_base64);
2378 verbosestream << "Server: " << sha1_hex << " is " << filename
2384 void Server::sendMediaAnnouncement(u16 peer_id)
2386 DSTACK(FUNCTION_NAME);
2388 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2392 std::ostringstream os(std::ios_base::binary);
2394 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2395 pkt << (u16) m_media.size();
2397 for (const auto &i : m_media) {
2398 pkt << i.first << i.second.sha1_digest;
2401 pkt << g_settings->get("remote_media");
2405 struct SendableMedia
2411 SendableMedia(const std::string &name_="", const std::string &path_="",
2412 const std::string &data_=""):
2419 void Server::sendRequestedMedia(u16 peer_id,
2420 const std::vector<std::string> &tosend)
2422 DSTACK(FUNCTION_NAME);
2424 verbosestream<<"Server::sendRequestedMedia(): "
2425 <<"Sending files to client"<<std::endl;
2429 // Put 5kB in one bunch (this is not accurate)
2430 u32 bytes_per_bunch = 5000;
2432 std::vector< std::vector<SendableMedia> > file_bunches;
2433 file_bunches.emplace_back();
2435 u32 file_size_bunch_total = 0;
2437 for (const std::string &name : tosend) {
2438 if (m_media.find(name) == m_media.end()) {
2439 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2440 <<"unknown file \""<<(name)<<"\""<<std::endl;
2444 //TODO get path + name
2445 std::string tpath = m_media[name].path;
2448 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2450 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2451 <<tpath<<"\" for reading"<<std::endl;
2454 std::ostringstream tmp_os(std::ios_base::binary);
2458 fis.read(buf, 1024);
2459 std::streamsize len = fis.gcount();
2460 tmp_os.write(buf, len);
2461 file_size_bunch_total += len;
2470 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2471 <<name<<"\""<<std::endl;
2474 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2475 <<tname<<"\""<<std::endl;*/
2477 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2479 // Start next bunch if got enough data
2480 if(file_size_bunch_total >= bytes_per_bunch) {
2481 file_bunches.emplace_back();
2482 file_size_bunch_total = 0;
2487 /* Create and send packets */
2489 u16 num_bunches = file_bunches.size();
2490 for (u16 i = 0; i < num_bunches; i++) {
2493 u16 total number of texture bunches
2494 u16 index of this bunch
2495 u32 number of files in this bunch
2504 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2505 pkt << num_bunches << i << (u32) file_bunches[i].size();
2507 for (const SendableMedia &j : file_bunches[i]) {
2509 pkt.putLongString(j.data);
2512 verbosestream << "Server::sendRequestedMedia(): bunch "
2513 << i << "/" << num_bunches
2514 << " files=" << file_bunches[i].size()
2515 << " size=" << pkt.getSize() << std::endl;
2520 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
2522 if(m_detached_inventories.count(name) == 0) {
2523 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2526 Inventory *inv = m_detached_inventories[name];
2527 std::ostringstream os(std::ios_base::binary);
2529 os << serializeString(name);
2533 std::string s = os.str();
2535 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2536 pkt.putRawString(s.c_str(), s.size());
2538 const std::string &check = m_detached_inventories_player[name];
2539 if (peer_id == PEER_ID_INEXISTENT) {
2541 return m_clients.sendToAll(&pkt);
2542 RemotePlayer *p = m_env->getPlayer(check.c_str());
2544 m_clients.send(p->peer_id, 0, &pkt, true);
2546 if (check.empty() || getPlayerName(peer_id) == check)
2551 void Server::sendDetachedInventories(u16 peer_id)
2553 DSTACK(FUNCTION_NAME);
2555 for (const auto &detached_inventory : m_detached_inventories) {
2556 const std::string &name = detached_inventory.first;
2557 //Inventory *inv = i->second;
2558 sendDetachedInventory(name, peer_id);
2566 void Server::DiePlayer(u16 peer_id)
2568 DSTACK(FUNCTION_NAME);
2569 PlayerSAO *playersao = getPlayerSAO(peer_id);
2570 // In some rare cases this can be NULL -- if the player is disconnected
2571 // when a Lua function modifies l_punch, for example
2575 infostream << "Server::DiePlayer(): Player "
2576 << playersao->getPlayer()->getName()
2577 << " dies" << std::endl;
2579 playersao->setHP(0);
2581 // Trigger scripted stuff
2582 m_script->on_dieplayer(playersao);
2584 SendPlayerHP(peer_id);
2585 SendDeathscreen(peer_id, false, v3f(0,0,0));
2588 void Server::RespawnPlayer(u16 peer_id)
2590 DSTACK(FUNCTION_NAME);
2592 PlayerSAO *playersao = getPlayerSAO(peer_id);
2595 infostream << "Server::RespawnPlayer(): Player "
2596 << playersao->getPlayer()->getName()
2597 << " respawns" << std::endl;
2599 playersao->setHP(PLAYER_MAX_HP);
2600 playersao->setBreath(PLAYER_MAX_BREATH);
2602 bool repositioned = m_script->on_respawnplayer(playersao);
2603 if (!repositioned) {
2604 // setPos will send the new position to client
2605 playersao->setPos(findSpawnPos());
2608 SendPlayerHP(peer_id);
2612 void Server::DenySudoAccess(u16 peer_id)
2614 DSTACK(FUNCTION_NAME);
2616 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2621 void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
2622 const std::string &str_reason, bool reconnect)
2624 if (proto_ver >= 25) {
2625 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2627 std::wstring wreason = utf8_to_wide(
2628 reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
2629 accessDeniedStrings[(u8)reason]);
2630 SendAccessDenied_Legacy(peer_id, wreason);
2633 m_clients.event(peer_id, CSE_SetDenied);
2634 m_con.DisconnectPeer(peer_id);
2638 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
2640 DSTACK(FUNCTION_NAME);
2642 SendAccessDenied(peer_id, reason, custom_reason);
2643 m_clients.event(peer_id, CSE_SetDenied);
2644 m_con.DisconnectPeer(peer_id);
2647 // 13/03/15: remove this function when protocol version 25 will become
2648 // the minimum version for MT users, maybe in 1 year
2649 void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
2651 DSTACK(FUNCTION_NAME);
2653 SendAccessDenied_Legacy(peer_id, reason);
2654 m_clients.event(peer_id, CSE_SetDenied);
2655 m_con.DisconnectPeer(peer_id);
2658 void Server::acceptAuth(u16 peer_id, bool forSudoMode)
2660 DSTACK(FUNCTION_NAME);
2663 RemoteClient* client = getClient(peer_id, CS_Invalid);
2665 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2667 // Right now, the auth mechs don't change between login and sudo mode.
2668 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2669 client->allowed_sudo_mechs = sudo_auth_mechs;
2671 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2672 << g_settings->getFloat("dedicated_server_step")
2676 m_clients.event(peer_id, CSE_AuthAccept);
2678 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2680 // We only support SRP right now
2681 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2683 resp_pkt << sudo_auth_mechs;
2685 m_clients.event(peer_id, CSE_SudoSuccess);
2689 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
2691 DSTACK(FUNCTION_NAME);
2692 std::wstring message;
2695 Clear references to playing sounds
2697 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2698 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2699 ServerPlayingSound &psound = i->second;
2700 psound.clients.erase(peer_id);
2701 if (psound.clients.empty())
2702 m_playing_sounds.erase(i++);
2707 RemotePlayer *player = m_env->getPlayer(peer_id);
2709 /* Run scripts and remove from environment */
2711 PlayerSAO *playersao = player->getPlayerSAO();
2714 // inform connected clients
2715 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2716 // (u16) 1 + std::string represents a vector serialization representation
2717 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2718 m_clients.sendToAll(¬ice);
2720 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2722 playersao->disconnected();
2729 if (player && reason != CDR_DENY) {
2730 std::ostringstream os(std::ios_base::binary);
2731 std::vector<u16> clients = m_clients.getClientIDs();
2733 for (const u16 client_id : clients) {
2735 RemotePlayer *player = m_env->getPlayer(client_id);
2739 // Get name of player
2740 os << player->getName() << " ";
2743 std::string name = player->getName();
2744 actionstream << name << " "
2745 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2746 << " List of players: " << os.str() << std::endl;
2748 m_admin_chat->outgoing_queue.push_back(
2749 new ChatEventNick(CET_NICK_REMOVE, name));
2753 MutexAutoLock env_lock(m_env_mutex);
2754 m_clients.DeleteClient(peer_id);
2758 // Send leave chat message to all remaining clients
2759 if (!message.empty()) {
2760 SendChatMessage(PEER_ID_INEXISTENT,
2761 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2765 void Server::UpdateCrafting(RemotePlayer *player)
2767 DSTACK(FUNCTION_NAME);
2769 // Get a preview for crafting
2771 InventoryLocation loc;
2772 loc.setPlayer(player->getName());
2773 std::vector<ItemStack> output_replacements;
2774 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2775 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2776 (&player->inventory)->getList("craft"), loc);
2778 // Put the new preview in
2779 InventoryList *plist = player->inventory.getList("craftpreview");
2780 sanity_check(plist);
2781 sanity_check(plist->getSize() >= 1);
2782 plist->changeItem(0, preview);
2785 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2787 if (evt->type == CET_NICK_ADD) {
2788 // The terminal informed us of its nick choice
2789 m_admin_nick = ((ChatEventNick *)evt)->nick;
2790 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2791 errorstream << "You haven't set up an account." << std::endl
2792 << "Please log in using the client as '"
2793 << m_admin_nick << "' with a secure password." << std::endl
2794 << "Until then, you can't execute admin tasks via the console," << std::endl
2795 << "and everybody can claim the user account instead of you," << std::endl
2796 << "giving them full control over this server." << std::endl;
2799 assert(evt->type == CET_CHAT);
2800 handleAdminChat((ChatEventChat *)evt);
2804 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2805 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2807 // If something goes wrong, this player is to blame
2808 RollbackScopeActor rollback_scope(m_rollback,
2809 std::string("player:") + name);
2811 if (g_settings->getBool("strip_color_codes"))
2812 wmessage = unescape_enriched(wmessage);
2815 switch (player->canSendChatMessage()) {
2816 case RPLAYER_CHATRESULT_FLOODING: {
2817 std::wstringstream ws;
2818 ws << L"You cannot send more messages. You are limited to "
2819 << g_settings->getFloat("chat_message_limit_per_10sec")
2820 << L" messages per 10 seconds.";
2823 case RPLAYER_CHATRESULT_KICK:
2824 DenyAccess_Legacy(player->peer_id,
2825 L"You have been kicked due to message flooding.");
2827 case RPLAYER_CHATRESULT_OK:
2830 FATAL_ERROR("Unhandled chat filtering result found.");
2834 if (m_max_chatmessage_length > 0
2835 && wmessage.length() > m_max_chatmessage_length) {
2836 return L"Your message exceed the maximum chat message limit set on the server. "
2837 L"It was refused. Send a shorter message";
2840 // Run script hook, exit if script ate the chat message
2841 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2846 // Whether to send line to the player that sent the message, or to all players
2847 bool broadcast_line = true;
2849 if (check_shout_priv && !checkPriv(name, "shout")) {
2850 line += L"-!- You don't have permission to shout.";
2851 broadcast_line = false;
2860 Tell calling method to send the message to sender
2862 if (!broadcast_line)
2866 Send the message to others
2868 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2870 std::vector<u16> clients = m_clients.getClientIDs();
2873 Send the message back to the inital sender
2874 if they are using protocol version >= 29
2877 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2878 if (player && player->protocol_version >= 29)
2879 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2881 for (u16 cid : clients) {
2882 if (cid != peer_id_to_avoid_sending)
2883 SendChatMessage(cid, ChatMessage(line));
2888 void Server::handleAdminChat(const ChatEventChat *evt)
2890 std::string name = evt->nick;
2891 std::wstring wname = utf8_to_wide(name);
2892 std::wstring wmessage = evt->evt_msg;
2894 std::wstring answer = handleChat(name, wname, wmessage);
2896 // If asked to send answer to sender
2897 if (!answer.empty()) {
2898 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2902 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2904 RemoteClient *client = getClientNoEx(peer_id,state_min);
2906 throw ClientNotFoundException("Client not found");
2910 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2912 return m_clients.getClientNoEx(peer_id, state_min);
2915 std::string Server::getPlayerName(u16 peer_id)
2917 RemotePlayer *player = m_env->getPlayer(peer_id);
2919 return "[id="+itos(peer_id)+"]";
2920 return player->getName();
2923 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2925 RemotePlayer *player = m_env->getPlayer(peer_id);
2928 return player->getPlayerSAO();
2931 std::wstring Server::getStatusString()
2933 std::wostringstream os(std::ios_base::binary);
2936 os<<L"version="<<narrow_to_wide(g_version_string);
2938 os<<L", uptime="<<m_uptime.get();
2940 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2941 // Information about clients
2944 std::vector<u16> clients = m_clients.getClientIDs();
2945 for (u16 client_id : clients) {
2947 RemotePlayer *player = m_env->getPlayer(client_id);
2948 // Get name of player
2949 std::wstring name = L"unknown";
2951 name = narrow_to_wide(player->getName());
2952 // Add name to information string
2961 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2962 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2964 if (!g_settings->get("motd").empty())
2965 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2969 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2971 std::set<std::string> privs;
2972 m_script->getAuth(name, NULL, &privs);
2976 bool Server::checkPriv(const std::string &name, const std::string &priv)
2978 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2979 return (privs.count(priv) != 0);
2982 void Server::reportPrivsModified(const std::string &name)
2985 std::vector<u16> clients = m_clients.getClientIDs();
2986 for (const u16 client_id : clients) {
2987 RemotePlayer *player = m_env->getPlayer(client_id);
2988 reportPrivsModified(player->getName());
2991 RemotePlayer *player = m_env->getPlayer(name.c_str());
2994 SendPlayerPrivileges(player->peer_id);
2995 PlayerSAO *sao = player->getPlayerSAO();
2998 sao->updatePrivileges(
2999 getPlayerEffectivePrivs(name),
3004 void Server::reportInventoryFormspecModified(const std::string &name)
3006 RemotePlayer *player = m_env->getPlayer(name.c_str());
3009 SendPlayerInventoryFormspec(player->peer_id);
3012 void Server::setIpBanned(const std::string &ip, const std::string &name)
3014 m_banmanager->add(ip, name);
3017 void Server::unsetIpBanned(const std::string &ip_or_name)
3019 m_banmanager->remove(ip_or_name);
3022 std::string Server::getBanDescription(const std::string &ip_or_name)
3024 return m_banmanager->getBanDescription(ip_or_name);
3027 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3029 // m_env will be NULL if the server is initializing
3033 if (m_admin_nick == name && !m_admin_nick.empty()) {
3034 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3037 RemotePlayer *player = m_env->getPlayer(name);
3042 if (player->peer_id == PEER_ID_INEXISTENT)
3045 SendChatMessage(player->peer_id, ChatMessage(msg));
3048 bool Server::showFormspec(const char *playername, const std::string &formspec,
3049 const std::string &formname)
3051 // m_env will be NULL if the server is initializing
3055 RemotePlayer *player = m_env->getPlayer(playername);
3059 SendShowFormspecMessage(player->peer_id, formspec, formname);
3063 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3068 u32 id = player->addHud(form);
3070 SendHUDAdd(player->peer_id, id, form);
3075 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3079 HudElement* todel = player->removeHud(id);
3086 SendHUDRemove(player->peer_id, id);
3090 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3095 SendHUDChange(player->peer_id, id, stat, data);
3099 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3104 SendHUDSetFlags(player->peer_id, flags, mask);
3105 player->hud_flags &= ~mask;
3106 player->hud_flags |= flags;
3108 PlayerSAO* playersao = player->getPlayerSAO();
3113 m_script->player_event(playersao, "hud_changed");
3117 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3122 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3125 player->setHotbarItemcount(hotbar_itemcount);
3126 std::ostringstream os(std::ios::binary);
3127 writeS32(os, hotbar_itemcount);
3128 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3132 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3137 player->setHotbarImage(name);
3138 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3141 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3145 return player->getHotbarImage();
3148 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3153 player->setHotbarSelectedImage(name);
3154 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3157 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3158 v2s32 animation_frames[4], f32 frame_speed)
3163 player->setLocalAnimations(animation_frames, frame_speed);
3164 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3168 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3173 player->eye_offset_first = first;
3174 player->eye_offset_third = third;
3175 SendEyeOffset(player->peer_id, first, third);
3179 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3180 const std::string &type, const std::vector<std::string> ¶ms,
3186 player->setSky(bgcolor, type, params, clouds);
3187 SendSetSky(player->peer_id, bgcolor, type, params, clouds);
3191 bool Server::setClouds(RemotePlayer *player, float density,
3192 const video::SColor &color_bright,
3193 const video::SColor &color_ambient,
3201 SendCloudParams(player->peer_id, density,
3202 color_bright, color_ambient, height,
3207 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3213 player->overrideDayNightRatio(do_override, ratio);
3214 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3218 void Server::notifyPlayers(const std::wstring &msg)
3220 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3223 void Server::spawnParticle(const std::string &playername, v3f pos,
3224 v3f velocity, v3f acceleration,
3225 float expirationtime, float size, bool
3226 collisiondetection, bool collision_removal,
3227 bool vertical, const std::string &texture,
3228 const struct TileAnimationParams &animation, u8 glow)
3230 // m_env will be NULL if the server is initializing
3234 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3235 if (!playername.empty()) {
3236 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3239 peer_id = player->peer_id;
3240 proto_ver = player->protocol_version;
3243 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3244 expirationtime, size, collisiondetection,
3245 collision_removal, vertical, texture, animation, glow);
3248 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3249 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3250 float minexptime, float maxexptime, float minsize, float maxsize,
3251 bool collisiondetection, bool collision_removal,
3252 ServerActiveObject *attached, bool vertical, const std::string &texture,
3253 const std::string &playername, const struct TileAnimationParams &animation,
3256 // m_env will be NULL if the server is initializing
3260 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3261 if (!playername.empty()) {
3262 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3265 peer_id = player->peer_id;
3266 proto_ver = player->protocol_version;
3269 u16 attached_id = attached ? attached->getId() : 0;
3272 if (attached_id == 0)
3273 id = m_env->addParticleSpawner(spawntime);
3275 id = m_env->addParticleSpawner(spawntime, attached_id);
3277 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3278 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3279 minexptime, maxexptime, minsize, maxsize,
3280 collisiondetection, collision_removal, attached_id, vertical,
3281 texture, id, animation, glow);
3286 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3288 // m_env will be NULL if the server is initializing
3290 throw ServerError("Can't delete particle spawners during initialisation!");
3292 u16 peer_id = PEER_ID_INEXISTENT;
3293 if (!playername.empty()) {
3294 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3297 peer_id = player->peer_id;
3300 m_env->deleteParticleSpawner(id);
3301 SendDeleteParticleSpawner(peer_id, id);
3304 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3306 if(m_detached_inventories.count(name) > 0){
3307 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3308 delete m_detached_inventories[name];
3310 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3312 Inventory *inv = new Inventory(m_itemdef);
3314 m_detached_inventories[name] = inv;
3315 m_detached_inventories_player[name] = player;
3316 //TODO find a better way to do this
3317 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3321 // actions: time-reversed list
3322 // Return value: success/failure
3323 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3324 std::list<std::string> *log)
3326 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3327 ServerMap *map = (ServerMap*)(&m_env->getMap());
3329 // Fail if no actions to handle
3330 if(actions.empty()){
3331 log->push_back("Nothing to do.");
3338 for (const RollbackAction &action : actions) {
3340 bool success = action.applyRevert(map, this, this);
3343 std::ostringstream os;
3344 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3345 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3347 log->push_back(os.str());
3349 std::ostringstream os;
3350 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3351 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3353 log->push_back(os.str());
3357 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3358 <<" failed"<<std::endl;
3360 // Call it done if less than half failed
3361 return num_failed <= num_tried/2;
3364 // IGameDef interface
3366 IItemDefManager *Server::getItemDefManager()
3371 INodeDefManager *Server::getNodeDefManager()
3376 ICraftDefManager *Server::getCraftDefManager()
3381 u16 Server::allocateUnknownNodeId(const std::string &name)
3383 return m_nodedef->allocateDummy(name);
3386 MtEventManager *Server::getEventManager()
3391 IWritableItemDefManager *Server::getWritableItemDefManager()
3396 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3401 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3406 const ModSpec *Server::getModSpec(const std::string &modname) const
3408 std::vector<ModSpec>::const_iterator it;
3409 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3410 const ModSpec &mod = *it;
3411 if (mod.name == modname)
3417 void Server::getModNames(std::vector<std::string> &modlist)
3419 std::vector<ModSpec>::iterator it;
3420 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3421 modlist.push_back(it->name);
3424 std::string Server::getBuiltinLuaPath()
3426 return porting::path_share + DIR_DELIM + "builtin";
3429 std::string Server::getModStoragePath() const
3431 return m_path_world + DIR_DELIM + "mod_storage";
3434 v3f Server::findSpawnPos()
3436 ServerMap &map = m_env->getServerMap();
3438 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3439 return nodeposf * BS;
3442 bool is_good = false;
3443 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3444 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3446 // Try to find a good place a few times
3447 for(s32 i = 0; i < 4000 && !is_good; i++) {
3448 s32 range = MYMIN(1 + i, range_max);
3449 // We're going to try to throw the player to this position
3450 v2s16 nodepos2d = v2s16(
3451 -range + (myrand() % (range * 2)),
3452 -range + (myrand() % (range * 2)));
3454 // Get spawn level at point
3455 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3456 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3457 // the mapgen to signify an unsuitable spawn position
3458 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3461 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3464 for (s32 i = 0; i < 10; i++) {
3465 v3s16 blockpos = getNodeBlockPos(nodepos);
3466 map.emergeBlock(blockpos, true);
3467 content_t c = map.getNodeNoEx(nodepos).getContent();
3468 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3470 if (air_count >= 2) {
3471 nodeposf = intToFloat(nodepos, BS);
3472 // Don't spawn the player outside map boundaries
3473 if (objectpos_over_limit(nodeposf))
3486 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3488 m_shutdown_timer = delay;
3489 m_shutdown_msg = msg;
3490 m_shutdown_ask_reconnect = reconnect;
3492 if (delay == 0.0f) {
3493 // No delay, shutdown immediately
3494 m_shutdown_requested = true;
3495 // only print to the infostream, a chat message saying
3496 // "Server Shutting Down" is sent when the server destructs.
3497 infostream << "*** Immediate Server shutdown requested." << std::endl;
3498 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3499 // Negative delay, cancel shutdown if requested
3500 m_shutdown_timer = 0.0f;
3501 m_shutdown_msg = "";
3502 m_shutdown_ask_reconnect = false;
3503 m_shutdown_requested = false;
3504 std::wstringstream ws;
3506 ws << L"*** Server shutdown canceled.";
3508 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3509 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3510 } else if (delay > 0.0f) {
3511 // Positive delay, tell the clients when the server will shut down
3512 std::wstringstream ws;
3514 ws << L"*** Server shutting down in "
3515 << duration_to_string(myround(m_shutdown_timer)).c_str()
3518 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3519 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3523 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3526 Try to get an existing player
3528 RemotePlayer *player = m_env->getPlayer(name);
3530 // If player is already connected, cancel
3531 if (player && player->peer_id != 0) {
3532 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3537 If player with the wanted peer_id already exists, cancel.
3539 if (m_env->getPlayer(peer_id)) {
3540 infostream<<"emergePlayer(): Player with wrong name but same"
3541 " peer_id already exists"<<std::endl;
3546 player = new RemotePlayer(name, idef());
3549 bool newplayer = false;
3552 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3554 // Complete init with server parts
3555 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3556 player->protocol_version = proto_version;
3560 m_script->on_newplayer(playersao);
3566 bool Server::registerModStorage(ModMetadata *storage)
3568 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3569 errorstream << "Unable to register same mod storage twice. Storage name: "
3570 << storage->getModName() << std::endl;
3574 m_mod_storages[storage->getModName()] = storage;
3578 void Server::unregisterModStorage(const std::string &name)
3580 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3581 if (it != m_mod_storages.end()) {
3582 // Save unconditionaly on unregistration
3583 it->second->save(getModStoragePath());
3584 m_mod_storages.erase(name);
3588 void dedicated_server_loop(Server &server, bool &kill)
3590 DSTACK(FUNCTION_NAME);
3592 verbosestream<<"dedicated_server_loop()"<<std::endl;
3594 IntervalLimiter m_profiler_interval;
3596 static thread_local const float steplen =
3597 g_settings->getFloat("dedicated_server_step");
3598 static thread_local const float profiler_print_interval =
3599 g_settings->getFloat("profiler_print_interval");
3602 // This is kind of a hack but can be done like this
3603 // because server.step() is very light
3605 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3606 sleep_ms((int)(steplen*1000.0));
3608 server.step(steplen);
3610 if (server.getShutdownRequested() || kill)
3616 if (profiler_print_interval != 0) {
3617 if(m_profiler_interval.step(steplen, profiler_print_interval))
3619 infostream<<"Profiler:"<<std::endl;
3620 g_profiler->print(infostream);
3621 g_profiler->clear();
3626 infostream << "Dedicated server quitting" << std::endl;
3628 if (g_settings->getBool("server_announce"))
3629 ServerList::sendAnnounce(ServerList::AA_DELETE,
3630 server.m_bind_addr.getPort());