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"
47 #include "mapgen/mapgen.h"
48 #include "mapgen/mg_biome.h"
49 #include "content_mapnode.h"
50 #include "content_nodemeta.h"
51 #include "content_sao.h"
53 #include "event_manager.h"
54 #include "modchannels.h"
55 #include "serverlist.h"
56 #include "util/string.h"
58 #include "util/serialize.h"
59 #include "util/thread.h"
60 #include "defaultsettings.h"
61 #include "util/base64.h"
62 #include "util/sha1.h"
64 #include "database/database.h"
65 #include "chatmessage.h"
66 #include "chat_interface.h"
67 #include "remoteplayer.h"
69 class ClientNotFoundException : public BaseException
72 ClientNotFoundException(const char *s):
77 class ServerThread : public Thread
81 ServerThread(Server *server):
92 void *ServerThread::run()
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_bind_addr(bind_addr),
157 m_path_world(path_world),
158 m_gamespec(gamespec),
159 m_simple_singleplayer_mode(simple_singleplayer_mode),
160 m_dedicated(dedicated),
161 m_async_fatal_error(""),
162 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
165 m_bind_addr.isIPv6(),
167 m_itemdef(createItemDefManager()),
168 m_nodedef(createNodeDefManager()),
169 m_craftdef(createCraftDefManager()),
170 m_event(new EventManager()),
174 m_modchannel_mgr(new ModChannelMgr())
176 m_lag = g_settings->getFloat("dedicated_server_step");
178 if (path_world.empty())
179 throw ServerError("Supplied empty world path");
181 if(!gamespec.isValid())
182 throw ServerError("Supplied invalid gamespec");
184 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
185 if(m_simple_singleplayer_mode)
186 infostream<<" in simple singleplayer mode"<<std::endl;
188 infostream<<std::endl;
189 infostream<<"- world: "<<m_path_world<<std::endl;
190 infostream<<"- game: "<<m_gamespec.path<<std::endl;
192 // Create world if it doesn't exist
193 if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
194 throw ServerError("Failed to initialize world");
196 // Create server thread
197 m_thread = new ServerThread(this);
199 // Create emerge manager
200 m_emerge = new EmergeManager(this);
202 // Create ban manager
203 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
204 m_banmanager = new BanManager(ban_path);
206 ServerModConfiguration modconf(m_path_world);
207 m_mods = modconf.getMods();
208 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
209 // complain about mods with unsatisfied dependencies
210 if (!modconf.isConsistent()) {
211 modconf.printUnsatisfiedModsError();
215 MutexAutoLock envlock(m_env_mutex);
217 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
218 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
220 // Initialize scripting
221 infostream<<"Server: Initializing Lua"<<std::endl;
223 m_script = new ServerScripting(this);
225 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
228 infostream << "Server: Loading mods: ";
229 for (std::vector<ModSpec>::const_iterator i = m_mods.begin();
230 i != m_mods.end(); ++i) {
231 infostream << (*i).name << " ";
233 infostream << std::endl;
234 // Load and run "mod" scripts
235 for (std::vector<ModSpec>::const_iterator it = m_mods.begin();
236 it != m_mods.end(); ++it) {
237 const ModSpec &mod = *it;
238 if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
239 throw ModError("Error loading mod \"" + mod.name +
240 "\": Mod name does not follow naming conventions: "
241 "Only characters [a-z0-9_] are allowed.");
243 std::string script_path = mod.path + DIR_DELIM + "init.lua";
244 infostream << " [" << padStringRight(mod.name, 12) << "] [\""
245 << script_path << "\"]" << std::endl;
246 m_script->loadMod(script_path, mod.name);
249 // Read Textures and calculate sha1 sums
252 // Apply item aliases in the node definition manager
253 m_nodedef->updateAliases(m_itemdef);
255 // Apply texture overrides from texturepack/override.txt
256 for (const auto &path : fs::GetRecursiveDirs(g_settings->get("texture_path")))
257 m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
259 m_nodedef->setNodeRegistrationStatus(true);
261 // Perform pending node name resolutions
262 m_nodedef->runNodeResolveCallbacks();
264 // unmap node names for connected nodeboxes
265 m_nodedef->mapNodeboxConnections();
267 // init the recipe hashes to speed up crafting
268 m_craftdef->initHashes(this);
270 // Initialize Environment
271 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
273 m_clients.setEnv(m_env);
275 if (!servermap->settings_mgr.makeMapgenParams())
276 FATAL_ERROR("Couldn't create any mapgen type");
278 // Initialize mapgens
279 m_emerge->initMapgens(servermap->getMapgenParams());
281 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
282 if (m_enable_rollback_recording) {
283 // Create rollback manager
284 m_rollback = new RollbackManager(m_path_world, this);
287 // Give environment reference to scripting api
288 m_script->initializeEnvironment(m_env);
290 // Register us to receive map edit events
291 servermap->addEventReceiver(this);
293 // If file exists, load environment metadata
294 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
295 infostream << "Server: Loading environment metadata" << std::endl;
298 m_env->loadDefaultMeta();
301 m_liquid_transform_every = g_settings->getFloat("liquid_update");
302 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
303 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
304 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
309 infostream<<"Server destructing"<<std::endl;
311 // Send shutdown message
312 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
313 L"*** Server shutting down"));
316 MutexAutoLock envlock(m_env_mutex);
318 // Execute script shutdown hooks
319 m_script->on_shutdown();
321 infostream << "Server: Saving players" << std::endl;
322 m_env->saveLoadedPlayers();
324 infostream << "Server: Kicking players" << std::endl;
325 std::string kick_msg;
326 bool reconnect = false;
327 if (getShutdownRequested()) {
328 reconnect = m_shutdown_ask_reconnect;
329 kick_msg = m_shutdown_msg;
331 if (kick_msg.empty()) {
332 kick_msg = g_settings->get("kick_msg_shutdown");
334 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
335 kick_msg, reconnect);
337 infostream << "Server: Saving environment metadata" << std::endl;
345 // stop all emerge threads before deleting players that may have
346 // requested blocks to be emerged
347 m_emerge->stopThreads();
349 // Delete things in the reverse order of creation
359 // Deinitialize scripting
360 infostream<<"Server: Deinitializing scripting"<<std::endl;
363 // Delete detached inventories
364 for (auto &detached_inventory : m_detached_inventories) {
365 delete detached_inventory.second;
371 infostream << "Starting server on " << m_bind_addr.serializeString()
372 << "..." << std::endl;
374 // Stop thread if already running
377 // Initialize connection
378 m_con->SetTimeoutMs(30);
379 m_con->Serve(m_bind_addr);
384 // ASCII art for the win!
386 << " .__ __ __ " << std::endl
387 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
388 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
389 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
390 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
391 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
392 actionstream << "World at [" << m_path_world << "]" << std::endl;
393 actionstream << "Server for gameid=\"" << m_gamespec.id
394 << "\" listening on " << m_bind_addr.serializeString() << ":"
395 << m_bind_addr.getPort() << "." << std::endl;
400 infostream<<"Server: Stopping and waiting threads"<<std::endl;
402 // Stop threads (set run=false first so both start stopping)
404 //m_emergethread.setRun(false);
406 //m_emergethread.stop();
408 infostream<<"Server: Threads stopped"<<std::endl;
411 void Server::step(float dtime)
417 MutexAutoLock lock(m_step_dtime_mutex);
418 m_step_dtime += dtime;
420 // Throw if fatal error occurred in thread
421 std::string async_err = m_async_fatal_error.get();
422 if (!async_err.empty()) {
423 if (!m_simple_singleplayer_mode) {
424 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
425 g_settings->get("kick_msg_crash"),
426 g_settings->getBool("ask_reconnect_on_crash"));
428 throw ServerError("AsyncErr: " + async_err);
432 void Server::AsyncRunStep(bool initial_step)
434 g_profiler->add("Server::AsyncRunStep (num)", 1);
438 MutexAutoLock lock1(m_step_dtime_mutex);
439 dtime = m_step_dtime;
443 // Send blocks to clients
447 if((dtime < 0.001) && !initial_step)
450 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
452 //infostream<<"Server steps "<<dtime<<std::endl;
453 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
456 MutexAutoLock lock1(m_step_dtime_mutex);
457 m_step_dtime -= dtime;
464 m_uptime.set(m_uptime.get() + dtime);
470 Update time of day and overall game time
472 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
475 Send to clients at constant intervals
478 m_time_of_day_send_timer -= dtime;
479 if(m_time_of_day_send_timer < 0.0) {
480 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
481 u16 time = m_env->getTimeOfDay();
482 float time_speed = g_settings->getFloat("time_speed");
483 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
487 MutexAutoLock lock(m_env_mutex);
488 // Figure out and report maximum lag to environment
489 float max_lag = m_env->getMaxLagEstimate();
490 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
492 if(dtime > 0.1 && dtime > max_lag * 2.0)
493 infostream<<"Server: Maximum lag peaked to "<<dtime
497 m_env->reportMaxLagEstimate(max_lag);
499 ScopeProfiler sp(g_profiler, "SEnv step");
500 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
504 static const float map_timer_and_unload_dtime = 2.92;
505 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
507 MutexAutoLock lock(m_env_mutex);
508 // Run Map's timers and unload unused data
509 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
510 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
511 g_settings->getFloat("server_unload_unused_data_timeout"),
516 Listen to the admin chat, if available
519 if (!m_admin_chat->command_queue.empty()) {
520 MutexAutoLock lock(m_env_mutex);
521 while (!m_admin_chat->command_queue.empty()) {
522 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
523 handleChatInterfaceEvent(evt);
527 m_admin_chat->outgoing_queue.push_back(
528 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
535 /* Transform liquids */
536 m_liquid_transform_timer += dtime;
537 if(m_liquid_transform_timer >= m_liquid_transform_every)
539 m_liquid_transform_timer -= m_liquid_transform_every;
541 MutexAutoLock lock(m_env_mutex);
543 ScopeProfiler sp(g_profiler, "Server: liquid transform");
545 std::map<v3s16, MapBlock*> modified_blocks;
546 m_env->getMap().transformLiquids(modified_blocks, m_env);
549 Set the modified blocks unsent for all the clients
551 if(!modified_blocks.empty())
553 SetBlocksNotSent(modified_blocks);
556 m_clients.step(dtime);
558 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
560 // send masterserver announce
562 float &counter = m_masterserver_timer;
563 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
564 g_settings->getBool("server_announce")) {
565 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
566 ServerList::AA_START,
567 m_bind_addr.getPort(),
568 m_clients.getPlayerNames(),
570 m_env->getGameTime(),
573 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
583 Check added and deleted active objects
586 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
587 MutexAutoLock envlock(m_env_mutex);
590 const RemoteClientMap &clients = m_clients.getClientList();
591 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
593 // Radius inside which objects are active
594 static thread_local const s16 radius =
595 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
597 // Radius inside which players are active
598 static thread_local const bool is_transfer_limited =
599 g_settings->exists("unlimited_player_transfer_distance") &&
600 !g_settings->getBool("unlimited_player_transfer_distance");
601 static thread_local const s16 player_transfer_dist =
602 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
603 s16 player_radius = player_transfer_dist;
604 if (player_radius == 0 && is_transfer_limited)
605 player_radius = radius;
607 for (const auto &client_it : clients) {
608 RemoteClient *client = client_it.second;
610 // If definitions and textures have not been sent, don't
611 // send objects either
612 if (client->getState() < CS_DefinitionsSent)
615 RemotePlayer *player = m_env->getPlayer(client->peer_id);
617 // This can happen if the client timeouts somehow
621 PlayerSAO *playersao = player->getPlayerSAO();
625 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
626 if (my_radius <= 0) my_radius = radius;
627 //infostream << "Server: Active Radius " << my_radius << std::endl;
629 std::queue<u16> removed_objects;
630 std::queue<u16> added_objects;
631 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
632 client->m_known_objects, removed_objects);
633 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
634 client->m_known_objects, added_objects);
636 // Ignore if nothing happened
637 if (removed_objects.empty() && added_objects.empty()) {
641 std::string data_buffer;
645 // Handle removed objects
646 writeU16((u8*)buf, removed_objects.size());
647 data_buffer.append(buf, 2);
648 while (!removed_objects.empty()) {
650 u16 id = removed_objects.front();
651 ServerActiveObject* obj = m_env->getActiveObject(id);
653 // Add to data buffer for sending
654 writeU16((u8*)buf, id);
655 data_buffer.append(buf, 2);
657 // Remove from known objects
658 client->m_known_objects.erase(id);
660 if(obj && obj->m_known_by_count > 0)
661 obj->m_known_by_count--;
662 removed_objects.pop();
665 // Handle added objects
666 writeU16((u8*)buf, added_objects.size());
667 data_buffer.append(buf, 2);
668 while (!added_objects.empty()) {
670 u16 id = added_objects.front();
671 ServerActiveObject* obj = m_env->getActiveObject(id);
674 u8 type = ACTIVEOBJECT_TYPE_INVALID;
676 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
678 type = obj->getSendType();
680 // Add to data buffer for sending
681 writeU16((u8*)buf, id);
682 data_buffer.append(buf, 2);
683 writeU8((u8*)buf, type);
684 data_buffer.append(buf, 1);
687 data_buffer.append(serializeLongString(
688 obj->getClientInitializationData(client->net_proto_version)));
690 data_buffer.append(serializeLongString(""));
692 // Add to known objects
693 client->m_known_objects.insert(id);
696 obj->m_known_by_count++;
701 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
702 verbosestream << "Server: Sent object remove/add: "
703 << removed_objects.size() << " removed, "
704 << added_objects.size() << " added, "
705 << "packet size is " << pktSize << std::endl;
709 m_mod_storage_save_timer -= dtime;
710 if (m_mod_storage_save_timer <= 0.0f) {
711 infostream << "Saving registered mod storages." << std::endl;
712 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
713 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
714 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
715 if (it->second->isModified()) {
716 it->second->save(getModStoragePath());
726 MutexAutoLock envlock(m_env_mutex);
727 ScopeProfiler sp(g_profiler, "Server: sending object messages");
730 // Value = data sent by object
731 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
733 // Get active object messages from environment
735 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
739 std::vector<ActiveObjectMessage>* message_list = nullptr;
740 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
741 n = buffered_messages.find(aom.id);
742 if (n == buffered_messages.end()) {
743 message_list = new std::vector<ActiveObjectMessage>;
744 buffered_messages[aom.id] = message_list;
747 message_list = n->second;
749 message_list->push_back(aom);
753 const RemoteClientMap &clients = m_clients.getClientList();
754 // Route data to every client
755 for (const auto &client_it : clients) {
756 RemoteClient *client = client_it.second;
757 std::string reliable_data;
758 std::string unreliable_data;
759 // Go through all objects in message buffer
760 for (const auto &buffered_message : buffered_messages) {
761 // If object is not known by client, skip it
762 u16 id = buffered_message.first;
763 if (client->m_known_objects.find(id) == client->m_known_objects.end())
766 // Get message list of object
767 std::vector<ActiveObjectMessage>* list = buffered_message.second;
768 // Go through every message
769 for (const ActiveObjectMessage &aom : *list) {
770 // Compose the full new data with header
771 std::string new_data;
774 writeU16((u8*)&buf[0], aom.id);
775 new_data.append(buf, 2);
777 new_data += serializeString(aom.datastring);
778 // Add data to buffer
780 reliable_data += new_data;
782 unreliable_data += new_data;
786 reliable_data and unreliable_data are now ready.
789 if (!reliable_data.empty()) {
790 SendActiveObjectMessages(client->peer_id, reliable_data);
793 if (!unreliable_data.empty()) {
794 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
799 // Clear buffered_messages
800 for (auto &buffered_message : buffered_messages) {
801 delete buffered_message.second;
806 Send queued-for-sending map edit events.
809 // We will be accessing the environment
810 MutexAutoLock lock(m_env_mutex);
812 // Don't send too many at a time
815 // Single change sending is disabled if queue size is not small
816 bool disable_single_change_sending = false;
817 if(m_unsent_map_edit_queue.size() >= 4)
818 disable_single_change_sending = true;
820 int event_count = m_unsent_map_edit_queue.size();
822 // We'll log the amount of each
825 while (!m_unsent_map_edit_queue.empty()) {
826 MapEditEvent* event = m_unsent_map_edit_queue.front();
827 m_unsent_map_edit_queue.pop();
829 // Players far away from the change are stored here.
830 // Instead of sending the changes, MapBlocks are set not sent
832 std::vector<u16> far_players;
834 switch (event->type) {
837 prof.add("MEET_ADDNODE", 1);
838 sendAddNode(event->p, event->n, event->already_known_by_peer,
839 &far_players, disable_single_change_sending ? 5 : 30,
840 event->type == MEET_ADDNODE);
842 case MEET_REMOVENODE:
843 prof.add("MEET_REMOVENODE", 1);
844 sendRemoveNode(event->p, event->already_known_by_peer,
845 &far_players, disable_single_change_sending ? 5 : 30);
847 case MEET_BLOCK_NODE_METADATA_CHANGED:
848 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
849 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
850 setBlockNotSent(event->p);
853 infostream << "Server: MEET_OTHER" << std::endl;
854 prof.add("MEET_OTHER", 1);
855 for (const v3s16 &modified_block : event->modified_blocks) {
856 setBlockNotSent(modified_block);
860 prof.add("unknown", 1);
861 warningstream << "Server: Unknown MapEditEvent "
862 << ((u32)event->type) << std::endl;
867 Set blocks not sent to far players
869 if (!far_players.empty()) {
870 // Convert list format to that wanted by SetBlocksNotSent
871 std::map<v3s16, MapBlock*> modified_blocks2;
872 for (const v3s16 &modified_block : event->modified_blocks) {
873 modified_blocks2[modified_block] =
874 m_env->getMap().getBlockNoCreateNoEx(modified_block);
877 // Set blocks not sent
878 for (const u16 far_player : far_players) {
879 if (RemoteClient *client = getClient(far_player))
880 client->SetBlocksNotSent(modified_blocks2);
887 if (event_count >= 5) {
888 infostream << "Server: MapEditEvents:" << std::endl;
889 prof.print(infostream);
890 } else if (event_count != 0) {
891 verbosestream << "Server: MapEditEvents:" << std::endl;
892 prof.print(verbosestream);
898 Trigger emergethread (it somehow gets to a non-triggered but
899 bysy state sometimes)
902 float &counter = m_emergethread_trigger_timer;
904 if (counter >= 2.0) {
907 m_emerge->startThreads();
911 // Save map, players and auth stuff
913 float &counter = m_savemap_timer;
915 static thread_local const float save_interval =
916 g_settings->getFloat("server_map_save_interval");
917 if (counter >= save_interval) {
919 MutexAutoLock lock(m_env_mutex);
921 ScopeProfiler sp(g_profiler, "Server: saving stuff");
924 if (m_banmanager->isModified()) {
925 m_banmanager->save();
928 // Save changed parts of map
929 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
932 m_env->saveLoadedPlayers();
934 // Save environment metadata
940 static const float shutdown_msg_times[] =
942 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
945 if (m_shutdown_timer > 0.0f) {
946 // Automated messages
947 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
948 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
949 // If shutdown timer matches an automessage, shot it
950 if (m_shutdown_timer > shutdown_msg_times[i] &&
951 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
952 std::wstringstream ws;
954 ws << L"*** Server shutting down in "
955 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
958 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
959 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
965 m_shutdown_timer -= dtime;
966 if (m_shutdown_timer < 0.0f) {
967 m_shutdown_timer = 0.0f;
968 m_shutdown_requested = true;
973 void Server::Receive()
978 m_con->Receive(&pkt);
979 peer_id = pkt.getPeerId();
981 } catch (const con::InvalidIncomingDataException &e) {
982 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
983 << e.what() << std::endl;
984 } catch (const SerializationError &e) {
985 infostream << "Server::Receive(): SerializationError: what()="
986 << e.what() << std::endl;
987 } catch (const ClientStateError &e) {
988 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
989 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
990 L"Try reconnecting or updating your client");
991 } catch (const con::PeerNotFoundException &e) {
996 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
998 std::string playername;
999 PlayerSAO *playersao = NULL;
1002 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1004 playername = client->getName();
1005 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1007 } catch (std::exception &e) {
1013 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1015 // If failed, cancel
1016 if (!playersao || !player) {
1017 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1018 actionstream << "Server: Failed to emerge player \"" << playername
1019 << "\" (player allocated to an another client)" << std::endl;
1020 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1021 L"name. If your client closed unexpectedly, try again in "
1024 errorstream << "Server: " << playername << ": Failed to emerge player"
1026 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1032 Send complete position information
1034 SendMovePlayer(peer_id);
1037 SendPlayerPrivileges(peer_id);
1039 // Send inventory formspec
1040 SendPlayerInventoryFormspec(peer_id);
1043 SendInventory(playersao);
1045 // Send HP or death screen
1046 if (playersao->isDead())
1047 SendDeathscreen(peer_id, false, v3f(0,0,0));
1049 SendPlayerHPOrDie(playersao);
1052 SendPlayerBreath(playersao);
1054 // Note things in chat if not in simple singleplayer mode
1055 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1056 // Send information about server to player in chat
1057 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1059 Address addr = getPeerAddress(player->getPeerId());
1060 std::string ip_str = addr.serializeString();
1061 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1066 const std::vector<std::string> &names = m_clients.getPlayerNames();
1068 actionstream << player->getName() << " joins game. List of players: ";
1070 for (const std::string &name : names) {
1071 actionstream << name << " ";
1074 actionstream << player->getName() <<std::endl;
1079 inline void Server::handleCommand(NetworkPacket* pkt)
1081 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1082 (this->*opHandle.handler)(pkt);
1085 void Server::ProcessData(NetworkPacket *pkt)
1087 // Environment is locked first.
1088 MutexAutoLock envlock(m_env_mutex);
1090 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1091 u32 peer_id = pkt->getPeerId();
1094 Address address = getPeerAddress(peer_id);
1095 std::string addr_s = address.serializeString();
1097 if(m_banmanager->isIpBanned(addr_s)) {
1098 std::string ban_name = m_banmanager->getBanName(addr_s);
1099 infostream << "Server: A banned client tried to connect from "
1100 << addr_s << "; banned name was "
1101 << ban_name << std::endl;
1102 // This actually doesn't seem to transfer to the client
1103 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1104 + utf8_to_wide(ban_name));
1108 catch(con::PeerNotFoundException &e) {
1110 * no peer for this packet found
1111 * most common reason is peer timeout, e.g. peer didn't
1112 * respond for some time, your server was overloaded or
1115 infostream << "Server::ProcessData(): Canceling: peer "
1116 << peer_id << " not found" << std::endl;
1121 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1123 // Command must be handled into ToServerCommandHandler
1124 if (command >= TOSERVER_NUM_MSG_TYPES) {
1125 infostream << "Server: Ignoring unknown command "
1126 << command << std::endl;
1130 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1135 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1137 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1138 errorstream << "Server::ProcessData(): Cancelling: Peer"
1139 " serialization format invalid or not initialized."
1140 " Skipping incoming command=" << command << std::endl;
1144 /* Handle commands related to client startup */
1145 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1150 if (m_clients.getClientState(peer_id) < CS_Active) {
1151 if (command == TOSERVER_PLAYERPOS) return;
1153 errorstream << "Got packet command: " << command << " for peer id "
1154 << peer_id << " but client isn't active yet. Dropping packet "
1160 } catch (SendFailedException &e) {
1161 errorstream << "Server::ProcessData(): SendFailedException: "
1162 << "what=" << e.what()
1164 } catch (PacketError &e) {
1165 actionstream << "Server::ProcessData(): PacketError: "
1166 << "what=" << e.what()
1171 void Server::setTimeOfDay(u32 time)
1173 m_env->setTimeOfDay(time);
1174 m_time_of_day_send_timer = 0;
1177 void Server::onMapEditEvent(MapEditEvent *event)
1179 if(m_ignore_map_edit_events)
1181 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1183 MapEditEvent *e = event->clone();
1184 m_unsent_map_edit_queue.push(e);
1187 Inventory* Server::getInventory(const InventoryLocation &loc)
1190 case InventoryLocation::UNDEFINED:
1191 case InventoryLocation::CURRENT_PLAYER:
1193 case InventoryLocation::PLAYER:
1195 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1198 PlayerSAO *playersao = player->getPlayerSAO();
1201 return playersao->getInventory();
1204 case InventoryLocation::NODEMETA:
1206 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1209 return meta->getInventory();
1212 case InventoryLocation::DETACHED:
1214 if(m_detached_inventories.count(loc.name) == 0)
1216 return m_detached_inventories[loc.name];
1220 sanity_check(false); // abort
1225 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1228 case InventoryLocation::UNDEFINED:
1230 case InventoryLocation::PLAYER:
1235 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1240 PlayerSAO *playersao = player->getPlayerSAO();
1244 SendInventory(playersao);
1247 case InventoryLocation::NODEMETA:
1249 v3s16 blockpos = getNodeBlockPos(loc.p);
1251 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1253 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1255 setBlockNotSent(blockpos);
1258 case InventoryLocation::DETACHED:
1260 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1264 sanity_check(false); // abort
1269 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1271 std::vector<session_t> clients = m_clients.getClientIDs();
1273 // Set the modified blocks unsent for all the clients
1274 for (const session_t client_id : clients) {
1275 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1276 client->SetBlocksNotSent(block);
1281 void Server::peerAdded(con::Peer *peer)
1283 verbosestream<<"Server::peerAdded(): peer->id="
1284 <<peer->id<<std::endl;
1286 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1289 void Server::deletingPeer(con::Peer *peer, bool timeout)
1291 verbosestream<<"Server::deletingPeer(): peer->id="
1292 <<peer->id<<", timeout="<<timeout<<std::endl;
1294 m_clients.event(peer->id, CSE_Disconnect);
1295 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1298 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1300 *retval = m_con->getPeerStat(peer_id,type);
1301 return *retval != -1;
1304 bool Server::getClientInfo(
1313 std::string* vers_string
1316 *state = m_clients.getClientState(peer_id);
1318 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1325 *uptime = client->uptime();
1326 *ser_vers = client->serialization_version;
1327 *prot_vers = client->net_proto_version;
1329 *major = client->getMajor();
1330 *minor = client->getMinor();
1331 *patch = client->getPatch();
1332 *vers_string = client->getPatch();
1339 void Server::handlePeerChanges()
1341 while(!m_peer_change_queue.empty())
1343 con::PeerChange c = m_peer_change_queue.front();
1344 m_peer_change_queue.pop();
1346 verbosestream<<"Server: Handling peer change: "
1347 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1352 case con::PEER_ADDED:
1353 m_clients.CreateClient(c.peer_id);
1356 case con::PEER_REMOVED:
1357 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1361 FATAL_ERROR("Invalid peer change event received!");
1367 void Server::printToConsoleOnly(const std::string &text)
1370 m_admin_chat->outgoing_queue.push_back(
1371 new ChatEventChat("", utf8_to_wide(text)));
1373 std::cout << text << std::endl;
1377 void Server::Send(NetworkPacket *pkt)
1379 Send(pkt->getPeerId(), pkt);
1382 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1384 m_clients.send(peer_id,
1385 clientCommandFactoryTable[pkt->getCommand()].channel,
1387 clientCommandFactoryTable[pkt->getCommand()].reliable);
1390 void Server::SendMovement(session_t peer_id)
1392 std::ostringstream os(std::ios_base::binary);
1394 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1396 pkt << g_settings->getFloat("movement_acceleration_default");
1397 pkt << g_settings->getFloat("movement_acceleration_air");
1398 pkt << g_settings->getFloat("movement_acceleration_fast");
1399 pkt << g_settings->getFloat("movement_speed_walk");
1400 pkt << g_settings->getFloat("movement_speed_crouch");
1401 pkt << g_settings->getFloat("movement_speed_fast");
1402 pkt << g_settings->getFloat("movement_speed_climb");
1403 pkt << g_settings->getFloat("movement_speed_jump");
1404 pkt << g_settings->getFloat("movement_liquid_fluidity");
1405 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1406 pkt << g_settings->getFloat("movement_liquid_sink");
1407 pkt << g_settings->getFloat("movement_gravity");
1412 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1414 if (!g_settings->getBool("enable_damage"))
1417 session_t peer_id = playersao->getPeerID();
1418 bool is_alive = playersao->getHP() > 0;
1421 SendPlayerHP(peer_id);
1426 void Server::SendHP(session_t peer_id, u16 hp)
1428 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1433 void Server::SendBreath(session_t peer_id, u16 breath)
1435 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1436 pkt << (u16) breath;
1440 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1441 const std::string &custom_reason, bool reconnect)
1443 assert(reason < SERVER_ACCESSDENIED_MAX);
1445 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1447 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1448 pkt << custom_reason;
1449 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1450 reason == SERVER_ACCESSDENIED_CRASH)
1451 pkt << custom_reason << (u8)reconnect;
1455 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1457 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1462 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1463 v3f camera_point_target)
1465 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1466 pkt << set_camera_point_target << camera_point_target;
1470 void Server::SendItemDef(session_t peer_id,
1471 IItemDefManager *itemdef, u16 protocol_version)
1473 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1477 u32 length of the next item
1478 zlib-compressed serialized ItemDefManager
1480 std::ostringstream tmp_os(std::ios::binary);
1481 itemdef->serialize(tmp_os, protocol_version);
1482 std::ostringstream tmp_os2(std::ios::binary);
1483 compressZlib(tmp_os.str(), tmp_os2);
1484 pkt.putLongString(tmp_os2.str());
1487 verbosestream << "Server: Sending item definitions to id(" << peer_id
1488 << "): size=" << pkt.getSize() << std::endl;
1493 void Server::SendNodeDef(session_t peer_id,
1494 INodeDefManager *nodedef, u16 protocol_version)
1496 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1500 u32 length of the next item
1501 zlib-compressed serialized NodeDefManager
1503 std::ostringstream tmp_os(std::ios::binary);
1504 nodedef->serialize(tmp_os, protocol_version);
1505 std::ostringstream tmp_os2(std::ios::binary);
1506 compressZlib(tmp_os.str(), tmp_os2);
1508 pkt.putLongString(tmp_os2.str());
1511 verbosestream << "Server: Sending node definitions to id(" << peer_id
1512 << "): size=" << pkt.getSize() << std::endl;
1518 Non-static send methods
1521 void Server::SendInventory(PlayerSAO* playerSAO)
1523 UpdateCrafting(playerSAO->getPlayer());
1529 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1531 std::ostringstream os;
1532 playerSAO->getInventory()->serialize(os);
1534 std::string s = os.str();
1536 pkt.putRawString(s.c_str(), s.size());
1540 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1542 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1544 u8 type = message.type;
1545 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1547 if (peer_id != PEER_ID_INEXISTENT) {
1548 RemotePlayer *player = m_env->getPlayer(peer_id);
1554 m_clients.sendToAll(&pkt);
1558 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1559 const std::string &formname)
1561 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1562 if (formspec.empty()){
1563 //the client should close the formspec
1564 pkt.putLongString("");
1566 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1573 // Spawns a particle on peer with peer_id
1574 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1575 v3f pos, v3f velocity, v3f acceleration,
1576 float expirationtime, float size, bool collisiondetection,
1577 bool collision_removal,
1578 bool vertical, const std::string &texture,
1579 const struct TileAnimationParams &animation, u8 glow)
1581 static thread_local const float radius =
1582 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1584 if (peer_id == PEER_ID_INEXISTENT) {
1585 std::vector<session_t> clients = m_clients.getClientIDs();
1587 for (const session_t client_id : clients) {
1588 RemotePlayer *player = m_env->getPlayer(client_id);
1592 PlayerSAO *sao = player->getPlayerSAO();
1596 // Do not send to distant clients
1597 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1600 SendSpawnParticle(client_id, player->protocol_version,
1601 pos, velocity, acceleration,
1602 expirationtime, size, collisiondetection,
1603 collision_removal, vertical, texture, animation, glow);
1608 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1610 pkt << pos << velocity << acceleration << expirationtime
1611 << size << collisiondetection;
1612 pkt.putLongString(texture);
1614 pkt << collision_removal;
1615 // This is horrible but required (why are there two ways to serialize pkts?)
1616 std::ostringstream os(std::ios_base::binary);
1617 animation.serialize(os, protocol_version);
1618 pkt.putRawString(os.str());
1624 // Adds a ParticleSpawner on peer with peer_id
1625 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1626 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1627 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1628 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1629 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1630 const struct TileAnimationParams &animation, u8 glow)
1632 if (peer_id == PEER_ID_INEXISTENT) {
1633 // This sucks and should be replaced:
1634 std::vector<session_t> clients = m_clients.getClientIDs();
1635 for (const session_t client_id : clients) {
1636 RemotePlayer *player = m_env->getPlayer(client_id);
1639 SendAddParticleSpawner(client_id, player->protocol_version,
1640 amount, spawntime, minpos, maxpos,
1641 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1642 minsize, maxsize, collisiondetection, collision_removal,
1643 attached_id, vertical, texture, id, animation, glow);
1648 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1650 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1651 << minacc << maxacc << minexptime << maxexptime << minsize
1652 << maxsize << collisiondetection;
1654 pkt.putLongString(texture);
1656 pkt << id << vertical;
1657 pkt << collision_removal;
1659 // This is horrible but required
1660 std::ostringstream os(std::ios_base::binary);
1661 animation.serialize(os, protocol_version);
1662 pkt.putRawString(os.str());
1668 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1670 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1672 // Ugly error in this packet
1675 if (peer_id != PEER_ID_INEXISTENT)
1678 m_clients.sendToAll(&pkt);
1682 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1684 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1686 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1687 << form->text << form->number << form->item << form->dir
1688 << form->align << form->offset << form->world_pos << form->size;
1693 void Server::SendHUDRemove(session_t peer_id, u32 id)
1695 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1700 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1702 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1703 pkt << id << (u8) stat;
1707 case HUD_STAT_SCALE:
1708 case HUD_STAT_ALIGN:
1709 case HUD_STAT_OFFSET:
1710 pkt << *(v2f *) value;
1714 pkt << *(std::string *) value;
1716 case HUD_STAT_WORLD_POS:
1717 pkt << *(v3f *) value;
1720 pkt << *(v2s32 *) value;
1722 case HUD_STAT_NUMBER:
1726 pkt << *(u32 *) value;
1733 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1735 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1737 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1739 pkt << flags << mask;
1744 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1746 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1747 pkt << param << value;
1751 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1752 const std::string &type, const std::vector<std::string> ¶ms,
1755 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1756 pkt << bgcolor << type << (u16) params.size();
1758 for (const std::string ¶m : params)
1766 void Server::SendCloudParams(session_t peer_id, float density,
1767 const video::SColor &color_bright,
1768 const video::SColor &color_ambient,
1773 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1774 pkt << density << color_bright << color_ambient
1775 << height << thickness << speed;
1780 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1783 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1786 pkt << do_override << (u16) (ratio * 65535);
1791 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1793 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1794 pkt << time << time_speed;
1796 if (peer_id == PEER_ID_INEXISTENT) {
1797 m_clients.sendToAll(&pkt);
1804 void Server::SendPlayerHP(session_t peer_id)
1806 PlayerSAO *playersao = getPlayerSAO(peer_id);
1807 // In some rare case if the player is disconnected
1808 // while Lua call l_punch, for example, this can be NULL
1812 SendHP(peer_id, playersao->getHP());
1813 m_script->player_event(playersao,"health_changed");
1815 // Send to other clients
1816 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1817 ActiveObjectMessage aom(playersao->getId(), true, str);
1818 playersao->m_messages_out.push(aom);
1821 void Server::SendPlayerBreath(PlayerSAO *sao)
1825 m_script->player_event(sao, "breath_changed");
1826 SendBreath(sao->getPeerID(), sao->getBreath());
1829 void Server::SendMovePlayer(session_t peer_id)
1831 RemotePlayer *player = m_env->getPlayer(peer_id);
1833 PlayerSAO *sao = player->getPlayerSAO();
1836 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1837 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1840 v3f pos = sao->getBasePosition();
1841 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1842 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1843 << " pitch=" << sao->getPitch()
1844 << " yaw=" << sao->getYaw()
1851 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1852 f32 animation_speed)
1854 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1857 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1858 << animation_frames[3] << animation_speed;
1863 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1865 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1866 pkt << first << third;
1870 void Server::SendPlayerPrivileges(session_t peer_id)
1872 RemotePlayer *player = m_env->getPlayer(peer_id);
1874 if(player->getPeerId() == PEER_ID_INEXISTENT)
1877 std::set<std::string> privs;
1878 m_script->getAuth(player->getName(), NULL, &privs);
1880 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1881 pkt << (u16) privs.size();
1883 for (const std::string &priv : privs) {
1890 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1892 RemotePlayer *player = m_env->getPlayer(peer_id);
1894 if(player->getPeerId() == PEER_ID_INEXISTENT)
1897 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1898 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1902 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1904 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1905 pkt.putRawString(datas.c_str(), datas.size());
1907 return pkt.getSize();
1910 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1913 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1914 datas.size(), peer_id);
1916 pkt.putRawString(datas.c_str(), datas.size());
1918 m_clients.send(pkt.getPeerId(),
1919 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1923 void Server::SendCSMFlavourLimits(session_t peer_id)
1925 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1926 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1927 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1931 s32 Server::playSound(const SimpleSoundSpec &spec,
1932 const ServerSoundParams ¶ms)
1934 // Find out initial position of sound
1935 bool pos_exists = false;
1936 v3f pos = params.getPos(m_env, &pos_exists);
1937 // If position is not found while it should be, cancel sound
1938 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1941 // Filter destination clients
1942 std::vector<session_t> dst_clients;
1943 if(!params.to_player.empty()) {
1944 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1946 infostream<<"Server::playSound: Player \""<<params.to_player
1947 <<"\" not found"<<std::endl;
1950 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1951 infostream<<"Server::playSound: Player \""<<params.to_player
1952 <<"\" not connected"<<std::endl;
1955 dst_clients.push_back(player->getPeerId());
1957 std::vector<session_t> clients = m_clients.getClientIDs();
1959 for (const session_t client_id : clients) {
1960 RemotePlayer *player = m_env->getPlayer(client_id);
1964 PlayerSAO *sao = player->getPlayerSAO();
1969 if(sao->getBasePosition().getDistanceFrom(pos) >
1970 params.max_hear_distance)
1973 dst_clients.push_back(client_id);
1977 if(dst_clients.empty())
1981 s32 id = m_next_sound_id++;
1982 // The sound will exist as a reference in m_playing_sounds
1983 m_playing_sounds[id] = ServerPlayingSound();
1984 ServerPlayingSound &psound = m_playing_sounds[id];
1985 psound.params = params;
1988 float gain = params.gain * spec.gain;
1989 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1990 pkt << id << spec.name << gain
1991 << (u8) params.type << pos << params.object
1992 << params.loop << params.fade << params.pitch;
1994 // Backwards compability
1995 bool play_sound = gain > 0;
1997 for (const u16 dst_client : dst_clients) {
1998 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
1999 psound.clients.insert(dst_client);
2000 m_clients.send(dst_client, 0, &pkt, true);
2005 void Server::stopSound(s32 handle)
2007 // Get sound reference
2008 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2009 m_playing_sounds.find(handle);
2010 if (i == m_playing_sounds.end())
2012 ServerPlayingSound &psound = i->second;
2014 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2017 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2018 si != psound.clients.end(); ++si) {
2020 m_clients.send(*si, 0, &pkt, true);
2022 // Remove sound reference
2023 m_playing_sounds.erase(i);
2026 void Server::fadeSound(s32 handle, float step, float gain)
2028 // Get sound reference
2029 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2030 m_playing_sounds.find(handle);
2031 if (i == m_playing_sounds.end())
2034 ServerPlayingSound &psound = i->second;
2035 psound.params.gain = gain;
2037 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2038 pkt << handle << step << gain;
2040 // Backwards compability
2041 bool play_sound = gain > 0;
2042 ServerPlayingSound compat_psound = psound;
2043 compat_psound.clients.clear();
2045 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2046 compat_pkt << handle;
2048 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2049 it != psound.clients.end();) {
2050 if (m_clients.getProtocolVersion(*it) >= 32) {
2052 m_clients.send(*it, 0, &pkt, true);
2055 compat_psound.clients.insert(*it);
2057 m_clients.send(*it, 0, &compat_pkt, true);
2058 psound.clients.erase(it++);
2062 // Remove sound reference
2063 if (!play_sound || psound.clients.empty())
2064 m_playing_sounds.erase(i);
2066 if (play_sound && !compat_psound.clients.empty()) {
2067 // Play new sound volume on older clients
2068 playSound(compat_psound.spec, compat_psound.params);
2072 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2073 std::vector<u16> *far_players, float far_d_nodes)
2075 float maxd = far_d_nodes*BS;
2076 v3f p_f = intToFloat(p, BS);
2078 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2081 std::vector<session_t> clients = m_clients.getClientIDs();
2082 for (session_t client_id : clients) {
2085 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2086 PlayerSAO *sao = player->getPlayerSAO();
2090 // If player is far away, only set modified blocks not sent
2091 v3f player_pos = sao->getBasePosition();
2092 if (player_pos.getDistanceFrom(p_f) > maxd) {
2093 far_players->push_back(client_id);
2100 m_clients.send(client_id, 0, &pkt, true);
2104 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2105 std::vector<u16> *far_players, float far_d_nodes,
2106 bool remove_metadata)
2108 float maxd = far_d_nodes*BS;
2109 v3f p_f = intToFloat(p, BS);
2111 std::vector<session_t> clients = m_clients.getClientIDs();
2112 for (const session_t client_id : clients) {
2115 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2116 PlayerSAO *sao = player->getPlayerSAO();
2120 // If player is far away, only set modified blocks not sent
2121 v3f player_pos = sao->getBasePosition();
2122 if(player_pos.getDistanceFrom(p_f) > maxd) {
2123 far_players->push_back(client_id);
2129 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2131 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2133 pkt << p << n.param0 << n.param1 << n.param2
2134 << (u8) (remove_metadata ? 0 : 1);
2139 if (pkt.getSize() > 0)
2140 m_clients.send(client_id, 0, &pkt, true);
2144 void Server::setBlockNotSent(v3s16 p)
2146 std::vector<session_t> clients = m_clients.getClientIDs();
2148 for (const session_t i : clients) {
2149 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2150 client->SetBlockNotSent(p);
2155 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2156 u16 net_proto_version)
2158 v3s16 p = block->getPos();
2161 Create a packet with the block in the right format
2164 std::ostringstream os(std::ios_base::binary);
2165 block->serialize(os, ver, false);
2166 block->serializeNetworkSpecific(os);
2167 std::string s = os.str();
2169 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2172 pkt.putRawString(s.c_str(), s.size());
2176 void Server::SendBlocks(float dtime)
2178 MutexAutoLock envlock(m_env_mutex);
2179 //TODO check if one big lock could be faster then multiple small ones
2181 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2183 std::vector<PrioritySortedBlockTransfer> queue;
2185 u32 total_sending = 0;
2188 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2190 std::vector<session_t> clients = m_clients.getClientIDs();
2193 for (const session_t client_id : clients) {
2194 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2199 total_sending += client->getSendingCount();
2200 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2206 // Lowest priority number comes first.
2207 // Lowest is most important.
2208 std::sort(queue.begin(), queue.end());
2212 // Maximal total count calculation
2213 // The per-client block sends is halved with the maximal online users
2214 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2215 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2217 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2218 if (total_sending >= max_blocks_to_send)
2221 MapBlock *block = nullptr;
2223 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2224 } catch (const InvalidPositionException &e) {
2228 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2233 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2234 client->net_proto_version);
2236 client->SentBlock(block_to_send.pos);
2242 void Server::fillMediaCache()
2244 infostream<<"Server: Calculating media file checksums"<<std::endl;
2246 // Collect all media file paths
2247 std::vector<std::string> paths;
2248 for (const ModSpec &mod : m_mods) {
2249 paths.push_back(mod.path + DIR_DELIM + "textures");
2250 paths.push_back(mod.path + DIR_DELIM + "sounds");
2251 paths.push_back(mod.path + DIR_DELIM + "media");
2252 paths.push_back(mod.path + DIR_DELIM + "models");
2253 paths.push_back(mod.path + DIR_DELIM + "locale");
2255 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM +
2256 "textures" + DIR_DELIM + "server");
2257 // Collect media file information from paths into cache
2258 for (const std::string &mediapath : paths) {
2259 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2260 for (const fs::DirListNode &dln : dirlist) {
2261 if (dln.dir) // Ignode dirs
2263 std::string filename = dln.name;
2264 // If name contains illegal characters, ignore the file
2265 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2266 infostream<<"Server: ignoring illegal file name: \""
2267 << filename << "\"" << std::endl;
2270 // If name is not in a supported format, ignore it
2271 const char *supported_ext[] = {
2272 ".png", ".jpg", ".bmp", ".tga",
2273 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2275 ".x", ".b3d", ".md2", ".obj",
2276 // Custom translation file format
2280 if (removeStringEnd(filename, supported_ext).empty()){
2281 infostream << "Server: ignoring unsupported file extension: \""
2282 << filename << "\"" << std::endl;
2285 // Ok, attempt to load the file and add to cache
2286 std::string filepath;
2287 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2290 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2292 errorstream << "Server::fillMediaCache(): Could not open \""
2293 << filename << "\" for reading" << std::endl;
2296 std::ostringstream tmp_os(std::ios_base::binary);
2300 fis.read(buf, 1024);
2301 std::streamsize len = fis.gcount();
2302 tmp_os.write(buf, len);
2311 errorstream<<"Server::fillMediaCache(): Failed to read \""
2312 << filename << "\"" << std::endl;
2315 if(tmp_os.str().length() == 0) {
2316 errorstream << "Server::fillMediaCache(): Empty file \""
2317 << filepath << "\"" << std::endl;
2322 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2324 unsigned char *digest = sha1.getDigest();
2325 std::string sha1_base64 = base64_encode(digest, 20);
2326 std::string sha1_hex = hex_encode((char*)digest, 20);
2330 m_media[filename] = MediaInfo(filepath, sha1_base64);
2331 verbosestream << "Server: " << sha1_hex << " is " << filename
2337 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2339 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2343 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2346 std::string lang_suffix;
2347 lang_suffix.append(".").append(lang_code).append(".tr");
2348 for (const auto &i : m_media) {
2349 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2356 for (const auto &i : m_media) {
2357 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2359 pkt << i.first << i.second.sha1_digest;
2362 pkt << g_settings->get("remote_media");
2366 struct SendableMedia
2372 SendableMedia(const std::string &name_="", const std::string &path_="",
2373 const std::string &data_=""):
2380 void Server::sendRequestedMedia(session_t peer_id,
2381 const std::vector<std::string> &tosend)
2383 verbosestream<<"Server::sendRequestedMedia(): "
2384 <<"Sending files to client"<<std::endl;
2388 // Put 5kB in one bunch (this is not accurate)
2389 u32 bytes_per_bunch = 5000;
2391 std::vector< std::vector<SendableMedia> > file_bunches;
2392 file_bunches.emplace_back();
2394 u32 file_size_bunch_total = 0;
2396 for (const std::string &name : tosend) {
2397 if (m_media.find(name) == m_media.end()) {
2398 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2399 <<"unknown file \""<<(name)<<"\""<<std::endl;
2403 //TODO get path + name
2404 std::string tpath = m_media[name].path;
2407 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2409 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2410 <<tpath<<"\" for reading"<<std::endl;
2413 std::ostringstream tmp_os(std::ios_base::binary);
2417 fis.read(buf, 1024);
2418 std::streamsize len = fis.gcount();
2419 tmp_os.write(buf, len);
2420 file_size_bunch_total += len;
2429 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2430 <<name<<"\""<<std::endl;
2433 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2434 <<tname<<"\""<<std::endl;*/
2436 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2438 // Start next bunch if got enough data
2439 if(file_size_bunch_total >= bytes_per_bunch) {
2440 file_bunches.emplace_back();
2441 file_size_bunch_total = 0;
2446 /* Create and send packets */
2448 u16 num_bunches = file_bunches.size();
2449 for (u16 i = 0; i < num_bunches; i++) {
2452 u16 total number of texture bunches
2453 u16 index of this bunch
2454 u32 number of files in this bunch
2463 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2464 pkt << num_bunches << i << (u32) file_bunches[i].size();
2466 for (const SendableMedia &j : file_bunches[i]) {
2468 pkt.putLongString(j.data);
2471 verbosestream << "Server::sendRequestedMedia(): bunch "
2472 << i << "/" << num_bunches
2473 << " files=" << file_bunches[i].size()
2474 << " size=" << pkt.getSize() << std::endl;
2479 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2481 if(m_detached_inventories.count(name) == 0) {
2482 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2485 Inventory *inv = m_detached_inventories[name];
2486 std::ostringstream os(std::ios_base::binary);
2488 os << serializeString(name);
2492 std::string s = os.str();
2494 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2495 pkt.putRawString(s.c_str(), s.size());
2497 const std::string &check = m_detached_inventories_player[name];
2498 if (peer_id == PEER_ID_INEXISTENT) {
2500 return m_clients.sendToAll(&pkt);
2501 RemotePlayer *p = m_env->getPlayer(check.c_str());
2503 m_clients.send(p->getPeerId(), 0, &pkt, true);
2505 if (check.empty() || getPlayerName(peer_id) == check)
2510 void Server::sendDetachedInventories(session_t peer_id)
2512 for (const auto &detached_inventory : m_detached_inventories) {
2513 const std::string &name = detached_inventory.first;
2514 //Inventory *inv = i->second;
2515 sendDetachedInventory(name, peer_id);
2523 void Server::DiePlayer(session_t peer_id)
2525 PlayerSAO *playersao = getPlayerSAO(peer_id);
2526 // In some rare cases this can be NULL -- if the player is disconnected
2527 // when a Lua function modifies l_punch, for example
2531 infostream << "Server::DiePlayer(): Player "
2532 << playersao->getPlayer()->getName()
2533 << " dies" << std::endl;
2535 playersao->setHP(0);
2537 // Trigger scripted stuff
2538 m_script->on_dieplayer(playersao);
2540 SendPlayerHP(peer_id);
2541 SendDeathscreen(peer_id, false, v3f(0,0,0));
2544 void Server::RespawnPlayer(session_t peer_id)
2546 PlayerSAO *playersao = getPlayerSAO(peer_id);
2549 infostream << "Server::RespawnPlayer(): Player "
2550 << playersao->getPlayer()->getName()
2551 << " respawns" << std::endl;
2553 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2554 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2556 bool repositioned = m_script->on_respawnplayer(playersao);
2557 if (!repositioned) {
2558 // setPos will send the new position to client
2559 playersao->setPos(findSpawnPos());
2562 SendPlayerHP(peer_id);
2566 void Server::DenySudoAccess(session_t peer_id)
2568 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2573 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2574 const std::string &str_reason, bool reconnect)
2576 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2578 m_clients.event(peer_id, CSE_SetDenied);
2579 DisconnectPeer(peer_id);
2583 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2584 const std::string &custom_reason)
2586 SendAccessDenied(peer_id, reason, custom_reason);
2587 m_clients.event(peer_id, CSE_SetDenied);
2588 DisconnectPeer(peer_id);
2591 // 13/03/15: remove this function when protocol version 25 will become
2592 // the minimum version for MT users, maybe in 1 year
2593 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2595 SendAccessDenied_Legacy(peer_id, reason);
2596 m_clients.event(peer_id, CSE_SetDenied);
2597 DisconnectPeer(peer_id);
2600 void Server::DisconnectPeer(session_t peer_id)
2602 m_modchannel_mgr->leaveAllChannels(peer_id);
2603 m_con->DisconnectPeer(peer_id);
2606 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2609 RemoteClient* client = getClient(peer_id, CS_Invalid);
2611 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2613 // Right now, the auth mechs don't change between login and sudo mode.
2614 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2615 client->allowed_sudo_mechs = sudo_auth_mechs;
2617 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2618 << g_settings->getFloat("dedicated_server_step")
2622 m_clients.event(peer_id, CSE_AuthAccept);
2624 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2626 // We only support SRP right now
2627 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2629 resp_pkt << sudo_auth_mechs;
2631 m_clients.event(peer_id, CSE_SudoSuccess);
2635 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2637 std::wstring message;
2640 Clear references to playing sounds
2642 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2643 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2644 ServerPlayingSound &psound = i->second;
2645 psound.clients.erase(peer_id);
2646 if (psound.clients.empty())
2647 m_playing_sounds.erase(i++);
2652 RemotePlayer *player = m_env->getPlayer(peer_id);
2654 /* Run scripts and remove from environment */
2656 PlayerSAO *playersao = player->getPlayerSAO();
2659 // inform connected clients
2660 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2661 // (u16) 1 + std::string represents a vector serialization representation
2662 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2663 m_clients.sendToAll(¬ice);
2665 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2667 playersao->disconnected();
2674 if (player && reason != CDR_DENY) {
2675 std::ostringstream os(std::ios_base::binary);
2676 std::vector<session_t> clients = m_clients.getClientIDs();
2678 for (const session_t client_id : clients) {
2680 RemotePlayer *player = m_env->getPlayer(client_id);
2684 // Get name of player
2685 os << player->getName() << " ";
2688 std::string name = player->getName();
2689 actionstream << name << " "
2690 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2691 << " List of players: " << os.str() << std::endl;
2693 m_admin_chat->outgoing_queue.push_back(
2694 new ChatEventNick(CET_NICK_REMOVE, name));
2698 MutexAutoLock env_lock(m_env_mutex);
2699 m_clients.DeleteClient(peer_id);
2703 // Send leave chat message to all remaining clients
2704 if (!message.empty()) {
2705 SendChatMessage(PEER_ID_INEXISTENT,
2706 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2710 void Server::UpdateCrafting(RemotePlayer *player)
2712 // Get a preview for crafting
2714 InventoryLocation loc;
2715 loc.setPlayer(player->getName());
2716 std::vector<ItemStack> output_replacements;
2717 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2718 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2719 (&player->inventory)->getList("craft"), loc);
2721 // Put the new preview in
2722 InventoryList *plist = player->inventory.getList("craftpreview");
2723 sanity_check(plist);
2724 sanity_check(plist->getSize() >= 1);
2725 plist->changeItem(0, preview);
2728 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2730 if (evt->type == CET_NICK_ADD) {
2731 // The terminal informed us of its nick choice
2732 m_admin_nick = ((ChatEventNick *)evt)->nick;
2733 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2734 errorstream << "You haven't set up an account." << std::endl
2735 << "Please log in using the client as '"
2736 << m_admin_nick << "' with a secure password." << std::endl
2737 << "Until then, you can't execute admin tasks via the console," << std::endl
2738 << "and everybody can claim the user account instead of you," << std::endl
2739 << "giving them full control over this server." << std::endl;
2742 assert(evt->type == CET_CHAT);
2743 handleAdminChat((ChatEventChat *)evt);
2747 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2748 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2750 // If something goes wrong, this player is to blame
2751 RollbackScopeActor rollback_scope(m_rollback,
2752 std::string("player:") + name);
2754 if (g_settings->getBool("strip_color_codes"))
2755 wmessage = unescape_enriched(wmessage);
2758 switch (player->canSendChatMessage()) {
2759 case RPLAYER_CHATRESULT_FLOODING: {
2760 std::wstringstream ws;
2761 ws << L"You cannot send more messages. You are limited to "
2762 << g_settings->getFloat("chat_message_limit_per_10sec")
2763 << L" messages per 10 seconds.";
2766 case RPLAYER_CHATRESULT_KICK:
2767 DenyAccess_Legacy(player->getPeerId(),
2768 L"You have been kicked due to message flooding.");
2770 case RPLAYER_CHATRESULT_OK:
2773 FATAL_ERROR("Unhandled chat filtering result found.");
2777 if (m_max_chatmessage_length > 0
2778 && wmessage.length() > m_max_chatmessage_length) {
2779 return L"Your message exceed the maximum chat message limit set on the server. "
2780 L"It was refused. Send a shorter message";
2783 // Run script hook, exit if script ate the chat message
2784 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2789 // Whether to send line to the player that sent the message, or to all players
2790 bool broadcast_line = true;
2792 if (check_shout_priv && !checkPriv(name, "shout")) {
2793 line += L"-!- You don't have permission to shout.";
2794 broadcast_line = false;
2803 Tell calling method to send the message to sender
2805 if (!broadcast_line)
2809 Send the message to others
2811 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2813 std::vector<session_t> clients = m_clients.getClientIDs();
2816 Send the message back to the inital sender
2817 if they are using protocol version >= 29
2820 session_t peer_id_to_avoid_sending =
2821 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2823 if (player && player->protocol_version >= 29)
2824 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2826 for (u16 cid : clients) {
2827 if (cid != peer_id_to_avoid_sending)
2828 SendChatMessage(cid, ChatMessage(line));
2833 void Server::handleAdminChat(const ChatEventChat *evt)
2835 std::string name = evt->nick;
2836 std::wstring wname = utf8_to_wide(name);
2837 std::wstring wmessage = evt->evt_msg;
2839 std::wstring answer = handleChat(name, wname, wmessage);
2841 // If asked to send answer to sender
2842 if (!answer.empty()) {
2843 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2847 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2849 RemoteClient *client = getClientNoEx(peer_id,state_min);
2851 throw ClientNotFoundException("Client not found");
2855 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2857 return m_clients.getClientNoEx(peer_id, state_min);
2860 std::string Server::getPlayerName(session_t peer_id)
2862 RemotePlayer *player = m_env->getPlayer(peer_id);
2864 return "[id="+itos(peer_id)+"]";
2865 return player->getName();
2868 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2870 RemotePlayer *player = m_env->getPlayer(peer_id);
2873 return player->getPlayerSAO();
2876 std::wstring Server::getStatusString()
2878 std::wostringstream os(std::ios_base::binary);
2881 os<<L"version="<<narrow_to_wide(g_version_string);
2883 os<<L", uptime="<<m_uptime.get();
2885 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2886 // Information about clients
2889 std::vector<session_t> clients = m_clients.getClientIDs();
2890 for (session_t client_id : clients) {
2892 RemotePlayer *player = m_env->getPlayer(client_id);
2893 // Get name of player
2894 std::wstring name = L"unknown";
2896 name = narrow_to_wide(player->getName());
2897 // Add name to information string
2906 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2907 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2909 if (!g_settings->get("motd").empty())
2910 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2914 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2916 std::set<std::string> privs;
2917 m_script->getAuth(name, NULL, &privs);
2921 bool Server::checkPriv(const std::string &name, const std::string &priv)
2923 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2924 return (privs.count(priv) != 0);
2927 void Server::reportPrivsModified(const std::string &name)
2930 std::vector<session_t> clients = m_clients.getClientIDs();
2931 for (const session_t client_id : clients) {
2932 RemotePlayer *player = m_env->getPlayer(client_id);
2933 reportPrivsModified(player->getName());
2936 RemotePlayer *player = m_env->getPlayer(name.c_str());
2939 SendPlayerPrivileges(player->getPeerId());
2940 PlayerSAO *sao = player->getPlayerSAO();
2943 sao->updatePrivileges(
2944 getPlayerEffectivePrivs(name),
2949 void Server::reportInventoryFormspecModified(const std::string &name)
2951 RemotePlayer *player = m_env->getPlayer(name.c_str());
2954 SendPlayerInventoryFormspec(player->getPeerId());
2957 void Server::setIpBanned(const std::string &ip, const std::string &name)
2959 m_banmanager->add(ip, name);
2962 void Server::unsetIpBanned(const std::string &ip_or_name)
2964 m_banmanager->remove(ip_or_name);
2967 std::string Server::getBanDescription(const std::string &ip_or_name)
2969 return m_banmanager->getBanDescription(ip_or_name);
2972 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2974 // m_env will be NULL if the server is initializing
2978 if (m_admin_nick == name && !m_admin_nick.empty()) {
2979 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2982 RemotePlayer *player = m_env->getPlayer(name);
2987 if (player->getPeerId() == PEER_ID_INEXISTENT)
2990 SendChatMessage(player->getPeerId(), ChatMessage(msg));
2993 bool Server::showFormspec(const char *playername, const std::string &formspec,
2994 const std::string &formname)
2996 // m_env will be NULL if the server is initializing
3000 RemotePlayer *player = m_env->getPlayer(playername);
3004 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3008 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3013 u32 id = player->addHud(form);
3015 SendHUDAdd(player->getPeerId(), id, form);
3020 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3024 HudElement* todel = player->removeHud(id);
3031 SendHUDRemove(player->getPeerId(), id);
3035 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3040 SendHUDChange(player->getPeerId(), id, stat, data);
3044 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3049 SendHUDSetFlags(player->getPeerId(), flags, mask);
3050 player->hud_flags &= ~mask;
3051 player->hud_flags |= flags;
3053 PlayerSAO* playersao = player->getPlayerSAO();
3058 m_script->player_event(playersao, "hud_changed");
3062 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3067 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3070 player->setHotbarItemcount(hotbar_itemcount);
3071 std::ostringstream os(std::ios::binary);
3072 writeS32(os, hotbar_itemcount);
3073 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3077 s32 Server::hudGetHotbarItemcount(RemotePlayer *player) const
3079 return player->getHotbarItemcount();
3082 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3087 player->setHotbarImage(name);
3088 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3091 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3095 return player->getHotbarImage();
3098 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3103 player->setHotbarSelectedImage(name);
3104 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3107 const std::string& Server::hudGetHotbarSelectedImage(RemotePlayer *player) const
3109 return player->getHotbarSelectedImage();
3112 Address Server::getPeerAddress(session_t peer_id)
3114 return m_con->GetPeerAddress(peer_id);
3117 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3118 v2s32 animation_frames[4], f32 frame_speed)
3123 player->setLocalAnimations(animation_frames, frame_speed);
3124 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3128 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3133 player->eye_offset_first = first;
3134 player->eye_offset_third = third;
3135 SendEyeOffset(player->getPeerId(), first, third);
3139 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3140 const std::string &type, const std::vector<std::string> ¶ms,
3146 player->setSky(bgcolor, type, params, clouds);
3147 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3151 bool Server::setClouds(RemotePlayer *player, float density,
3152 const video::SColor &color_bright,
3153 const video::SColor &color_ambient,
3161 SendCloudParams(player->getPeerId(), density,
3162 color_bright, color_ambient, height,
3167 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3173 player->overrideDayNightRatio(do_override, ratio);
3174 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3178 void Server::notifyPlayers(const std::wstring &msg)
3180 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3183 void Server::spawnParticle(const std::string &playername, v3f pos,
3184 v3f velocity, v3f acceleration,
3185 float expirationtime, float size, bool
3186 collisiondetection, bool collision_removal,
3187 bool vertical, const std::string &texture,
3188 const struct TileAnimationParams &animation, u8 glow)
3190 // m_env will be NULL if the server is initializing
3194 session_t peer_id = PEER_ID_INEXISTENT;
3196 if (!playername.empty()) {
3197 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3200 peer_id = player->getPeerId();
3201 proto_ver = player->protocol_version;
3204 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3205 expirationtime, size, collisiondetection,
3206 collision_removal, vertical, texture, animation, glow);
3209 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3210 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3211 float minexptime, float maxexptime, float minsize, float maxsize,
3212 bool collisiondetection, bool collision_removal,
3213 ServerActiveObject *attached, bool vertical, const std::string &texture,
3214 const std::string &playername, const struct TileAnimationParams &animation,
3217 // m_env will be NULL if the server is initializing
3221 session_t peer_id = PEER_ID_INEXISTENT;
3223 if (!playername.empty()) {
3224 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3227 peer_id = player->getPeerId();
3228 proto_ver = player->protocol_version;
3231 u16 attached_id = attached ? attached->getId() : 0;
3234 if (attached_id == 0)
3235 id = m_env->addParticleSpawner(spawntime);
3237 id = m_env->addParticleSpawner(spawntime, attached_id);
3239 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3240 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3241 minexptime, maxexptime, minsize, maxsize,
3242 collisiondetection, collision_removal, attached_id, vertical,
3243 texture, id, animation, glow);
3248 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3250 // m_env will be NULL if the server is initializing
3252 throw ServerError("Can't delete particle spawners during initialisation!");
3254 session_t peer_id = PEER_ID_INEXISTENT;
3255 if (!playername.empty()) {
3256 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3259 peer_id = player->getPeerId();
3262 m_env->deleteParticleSpawner(id);
3263 SendDeleteParticleSpawner(peer_id, id);
3266 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3268 if(m_detached_inventories.count(name) > 0){
3269 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3270 delete m_detached_inventories[name];
3272 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3274 Inventory *inv = new Inventory(m_itemdef);
3276 m_detached_inventories[name] = inv;
3277 m_detached_inventories_player[name] = player;
3278 //TODO find a better way to do this
3279 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3283 // actions: time-reversed list
3284 // Return value: success/failure
3285 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3286 std::list<std::string> *log)
3288 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3289 ServerMap *map = (ServerMap*)(&m_env->getMap());
3291 // Fail if no actions to handle
3292 if(actions.empty()){
3293 log->push_back("Nothing to do.");
3300 for (const RollbackAction &action : actions) {
3302 bool success = action.applyRevert(map, this, this);
3305 std::ostringstream os;
3306 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3307 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3309 log->push_back(os.str());
3311 std::ostringstream os;
3312 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3313 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3315 log->push_back(os.str());
3319 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3320 <<" failed"<<std::endl;
3322 // Call it done if less than half failed
3323 return num_failed <= num_tried/2;
3326 // IGameDef interface
3328 IItemDefManager *Server::getItemDefManager()
3333 INodeDefManager *Server::getNodeDefManager()
3338 ICraftDefManager *Server::getCraftDefManager()
3343 u16 Server::allocateUnknownNodeId(const std::string &name)
3345 return m_nodedef->allocateDummy(name);
3348 MtEventManager *Server::getEventManager()
3353 IWritableItemDefManager *Server::getWritableItemDefManager()
3358 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3363 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3368 const ModSpec *Server::getModSpec(const std::string &modname) const
3370 std::vector<ModSpec>::const_iterator it;
3371 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3372 const ModSpec &mod = *it;
3373 if (mod.name == modname)
3379 void Server::getModNames(std::vector<std::string> &modlist)
3381 std::vector<ModSpec>::iterator it;
3382 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3383 modlist.push_back(it->name);
3386 std::string Server::getBuiltinLuaPath()
3388 return porting::path_share + DIR_DELIM + "builtin";
3391 std::string Server::getModStoragePath() const
3393 return m_path_world + DIR_DELIM + "mod_storage";
3396 v3f Server::findSpawnPos()
3398 ServerMap &map = m_env->getServerMap();
3400 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3401 return nodeposf * BS;
3404 bool is_good = false;
3405 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3406 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3408 // Try to find a good place a few times
3409 for(s32 i = 0; i < 4000 && !is_good; i++) {
3410 s32 range = MYMIN(1 + i, range_max);
3411 // We're going to try to throw the player to this position
3412 v2s16 nodepos2d = v2s16(
3413 -range + (myrand() % (range * 2)),
3414 -range + (myrand() % (range * 2)));
3416 // Get spawn level at point
3417 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3418 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3419 // the mapgen to signify an unsuitable spawn position
3420 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3423 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3426 for (s32 i = 0; i < 10; i++) {
3427 v3s16 blockpos = getNodeBlockPos(nodepos);
3428 map.emergeBlock(blockpos, true);
3429 content_t c = map.getNodeNoEx(nodepos).getContent();
3430 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3432 if (air_count >= 2) {
3433 nodeposf = intToFloat(nodepos, BS);
3434 // Don't spawn the player outside map boundaries
3435 if (objectpos_over_limit(nodeposf))
3448 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3450 m_shutdown_timer = delay;
3451 m_shutdown_msg = msg;
3452 m_shutdown_ask_reconnect = reconnect;
3454 if (delay == 0.0f) {
3455 // No delay, shutdown immediately
3456 m_shutdown_requested = true;
3457 // only print to the infostream, a chat message saying
3458 // "Server Shutting Down" is sent when the server destructs.
3459 infostream << "*** Immediate Server shutdown requested." << std::endl;
3460 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3461 // Negative delay, cancel shutdown if requested
3462 m_shutdown_timer = 0.0f;
3463 m_shutdown_msg = "";
3464 m_shutdown_ask_reconnect = false;
3465 m_shutdown_requested = false;
3466 std::wstringstream ws;
3468 ws << L"*** Server shutdown canceled.";
3470 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3471 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3472 } else if (delay > 0.0f) {
3473 // Positive delay, tell the clients when the server will shut down
3474 std::wstringstream ws;
3476 ws << L"*** Server shutting down in "
3477 << duration_to_string(myround(m_shutdown_timer)).c_str()
3480 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3481 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3485 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3488 Try to get an existing player
3490 RemotePlayer *player = m_env->getPlayer(name);
3492 // If player is already connected, cancel
3493 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3494 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3499 If player with the wanted peer_id already exists, cancel.
3501 if (m_env->getPlayer(peer_id)) {
3502 infostream<<"emergePlayer(): Player with wrong name but same"
3503 " peer_id already exists"<<std::endl;
3508 player = new RemotePlayer(name, idef());
3511 bool newplayer = false;
3514 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3516 // Complete init with server parts
3517 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3518 player->protocol_version = proto_version;
3522 m_script->on_newplayer(playersao);
3528 bool Server::registerModStorage(ModMetadata *storage)
3530 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3531 errorstream << "Unable to register same mod storage twice. Storage name: "
3532 << storage->getModName() << std::endl;
3536 m_mod_storages[storage->getModName()] = storage;
3540 void Server::unregisterModStorage(const std::string &name)
3542 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3543 if (it != m_mod_storages.end()) {
3544 // Save unconditionaly on unregistration
3545 it->second->save(getModStoragePath());
3546 m_mod_storages.erase(name);
3550 void dedicated_server_loop(Server &server, bool &kill)
3552 verbosestream<<"dedicated_server_loop()"<<std::endl;
3554 IntervalLimiter m_profiler_interval;
3556 static thread_local const float steplen =
3557 g_settings->getFloat("dedicated_server_step");
3558 static thread_local const float profiler_print_interval =
3559 g_settings->getFloat("profiler_print_interval");
3562 // This is kind of a hack but can be done like this
3563 // because server.step() is very light
3565 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3566 sleep_ms((int)(steplen*1000.0));
3568 server.step(steplen);
3570 if (server.getShutdownRequested() || kill)
3576 if (profiler_print_interval != 0) {
3577 if(m_profiler_interval.step(steplen, profiler_print_interval))
3579 infostream<<"Profiler:"<<std::endl;
3580 g_profiler->print(infostream);
3581 g_profiler->clear();
3586 infostream << "Dedicated server quitting" << std::endl;
3588 if (g_settings->getBool("server_announce"))
3589 ServerList::sendAnnounce(ServerList::AA_DELETE,
3590 server.m_bind_addr.getPort());
3599 bool Server::joinModChannel(const std::string &channel)
3601 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3602 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3605 bool Server::leaveModChannel(const std::string &channel)
3607 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3610 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3612 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3615 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3619 ModChannel* Server::getModChannel(const std::string &channel)
3621 return m_modchannel_mgr->getModChannel(channel);
3624 void Server::broadcastModChannelMessage(const std::string &channel,
3625 const std::string &message, session_t from_peer)
3627 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3631 if (message.size() > STRING_MAX_LEN) {
3632 warningstream << "ModChannel message too long, dropping before sending "
3633 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3634 << channel << ")" << std::endl;
3639 if (from_peer != PEER_ID_SERVER) {
3640 sender = getPlayerName(from_peer);
3643 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3644 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3645 resp_pkt << channel << sender << message;
3646 for (session_t peer_id : peers) {
3648 if (peer_id == from_peer)
3651 Send(peer_id, &resp_pkt);
3654 if (from_peer != PEER_ID_SERVER) {
3655 m_script->on_modchannel_message(channel, sender, message);