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 std::vector<std::string> paths;
257 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
258 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
259 for (const std::string &path : paths)
260 m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
262 m_nodedef->setNodeRegistrationStatus(true);
264 // Perform pending node name resolutions
265 m_nodedef->runNodeResolveCallbacks();
267 // unmap node names for connected nodeboxes
268 m_nodedef->mapNodeboxConnections();
270 // init the recipe hashes to speed up crafting
271 m_craftdef->initHashes(this);
273 // Initialize Environment
274 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
276 m_clients.setEnv(m_env);
278 if (!servermap->settings_mgr.makeMapgenParams())
279 FATAL_ERROR("Couldn't create any mapgen type");
281 // Initialize mapgens
282 m_emerge->initMapgens(servermap->getMapgenParams());
284 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
285 if (m_enable_rollback_recording) {
286 // Create rollback manager
287 m_rollback = new RollbackManager(m_path_world, this);
290 // Give environment reference to scripting api
291 m_script->initializeEnvironment(m_env);
293 // Register us to receive map edit events
294 servermap->addEventReceiver(this);
296 // If file exists, load environment metadata
297 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
298 infostream << "Server: Loading environment metadata" << std::endl;
301 m_env->loadDefaultMeta();
304 m_liquid_transform_every = g_settings->getFloat("liquid_update");
305 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
306 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
307 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
312 infostream << "Server destructing" << std::endl;
314 // Send shutdown message
315 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
316 L"*** Server shutting down"));
319 MutexAutoLock envlock(m_env_mutex);
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);
338 // Do this before stopping the server in case mapgen callbacks need to access
339 // server-controlled resources (like ModStorages). Also do them before
340 // shutdown callbacks since they may modify state that is finalized in a
342 m_emerge->stopThreads();
345 MutexAutoLock envlock(m_env_mutex);
347 // Execute script shutdown hooks
348 infostream << "Executing shutdown hooks" << std::endl;
349 m_script->on_shutdown();
351 infostream << "Server: Saving environment metadata" << std::endl;
359 // Delete things in the reverse order of creation
369 // Deinitialize scripting
370 infostream << "Server: Deinitializing scripting" << std::endl;
373 // Delete detached inventories
374 for (auto &detached_inventory : m_detached_inventories) {
375 delete detached_inventory.second;
381 infostream << "Starting server on " << m_bind_addr.serializeString()
382 << "..." << std::endl;
384 // Stop thread if already running
387 // Initialize connection
388 m_con->SetTimeoutMs(30);
389 m_con->Serve(m_bind_addr);
394 // ASCII art for the win!
396 << " .__ __ __ " << std::endl
397 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
398 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
399 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
400 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
401 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
402 actionstream << "World at [" << m_path_world << "]" << std::endl;
403 actionstream << "Server for gameid=\"" << m_gamespec.id
404 << "\" listening on " << m_bind_addr.serializeString() << ":"
405 << m_bind_addr.getPort() << "." << std::endl;
410 infostream<<"Server: Stopping and waiting threads"<<std::endl;
412 // Stop threads (set run=false first so both start stopping)
414 //m_emergethread.setRun(false);
416 //m_emergethread.stop();
418 infostream<<"Server: Threads stopped"<<std::endl;
421 void Server::step(float dtime)
427 MutexAutoLock lock(m_step_dtime_mutex);
428 m_step_dtime += dtime;
430 // Throw if fatal error occurred in thread
431 std::string async_err = m_async_fatal_error.get();
432 if (!async_err.empty()) {
433 if (!m_simple_singleplayer_mode) {
434 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
435 g_settings->get("kick_msg_crash"),
436 g_settings->getBool("ask_reconnect_on_crash"));
438 throw ServerError("AsyncErr: " + async_err);
442 void Server::AsyncRunStep(bool initial_step)
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()
988 m_con->Receive(&pkt);
989 peer_id = pkt.getPeerId();
991 } catch (const con::InvalidIncomingDataException &e) {
992 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
993 << e.what() << std::endl;
994 } catch (const SerializationError &e) {
995 infostream << "Server::Receive(): SerializationError: what()="
996 << e.what() << std::endl;
997 } catch (const ClientStateError &e) {
998 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
999 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
1000 L"Try reconnecting or updating your client");
1001 } catch (const con::PeerNotFoundException &e) {
1006 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1008 std::string playername;
1009 PlayerSAO *playersao = NULL;
1012 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1014 playername = client->getName();
1015 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1017 } catch (std::exception &e) {
1023 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1025 // If failed, cancel
1026 if (!playersao || !player) {
1027 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1028 actionstream << "Server: Failed to emerge player \"" << playername
1029 << "\" (player allocated to an another client)" << std::endl;
1030 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1031 L"name. If your client closed unexpectedly, try again in "
1034 errorstream << "Server: " << playername << ": Failed to emerge player"
1036 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1042 Send complete position information
1044 SendMovePlayer(peer_id);
1047 SendPlayerPrivileges(peer_id);
1049 // Send inventory formspec
1050 SendPlayerInventoryFormspec(peer_id);
1053 SendInventory(playersao);
1055 // Send HP or death screen
1056 if (playersao->isDead())
1057 SendDeathscreen(peer_id, false, v3f(0,0,0));
1059 SendPlayerHPOrDie(playersao);
1062 SendPlayerBreath(playersao);
1064 // Note things in chat if not in simple singleplayer mode
1065 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1066 // Send information about server to player in chat
1067 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1069 Address addr = getPeerAddress(player->getPeerId());
1070 std::string ip_str = addr.serializeString();
1071 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1076 const std::vector<std::string> &names = m_clients.getPlayerNames();
1078 actionstream << player->getName() << " joins game. List of players: ";
1080 for (const std::string &name : names) {
1081 actionstream << name << " ";
1084 actionstream << player->getName() <<std::endl;
1089 inline void Server::handleCommand(NetworkPacket* pkt)
1091 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1092 (this->*opHandle.handler)(pkt);
1095 void Server::ProcessData(NetworkPacket *pkt)
1097 // Environment is locked first.
1098 MutexAutoLock envlock(m_env_mutex);
1100 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1101 u32 peer_id = pkt->getPeerId();
1104 Address address = getPeerAddress(peer_id);
1105 std::string addr_s = address.serializeString();
1107 if(m_banmanager->isIpBanned(addr_s)) {
1108 std::string ban_name = m_banmanager->getBanName(addr_s);
1109 infostream << "Server: A banned client tried to connect from "
1110 << addr_s << "; banned name was "
1111 << ban_name << std::endl;
1112 // This actually doesn't seem to transfer to the client
1113 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1114 + utf8_to_wide(ban_name));
1118 catch(con::PeerNotFoundException &e) {
1120 * no peer for this packet found
1121 * most common reason is peer timeout, e.g. peer didn't
1122 * respond for some time, your server was overloaded or
1125 infostream << "Server::ProcessData(): Canceling: peer "
1126 << peer_id << " not found" << std::endl;
1131 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1133 // Command must be handled into ToServerCommandHandler
1134 if (command >= TOSERVER_NUM_MSG_TYPES) {
1135 infostream << "Server: Ignoring unknown command "
1136 << command << std::endl;
1140 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1145 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1147 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1148 errorstream << "Server::ProcessData(): Cancelling: Peer"
1149 " serialization format invalid or not initialized."
1150 " Skipping incoming command=" << command << std::endl;
1154 /* Handle commands related to client startup */
1155 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1160 if (m_clients.getClientState(peer_id) < CS_Active) {
1161 if (command == TOSERVER_PLAYERPOS) return;
1163 errorstream << "Got packet command: " << command << " for peer id "
1164 << peer_id << " but client isn't active yet. Dropping packet "
1170 } catch (SendFailedException &e) {
1171 errorstream << "Server::ProcessData(): SendFailedException: "
1172 << "what=" << e.what()
1174 } catch (PacketError &e) {
1175 actionstream << "Server::ProcessData(): PacketError: "
1176 << "what=" << e.what()
1181 void Server::setTimeOfDay(u32 time)
1183 m_env->setTimeOfDay(time);
1184 m_time_of_day_send_timer = 0;
1187 void Server::onMapEditEvent(MapEditEvent *event)
1189 if(m_ignore_map_edit_events)
1191 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1193 MapEditEvent *e = event->clone();
1194 m_unsent_map_edit_queue.push(e);
1197 Inventory* Server::getInventory(const InventoryLocation &loc)
1200 case InventoryLocation::UNDEFINED:
1201 case InventoryLocation::CURRENT_PLAYER:
1203 case InventoryLocation::PLAYER:
1205 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1208 PlayerSAO *playersao = player->getPlayerSAO();
1211 return playersao->getInventory();
1214 case InventoryLocation::NODEMETA:
1216 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1219 return meta->getInventory();
1222 case InventoryLocation::DETACHED:
1224 if(m_detached_inventories.count(loc.name) == 0)
1226 return m_detached_inventories[loc.name];
1230 sanity_check(false); // abort
1235 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1238 case InventoryLocation::UNDEFINED:
1240 case InventoryLocation::PLAYER:
1245 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1250 PlayerSAO *playersao = player->getPlayerSAO();
1254 SendInventory(playersao);
1257 case InventoryLocation::NODEMETA:
1259 v3s16 blockpos = getNodeBlockPos(loc.p);
1261 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1263 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1265 setBlockNotSent(blockpos);
1268 case InventoryLocation::DETACHED:
1270 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1274 sanity_check(false); // abort
1279 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1281 std::vector<session_t> clients = m_clients.getClientIDs();
1283 // Set the modified blocks unsent for all the clients
1284 for (const session_t client_id : clients) {
1285 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1286 client->SetBlocksNotSent(block);
1291 void Server::peerAdded(con::Peer *peer)
1293 verbosestream<<"Server::peerAdded(): peer->id="
1294 <<peer->id<<std::endl;
1296 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1299 void Server::deletingPeer(con::Peer *peer, bool timeout)
1301 verbosestream<<"Server::deletingPeer(): peer->id="
1302 <<peer->id<<", timeout="<<timeout<<std::endl;
1304 m_clients.event(peer->id, CSE_Disconnect);
1305 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1308 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1310 *retval = m_con->getPeerStat(peer_id,type);
1311 return *retval != -1;
1314 bool Server::getClientInfo(
1323 std::string* vers_string
1326 *state = m_clients.getClientState(peer_id);
1328 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1335 *uptime = client->uptime();
1336 *ser_vers = client->serialization_version;
1337 *prot_vers = client->net_proto_version;
1339 *major = client->getMajor();
1340 *minor = client->getMinor();
1341 *patch = client->getPatch();
1342 *vers_string = client->getPatch();
1349 void Server::handlePeerChanges()
1351 while(!m_peer_change_queue.empty())
1353 con::PeerChange c = m_peer_change_queue.front();
1354 m_peer_change_queue.pop();
1356 verbosestream<<"Server: Handling peer change: "
1357 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1362 case con::PEER_ADDED:
1363 m_clients.CreateClient(c.peer_id);
1366 case con::PEER_REMOVED:
1367 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1371 FATAL_ERROR("Invalid peer change event received!");
1377 void Server::printToConsoleOnly(const std::string &text)
1380 m_admin_chat->outgoing_queue.push_back(
1381 new ChatEventChat("", utf8_to_wide(text)));
1383 std::cout << text << std::endl;
1387 void Server::Send(NetworkPacket *pkt)
1389 Send(pkt->getPeerId(), pkt);
1392 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1394 m_clients.send(peer_id,
1395 clientCommandFactoryTable[pkt->getCommand()].channel,
1397 clientCommandFactoryTable[pkt->getCommand()].reliable);
1400 void Server::SendMovement(session_t peer_id)
1402 std::ostringstream os(std::ios_base::binary);
1404 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1406 pkt << g_settings->getFloat("movement_acceleration_default");
1407 pkt << g_settings->getFloat("movement_acceleration_air");
1408 pkt << g_settings->getFloat("movement_acceleration_fast");
1409 pkt << g_settings->getFloat("movement_speed_walk");
1410 pkt << g_settings->getFloat("movement_speed_crouch");
1411 pkt << g_settings->getFloat("movement_speed_fast");
1412 pkt << g_settings->getFloat("movement_speed_climb");
1413 pkt << g_settings->getFloat("movement_speed_jump");
1414 pkt << g_settings->getFloat("movement_liquid_fluidity");
1415 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1416 pkt << g_settings->getFloat("movement_liquid_sink");
1417 pkt << g_settings->getFloat("movement_gravity");
1422 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1424 if (!g_settings->getBool("enable_damage"))
1427 session_t peer_id = playersao->getPeerID();
1428 bool is_alive = playersao->getHP() > 0;
1431 SendPlayerHP(peer_id);
1436 void Server::SendHP(session_t peer_id, u16 hp)
1438 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1443 void Server::SendBreath(session_t peer_id, u16 breath)
1445 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1446 pkt << (u16) breath;
1450 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1451 const std::string &custom_reason, bool reconnect)
1453 assert(reason < SERVER_ACCESSDENIED_MAX);
1455 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1457 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1458 pkt << custom_reason;
1459 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1460 reason == SERVER_ACCESSDENIED_CRASH)
1461 pkt << custom_reason << (u8)reconnect;
1465 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1467 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1472 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1473 v3f camera_point_target)
1475 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1476 pkt << set_camera_point_target << camera_point_target;
1480 void Server::SendItemDef(session_t peer_id,
1481 IItemDefManager *itemdef, u16 protocol_version)
1483 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1487 u32 length of the next item
1488 zlib-compressed serialized ItemDefManager
1490 std::ostringstream tmp_os(std::ios::binary);
1491 itemdef->serialize(tmp_os, protocol_version);
1492 std::ostringstream tmp_os2(std::ios::binary);
1493 compressZlib(tmp_os.str(), tmp_os2);
1494 pkt.putLongString(tmp_os2.str());
1497 verbosestream << "Server: Sending item definitions to id(" << peer_id
1498 << "): size=" << pkt.getSize() << std::endl;
1503 void Server::SendNodeDef(session_t peer_id,
1504 const NodeDefManager *nodedef, u16 protocol_version)
1506 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1510 u32 length of the next item
1511 zlib-compressed serialized NodeDefManager
1513 std::ostringstream tmp_os(std::ios::binary);
1514 nodedef->serialize(tmp_os, protocol_version);
1515 std::ostringstream tmp_os2(std::ios::binary);
1516 compressZlib(tmp_os.str(), tmp_os2);
1518 pkt.putLongString(tmp_os2.str());
1521 verbosestream << "Server: Sending node definitions to id(" << peer_id
1522 << "): size=" << pkt.getSize() << std::endl;
1528 Non-static send methods
1531 void Server::SendInventory(PlayerSAO* playerSAO)
1533 UpdateCrafting(playerSAO->getPlayer());
1539 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1541 std::ostringstream os;
1542 playerSAO->getInventory()->serialize(os);
1544 std::string s = os.str();
1546 pkt.putRawString(s.c_str(), s.size());
1550 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1552 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1554 u8 type = message.type;
1555 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1557 if (peer_id != PEER_ID_INEXISTENT) {
1558 RemotePlayer *player = m_env->getPlayer(peer_id);
1564 m_clients.sendToAll(&pkt);
1568 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1569 const std::string &formname)
1571 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1572 if (formspec.empty()){
1573 //the client should close the formspec
1574 m_formspec_state_data.erase(peer_id);
1575 pkt.putLongString("");
1577 m_formspec_state_data[peer_id] = formname;
1578 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1585 // Spawns a particle on peer with peer_id
1586 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1587 v3f pos, v3f velocity, v3f acceleration,
1588 float expirationtime, float size, bool collisiondetection,
1589 bool collision_removal,
1590 bool vertical, const std::string &texture,
1591 const struct TileAnimationParams &animation, u8 glow)
1593 static thread_local const float radius =
1594 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1596 if (peer_id == PEER_ID_INEXISTENT) {
1597 std::vector<session_t> clients = m_clients.getClientIDs();
1599 for (const session_t client_id : clients) {
1600 RemotePlayer *player = m_env->getPlayer(client_id);
1604 PlayerSAO *sao = player->getPlayerSAO();
1608 // Do not send to distant clients
1609 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1612 SendSpawnParticle(client_id, player->protocol_version,
1613 pos, velocity, acceleration,
1614 expirationtime, size, collisiondetection,
1615 collision_removal, vertical, texture, animation, glow);
1620 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1622 pkt << pos << velocity << acceleration << expirationtime
1623 << size << collisiondetection;
1624 pkt.putLongString(texture);
1626 pkt << collision_removal;
1627 // This is horrible but required (why are there two ways to serialize pkts?)
1628 std::ostringstream os(std::ios_base::binary);
1629 animation.serialize(os, protocol_version);
1630 pkt.putRawString(os.str());
1636 // Adds a ParticleSpawner on peer with peer_id
1637 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1638 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1639 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1640 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1641 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1642 const struct TileAnimationParams &animation, u8 glow)
1644 if (peer_id == PEER_ID_INEXISTENT) {
1645 // This sucks and should be replaced:
1646 std::vector<session_t> clients = m_clients.getClientIDs();
1647 for (const session_t client_id : clients) {
1648 RemotePlayer *player = m_env->getPlayer(client_id);
1651 SendAddParticleSpawner(client_id, player->protocol_version,
1652 amount, spawntime, minpos, maxpos,
1653 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1654 minsize, maxsize, collisiondetection, collision_removal,
1655 attached_id, vertical, texture, id, animation, glow);
1660 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1662 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1663 << minacc << maxacc << minexptime << maxexptime << minsize
1664 << maxsize << collisiondetection;
1666 pkt.putLongString(texture);
1668 pkt << id << vertical;
1669 pkt << collision_removal;
1671 // This is horrible but required
1672 std::ostringstream os(std::ios_base::binary);
1673 animation.serialize(os, protocol_version);
1674 pkt.putRawString(os.str());
1680 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1682 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1684 // Ugly error in this packet
1687 if (peer_id != PEER_ID_INEXISTENT)
1690 m_clients.sendToAll(&pkt);
1694 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1696 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1698 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1699 << form->text << form->number << form->item << form->dir
1700 << form->align << form->offset << form->world_pos << form->size;
1705 void Server::SendHUDRemove(session_t peer_id, u32 id)
1707 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1712 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1714 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1715 pkt << id << (u8) stat;
1719 case HUD_STAT_SCALE:
1720 case HUD_STAT_ALIGN:
1721 case HUD_STAT_OFFSET:
1722 pkt << *(v2f *) value;
1726 pkt << *(std::string *) value;
1728 case HUD_STAT_WORLD_POS:
1729 pkt << *(v3f *) value;
1732 pkt << *(v2s32 *) value;
1734 case HUD_STAT_NUMBER:
1738 pkt << *(u32 *) value;
1745 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1747 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1749 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1751 pkt << flags << mask;
1756 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1758 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1759 pkt << param << value;
1763 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1764 const std::string &type, const std::vector<std::string> ¶ms,
1767 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1768 pkt << bgcolor << type << (u16) params.size();
1770 for (const std::string ¶m : params)
1778 void Server::SendCloudParams(session_t peer_id, float density,
1779 const video::SColor &color_bright,
1780 const video::SColor &color_ambient,
1785 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1786 pkt << density << color_bright << color_ambient
1787 << height << thickness << speed;
1792 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1795 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1798 pkt << do_override << (u16) (ratio * 65535);
1803 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1805 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1806 pkt << time << time_speed;
1808 if (peer_id == PEER_ID_INEXISTENT) {
1809 m_clients.sendToAll(&pkt);
1816 void Server::SendPlayerHP(session_t peer_id)
1818 PlayerSAO *playersao = getPlayerSAO(peer_id);
1819 // In some rare case if the player is disconnected
1820 // while Lua call l_punch, for example, this can be NULL
1824 SendHP(peer_id, playersao->getHP());
1825 m_script->player_event(playersao,"health_changed");
1827 // Send to other clients
1828 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1829 ActiveObjectMessage aom(playersao->getId(), true, str);
1830 playersao->m_messages_out.push(aom);
1833 void Server::SendPlayerBreath(PlayerSAO *sao)
1837 m_script->player_event(sao, "breath_changed");
1838 SendBreath(sao->getPeerID(), sao->getBreath());
1841 void Server::SendMovePlayer(session_t peer_id)
1843 RemotePlayer *player = m_env->getPlayer(peer_id);
1845 PlayerSAO *sao = player->getPlayerSAO();
1848 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1849 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1852 v3f pos = sao->getBasePosition();
1853 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1854 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1855 << " pitch=" << sao->getPitch()
1856 << " yaw=" << sao->getYaw()
1863 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1864 f32 animation_speed)
1866 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1869 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1870 << animation_frames[3] << animation_speed;
1875 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1877 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1878 pkt << first << third;
1882 void Server::SendPlayerPrivileges(session_t peer_id)
1884 RemotePlayer *player = m_env->getPlayer(peer_id);
1886 if(player->getPeerId() == PEER_ID_INEXISTENT)
1889 std::set<std::string> privs;
1890 m_script->getAuth(player->getName(), NULL, &privs);
1892 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1893 pkt << (u16) privs.size();
1895 for (const std::string &priv : privs) {
1902 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1904 RemotePlayer *player = m_env->getPlayer(peer_id);
1906 if(player->getPeerId() == PEER_ID_INEXISTENT)
1909 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1910 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1914 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1916 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1917 pkt.putRawString(datas.c_str(), datas.size());
1919 return pkt.getSize();
1922 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1925 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1926 datas.size(), peer_id);
1928 pkt.putRawString(datas.c_str(), datas.size());
1930 m_clients.send(pkt.getPeerId(),
1931 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1935 void Server::SendCSMFlavourLimits(session_t peer_id)
1937 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1938 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1939 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1943 s32 Server::playSound(const SimpleSoundSpec &spec,
1944 const ServerSoundParams ¶ms)
1946 // Find out initial position of sound
1947 bool pos_exists = false;
1948 v3f pos = params.getPos(m_env, &pos_exists);
1949 // If position is not found while it should be, cancel sound
1950 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1953 // Filter destination clients
1954 std::vector<session_t> dst_clients;
1955 if(!params.to_player.empty()) {
1956 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1958 infostream<<"Server::playSound: Player \""<<params.to_player
1959 <<"\" not found"<<std::endl;
1962 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1963 infostream<<"Server::playSound: Player \""<<params.to_player
1964 <<"\" not connected"<<std::endl;
1967 dst_clients.push_back(player->getPeerId());
1969 std::vector<session_t> clients = m_clients.getClientIDs();
1971 for (const session_t client_id : clients) {
1972 RemotePlayer *player = m_env->getPlayer(client_id);
1976 PlayerSAO *sao = player->getPlayerSAO();
1981 if(sao->getBasePosition().getDistanceFrom(pos) >
1982 params.max_hear_distance)
1985 dst_clients.push_back(client_id);
1989 if(dst_clients.empty())
1993 s32 id = m_next_sound_id++;
1994 // The sound will exist as a reference in m_playing_sounds
1995 m_playing_sounds[id] = ServerPlayingSound();
1996 ServerPlayingSound &psound = m_playing_sounds[id];
1997 psound.params = params;
2000 float gain = params.gain * spec.gain;
2001 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2002 pkt << id << spec.name << gain
2003 << (u8) params.type << pos << params.object
2004 << params.loop << params.fade << params.pitch;
2006 // Backwards compability
2007 bool play_sound = gain > 0;
2009 for (const u16 dst_client : dst_clients) {
2010 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2011 psound.clients.insert(dst_client);
2012 m_clients.send(dst_client, 0, &pkt, true);
2017 void Server::stopSound(s32 handle)
2019 // Get sound reference
2020 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2021 m_playing_sounds.find(handle);
2022 if (i == m_playing_sounds.end())
2024 ServerPlayingSound &psound = i->second;
2026 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2029 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2030 si != psound.clients.end(); ++si) {
2032 m_clients.send(*si, 0, &pkt, true);
2034 // Remove sound reference
2035 m_playing_sounds.erase(i);
2038 void Server::fadeSound(s32 handle, float step, float gain)
2040 // Get sound reference
2041 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2042 m_playing_sounds.find(handle);
2043 if (i == m_playing_sounds.end())
2046 ServerPlayingSound &psound = i->second;
2047 psound.params.gain = gain;
2049 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2050 pkt << handle << step << gain;
2052 // Backwards compability
2053 bool play_sound = gain > 0;
2054 ServerPlayingSound compat_psound = psound;
2055 compat_psound.clients.clear();
2057 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2058 compat_pkt << handle;
2060 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2061 it != psound.clients.end();) {
2062 if (m_clients.getProtocolVersion(*it) >= 32) {
2064 m_clients.send(*it, 0, &pkt, true);
2067 compat_psound.clients.insert(*it);
2069 m_clients.send(*it, 0, &compat_pkt, true);
2070 psound.clients.erase(it++);
2074 // Remove sound reference
2075 if (!play_sound || psound.clients.empty())
2076 m_playing_sounds.erase(i);
2078 if (play_sound && !compat_psound.clients.empty()) {
2079 // Play new sound volume on older clients
2080 playSound(compat_psound.spec, compat_psound.params);
2084 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2085 std::vector<u16> *far_players, float far_d_nodes)
2087 float maxd = far_d_nodes*BS;
2088 v3f p_f = intToFloat(p, BS);
2090 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2093 std::vector<session_t> clients = m_clients.getClientIDs();
2094 for (session_t client_id : clients) {
2097 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2098 PlayerSAO *sao = player->getPlayerSAO();
2102 // If player is far away, only set modified blocks not sent
2103 v3f player_pos = sao->getBasePosition();
2104 if (player_pos.getDistanceFrom(p_f) > maxd) {
2105 far_players->push_back(client_id);
2112 m_clients.send(client_id, 0, &pkt, true);
2116 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2117 std::vector<u16> *far_players, float far_d_nodes,
2118 bool remove_metadata)
2120 float maxd = far_d_nodes*BS;
2121 v3f p_f = intToFloat(p, BS);
2123 std::vector<session_t> clients = m_clients.getClientIDs();
2124 for (const session_t client_id : clients) {
2127 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2128 PlayerSAO *sao = player->getPlayerSAO();
2132 // If player is far away, only set modified blocks not sent
2133 v3f player_pos = sao->getBasePosition();
2134 if(player_pos.getDistanceFrom(p_f) > maxd) {
2135 far_players->push_back(client_id);
2141 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2143 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2145 pkt << p << n.param0 << n.param1 << n.param2
2146 << (u8) (remove_metadata ? 0 : 1);
2151 if (pkt.getSize() > 0)
2152 m_clients.send(client_id, 0, &pkt, true);
2156 void Server::setBlockNotSent(v3s16 p)
2158 std::vector<session_t> clients = m_clients.getClientIDs();
2160 for (const session_t i : clients) {
2161 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2162 client->SetBlockNotSent(p);
2167 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2168 u16 net_proto_version)
2170 v3s16 p = block->getPos();
2173 Create a packet with the block in the right format
2176 std::ostringstream os(std::ios_base::binary);
2177 block->serialize(os, ver, false);
2178 block->serializeNetworkSpecific(os);
2179 std::string s = os.str();
2181 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2184 pkt.putRawString(s.c_str(), s.size());
2188 void Server::SendBlocks(float dtime)
2190 MutexAutoLock envlock(m_env_mutex);
2191 //TODO check if one big lock could be faster then multiple small ones
2193 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2195 std::vector<PrioritySortedBlockTransfer> queue;
2197 u32 total_sending = 0;
2200 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2202 std::vector<session_t> clients = m_clients.getClientIDs();
2205 for (const session_t client_id : clients) {
2206 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2211 total_sending += client->getSendingCount();
2212 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2218 // Lowest priority number comes first.
2219 // Lowest is most important.
2220 std::sort(queue.begin(), queue.end());
2224 // Maximal total count calculation
2225 // The per-client block sends is halved with the maximal online users
2226 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2227 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2229 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2230 if (total_sending >= max_blocks_to_send)
2233 MapBlock *block = nullptr;
2235 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2236 } catch (const InvalidPositionException &e) {
2240 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2245 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2246 client->net_proto_version);
2248 client->SentBlock(block_to_send.pos);
2254 void Server::fillMediaCache()
2256 infostream<<"Server: Calculating media file checksums"<<std::endl;
2258 // Collect all media file paths
2259 std::vector<std::string> paths;
2260 for (const ModSpec &mod : m_mods) {
2261 paths.push_back(mod.path + DIR_DELIM + "textures");
2262 paths.push_back(mod.path + DIR_DELIM + "sounds");
2263 paths.push_back(mod.path + DIR_DELIM + "media");
2264 paths.push_back(mod.path + DIR_DELIM + "models");
2265 paths.push_back(mod.path + DIR_DELIM + "locale");
2267 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2268 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2270 // Collect media file information from paths into cache
2271 for (const std::string &mediapath : paths) {
2272 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2273 for (const fs::DirListNode &dln : dirlist) {
2274 if (dln.dir) // Ignode dirs
2276 std::string filename = dln.name;
2277 // If name contains illegal characters, ignore the file
2278 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2279 infostream<<"Server: ignoring illegal file name: \""
2280 << filename << "\"" << std::endl;
2283 // If name is not in a supported format, ignore it
2284 const char *supported_ext[] = {
2285 ".png", ".jpg", ".bmp", ".tga",
2286 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2288 ".x", ".b3d", ".md2", ".obj",
2289 // Custom translation file format
2293 if (removeStringEnd(filename, supported_ext).empty()){
2294 infostream << "Server: ignoring unsupported file extension: \""
2295 << filename << "\"" << std::endl;
2298 // Ok, attempt to load the file and add to cache
2299 std::string filepath;
2300 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2303 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2305 errorstream << "Server::fillMediaCache(): Could not open \""
2306 << filename << "\" for reading" << std::endl;
2309 std::ostringstream tmp_os(std::ios_base::binary);
2313 fis.read(buf, 1024);
2314 std::streamsize len = fis.gcount();
2315 tmp_os.write(buf, len);
2324 errorstream<<"Server::fillMediaCache(): Failed to read \""
2325 << filename << "\"" << std::endl;
2328 if(tmp_os.str().length() == 0) {
2329 errorstream << "Server::fillMediaCache(): Empty file \""
2330 << filepath << "\"" << std::endl;
2335 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2337 unsigned char *digest = sha1.getDigest();
2338 std::string sha1_base64 = base64_encode(digest, 20);
2339 std::string sha1_hex = hex_encode((char*)digest, 20);
2343 m_media[filename] = MediaInfo(filepath, sha1_base64);
2344 verbosestream << "Server: " << sha1_hex << " is " << filename
2350 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2352 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2356 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2359 std::string lang_suffix;
2360 lang_suffix.append(".").append(lang_code).append(".tr");
2361 for (const auto &i : m_media) {
2362 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2369 for (const auto &i : m_media) {
2370 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2372 pkt << i.first << i.second.sha1_digest;
2375 pkt << g_settings->get("remote_media");
2379 struct SendableMedia
2385 SendableMedia(const std::string &name_="", const std::string &path_="",
2386 const std::string &data_=""):
2393 void Server::sendRequestedMedia(session_t peer_id,
2394 const std::vector<std::string> &tosend)
2396 verbosestream<<"Server::sendRequestedMedia(): "
2397 <<"Sending files to client"<<std::endl;
2401 // Put 5kB in one bunch (this is not accurate)
2402 u32 bytes_per_bunch = 5000;
2404 std::vector< std::vector<SendableMedia> > file_bunches;
2405 file_bunches.emplace_back();
2407 u32 file_size_bunch_total = 0;
2409 for (const std::string &name : tosend) {
2410 if (m_media.find(name) == m_media.end()) {
2411 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2412 <<"unknown file \""<<(name)<<"\""<<std::endl;
2416 //TODO get path + name
2417 std::string tpath = m_media[name].path;
2420 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2422 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2423 <<tpath<<"\" for reading"<<std::endl;
2426 std::ostringstream tmp_os(std::ios_base::binary);
2430 fis.read(buf, 1024);
2431 std::streamsize len = fis.gcount();
2432 tmp_os.write(buf, len);
2433 file_size_bunch_total += len;
2442 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2443 <<name<<"\""<<std::endl;
2446 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2447 <<tname<<"\""<<std::endl;*/
2449 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2451 // Start next bunch if got enough data
2452 if(file_size_bunch_total >= bytes_per_bunch) {
2453 file_bunches.emplace_back();
2454 file_size_bunch_total = 0;
2459 /* Create and send packets */
2461 u16 num_bunches = file_bunches.size();
2462 for (u16 i = 0; i < num_bunches; i++) {
2465 u16 total number of texture bunches
2466 u16 index of this bunch
2467 u32 number of files in this bunch
2476 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2477 pkt << num_bunches << i << (u32) file_bunches[i].size();
2479 for (const SendableMedia &j : file_bunches[i]) {
2481 pkt.putLongString(j.data);
2484 verbosestream << "Server::sendRequestedMedia(): bunch "
2485 << i << "/" << num_bunches
2486 << " files=" << file_bunches[i].size()
2487 << " size=" << pkt.getSize() << std::endl;
2492 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2494 if(m_detached_inventories.count(name) == 0) {
2495 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2498 Inventory *inv = m_detached_inventories[name];
2499 std::ostringstream os(std::ios_base::binary);
2501 os << serializeString(name);
2505 std::string s = os.str();
2507 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2508 pkt.putRawString(s.c_str(), s.size());
2510 const std::string &check = m_detached_inventories_player[name];
2511 if (peer_id == PEER_ID_INEXISTENT) {
2513 return m_clients.sendToAll(&pkt);
2514 RemotePlayer *p = m_env->getPlayer(check.c_str());
2516 m_clients.send(p->getPeerId(), 0, &pkt, true);
2518 if (check.empty() || getPlayerName(peer_id) == check)
2523 void Server::sendDetachedInventories(session_t peer_id)
2525 for (const auto &detached_inventory : m_detached_inventories) {
2526 const std::string &name = detached_inventory.first;
2527 //Inventory *inv = i->second;
2528 sendDetachedInventory(name, peer_id);
2536 void Server::DiePlayer(session_t peer_id)
2538 PlayerSAO *playersao = getPlayerSAO(peer_id);
2539 // In some rare cases this can be NULL -- if the player is disconnected
2540 // when a Lua function modifies l_punch, for example
2544 infostream << "Server::DiePlayer(): Player "
2545 << playersao->getPlayer()->getName()
2546 << " dies" << std::endl;
2548 playersao->setHP(0);
2550 // Trigger scripted stuff
2551 m_script->on_dieplayer(playersao);
2553 SendPlayerHP(peer_id);
2554 SendDeathscreen(peer_id, false, v3f(0,0,0));
2557 void Server::RespawnPlayer(session_t peer_id)
2559 PlayerSAO *playersao = getPlayerSAO(peer_id);
2562 infostream << "Server::RespawnPlayer(): Player "
2563 << playersao->getPlayer()->getName()
2564 << " respawns" << std::endl;
2566 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2567 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2569 bool repositioned = m_script->on_respawnplayer(playersao);
2570 if (!repositioned) {
2571 // setPos will send the new position to client
2572 playersao->setPos(findSpawnPos());
2575 SendPlayerHP(peer_id);
2579 void Server::DenySudoAccess(session_t peer_id)
2581 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2586 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2587 const std::string &str_reason, bool reconnect)
2589 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2591 m_clients.event(peer_id, CSE_SetDenied);
2592 DisconnectPeer(peer_id);
2596 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2597 const std::string &custom_reason)
2599 SendAccessDenied(peer_id, reason, custom_reason);
2600 m_clients.event(peer_id, CSE_SetDenied);
2601 DisconnectPeer(peer_id);
2604 // 13/03/15: remove this function when protocol version 25 will become
2605 // the minimum version for MT users, maybe in 1 year
2606 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2608 SendAccessDenied_Legacy(peer_id, reason);
2609 m_clients.event(peer_id, CSE_SetDenied);
2610 DisconnectPeer(peer_id);
2613 void Server::DisconnectPeer(session_t peer_id)
2615 m_modchannel_mgr->leaveAllChannels(peer_id);
2616 m_con->DisconnectPeer(peer_id);
2619 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2622 RemoteClient* client = getClient(peer_id, CS_Invalid);
2624 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2626 // Right now, the auth mechs don't change between login and sudo mode.
2627 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2628 client->allowed_sudo_mechs = sudo_auth_mechs;
2630 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2631 << g_settings->getFloat("dedicated_server_step")
2635 m_clients.event(peer_id, CSE_AuthAccept);
2637 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2639 // We only support SRP right now
2640 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2642 resp_pkt << sudo_auth_mechs;
2644 m_clients.event(peer_id, CSE_SudoSuccess);
2648 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2650 std::wstring message;
2653 Clear references to playing sounds
2655 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2656 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2657 ServerPlayingSound &psound = i->second;
2658 psound.clients.erase(peer_id);
2659 if (psound.clients.empty())
2660 m_playing_sounds.erase(i++);
2665 // clear formspec info so the next client can't abuse the current state
2666 m_formspec_state_data.erase(peer_id);
2668 RemotePlayer *player = m_env->getPlayer(peer_id);
2670 /* Run scripts and remove from environment */
2672 PlayerSAO *playersao = player->getPlayerSAO();
2675 // inform connected clients
2676 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2677 // (u16) 1 + std::string represents a vector serialization representation
2678 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2679 m_clients.sendToAll(¬ice);
2681 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2683 playersao->disconnected();
2690 if (player && reason != CDR_DENY) {
2691 std::ostringstream os(std::ios_base::binary);
2692 std::vector<session_t> clients = m_clients.getClientIDs();
2694 for (const session_t client_id : clients) {
2696 RemotePlayer *player = m_env->getPlayer(client_id);
2700 // Get name of player
2701 os << player->getName() << " ";
2704 std::string name = player->getName();
2705 actionstream << name << " "
2706 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2707 << " List of players: " << os.str() << std::endl;
2709 m_admin_chat->outgoing_queue.push_back(
2710 new ChatEventNick(CET_NICK_REMOVE, name));
2714 MutexAutoLock env_lock(m_env_mutex);
2715 m_clients.DeleteClient(peer_id);
2719 // Send leave chat message to all remaining clients
2720 if (!message.empty()) {
2721 SendChatMessage(PEER_ID_INEXISTENT,
2722 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2726 void Server::UpdateCrafting(RemotePlayer *player)
2728 // Get a preview for crafting
2730 InventoryLocation loc;
2731 loc.setPlayer(player->getName());
2732 std::vector<ItemStack> output_replacements;
2733 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2734 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2735 (&player->inventory)->getList("craft"), loc);
2737 // Put the new preview in
2738 InventoryList *plist = player->inventory.getList("craftpreview");
2739 sanity_check(plist);
2740 sanity_check(plist->getSize() >= 1);
2741 plist->changeItem(0, preview);
2744 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2746 if (evt->type == CET_NICK_ADD) {
2747 // The terminal informed us of its nick choice
2748 m_admin_nick = ((ChatEventNick *)evt)->nick;
2749 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2750 errorstream << "You haven't set up an account." << std::endl
2751 << "Please log in using the client as '"
2752 << m_admin_nick << "' with a secure password." << std::endl
2753 << "Until then, you can't execute admin tasks via the console," << std::endl
2754 << "and everybody can claim the user account instead of you," << std::endl
2755 << "giving them full control over this server." << std::endl;
2758 assert(evt->type == CET_CHAT);
2759 handleAdminChat((ChatEventChat *)evt);
2763 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2764 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2766 // If something goes wrong, this player is to blame
2767 RollbackScopeActor rollback_scope(m_rollback,
2768 std::string("player:") + name);
2770 if (g_settings->getBool("strip_color_codes"))
2771 wmessage = unescape_enriched(wmessage);
2774 switch (player->canSendChatMessage()) {
2775 case RPLAYER_CHATRESULT_FLOODING: {
2776 std::wstringstream ws;
2777 ws << L"You cannot send more messages. You are limited to "
2778 << g_settings->getFloat("chat_message_limit_per_10sec")
2779 << L" messages per 10 seconds.";
2782 case RPLAYER_CHATRESULT_KICK:
2783 DenyAccess_Legacy(player->getPeerId(),
2784 L"You have been kicked due to message flooding.");
2786 case RPLAYER_CHATRESULT_OK:
2789 FATAL_ERROR("Unhandled chat filtering result found.");
2793 if (m_max_chatmessage_length > 0
2794 && wmessage.length() > m_max_chatmessage_length) {
2795 return L"Your message exceed the maximum chat message limit set on the server. "
2796 L"It was refused. Send a shorter message";
2799 // Run script hook, exit if script ate the chat message
2800 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2805 // Whether to send line to the player that sent the message, or to all players
2806 bool broadcast_line = true;
2808 if (check_shout_priv && !checkPriv(name, "shout")) {
2809 line += L"-!- You don't have permission to shout.";
2810 broadcast_line = false;
2819 Tell calling method to send the message to sender
2821 if (!broadcast_line)
2825 Send the message to others
2827 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2829 std::vector<session_t> clients = m_clients.getClientIDs();
2832 Send the message back to the inital sender
2833 if they are using protocol version >= 29
2836 session_t peer_id_to_avoid_sending =
2837 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2839 if (player && player->protocol_version >= 29)
2840 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2842 for (u16 cid : clients) {
2843 if (cid != peer_id_to_avoid_sending)
2844 SendChatMessage(cid, ChatMessage(line));
2849 void Server::handleAdminChat(const ChatEventChat *evt)
2851 std::string name = evt->nick;
2852 std::wstring wname = utf8_to_wide(name);
2853 std::wstring wmessage = evt->evt_msg;
2855 std::wstring answer = handleChat(name, wname, wmessage);
2857 // If asked to send answer to sender
2858 if (!answer.empty()) {
2859 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2863 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2865 RemoteClient *client = getClientNoEx(peer_id,state_min);
2867 throw ClientNotFoundException("Client not found");
2871 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2873 return m_clients.getClientNoEx(peer_id, state_min);
2876 std::string Server::getPlayerName(session_t peer_id)
2878 RemotePlayer *player = m_env->getPlayer(peer_id);
2880 return "[id="+itos(peer_id)+"]";
2881 return player->getName();
2884 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2886 RemotePlayer *player = m_env->getPlayer(peer_id);
2889 return player->getPlayerSAO();
2892 std::wstring Server::getStatusString()
2894 std::wostringstream os(std::ios_base::binary);
2897 os<<L"version="<<narrow_to_wide(g_version_string);
2899 os<<L", uptime="<<m_uptime.get();
2901 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2902 // Information about clients
2905 std::vector<session_t> clients = m_clients.getClientIDs();
2906 for (session_t client_id : clients) {
2908 RemotePlayer *player = m_env->getPlayer(client_id);
2909 // Get name of player
2910 std::wstring name = L"unknown";
2912 name = narrow_to_wide(player->getName());
2913 // Add name to information string
2922 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2923 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2925 if (!g_settings->get("motd").empty())
2926 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2930 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2932 std::set<std::string> privs;
2933 m_script->getAuth(name, NULL, &privs);
2937 bool Server::checkPriv(const std::string &name, const std::string &priv)
2939 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2940 return (privs.count(priv) != 0);
2943 void Server::reportPrivsModified(const std::string &name)
2946 std::vector<session_t> clients = m_clients.getClientIDs();
2947 for (const session_t client_id : clients) {
2948 RemotePlayer *player = m_env->getPlayer(client_id);
2949 reportPrivsModified(player->getName());
2952 RemotePlayer *player = m_env->getPlayer(name.c_str());
2955 SendPlayerPrivileges(player->getPeerId());
2956 PlayerSAO *sao = player->getPlayerSAO();
2959 sao->updatePrivileges(
2960 getPlayerEffectivePrivs(name),
2965 void Server::reportInventoryFormspecModified(const std::string &name)
2967 RemotePlayer *player = m_env->getPlayer(name.c_str());
2970 SendPlayerInventoryFormspec(player->getPeerId());
2973 void Server::setIpBanned(const std::string &ip, const std::string &name)
2975 m_banmanager->add(ip, name);
2978 void Server::unsetIpBanned(const std::string &ip_or_name)
2980 m_banmanager->remove(ip_or_name);
2983 std::string Server::getBanDescription(const std::string &ip_or_name)
2985 return m_banmanager->getBanDescription(ip_or_name);
2988 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2990 // m_env will be NULL if the server is initializing
2994 if (m_admin_nick == name && !m_admin_nick.empty()) {
2995 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2998 RemotePlayer *player = m_env->getPlayer(name);
3003 if (player->getPeerId() == PEER_ID_INEXISTENT)
3006 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3009 bool Server::showFormspec(const char *playername, const std::string &formspec,
3010 const std::string &formname)
3012 // m_env will be NULL if the server is initializing
3016 RemotePlayer *player = m_env->getPlayer(playername);
3020 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3024 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3029 u32 id = player->addHud(form);
3031 SendHUDAdd(player->getPeerId(), id, form);
3036 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3040 HudElement* todel = player->removeHud(id);
3047 SendHUDRemove(player->getPeerId(), id);
3051 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3056 SendHUDChange(player->getPeerId(), id, stat, data);
3060 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3065 SendHUDSetFlags(player->getPeerId(), flags, mask);
3066 player->hud_flags &= ~mask;
3067 player->hud_flags |= flags;
3069 PlayerSAO* playersao = player->getPlayerSAO();
3074 m_script->player_event(playersao, "hud_changed");
3078 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3083 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3086 player->setHotbarItemcount(hotbar_itemcount);
3087 std::ostringstream os(std::ios::binary);
3088 writeS32(os, hotbar_itemcount);
3089 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3093 s32 Server::hudGetHotbarItemcount(RemotePlayer *player) const
3095 return player->getHotbarItemcount();
3098 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3103 player->setHotbarImage(name);
3104 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3107 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3111 return player->getHotbarImage();
3114 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3119 player->setHotbarSelectedImage(name);
3120 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3123 const std::string& Server::hudGetHotbarSelectedImage(RemotePlayer *player) const
3125 return player->getHotbarSelectedImage();
3128 Address Server::getPeerAddress(session_t peer_id)
3130 return m_con->GetPeerAddress(peer_id);
3133 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3134 v2s32 animation_frames[4], f32 frame_speed)
3139 player->setLocalAnimations(animation_frames, frame_speed);
3140 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3144 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3149 player->eye_offset_first = first;
3150 player->eye_offset_third = third;
3151 SendEyeOffset(player->getPeerId(), first, third);
3155 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3156 const std::string &type, const std::vector<std::string> ¶ms,
3162 player->setSky(bgcolor, type, params, clouds);
3163 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3167 bool Server::setClouds(RemotePlayer *player, float density,
3168 const video::SColor &color_bright,
3169 const video::SColor &color_ambient,
3177 SendCloudParams(player->getPeerId(), density,
3178 color_bright, color_ambient, height,
3183 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3189 player->overrideDayNightRatio(do_override, ratio);
3190 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3194 void Server::notifyPlayers(const std::wstring &msg)
3196 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3199 void Server::spawnParticle(const std::string &playername, v3f pos,
3200 v3f velocity, v3f acceleration,
3201 float expirationtime, float size, bool
3202 collisiondetection, bool collision_removal,
3203 bool vertical, const std::string &texture,
3204 const struct TileAnimationParams &animation, u8 glow)
3206 // m_env will be NULL if the server is initializing
3210 session_t peer_id = PEER_ID_INEXISTENT;
3212 if (!playername.empty()) {
3213 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3216 peer_id = player->getPeerId();
3217 proto_ver = player->protocol_version;
3220 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3221 expirationtime, size, collisiondetection,
3222 collision_removal, vertical, texture, animation, glow);
3225 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3226 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3227 float minexptime, float maxexptime, float minsize, float maxsize,
3228 bool collisiondetection, bool collision_removal,
3229 ServerActiveObject *attached, bool vertical, const std::string &texture,
3230 const std::string &playername, const struct TileAnimationParams &animation,
3233 // m_env will be NULL if the server is initializing
3237 session_t peer_id = PEER_ID_INEXISTENT;
3239 if (!playername.empty()) {
3240 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3243 peer_id = player->getPeerId();
3244 proto_ver = player->protocol_version;
3247 u16 attached_id = attached ? attached->getId() : 0;
3250 if (attached_id == 0)
3251 id = m_env->addParticleSpawner(spawntime);
3253 id = m_env->addParticleSpawner(spawntime, attached_id);
3255 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3256 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3257 minexptime, maxexptime, minsize, maxsize,
3258 collisiondetection, collision_removal, attached_id, vertical,
3259 texture, id, animation, glow);
3264 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3266 // m_env will be NULL if the server is initializing
3268 throw ServerError("Can't delete particle spawners during initialisation!");
3270 session_t peer_id = PEER_ID_INEXISTENT;
3271 if (!playername.empty()) {
3272 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3275 peer_id = player->getPeerId();
3278 m_env->deleteParticleSpawner(id);
3279 SendDeleteParticleSpawner(peer_id, id);
3282 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3284 if(m_detached_inventories.count(name) > 0){
3285 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3286 delete m_detached_inventories[name];
3288 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3290 Inventory *inv = new Inventory(m_itemdef);
3292 m_detached_inventories[name] = inv;
3293 m_detached_inventories_player[name] = player;
3294 //TODO find a better way to do this
3295 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3299 // actions: time-reversed list
3300 // Return value: success/failure
3301 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3302 std::list<std::string> *log)
3304 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3305 ServerMap *map = (ServerMap*)(&m_env->getMap());
3307 // Fail if no actions to handle
3308 if(actions.empty()){
3309 log->push_back("Nothing to do.");
3316 for (const RollbackAction &action : actions) {
3318 bool success = action.applyRevert(map, this, this);
3321 std::ostringstream os;
3322 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3323 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3325 log->push_back(os.str());
3327 std::ostringstream os;
3328 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3329 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3331 log->push_back(os.str());
3335 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3336 <<" failed"<<std::endl;
3338 // Call it done if less than half failed
3339 return num_failed <= num_tried/2;
3342 // IGameDef interface
3344 IItemDefManager *Server::getItemDefManager()
3349 const NodeDefManager *Server::getNodeDefManager()
3354 ICraftDefManager *Server::getCraftDefManager()
3359 u16 Server::allocateUnknownNodeId(const std::string &name)
3361 return m_nodedef->allocateDummy(name);
3364 MtEventManager *Server::getEventManager()
3369 IWritableItemDefManager *Server::getWritableItemDefManager()
3374 NodeDefManager *Server::getWritableNodeDefManager()
3379 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3384 const ModSpec *Server::getModSpec(const std::string &modname) const
3386 std::vector<ModSpec>::const_iterator it;
3387 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3388 const ModSpec &mod = *it;
3389 if (mod.name == modname)
3395 void Server::getModNames(std::vector<std::string> &modlist)
3397 std::vector<ModSpec>::iterator it;
3398 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3399 modlist.push_back(it->name);
3402 std::string Server::getBuiltinLuaPath()
3404 return porting::path_share + DIR_DELIM + "builtin";
3407 std::string Server::getModStoragePath() const
3409 return m_path_world + DIR_DELIM + "mod_storage";
3412 v3f Server::findSpawnPos()
3414 ServerMap &map = m_env->getServerMap();
3416 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3417 return nodeposf * BS;
3420 bool is_good = false;
3421 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3422 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3424 // Try to find a good place a few times
3425 for(s32 i = 0; i < 4000 && !is_good; i++) {
3426 s32 range = MYMIN(1 + i, range_max);
3427 // We're going to try to throw the player to this position
3428 v2s16 nodepos2d = v2s16(
3429 -range + (myrand() % (range * 2)),
3430 -range + (myrand() % (range * 2)));
3432 // Get spawn level at point
3433 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3434 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3435 // the mapgen to signify an unsuitable spawn position
3436 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3439 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3442 for (s32 i = 0; i < 10; i++) {
3443 v3s16 blockpos = getNodeBlockPos(nodepos);
3444 map.emergeBlock(blockpos, true);
3445 content_t c = map.getNodeNoEx(nodepos).getContent();
3446 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3448 if (air_count >= 2) {
3449 nodeposf = intToFloat(nodepos, BS);
3450 // Don't spawn the player outside map boundaries
3451 if (objectpos_over_limit(nodeposf))
3464 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3466 m_shutdown_timer = delay;
3467 m_shutdown_msg = msg;
3468 m_shutdown_ask_reconnect = reconnect;
3470 if (delay == 0.0f) {
3471 // No delay, shutdown immediately
3472 m_shutdown_requested = true;
3473 // only print to the infostream, a chat message saying
3474 // "Server Shutting Down" is sent when the server destructs.
3475 infostream << "*** Immediate Server shutdown requested." << std::endl;
3476 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3477 // Negative delay, cancel shutdown if requested
3478 m_shutdown_timer = 0.0f;
3479 m_shutdown_msg = "";
3480 m_shutdown_ask_reconnect = false;
3481 m_shutdown_requested = false;
3482 std::wstringstream ws;
3484 ws << L"*** Server shutdown canceled.";
3486 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3487 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3488 } else if (delay > 0.0f) {
3489 // Positive delay, tell the clients when the server will shut down
3490 std::wstringstream ws;
3492 ws << L"*** Server shutting down in "
3493 << duration_to_string(myround(m_shutdown_timer)).c_str()
3496 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3497 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3501 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3504 Try to get an existing player
3506 RemotePlayer *player = m_env->getPlayer(name);
3508 // If player is already connected, cancel
3509 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3510 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3515 If player with the wanted peer_id already exists, cancel.
3517 if (m_env->getPlayer(peer_id)) {
3518 infostream<<"emergePlayer(): Player with wrong name but same"
3519 " peer_id already exists"<<std::endl;
3524 player = new RemotePlayer(name, idef());
3527 bool newplayer = false;
3530 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3532 // Complete init with server parts
3533 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3534 player->protocol_version = proto_version;
3538 m_script->on_newplayer(playersao);
3544 bool Server::registerModStorage(ModMetadata *storage)
3546 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3547 errorstream << "Unable to register same mod storage twice. Storage name: "
3548 << storage->getModName() << std::endl;
3552 m_mod_storages[storage->getModName()] = storage;
3556 void Server::unregisterModStorage(const std::string &name)
3558 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3559 if (it != m_mod_storages.end()) {
3560 // Save unconditionaly on unregistration
3561 it->second->save(getModStoragePath());
3562 m_mod_storages.erase(name);
3566 void dedicated_server_loop(Server &server, bool &kill)
3568 verbosestream<<"dedicated_server_loop()"<<std::endl;
3570 IntervalLimiter m_profiler_interval;
3572 static thread_local const float steplen =
3573 g_settings->getFloat("dedicated_server_step");
3574 static thread_local const float profiler_print_interval =
3575 g_settings->getFloat("profiler_print_interval");
3578 // This is kind of a hack but can be done like this
3579 // because server.step() is very light
3581 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3582 sleep_ms((int)(steplen*1000.0));
3584 server.step(steplen);
3586 if (server.getShutdownRequested() || kill)
3592 if (profiler_print_interval != 0) {
3593 if(m_profiler_interval.step(steplen, profiler_print_interval))
3595 infostream<<"Profiler:"<<std::endl;
3596 g_profiler->print(infostream);
3597 g_profiler->clear();
3602 infostream << "Dedicated server quitting" << std::endl;
3604 if (g_settings->getBool("server_announce"))
3605 ServerList::sendAnnounce(ServerList::AA_DELETE,
3606 server.m_bind_addr.getPort());
3615 bool Server::joinModChannel(const std::string &channel)
3617 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3618 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3621 bool Server::leaveModChannel(const std::string &channel)
3623 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3626 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3628 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3631 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3635 ModChannel* Server::getModChannel(const std::string &channel)
3637 return m_modchannel_mgr->getModChannel(channel);
3640 void Server::broadcastModChannelMessage(const std::string &channel,
3641 const std::string &message, session_t from_peer)
3643 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3647 if (message.size() > STRING_MAX_LEN) {
3648 warningstream << "ModChannel message too long, dropping before sending "
3649 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3650 << channel << ")" << std::endl;
3655 if (from_peer != PEER_ID_SERVER) {
3656 sender = getPlayerName(from_peer);
3659 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3660 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3661 resp_pkt << channel << sender << message;
3662 for (session_t peer_id : peers) {
3664 if (peer_id == from_peer)
3667 Send(peer_id, &resp_pkt);
3670 if (from_peer != PEER_ID_SERVER) {
3671 m_script->on_modchannel_message(channel, sender, message);