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 INodeDefManager *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 pkt.putLongString("");
1576 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1583 // Spawns a particle on peer with peer_id
1584 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1585 v3f pos, v3f velocity, v3f acceleration,
1586 float expirationtime, float size, bool collisiondetection,
1587 bool collision_removal,
1588 bool vertical, const std::string &texture,
1589 const struct TileAnimationParams &animation, u8 glow)
1591 static thread_local const float radius =
1592 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1594 if (peer_id == PEER_ID_INEXISTENT) {
1595 std::vector<session_t> clients = m_clients.getClientIDs();
1597 for (const session_t client_id : clients) {
1598 RemotePlayer *player = m_env->getPlayer(client_id);
1602 PlayerSAO *sao = player->getPlayerSAO();
1606 // Do not send to distant clients
1607 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1610 SendSpawnParticle(client_id, player->protocol_version,
1611 pos, velocity, acceleration,
1612 expirationtime, size, collisiondetection,
1613 collision_removal, vertical, texture, animation, glow);
1618 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1620 pkt << pos << velocity << acceleration << expirationtime
1621 << size << collisiondetection;
1622 pkt.putLongString(texture);
1624 pkt << collision_removal;
1625 // This is horrible but required (why are there two ways to serialize pkts?)
1626 std::ostringstream os(std::ios_base::binary);
1627 animation.serialize(os, protocol_version);
1628 pkt.putRawString(os.str());
1634 // Adds a ParticleSpawner on peer with peer_id
1635 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1636 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1637 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1638 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1639 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1640 const struct TileAnimationParams &animation, u8 glow)
1642 if (peer_id == PEER_ID_INEXISTENT) {
1643 // This sucks and should be replaced:
1644 std::vector<session_t> clients = m_clients.getClientIDs();
1645 for (const session_t client_id : clients) {
1646 RemotePlayer *player = m_env->getPlayer(client_id);
1649 SendAddParticleSpawner(client_id, player->protocol_version,
1650 amount, spawntime, minpos, maxpos,
1651 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1652 minsize, maxsize, collisiondetection, collision_removal,
1653 attached_id, vertical, texture, id, animation, glow);
1658 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1660 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1661 << minacc << maxacc << minexptime << maxexptime << minsize
1662 << maxsize << collisiondetection;
1664 pkt.putLongString(texture);
1666 pkt << id << vertical;
1667 pkt << collision_removal;
1669 // This is horrible but required
1670 std::ostringstream os(std::ios_base::binary);
1671 animation.serialize(os, protocol_version);
1672 pkt.putRawString(os.str());
1678 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1680 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1682 // Ugly error in this packet
1685 if (peer_id != PEER_ID_INEXISTENT)
1688 m_clients.sendToAll(&pkt);
1692 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1694 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1696 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1697 << form->text << form->number << form->item << form->dir
1698 << form->align << form->offset << form->world_pos << form->size;
1703 void Server::SendHUDRemove(session_t peer_id, u32 id)
1705 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1710 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1712 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1713 pkt << id << (u8) stat;
1717 case HUD_STAT_SCALE:
1718 case HUD_STAT_ALIGN:
1719 case HUD_STAT_OFFSET:
1720 pkt << *(v2f *) value;
1724 pkt << *(std::string *) value;
1726 case HUD_STAT_WORLD_POS:
1727 pkt << *(v3f *) value;
1730 pkt << *(v2s32 *) value;
1732 case HUD_STAT_NUMBER:
1736 pkt << *(u32 *) value;
1743 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1745 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1747 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1749 pkt << flags << mask;
1754 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1756 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1757 pkt << param << value;
1761 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1762 const std::string &type, const std::vector<std::string> ¶ms,
1765 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1766 pkt << bgcolor << type << (u16) params.size();
1768 for (const std::string ¶m : params)
1776 void Server::SendCloudParams(session_t peer_id, float density,
1777 const video::SColor &color_bright,
1778 const video::SColor &color_ambient,
1783 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1784 pkt << density << color_bright << color_ambient
1785 << height << thickness << speed;
1790 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1793 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1796 pkt << do_override << (u16) (ratio * 65535);
1801 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1803 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1804 pkt << time << time_speed;
1806 if (peer_id == PEER_ID_INEXISTENT) {
1807 m_clients.sendToAll(&pkt);
1814 void Server::SendPlayerHP(session_t peer_id)
1816 PlayerSAO *playersao = getPlayerSAO(peer_id);
1817 // In some rare case if the player is disconnected
1818 // while Lua call l_punch, for example, this can be NULL
1822 SendHP(peer_id, playersao->getHP());
1823 m_script->player_event(playersao,"health_changed");
1825 // Send to other clients
1826 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1827 ActiveObjectMessage aom(playersao->getId(), true, str);
1828 playersao->m_messages_out.push(aom);
1831 void Server::SendPlayerBreath(PlayerSAO *sao)
1835 m_script->player_event(sao, "breath_changed");
1836 SendBreath(sao->getPeerID(), sao->getBreath());
1839 void Server::SendMovePlayer(session_t peer_id)
1841 RemotePlayer *player = m_env->getPlayer(peer_id);
1843 PlayerSAO *sao = player->getPlayerSAO();
1846 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1847 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1850 v3f pos = sao->getBasePosition();
1851 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1852 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1853 << " pitch=" << sao->getPitch()
1854 << " yaw=" << sao->getYaw()
1861 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1862 f32 animation_speed)
1864 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1867 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1868 << animation_frames[3] << animation_speed;
1873 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1875 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1876 pkt << first << third;
1880 void Server::SendPlayerPrivileges(session_t peer_id)
1882 RemotePlayer *player = m_env->getPlayer(peer_id);
1884 if(player->getPeerId() == PEER_ID_INEXISTENT)
1887 std::set<std::string> privs;
1888 m_script->getAuth(player->getName(), NULL, &privs);
1890 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1891 pkt << (u16) privs.size();
1893 for (const std::string &priv : privs) {
1900 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1902 RemotePlayer *player = m_env->getPlayer(peer_id);
1904 if(player->getPeerId() == PEER_ID_INEXISTENT)
1907 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1908 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1912 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1914 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1915 pkt.putRawString(datas.c_str(), datas.size());
1917 return pkt.getSize();
1920 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1923 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1924 datas.size(), peer_id);
1926 pkt.putRawString(datas.c_str(), datas.size());
1928 m_clients.send(pkt.getPeerId(),
1929 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1933 void Server::SendCSMFlavourLimits(session_t peer_id)
1935 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1936 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1937 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1941 s32 Server::playSound(const SimpleSoundSpec &spec,
1942 const ServerSoundParams ¶ms)
1944 // Find out initial position of sound
1945 bool pos_exists = false;
1946 v3f pos = params.getPos(m_env, &pos_exists);
1947 // If position is not found while it should be, cancel sound
1948 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1951 // Filter destination clients
1952 std::vector<session_t> dst_clients;
1953 if(!params.to_player.empty()) {
1954 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1956 infostream<<"Server::playSound: Player \""<<params.to_player
1957 <<"\" not found"<<std::endl;
1960 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1961 infostream<<"Server::playSound: Player \""<<params.to_player
1962 <<"\" not connected"<<std::endl;
1965 dst_clients.push_back(player->getPeerId());
1967 std::vector<session_t> clients = m_clients.getClientIDs();
1969 for (const session_t client_id : clients) {
1970 RemotePlayer *player = m_env->getPlayer(client_id);
1974 PlayerSAO *sao = player->getPlayerSAO();
1979 if(sao->getBasePosition().getDistanceFrom(pos) >
1980 params.max_hear_distance)
1983 dst_clients.push_back(client_id);
1987 if(dst_clients.empty())
1991 s32 id = m_next_sound_id++;
1992 // The sound will exist as a reference in m_playing_sounds
1993 m_playing_sounds[id] = ServerPlayingSound();
1994 ServerPlayingSound &psound = m_playing_sounds[id];
1995 psound.params = params;
1998 float gain = params.gain * spec.gain;
1999 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2000 pkt << id << spec.name << gain
2001 << (u8) params.type << pos << params.object
2002 << params.loop << params.fade << params.pitch;
2004 // Backwards compability
2005 bool play_sound = gain > 0;
2007 for (const u16 dst_client : dst_clients) {
2008 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2009 psound.clients.insert(dst_client);
2010 m_clients.send(dst_client, 0, &pkt, true);
2015 void Server::stopSound(s32 handle)
2017 // Get sound reference
2018 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2019 m_playing_sounds.find(handle);
2020 if (i == m_playing_sounds.end())
2022 ServerPlayingSound &psound = i->second;
2024 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2027 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2028 si != psound.clients.end(); ++si) {
2030 m_clients.send(*si, 0, &pkt, true);
2032 // Remove sound reference
2033 m_playing_sounds.erase(i);
2036 void Server::fadeSound(s32 handle, float step, float gain)
2038 // Get sound reference
2039 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2040 m_playing_sounds.find(handle);
2041 if (i == m_playing_sounds.end())
2044 ServerPlayingSound &psound = i->second;
2045 psound.params.gain = gain;
2047 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2048 pkt << handle << step << gain;
2050 // Backwards compability
2051 bool play_sound = gain > 0;
2052 ServerPlayingSound compat_psound = psound;
2053 compat_psound.clients.clear();
2055 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2056 compat_pkt << handle;
2058 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2059 it != psound.clients.end();) {
2060 if (m_clients.getProtocolVersion(*it) >= 32) {
2062 m_clients.send(*it, 0, &pkt, true);
2065 compat_psound.clients.insert(*it);
2067 m_clients.send(*it, 0, &compat_pkt, true);
2068 psound.clients.erase(it++);
2072 // Remove sound reference
2073 if (!play_sound || psound.clients.empty())
2074 m_playing_sounds.erase(i);
2076 if (play_sound && !compat_psound.clients.empty()) {
2077 // Play new sound volume on older clients
2078 playSound(compat_psound.spec, compat_psound.params);
2082 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2083 std::vector<u16> *far_players, float far_d_nodes)
2085 float maxd = far_d_nodes*BS;
2086 v3f p_f = intToFloat(p, BS);
2088 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2091 std::vector<session_t> clients = m_clients.getClientIDs();
2092 for (session_t client_id : clients) {
2095 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2096 PlayerSAO *sao = player->getPlayerSAO();
2100 // If player is far away, only set modified blocks not sent
2101 v3f player_pos = sao->getBasePosition();
2102 if (player_pos.getDistanceFrom(p_f) > maxd) {
2103 far_players->push_back(client_id);
2110 m_clients.send(client_id, 0, &pkt, true);
2114 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2115 std::vector<u16> *far_players, float far_d_nodes,
2116 bool remove_metadata)
2118 float maxd = far_d_nodes*BS;
2119 v3f p_f = intToFloat(p, BS);
2121 std::vector<session_t> clients = m_clients.getClientIDs();
2122 for (const session_t client_id : clients) {
2125 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2126 PlayerSAO *sao = player->getPlayerSAO();
2130 // If player is far away, only set modified blocks not sent
2131 v3f player_pos = sao->getBasePosition();
2132 if(player_pos.getDistanceFrom(p_f) > maxd) {
2133 far_players->push_back(client_id);
2139 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2141 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2143 pkt << p << n.param0 << n.param1 << n.param2
2144 << (u8) (remove_metadata ? 0 : 1);
2149 if (pkt.getSize() > 0)
2150 m_clients.send(client_id, 0, &pkt, true);
2154 void Server::setBlockNotSent(v3s16 p)
2156 std::vector<session_t> clients = m_clients.getClientIDs();
2158 for (const session_t i : clients) {
2159 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2160 client->SetBlockNotSent(p);
2165 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2166 u16 net_proto_version)
2168 v3s16 p = block->getPos();
2171 Create a packet with the block in the right format
2174 std::ostringstream os(std::ios_base::binary);
2175 block->serialize(os, ver, false);
2176 block->serializeNetworkSpecific(os);
2177 std::string s = os.str();
2179 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2182 pkt.putRawString(s.c_str(), s.size());
2186 void Server::SendBlocks(float dtime)
2188 MutexAutoLock envlock(m_env_mutex);
2189 //TODO check if one big lock could be faster then multiple small ones
2191 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2193 std::vector<PrioritySortedBlockTransfer> queue;
2195 u32 total_sending = 0;
2198 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2200 std::vector<session_t> clients = m_clients.getClientIDs();
2203 for (const session_t client_id : clients) {
2204 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2209 total_sending += client->getSendingCount();
2210 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2216 // Lowest priority number comes first.
2217 // Lowest is most important.
2218 std::sort(queue.begin(), queue.end());
2222 // Maximal total count calculation
2223 // The per-client block sends is halved with the maximal online users
2224 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2225 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2227 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2228 if (total_sending >= max_blocks_to_send)
2231 MapBlock *block = nullptr;
2233 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2234 } catch (const InvalidPositionException &e) {
2238 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2243 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2244 client->net_proto_version);
2246 client->SentBlock(block_to_send.pos);
2252 void Server::fillMediaCache()
2254 infostream<<"Server: Calculating media file checksums"<<std::endl;
2256 // Collect all media file paths
2257 std::vector<std::string> paths;
2258 for (const ModSpec &mod : m_mods) {
2259 paths.push_back(mod.path + DIR_DELIM + "textures");
2260 paths.push_back(mod.path + DIR_DELIM + "sounds");
2261 paths.push_back(mod.path + DIR_DELIM + "media");
2262 paths.push_back(mod.path + DIR_DELIM + "models");
2263 paths.push_back(mod.path + DIR_DELIM + "locale");
2265 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2266 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2268 // Collect media file information from paths into cache
2269 for (const std::string &mediapath : paths) {
2270 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2271 for (const fs::DirListNode &dln : dirlist) {
2272 if (dln.dir) // Ignode dirs
2274 std::string filename = dln.name;
2275 // If name contains illegal characters, ignore the file
2276 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2277 infostream<<"Server: ignoring illegal file name: \""
2278 << filename << "\"" << std::endl;
2281 // If name is not in a supported format, ignore it
2282 const char *supported_ext[] = {
2283 ".png", ".jpg", ".bmp", ".tga",
2284 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2286 ".x", ".b3d", ".md2", ".obj",
2287 // Custom translation file format
2291 if (removeStringEnd(filename, supported_ext).empty()){
2292 infostream << "Server: ignoring unsupported file extension: \""
2293 << filename << "\"" << std::endl;
2296 // Ok, attempt to load the file and add to cache
2297 std::string filepath;
2298 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2301 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2303 errorstream << "Server::fillMediaCache(): Could not open \""
2304 << filename << "\" for reading" << std::endl;
2307 std::ostringstream tmp_os(std::ios_base::binary);
2311 fis.read(buf, 1024);
2312 std::streamsize len = fis.gcount();
2313 tmp_os.write(buf, len);
2322 errorstream<<"Server::fillMediaCache(): Failed to read \""
2323 << filename << "\"" << std::endl;
2326 if(tmp_os.str().length() == 0) {
2327 errorstream << "Server::fillMediaCache(): Empty file \""
2328 << filepath << "\"" << std::endl;
2333 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2335 unsigned char *digest = sha1.getDigest();
2336 std::string sha1_base64 = base64_encode(digest, 20);
2337 std::string sha1_hex = hex_encode((char*)digest, 20);
2341 m_media[filename] = MediaInfo(filepath, sha1_base64);
2342 verbosestream << "Server: " << sha1_hex << " is " << filename
2348 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2350 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2354 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2357 std::string lang_suffix;
2358 lang_suffix.append(".").append(lang_code).append(".tr");
2359 for (const auto &i : m_media) {
2360 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2367 for (const auto &i : m_media) {
2368 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2370 pkt << i.first << i.second.sha1_digest;
2373 pkt << g_settings->get("remote_media");
2377 struct SendableMedia
2383 SendableMedia(const std::string &name_="", const std::string &path_="",
2384 const std::string &data_=""):
2391 void Server::sendRequestedMedia(session_t peer_id,
2392 const std::vector<std::string> &tosend)
2394 verbosestream<<"Server::sendRequestedMedia(): "
2395 <<"Sending files to client"<<std::endl;
2399 // Put 5kB in one bunch (this is not accurate)
2400 u32 bytes_per_bunch = 5000;
2402 std::vector< std::vector<SendableMedia> > file_bunches;
2403 file_bunches.emplace_back();
2405 u32 file_size_bunch_total = 0;
2407 for (const std::string &name : tosend) {
2408 if (m_media.find(name) == m_media.end()) {
2409 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2410 <<"unknown file \""<<(name)<<"\""<<std::endl;
2414 //TODO get path + name
2415 std::string tpath = m_media[name].path;
2418 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2420 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2421 <<tpath<<"\" for reading"<<std::endl;
2424 std::ostringstream tmp_os(std::ios_base::binary);
2428 fis.read(buf, 1024);
2429 std::streamsize len = fis.gcount();
2430 tmp_os.write(buf, len);
2431 file_size_bunch_total += len;
2440 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2441 <<name<<"\""<<std::endl;
2444 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2445 <<tname<<"\""<<std::endl;*/
2447 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2449 // Start next bunch if got enough data
2450 if(file_size_bunch_total >= bytes_per_bunch) {
2451 file_bunches.emplace_back();
2452 file_size_bunch_total = 0;
2457 /* Create and send packets */
2459 u16 num_bunches = file_bunches.size();
2460 for (u16 i = 0; i < num_bunches; i++) {
2463 u16 total number of texture bunches
2464 u16 index of this bunch
2465 u32 number of files in this bunch
2474 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2475 pkt << num_bunches << i << (u32) file_bunches[i].size();
2477 for (const SendableMedia &j : file_bunches[i]) {
2479 pkt.putLongString(j.data);
2482 verbosestream << "Server::sendRequestedMedia(): bunch "
2483 << i << "/" << num_bunches
2484 << " files=" << file_bunches[i].size()
2485 << " size=" << pkt.getSize() << std::endl;
2490 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2492 if(m_detached_inventories.count(name) == 0) {
2493 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2496 Inventory *inv = m_detached_inventories[name];
2497 std::ostringstream os(std::ios_base::binary);
2499 os << serializeString(name);
2503 std::string s = os.str();
2505 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2506 pkt.putRawString(s.c_str(), s.size());
2508 const std::string &check = m_detached_inventories_player[name];
2509 if (peer_id == PEER_ID_INEXISTENT) {
2511 return m_clients.sendToAll(&pkt);
2512 RemotePlayer *p = m_env->getPlayer(check.c_str());
2514 m_clients.send(p->getPeerId(), 0, &pkt, true);
2516 if (check.empty() || getPlayerName(peer_id) == check)
2521 void Server::sendDetachedInventories(session_t peer_id)
2523 for (const auto &detached_inventory : m_detached_inventories) {
2524 const std::string &name = detached_inventory.first;
2525 //Inventory *inv = i->second;
2526 sendDetachedInventory(name, peer_id);
2534 void Server::DiePlayer(session_t peer_id)
2536 PlayerSAO *playersao = getPlayerSAO(peer_id);
2537 // In some rare cases this can be NULL -- if the player is disconnected
2538 // when a Lua function modifies l_punch, for example
2542 infostream << "Server::DiePlayer(): Player "
2543 << playersao->getPlayer()->getName()
2544 << " dies" << std::endl;
2546 playersao->setHP(0);
2548 // Trigger scripted stuff
2549 m_script->on_dieplayer(playersao);
2551 SendPlayerHP(peer_id);
2552 SendDeathscreen(peer_id, false, v3f(0,0,0));
2555 void Server::RespawnPlayer(session_t peer_id)
2557 PlayerSAO *playersao = getPlayerSAO(peer_id);
2560 infostream << "Server::RespawnPlayer(): Player "
2561 << playersao->getPlayer()->getName()
2562 << " respawns" << std::endl;
2564 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2565 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2567 bool repositioned = m_script->on_respawnplayer(playersao);
2568 if (!repositioned) {
2569 // setPos will send the new position to client
2570 playersao->setPos(findSpawnPos());
2573 SendPlayerHP(peer_id);
2577 void Server::DenySudoAccess(session_t peer_id)
2579 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2584 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2585 const std::string &str_reason, bool reconnect)
2587 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2589 m_clients.event(peer_id, CSE_SetDenied);
2590 DisconnectPeer(peer_id);
2594 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2595 const std::string &custom_reason)
2597 SendAccessDenied(peer_id, reason, custom_reason);
2598 m_clients.event(peer_id, CSE_SetDenied);
2599 DisconnectPeer(peer_id);
2602 // 13/03/15: remove this function when protocol version 25 will become
2603 // the minimum version for MT users, maybe in 1 year
2604 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2606 SendAccessDenied_Legacy(peer_id, reason);
2607 m_clients.event(peer_id, CSE_SetDenied);
2608 DisconnectPeer(peer_id);
2611 void Server::DisconnectPeer(session_t peer_id)
2613 m_modchannel_mgr->leaveAllChannels(peer_id);
2614 m_con->DisconnectPeer(peer_id);
2617 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2620 RemoteClient* client = getClient(peer_id, CS_Invalid);
2622 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2624 // Right now, the auth mechs don't change between login and sudo mode.
2625 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2626 client->allowed_sudo_mechs = sudo_auth_mechs;
2628 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2629 << g_settings->getFloat("dedicated_server_step")
2633 m_clients.event(peer_id, CSE_AuthAccept);
2635 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2637 // We only support SRP right now
2638 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2640 resp_pkt << sudo_auth_mechs;
2642 m_clients.event(peer_id, CSE_SudoSuccess);
2646 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2648 std::wstring message;
2651 Clear references to playing sounds
2653 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2654 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2655 ServerPlayingSound &psound = i->second;
2656 psound.clients.erase(peer_id);
2657 if (psound.clients.empty())
2658 m_playing_sounds.erase(i++);
2663 RemotePlayer *player = m_env->getPlayer(peer_id);
2665 /* Run scripts and remove from environment */
2667 PlayerSAO *playersao = player->getPlayerSAO();
2670 // inform connected clients
2671 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2672 // (u16) 1 + std::string represents a vector serialization representation
2673 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2674 m_clients.sendToAll(¬ice);
2676 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2678 playersao->disconnected();
2685 if (player && reason != CDR_DENY) {
2686 std::ostringstream os(std::ios_base::binary);
2687 std::vector<session_t> clients = m_clients.getClientIDs();
2689 for (const session_t client_id : clients) {
2691 RemotePlayer *player = m_env->getPlayer(client_id);
2695 // Get name of player
2696 os << player->getName() << " ";
2699 std::string name = player->getName();
2700 actionstream << name << " "
2701 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2702 << " List of players: " << os.str() << std::endl;
2704 m_admin_chat->outgoing_queue.push_back(
2705 new ChatEventNick(CET_NICK_REMOVE, name));
2709 MutexAutoLock env_lock(m_env_mutex);
2710 m_clients.DeleteClient(peer_id);
2714 // Send leave chat message to all remaining clients
2715 if (!message.empty()) {
2716 SendChatMessage(PEER_ID_INEXISTENT,
2717 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2721 void Server::UpdateCrafting(RemotePlayer *player)
2723 // Get a preview for crafting
2725 InventoryLocation loc;
2726 loc.setPlayer(player->getName());
2727 std::vector<ItemStack> output_replacements;
2728 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2729 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2730 (&player->inventory)->getList("craft"), loc);
2732 // Put the new preview in
2733 InventoryList *plist = player->inventory.getList("craftpreview");
2734 sanity_check(plist);
2735 sanity_check(plist->getSize() >= 1);
2736 plist->changeItem(0, preview);
2739 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2741 if (evt->type == CET_NICK_ADD) {
2742 // The terminal informed us of its nick choice
2743 m_admin_nick = ((ChatEventNick *)evt)->nick;
2744 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2745 errorstream << "You haven't set up an account." << std::endl
2746 << "Please log in using the client as '"
2747 << m_admin_nick << "' with a secure password." << std::endl
2748 << "Until then, you can't execute admin tasks via the console," << std::endl
2749 << "and everybody can claim the user account instead of you," << std::endl
2750 << "giving them full control over this server." << std::endl;
2753 assert(evt->type == CET_CHAT);
2754 handleAdminChat((ChatEventChat *)evt);
2758 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2759 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2761 // If something goes wrong, this player is to blame
2762 RollbackScopeActor rollback_scope(m_rollback,
2763 std::string("player:") + name);
2765 if (g_settings->getBool("strip_color_codes"))
2766 wmessage = unescape_enriched(wmessage);
2769 switch (player->canSendChatMessage()) {
2770 case RPLAYER_CHATRESULT_FLOODING: {
2771 std::wstringstream ws;
2772 ws << L"You cannot send more messages. You are limited to "
2773 << g_settings->getFloat("chat_message_limit_per_10sec")
2774 << L" messages per 10 seconds.";
2777 case RPLAYER_CHATRESULT_KICK:
2778 DenyAccess_Legacy(player->getPeerId(),
2779 L"You have been kicked due to message flooding.");
2781 case RPLAYER_CHATRESULT_OK:
2784 FATAL_ERROR("Unhandled chat filtering result found.");
2788 if (m_max_chatmessage_length > 0
2789 && wmessage.length() > m_max_chatmessage_length) {
2790 return L"Your message exceed the maximum chat message limit set on the server. "
2791 L"It was refused. Send a shorter message";
2794 // Run script hook, exit if script ate the chat message
2795 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2800 // Whether to send line to the player that sent the message, or to all players
2801 bool broadcast_line = true;
2803 if (check_shout_priv && !checkPriv(name, "shout")) {
2804 line += L"-!- You don't have permission to shout.";
2805 broadcast_line = false;
2814 Tell calling method to send the message to sender
2816 if (!broadcast_line)
2820 Send the message to others
2822 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2824 std::vector<session_t> clients = m_clients.getClientIDs();
2827 Send the message back to the inital sender
2828 if they are using protocol version >= 29
2831 session_t peer_id_to_avoid_sending =
2832 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2834 if (player && player->protocol_version >= 29)
2835 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2837 for (u16 cid : clients) {
2838 if (cid != peer_id_to_avoid_sending)
2839 SendChatMessage(cid, ChatMessage(line));
2844 void Server::handleAdminChat(const ChatEventChat *evt)
2846 std::string name = evt->nick;
2847 std::wstring wname = utf8_to_wide(name);
2848 std::wstring wmessage = evt->evt_msg;
2850 std::wstring answer = handleChat(name, wname, wmessage);
2852 // If asked to send answer to sender
2853 if (!answer.empty()) {
2854 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2858 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2860 RemoteClient *client = getClientNoEx(peer_id,state_min);
2862 throw ClientNotFoundException("Client not found");
2866 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2868 return m_clients.getClientNoEx(peer_id, state_min);
2871 std::string Server::getPlayerName(session_t peer_id)
2873 RemotePlayer *player = m_env->getPlayer(peer_id);
2875 return "[id="+itos(peer_id)+"]";
2876 return player->getName();
2879 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2881 RemotePlayer *player = m_env->getPlayer(peer_id);
2884 return player->getPlayerSAO();
2887 std::wstring Server::getStatusString()
2889 std::wostringstream os(std::ios_base::binary);
2892 os<<L"version="<<narrow_to_wide(g_version_string);
2894 os<<L", uptime="<<m_uptime.get();
2896 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2897 // Information about clients
2900 std::vector<session_t> clients = m_clients.getClientIDs();
2901 for (session_t client_id : clients) {
2903 RemotePlayer *player = m_env->getPlayer(client_id);
2904 // Get name of player
2905 std::wstring name = L"unknown";
2907 name = narrow_to_wide(player->getName());
2908 // Add name to information string
2917 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2918 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2920 if (!g_settings->get("motd").empty())
2921 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2925 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2927 std::set<std::string> privs;
2928 m_script->getAuth(name, NULL, &privs);
2932 bool Server::checkPriv(const std::string &name, const std::string &priv)
2934 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2935 return (privs.count(priv) != 0);
2938 void Server::reportPrivsModified(const std::string &name)
2941 std::vector<session_t> clients = m_clients.getClientIDs();
2942 for (const session_t client_id : clients) {
2943 RemotePlayer *player = m_env->getPlayer(client_id);
2944 reportPrivsModified(player->getName());
2947 RemotePlayer *player = m_env->getPlayer(name.c_str());
2950 SendPlayerPrivileges(player->getPeerId());
2951 PlayerSAO *sao = player->getPlayerSAO();
2954 sao->updatePrivileges(
2955 getPlayerEffectivePrivs(name),
2960 void Server::reportInventoryFormspecModified(const std::string &name)
2962 RemotePlayer *player = m_env->getPlayer(name.c_str());
2965 SendPlayerInventoryFormspec(player->getPeerId());
2968 void Server::setIpBanned(const std::string &ip, const std::string &name)
2970 m_banmanager->add(ip, name);
2973 void Server::unsetIpBanned(const std::string &ip_or_name)
2975 m_banmanager->remove(ip_or_name);
2978 std::string Server::getBanDescription(const std::string &ip_or_name)
2980 return m_banmanager->getBanDescription(ip_or_name);
2983 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2985 // m_env will be NULL if the server is initializing
2989 if (m_admin_nick == name && !m_admin_nick.empty()) {
2990 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2993 RemotePlayer *player = m_env->getPlayer(name);
2998 if (player->getPeerId() == PEER_ID_INEXISTENT)
3001 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3004 bool Server::showFormspec(const char *playername, const std::string &formspec,
3005 const std::string &formname)
3007 // m_env will be NULL if the server is initializing
3011 RemotePlayer *player = m_env->getPlayer(playername);
3015 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3019 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3024 u32 id = player->addHud(form);
3026 SendHUDAdd(player->getPeerId(), id, form);
3031 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3035 HudElement* todel = player->removeHud(id);
3042 SendHUDRemove(player->getPeerId(), id);
3046 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3051 SendHUDChange(player->getPeerId(), id, stat, data);
3055 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3060 SendHUDSetFlags(player->getPeerId(), flags, mask);
3061 player->hud_flags &= ~mask;
3062 player->hud_flags |= flags;
3064 PlayerSAO* playersao = player->getPlayerSAO();
3069 m_script->player_event(playersao, "hud_changed");
3073 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3078 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3081 player->setHotbarItemcount(hotbar_itemcount);
3082 std::ostringstream os(std::ios::binary);
3083 writeS32(os, hotbar_itemcount);
3084 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3088 s32 Server::hudGetHotbarItemcount(RemotePlayer *player) const
3090 return player->getHotbarItemcount();
3093 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3098 player->setHotbarImage(name);
3099 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3102 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3106 return player->getHotbarImage();
3109 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3114 player->setHotbarSelectedImage(name);
3115 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3118 const std::string& Server::hudGetHotbarSelectedImage(RemotePlayer *player) const
3120 return player->getHotbarSelectedImage();
3123 Address Server::getPeerAddress(session_t peer_id)
3125 return m_con->GetPeerAddress(peer_id);
3128 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3129 v2s32 animation_frames[4], f32 frame_speed)
3134 player->setLocalAnimations(animation_frames, frame_speed);
3135 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3139 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3144 player->eye_offset_first = first;
3145 player->eye_offset_third = third;
3146 SendEyeOffset(player->getPeerId(), first, third);
3150 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3151 const std::string &type, const std::vector<std::string> ¶ms,
3157 player->setSky(bgcolor, type, params, clouds);
3158 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3162 bool Server::setClouds(RemotePlayer *player, float density,
3163 const video::SColor &color_bright,
3164 const video::SColor &color_ambient,
3172 SendCloudParams(player->getPeerId(), density,
3173 color_bright, color_ambient, height,
3178 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3184 player->overrideDayNightRatio(do_override, ratio);
3185 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3189 void Server::notifyPlayers(const std::wstring &msg)
3191 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3194 void Server::spawnParticle(const std::string &playername, v3f pos,
3195 v3f velocity, v3f acceleration,
3196 float expirationtime, float size, bool
3197 collisiondetection, bool collision_removal,
3198 bool vertical, const std::string &texture,
3199 const struct TileAnimationParams &animation, u8 glow)
3201 // m_env will be NULL if the server is initializing
3205 session_t peer_id = PEER_ID_INEXISTENT;
3207 if (!playername.empty()) {
3208 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3211 peer_id = player->getPeerId();
3212 proto_ver = player->protocol_version;
3215 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3216 expirationtime, size, collisiondetection,
3217 collision_removal, vertical, texture, animation, glow);
3220 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3221 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3222 float minexptime, float maxexptime, float minsize, float maxsize,
3223 bool collisiondetection, bool collision_removal,
3224 ServerActiveObject *attached, bool vertical, const std::string &texture,
3225 const std::string &playername, const struct TileAnimationParams &animation,
3228 // m_env will be NULL if the server is initializing
3232 session_t peer_id = PEER_ID_INEXISTENT;
3234 if (!playername.empty()) {
3235 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3238 peer_id = player->getPeerId();
3239 proto_ver = player->protocol_version;
3242 u16 attached_id = attached ? attached->getId() : 0;
3245 if (attached_id == 0)
3246 id = m_env->addParticleSpawner(spawntime);
3248 id = m_env->addParticleSpawner(spawntime, attached_id);
3250 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3251 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3252 minexptime, maxexptime, minsize, maxsize,
3253 collisiondetection, collision_removal, attached_id, vertical,
3254 texture, id, animation, glow);
3259 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3261 // m_env will be NULL if the server is initializing
3263 throw ServerError("Can't delete particle spawners during initialisation!");
3265 session_t peer_id = PEER_ID_INEXISTENT;
3266 if (!playername.empty()) {
3267 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3270 peer_id = player->getPeerId();
3273 m_env->deleteParticleSpawner(id);
3274 SendDeleteParticleSpawner(peer_id, id);
3277 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3279 if(m_detached_inventories.count(name) > 0){
3280 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3281 delete m_detached_inventories[name];
3283 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3285 Inventory *inv = new Inventory(m_itemdef);
3287 m_detached_inventories[name] = inv;
3288 m_detached_inventories_player[name] = player;
3289 //TODO find a better way to do this
3290 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3294 // actions: time-reversed list
3295 // Return value: success/failure
3296 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3297 std::list<std::string> *log)
3299 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3300 ServerMap *map = (ServerMap*)(&m_env->getMap());
3302 // Fail if no actions to handle
3303 if(actions.empty()){
3304 log->push_back("Nothing to do.");
3311 for (const RollbackAction &action : actions) {
3313 bool success = action.applyRevert(map, this, this);
3316 std::ostringstream os;
3317 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3318 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3320 log->push_back(os.str());
3322 std::ostringstream os;
3323 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3324 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3326 log->push_back(os.str());
3330 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3331 <<" failed"<<std::endl;
3333 // Call it done if less than half failed
3334 return num_failed <= num_tried/2;
3337 // IGameDef interface
3339 IItemDefManager *Server::getItemDefManager()
3344 INodeDefManager *Server::getNodeDefManager()
3349 ICraftDefManager *Server::getCraftDefManager()
3354 u16 Server::allocateUnknownNodeId(const std::string &name)
3356 return m_nodedef->allocateDummy(name);
3359 MtEventManager *Server::getEventManager()
3364 IWritableItemDefManager *Server::getWritableItemDefManager()
3369 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3374 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3379 const ModSpec *Server::getModSpec(const std::string &modname) const
3381 std::vector<ModSpec>::const_iterator it;
3382 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3383 const ModSpec &mod = *it;
3384 if (mod.name == modname)
3390 void Server::getModNames(std::vector<std::string> &modlist)
3392 std::vector<ModSpec>::iterator it;
3393 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3394 modlist.push_back(it->name);
3397 std::string Server::getBuiltinLuaPath()
3399 return porting::path_share + DIR_DELIM + "builtin";
3402 std::string Server::getModStoragePath() const
3404 return m_path_world + DIR_DELIM + "mod_storage";
3407 v3f Server::findSpawnPos()
3409 ServerMap &map = m_env->getServerMap();
3411 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3412 return nodeposf * BS;
3415 bool is_good = false;
3416 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3417 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3419 // Try to find a good place a few times
3420 for(s32 i = 0; i < 4000 && !is_good; i++) {
3421 s32 range = MYMIN(1 + i, range_max);
3422 // We're going to try to throw the player to this position
3423 v2s16 nodepos2d = v2s16(
3424 -range + (myrand() % (range * 2)),
3425 -range + (myrand() % (range * 2)));
3427 // Get spawn level at point
3428 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3429 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3430 // the mapgen to signify an unsuitable spawn position
3431 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3434 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3437 for (s32 i = 0; i < 10; i++) {
3438 v3s16 blockpos = getNodeBlockPos(nodepos);
3439 map.emergeBlock(blockpos, true);
3440 content_t c = map.getNodeNoEx(nodepos).getContent();
3441 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3443 if (air_count >= 2) {
3444 nodeposf = intToFloat(nodepos, BS);
3445 // Don't spawn the player outside map boundaries
3446 if (objectpos_over_limit(nodeposf))
3459 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3461 m_shutdown_timer = delay;
3462 m_shutdown_msg = msg;
3463 m_shutdown_ask_reconnect = reconnect;
3465 if (delay == 0.0f) {
3466 // No delay, shutdown immediately
3467 m_shutdown_requested = true;
3468 // only print to the infostream, a chat message saying
3469 // "Server Shutting Down" is sent when the server destructs.
3470 infostream << "*** Immediate Server shutdown requested." << std::endl;
3471 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3472 // Negative delay, cancel shutdown if requested
3473 m_shutdown_timer = 0.0f;
3474 m_shutdown_msg = "";
3475 m_shutdown_ask_reconnect = false;
3476 m_shutdown_requested = false;
3477 std::wstringstream ws;
3479 ws << L"*** Server shutdown canceled.";
3481 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3482 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3483 } else if (delay > 0.0f) {
3484 // Positive delay, tell the clients when the server will shut down
3485 std::wstringstream ws;
3487 ws << L"*** Server shutting down in "
3488 << duration_to_string(myround(m_shutdown_timer)).c_str()
3491 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3492 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3496 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3499 Try to get an existing player
3501 RemotePlayer *player = m_env->getPlayer(name);
3503 // If player is already connected, cancel
3504 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3505 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3510 If player with the wanted peer_id already exists, cancel.
3512 if (m_env->getPlayer(peer_id)) {
3513 infostream<<"emergePlayer(): Player with wrong name but same"
3514 " peer_id already exists"<<std::endl;
3519 player = new RemotePlayer(name, idef());
3522 bool newplayer = false;
3525 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3527 // Complete init with server parts
3528 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3529 player->protocol_version = proto_version;
3533 m_script->on_newplayer(playersao);
3539 bool Server::registerModStorage(ModMetadata *storage)
3541 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3542 errorstream << "Unable to register same mod storage twice. Storage name: "
3543 << storage->getModName() << std::endl;
3547 m_mod_storages[storage->getModName()] = storage;
3551 void Server::unregisterModStorage(const std::string &name)
3553 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3554 if (it != m_mod_storages.end()) {
3555 // Save unconditionaly on unregistration
3556 it->second->save(getModStoragePath());
3557 m_mod_storages.erase(name);
3561 void dedicated_server_loop(Server &server, bool &kill)
3563 verbosestream<<"dedicated_server_loop()"<<std::endl;
3565 IntervalLimiter m_profiler_interval;
3567 static thread_local const float steplen =
3568 g_settings->getFloat("dedicated_server_step");
3569 static thread_local const float profiler_print_interval =
3570 g_settings->getFloat("profiler_print_interval");
3573 // This is kind of a hack but can be done like this
3574 // because server.step() is very light
3576 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3577 sleep_ms((int)(steplen*1000.0));
3579 server.step(steplen);
3581 if (server.getShutdownRequested() || kill)
3587 if (profiler_print_interval != 0) {
3588 if(m_profiler_interval.step(steplen, profiler_print_interval))
3590 infostream<<"Profiler:"<<std::endl;
3591 g_profiler->print(infostream);
3592 g_profiler->clear();
3597 infostream << "Dedicated server quitting" << std::endl;
3599 if (g_settings->getBool("server_announce"))
3600 ServerList::sendAnnounce(ServerList::AA_DELETE,
3601 server.m_bind_addr.getPort());
3610 bool Server::joinModChannel(const std::string &channel)
3612 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3613 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3616 bool Server::leaveModChannel(const std::string &channel)
3618 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3621 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3623 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3626 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3630 ModChannel* Server::getModChannel(const std::string &channel)
3632 return m_modchannel_mgr->getModChannel(channel);
3635 void Server::broadcastModChannelMessage(const std::string &channel,
3636 const std::string &message, session_t from_peer)
3638 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3642 if (message.size() > STRING_MAX_LEN) {
3643 warningstream << "ModChannel message too long, dropping before sending "
3644 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3645 << channel << ")" << std::endl;
3650 if (from_peer != PEER_ID_SERVER) {
3651 sender = getPlayerName(from_peer);
3654 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3655 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3656 resp_pkt << channel << sender << message;
3657 for (session_t peer_id : peers) {
3659 if (peer_id == from_peer)
3662 Send(peer_id, &resp_pkt);
3665 if (from_peer != PEER_ID_SERVER) {
3666 m_script->on_modchannel_message(channel, sender, message);