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 "server/mods.h"
62 #include "util/base64.h"
63 #include "util/sha1.h"
65 #include "database/database.h"
66 #include "chatmessage.h"
67 #include "chat_interface.h"
68 #include "remoteplayer.h"
70 class ClientNotFoundException : public BaseException
73 ClientNotFoundException(const char *s):
78 class ServerThread : public Thread
82 ServerThread(Server *server):
93 void *ServerThread::run()
95 BEGIN_DEBUG_EXCEPTION_HANDLER
97 m_server->AsyncRunStep(true);
99 while (!stopRequested()) {
101 m_server->AsyncRunStep();
105 } catch (con::NoIncomingDataException &e) {
106 } catch (con::PeerNotFoundException &e) {
107 infostream<<"Server: PeerNotFoundException"<<std::endl;
108 } catch (ClientNotFoundException &e) {
109 } catch (con::ConnectionBindFailed &e) {
110 m_server->setAsyncFatalError(e.what());
111 } catch (LuaError &e) {
112 m_server->setAsyncFatalError(
113 "ServerThread::run Lua: " + std::string(e.what()));
117 END_DEBUG_EXCEPTION_HANDLER
122 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
124 if(pos_exists) *pos_exists = false;
129 if(pos_exists) *pos_exists = true;
134 ServerActiveObject *sao = env->getActiveObject(object);
137 if(pos_exists) *pos_exists = true;
138 return sao->getBasePosition(); }
150 const std::string &path_world,
151 const SubgameSpec &gamespec,
152 bool simple_singleplayer_mode,
157 m_bind_addr(bind_addr),
158 m_path_world(path_world),
159 m_gamespec(gamespec),
160 m_simple_singleplayer_mode(simple_singleplayer_mode),
161 m_dedicated(dedicated),
162 m_async_fatal_error(""),
163 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
166 m_bind_addr.isIPv6(),
168 m_itemdef(createItemDefManager()),
169 m_nodedef(createNodeDefManager()),
170 m_craftdef(createCraftDefManager()),
171 m_event(new EventManager()),
175 m_modchannel_mgr(new ModChannelMgr())
177 m_lag = g_settings->getFloat("dedicated_server_step");
179 if (path_world.empty())
180 throw ServerError("Supplied empty world path");
182 if(!gamespec.isValid())
183 throw ServerError("Supplied invalid gamespec");
185 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
186 if(m_simple_singleplayer_mode)
187 infostream<<" in simple singleplayer mode"<<std::endl;
189 infostream<<std::endl;
190 infostream<<"- world: "<<m_path_world<<std::endl;
191 infostream<<"- game: "<<m_gamespec.path<<std::endl;
193 // Create world if it doesn't exist
194 if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
195 throw ServerError("Failed to initialize world");
197 // Create server thread
198 m_thread = new ServerThread(this);
200 // Create emerge manager
201 m_emerge = new EmergeManager(this);
203 // Create ban manager
204 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
205 m_banmanager = new BanManager(ban_path);
207 m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager(
209 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
210 // complain about mods with unsatisfied dependencies
211 if (!m_modmgr->isConsistent()) {
212 m_modmgr->printUnsatisfiedModsError();
216 MutexAutoLock envlock(m_env_mutex);
218 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
219 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
221 // Initialize scripting
222 infostream<<"Server: Initializing Lua"<<std::endl;
224 m_script = new ServerScripting(this);
226 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
228 m_modmgr->loadMods(m_script);
230 // Read Textures and calculate sha1 sums
233 // Apply item aliases in the node definition manager
234 m_nodedef->updateAliases(m_itemdef);
236 // Apply texture overrides from texturepack/override.txt
237 std::vector<std::string> paths;
238 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
239 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
240 for (const std::string &path : paths)
241 m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
243 m_nodedef->setNodeRegistrationStatus(true);
245 // Perform pending node name resolutions
246 m_nodedef->runNodeResolveCallbacks();
248 // unmap node names for connected nodeboxes
249 m_nodedef->mapNodeboxConnections();
251 // init the recipe hashes to speed up crafting
252 m_craftdef->initHashes(this);
254 // Initialize Environment
255 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
257 m_clients.setEnv(m_env);
259 if (!servermap->settings_mgr.makeMapgenParams())
260 FATAL_ERROR("Couldn't create any mapgen type");
262 // Initialize mapgens
263 m_emerge->initMapgens(servermap->getMapgenParams());
265 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
266 if (m_enable_rollback_recording) {
267 // Create rollback manager
268 m_rollback = new RollbackManager(m_path_world, this);
271 // Give environment reference to scripting api
272 m_script->initializeEnvironment(m_env);
274 // Register us to receive map edit events
275 servermap->addEventReceiver(this);
277 // If file exists, load environment metadata
278 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
279 infostream << "Server: Loading environment metadata" << std::endl;
282 m_env->loadDefaultMeta();
285 m_liquid_transform_every = g_settings->getFloat("liquid_update");
286 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
287 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
288 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
293 infostream << "Server destructing" << std::endl;
295 // Send shutdown message
296 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
297 L"*** Server shutting down"));
300 MutexAutoLock envlock(m_env_mutex);
302 infostream << "Server: Saving players" << std::endl;
303 m_env->saveLoadedPlayers();
305 infostream << "Server: Kicking players" << std::endl;
306 std::string kick_msg;
307 bool reconnect = false;
308 if (getShutdownRequested()) {
309 reconnect = m_shutdown_ask_reconnect;
310 kick_msg = m_shutdown_msg;
312 if (kick_msg.empty()) {
313 kick_msg = g_settings->get("kick_msg_shutdown");
315 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
316 kick_msg, reconnect);
319 // Do this before stopping the server in case mapgen callbacks need to access
320 // server-controlled resources (like ModStorages). Also do them before
321 // shutdown callbacks since they may modify state that is finalized in a
323 m_emerge->stopThreads();
326 MutexAutoLock envlock(m_env_mutex);
328 // Execute script shutdown hooks
329 infostream << "Executing shutdown hooks" << std::endl;
330 m_script->on_shutdown();
332 infostream << "Server: Saving environment metadata" << std::endl;
340 // Delete things in the reverse order of creation
350 // Deinitialize scripting
351 infostream << "Server: Deinitializing scripting" << std::endl;
354 // Delete detached inventories
355 for (auto &detached_inventory : m_detached_inventories) {
356 delete detached_inventory.second;
362 infostream << "Starting server on " << m_bind_addr.serializeString()
363 << "..." << std::endl;
365 // Stop thread if already running
368 // Initialize connection
369 m_con->SetTimeoutMs(30);
370 m_con->Serve(m_bind_addr);
375 // ASCII art for the win!
377 << " .__ __ __ " << std::endl
378 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
379 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
380 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
381 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
382 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
383 actionstream << "World at [" << m_path_world << "]" << std::endl;
384 actionstream << "Server for gameid=\"" << m_gamespec.id
385 << "\" listening on " << m_bind_addr.serializeString() << ":"
386 << m_bind_addr.getPort() << "." << std::endl;
391 infostream<<"Server: Stopping and waiting threads"<<std::endl;
393 // Stop threads (set run=false first so both start stopping)
395 //m_emergethread.setRun(false);
397 //m_emergethread.stop();
399 infostream<<"Server: Threads stopped"<<std::endl;
402 void Server::step(float dtime)
408 MutexAutoLock lock(m_step_dtime_mutex);
409 m_step_dtime += dtime;
411 // Throw if fatal error occurred in thread
412 std::string async_err = m_async_fatal_error.get();
413 if (!async_err.empty()) {
414 if (!m_simple_singleplayer_mode) {
415 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
416 g_settings->get("kick_msg_crash"),
417 g_settings->getBool("ask_reconnect_on_crash"));
419 throw ServerError("AsyncErr: " + async_err);
423 void Server::AsyncRunStep(bool initial_step)
425 g_profiler->add("Server::AsyncRunStep (num)", 1);
429 MutexAutoLock lock1(m_step_dtime_mutex);
430 dtime = m_step_dtime;
434 // Send blocks to clients
438 if((dtime < 0.001) && !initial_step)
441 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
443 //infostream<<"Server steps "<<dtime<<std::endl;
444 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
447 MutexAutoLock lock1(m_step_dtime_mutex);
448 m_step_dtime -= dtime;
455 m_uptime.set(m_uptime.get() + dtime);
461 Update time of day and overall game time
463 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
466 Send to clients at constant intervals
469 m_time_of_day_send_timer -= dtime;
470 if(m_time_of_day_send_timer < 0.0) {
471 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
472 u16 time = m_env->getTimeOfDay();
473 float time_speed = g_settings->getFloat("time_speed");
474 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
478 MutexAutoLock lock(m_env_mutex);
479 // Figure out and report maximum lag to environment
480 float max_lag = m_env->getMaxLagEstimate();
481 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
483 if(dtime > 0.1 && dtime > max_lag * 2.0)
484 infostream<<"Server: Maximum lag peaked to "<<dtime
488 m_env->reportMaxLagEstimate(max_lag);
490 ScopeProfiler sp(g_profiler, "SEnv step");
491 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
495 static const float map_timer_and_unload_dtime = 2.92;
496 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
498 MutexAutoLock lock(m_env_mutex);
499 // Run Map's timers and unload unused data
500 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
501 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
502 g_settings->getFloat("server_unload_unused_data_timeout"),
507 Listen to the admin chat, if available
510 if (!m_admin_chat->command_queue.empty()) {
511 MutexAutoLock lock(m_env_mutex);
512 while (!m_admin_chat->command_queue.empty()) {
513 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
514 handleChatInterfaceEvent(evt);
518 m_admin_chat->outgoing_queue.push_back(
519 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
526 /* Transform liquids */
527 m_liquid_transform_timer += dtime;
528 if(m_liquid_transform_timer >= m_liquid_transform_every)
530 m_liquid_transform_timer -= m_liquid_transform_every;
532 MutexAutoLock lock(m_env_mutex);
534 ScopeProfiler sp(g_profiler, "Server: liquid transform");
536 std::map<v3s16, MapBlock*> modified_blocks;
537 m_env->getMap().transformLiquids(modified_blocks, m_env);
540 Set the modified blocks unsent for all the clients
542 if (!modified_blocks.empty()) {
543 SetBlocksNotSent(modified_blocks);
546 m_clients.step(dtime);
548 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
550 // send masterserver announce
552 float &counter = m_masterserver_timer;
553 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
554 g_settings->getBool("server_announce")) {
555 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
556 ServerList::AA_START,
557 m_bind_addr.getPort(),
558 m_clients.getPlayerNames(),
560 m_env->getGameTime(),
563 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
573 Check added and deleted active objects
576 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
577 MutexAutoLock envlock(m_env_mutex);
580 const RemoteClientMap &clients = m_clients.getClientList();
581 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
583 // Radius inside which objects are active
584 static thread_local const s16 radius =
585 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
587 // Radius inside which players are active
588 static thread_local const bool is_transfer_limited =
589 g_settings->exists("unlimited_player_transfer_distance") &&
590 !g_settings->getBool("unlimited_player_transfer_distance");
591 static thread_local const s16 player_transfer_dist =
592 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
593 s16 player_radius = player_transfer_dist;
594 if (player_radius == 0 && is_transfer_limited)
595 player_radius = radius;
597 for (const auto &client_it : clients) {
598 RemoteClient *client = client_it.second;
600 // If definitions and textures have not been sent, don't
601 // send objects either
602 if (client->getState() < CS_DefinitionsSent)
605 RemotePlayer *player = m_env->getPlayer(client->peer_id);
607 // This can happen if the client timeouts somehow
611 PlayerSAO *playersao = player->getPlayerSAO();
615 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
616 if (my_radius <= 0) my_radius = radius;
617 //infostream << "Server: Active Radius " << my_radius << std::endl;
619 std::queue<u16> removed_objects;
620 std::queue<u16> added_objects;
621 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
622 client->m_known_objects, removed_objects);
623 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
624 client->m_known_objects, added_objects);
626 // Ignore if nothing happened
627 if (removed_objects.empty() && added_objects.empty()) {
631 std::string data_buffer;
635 // Handle removed objects
636 writeU16((u8*)buf, removed_objects.size());
637 data_buffer.append(buf, 2);
638 while (!removed_objects.empty()) {
640 u16 id = removed_objects.front();
641 ServerActiveObject* obj = m_env->getActiveObject(id);
643 // Add to data buffer for sending
644 writeU16((u8*)buf, id);
645 data_buffer.append(buf, 2);
647 // Remove from known objects
648 client->m_known_objects.erase(id);
650 if(obj && obj->m_known_by_count > 0)
651 obj->m_known_by_count--;
652 removed_objects.pop();
655 // Handle added objects
656 writeU16((u8*)buf, added_objects.size());
657 data_buffer.append(buf, 2);
658 while (!added_objects.empty()) {
660 u16 id = added_objects.front();
661 ServerActiveObject* obj = m_env->getActiveObject(id);
664 u8 type = ACTIVEOBJECT_TYPE_INVALID;
666 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
668 type = obj->getSendType();
670 // Add to data buffer for sending
671 writeU16((u8*)buf, id);
672 data_buffer.append(buf, 2);
673 writeU8((u8*)buf, type);
674 data_buffer.append(buf, 1);
677 data_buffer.append(serializeLongString(
678 obj->getClientInitializationData(client->net_proto_version)));
680 data_buffer.append(serializeLongString(""));
682 // Add to known objects
683 client->m_known_objects.insert(id);
686 obj->m_known_by_count++;
691 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
692 verbosestream << "Server: Sent object remove/add: "
693 << removed_objects.size() << " removed, "
694 << added_objects.size() << " added, "
695 << "packet size is " << pktSize << std::endl;
699 m_mod_storage_save_timer -= dtime;
700 if (m_mod_storage_save_timer <= 0.0f) {
701 infostream << "Saving registered mod storages." << std::endl;
702 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
703 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
704 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
705 if (it->second->isModified()) {
706 it->second->save(getModStoragePath());
716 MutexAutoLock envlock(m_env_mutex);
717 ScopeProfiler sp(g_profiler, "Server: sending object messages");
720 // Value = data sent by object
721 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
723 // Get active object messages from environment
725 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
729 std::vector<ActiveObjectMessage>* message_list = nullptr;
730 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
731 n = buffered_messages.find(aom.id);
732 if (n == buffered_messages.end()) {
733 message_list = new std::vector<ActiveObjectMessage>;
734 buffered_messages[aom.id] = message_list;
737 message_list = n->second;
739 message_list->push_back(aom);
743 const RemoteClientMap &clients = m_clients.getClientList();
744 // Route data to every client
745 for (const auto &client_it : clients) {
746 RemoteClient *client = client_it.second;
747 std::string reliable_data;
748 std::string unreliable_data;
749 // Go through all objects in message buffer
750 for (const auto &buffered_message : buffered_messages) {
751 // If object is not known by client, skip it
752 u16 id = buffered_message.first;
753 if (client->m_known_objects.find(id) == client->m_known_objects.end())
756 // Get message list of object
757 std::vector<ActiveObjectMessage>* list = buffered_message.second;
758 // Go through every message
759 for (const ActiveObjectMessage &aom : *list) {
760 // Compose the full new data with header
761 std::string new_data;
764 writeU16((u8*)&buf[0], aom.id);
765 new_data.append(buf, 2);
767 new_data += serializeString(aom.datastring);
768 // Add data to buffer
770 reliable_data += new_data;
772 unreliable_data += new_data;
776 reliable_data and unreliable_data are now ready.
779 if (!reliable_data.empty()) {
780 SendActiveObjectMessages(client->peer_id, reliable_data);
783 if (!unreliable_data.empty()) {
784 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
789 // Clear buffered_messages
790 for (auto &buffered_message : buffered_messages) {
791 delete buffered_message.second;
796 Send queued-for-sending map edit events.
799 // We will be accessing the environment
800 MutexAutoLock lock(m_env_mutex);
802 // Don't send too many at a time
805 // Single change sending is disabled if queue size is not small
806 bool disable_single_change_sending = false;
807 if(m_unsent_map_edit_queue.size() >= 4)
808 disable_single_change_sending = true;
810 int event_count = m_unsent_map_edit_queue.size();
812 // We'll log the amount of each
815 while (!m_unsent_map_edit_queue.empty()) {
816 MapEditEvent* event = m_unsent_map_edit_queue.front();
817 m_unsent_map_edit_queue.pop();
819 // Players far away from the change are stored here.
820 // Instead of sending the changes, MapBlocks are set not sent
822 std::vector<u16> far_players;
824 switch (event->type) {
827 prof.add("MEET_ADDNODE", 1);
828 sendAddNode(event->p, event->n, event->already_known_by_peer,
829 &far_players, disable_single_change_sending ? 5 : 30,
830 event->type == MEET_ADDNODE);
832 case MEET_REMOVENODE:
833 prof.add("MEET_REMOVENODE", 1);
834 sendRemoveNode(event->p, event->already_known_by_peer,
835 &far_players, disable_single_change_sending ? 5 : 30);
837 case MEET_BLOCK_NODE_METADATA_CHANGED:
838 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
839 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
840 m_clients.markBlockposAsNotSent(event->p);
843 infostream << "Server: MEET_OTHER" << std::endl;
844 prof.add("MEET_OTHER", 1);
845 for (const v3s16 &modified_block : event->modified_blocks) {
846 m_clients.markBlockposAsNotSent(modified_block);
850 prof.add("unknown", 1);
851 warningstream << "Server: Unknown MapEditEvent "
852 << ((u32)event->type) << std::endl;
857 Set blocks not sent to far players
859 if (!far_players.empty()) {
860 // Convert list format to that wanted by SetBlocksNotSent
861 std::map<v3s16, MapBlock*> modified_blocks2;
862 for (const v3s16 &modified_block : event->modified_blocks) {
863 modified_blocks2[modified_block] =
864 m_env->getMap().getBlockNoCreateNoEx(modified_block);
867 // Set blocks not sent
868 for (const u16 far_player : far_players) {
869 if (RemoteClient *client = getClient(far_player))
870 client->SetBlocksNotSent(modified_blocks2);
877 if (event_count >= 5) {
878 infostream << "Server: MapEditEvents:" << std::endl;
879 prof.print(infostream);
880 } else if (event_count != 0) {
881 verbosestream << "Server: MapEditEvents:" << std::endl;
882 prof.print(verbosestream);
888 Trigger emergethread (it somehow gets to a non-triggered but
889 bysy state sometimes)
892 float &counter = m_emergethread_trigger_timer;
894 if (counter >= 2.0) {
897 m_emerge->startThreads();
901 // Save map, players and auth stuff
903 float &counter = m_savemap_timer;
905 static thread_local const float save_interval =
906 g_settings->getFloat("server_map_save_interval");
907 if (counter >= save_interval) {
909 MutexAutoLock lock(m_env_mutex);
911 ScopeProfiler sp(g_profiler, "Server: saving stuff");
914 if (m_banmanager->isModified()) {
915 m_banmanager->save();
918 // Save changed parts of map
919 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
922 m_env->saveLoadedPlayers();
924 // Save environment metadata
930 static const float shutdown_msg_times[] =
932 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
935 if (m_shutdown_timer > 0.0f) {
936 // Automated messages
937 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
938 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
939 // If shutdown timer matches an automessage, shot it
940 if (m_shutdown_timer > shutdown_msg_times[i] &&
941 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
942 std::wstringstream ws;
944 ws << L"*** Server shutting down in "
945 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
948 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
949 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
955 m_shutdown_timer -= dtime;
956 if (m_shutdown_timer < 0.0f) {
957 m_shutdown_timer = 0.0f;
958 m_shutdown_requested = true;
963 void Server::Receive()
968 m_con->Receive(&pkt);
969 peer_id = pkt.getPeerId();
971 } catch (const con::InvalidIncomingDataException &e) {
972 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
973 << e.what() << std::endl;
974 } catch (const SerializationError &e) {
975 infostream << "Server::Receive(): SerializationError: what()="
976 << e.what() << std::endl;
977 } catch (const ClientStateError &e) {
978 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
979 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
980 L"Try reconnecting or updating your client");
981 } catch (const con::PeerNotFoundException &e) {
986 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
988 std::string playername;
989 PlayerSAO *playersao = NULL;
992 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
994 playername = client->getName();
995 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
997 } catch (std::exception &e) {
1003 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1005 // If failed, cancel
1006 if (!playersao || !player) {
1007 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1008 actionstream << "Server: Failed to emerge player \"" << playername
1009 << "\" (player allocated to an another client)" << std::endl;
1010 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1011 L"name. If your client closed unexpectedly, try again in "
1014 errorstream << "Server: " << playername << ": Failed to emerge player"
1016 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1022 Send complete position information
1024 SendMovePlayer(peer_id);
1027 SendPlayerPrivileges(peer_id);
1029 // Send inventory formspec
1030 SendPlayerInventoryFormspec(peer_id);
1033 SendInventory(playersao);
1035 // Send HP or death screen
1036 if (playersao->isDead())
1037 SendDeathscreen(peer_id, false, v3f(0,0,0));
1039 SendPlayerHPOrDie(playersao);
1042 SendPlayerBreath(playersao);
1044 // Note things in chat if not in simple singleplayer mode
1045 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1046 // Send information about server to player in chat
1047 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1049 Address addr = getPeerAddress(player->getPeerId());
1050 std::string ip_str = addr.serializeString();
1051 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1056 const std::vector<std::string> &names = m_clients.getPlayerNames();
1058 actionstream << player->getName() << " joins game. List of players: ";
1060 for (const std::string &name : names) {
1061 actionstream << name << " ";
1064 actionstream << player->getName() <<std::endl;
1069 inline void Server::handleCommand(NetworkPacket* pkt)
1071 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1072 (this->*opHandle.handler)(pkt);
1075 void Server::ProcessData(NetworkPacket *pkt)
1077 // Environment is locked first.
1078 MutexAutoLock envlock(m_env_mutex);
1080 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1081 u32 peer_id = pkt->getPeerId();
1084 Address address = getPeerAddress(peer_id);
1085 std::string addr_s = address.serializeString();
1087 if(m_banmanager->isIpBanned(addr_s)) {
1088 std::string ban_name = m_banmanager->getBanName(addr_s);
1089 infostream << "Server: A banned client tried to connect from "
1090 << addr_s << "; banned name was "
1091 << ban_name << std::endl;
1092 // This actually doesn't seem to transfer to the client
1093 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1094 + utf8_to_wide(ban_name));
1098 catch(con::PeerNotFoundException &e) {
1100 * no peer for this packet found
1101 * most common reason is peer timeout, e.g. peer didn't
1102 * respond for some time, your server was overloaded or
1105 infostream << "Server::ProcessData(): Canceling: peer "
1106 << peer_id << " not found" << std::endl;
1111 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1113 // Command must be handled into ToServerCommandHandler
1114 if (command >= TOSERVER_NUM_MSG_TYPES) {
1115 infostream << "Server: Ignoring unknown command "
1116 << command << std::endl;
1120 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1125 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1127 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1128 errorstream << "Server::ProcessData(): Cancelling: Peer"
1129 " serialization format invalid or not initialized."
1130 " Skipping incoming command=" << command << std::endl;
1134 /* Handle commands related to client startup */
1135 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1140 if (m_clients.getClientState(peer_id) < CS_Active) {
1141 if (command == TOSERVER_PLAYERPOS) return;
1143 errorstream << "Got packet command: " << command << " for peer id "
1144 << peer_id << " but client isn't active yet. Dropping packet "
1150 } catch (SendFailedException &e) {
1151 errorstream << "Server::ProcessData(): SendFailedException: "
1152 << "what=" << e.what()
1154 } catch (PacketError &e) {
1155 actionstream << "Server::ProcessData(): PacketError: "
1156 << "what=" << e.what()
1161 void Server::setTimeOfDay(u32 time)
1163 m_env->setTimeOfDay(time);
1164 m_time_of_day_send_timer = 0;
1167 void Server::onMapEditEvent(MapEditEvent *event)
1169 if(m_ignore_map_edit_events)
1171 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1173 MapEditEvent *e = event->clone();
1174 m_unsent_map_edit_queue.push(e);
1177 Inventory* Server::getInventory(const InventoryLocation &loc)
1180 case InventoryLocation::UNDEFINED:
1181 case InventoryLocation::CURRENT_PLAYER:
1183 case InventoryLocation::PLAYER:
1185 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1188 PlayerSAO *playersao = player->getPlayerSAO();
1191 return playersao->getInventory();
1194 case InventoryLocation::NODEMETA:
1196 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1199 return meta->getInventory();
1202 case InventoryLocation::DETACHED:
1204 if(m_detached_inventories.count(loc.name) == 0)
1206 return m_detached_inventories[loc.name];
1210 sanity_check(false); // abort
1215 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1218 case InventoryLocation::UNDEFINED:
1220 case InventoryLocation::PLAYER:
1225 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1230 PlayerSAO *playersao = player->getPlayerSAO();
1234 SendInventory(playersao);
1237 case InventoryLocation::NODEMETA:
1239 v3s16 blockpos = getNodeBlockPos(loc.p);
1241 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1243 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1245 m_clients.markBlockposAsNotSent(blockpos);
1248 case InventoryLocation::DETACHED:
1250 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1254 sanity_check(false); // abort
1259 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1261 std::vector<session_t> clients = m_clients.getClientIDs();
1263 // Set the modified blocks unsent for all the clients
1264 for (const session_t client_id : clients) {
1265 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1266 client->SetBlocksNotSent(block);
1271 void Server::peerAdded(con::Peer *peer)
1273 verbosestream<<"Server::peerAdded(): peer->id="
1274 <<peer->id<<std::endl;
1276 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1279 void Server::deletingPeer(con::Peer *peer, bool timeout)
1281 verbosestream<<"Server::deletingPeer(): peer->id="
1282 <<peer->id<<", timeout="<<timeout<<std::endl;
1284 m_clients.event(peer->id, CSE_Disconnect);
1285 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1288 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1290 *retval = m_con->getPeerStat(peer_id,type);
1291 return *retval != -1;
1294 bool Server::getClientInfo(
1303 std::string* vers_string
1306 *state = m_clients.getClientState(peer_id);
1308 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1315 *uptime = client->uptime();
1316 *ser_vers = client->serialization_version;
1317 *prot_vers = client->net_proto_version;
1319 *major = client->getMajor();
1320 *minor = client->getMinor();
1321 *patch = client->getPatch();
1322 *vers_string = client->getPatch();
1329 void Server::handlePeerChanges()
1331 while(!m_peer_change_queue.empty())
1333 con::PeerChange c = m_peer_change_queue.front();
1334 m_peer_change_queue.pop();
1336 verbosestream<<"Server: Handling peer change: "
1337 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1342 case con::PEER_ADDED:
1343 m_clients.CreateClient(c.peer_id);
1346 case con::PEER_REMOVED:
1347 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1351 FATAL_ERROR("Invalid peer change event received!");
1357 void Server::printToConsoleOnly(const std::string &text)
1360 m_admin_chat->outgoing_queue.push_back(
1361 new ChatEventChat("", utf8_to_wide(text)));
1363 std::cout << text << std::endl;
1367 void Server::Send(NetworkPacket *pkt)
1369 Send(pkt->getPeerId(), pkt);
1372 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1374 m_clients.send(peer_id,
1375 clientCommandFactoryTable[pkt->getCommand()].channel,
1377 clientCommandFactoryTable[pkt->getCommand()].reliable);
1380 void Server::SendMovement(session_t peer_id)
1382 std::ostringstream os(std::ios_base::binary);
1384 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1386 pkt << g_settings->getFloat("movement_acceleration_default");
1387 pkt << g_settings->getFloat("movement_acceleration_air");
1388 pkt << g_settings->getFloat("movement_acceleration_fast");
1389 pkt << g_settings->getFloat("movement_speed_walk");
1390 pkt << g_settings->getFloat("movement_speed_crouch");
1391 pkt << g_settings->getFloat("movement_speed_fast");
1392 pkt << g_settings->getFloat("movement_speed_climb");
1393 pkt << g_settings->getFloat("movement_speed_jump");
1394 pkt << g_settings->getFloat("movement_liquid_fluidity");
1395 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1396 pkt << g_settings->getFloat("movement_liquid_sink");
1397 pkt << g_settings->getFloat("movement_gravity");
1402 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1404 if (!g_settings->getBool("enable_damage"))
1407 session_t peer_id = playersao->getPeerID();
1408 bool is_alive = playersao->getHP() > 0;
1411 SendPlayerHP(peer_id);
1416 void Server::SendHP(session_t peer_id, u16 hp)
1418 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1423 void Server::SendBreath(session_t peer_id, u16 breath)
1425 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1426 pkt << (u16) breath;
1430 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1431 const std::string &custom_reason, bool reconnect)
1433 assert(reason < SERVER_ACCESSDENIED_MAX);
1435 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1437 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1438 pkt << custom_reason;
1439 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1440 reason == SERVER_ACCESSDENIED_CRASH)
1441 pkt << custom_reason << (u8)reconnect;
1445 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1447 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1452 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1453 v3f camera_point_target)
1455 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1456 pkt << set_camera_point_target << camera_point_target;
1460 void Server::SendItemDef(session_t peer_id,
1461 IItemDefManager *itemdef, u16 protocol_version)
1463 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1467 u32 length of the next item
1468 zlib-compressed serialized ItemDefManager
1470 std::ostringstream tmp_os(std::ios::binary);
1471 itemdef->serialize(tmp_os, protocol_version);
1472 std::ostringstream tmp_os2(std::ios::binary);
1473 compressZlib(tmp_os.str(), tmp_os2);
1474 pkt.putLongString(tmp_os2.str());
1477 verbosestream << "Server: Sending item definitions to id(" << peer_id
1478 << "): size=" << pkt.getSize() << std::endl;
1483 void Server::SendNodeDef(session_t peer_id,
1484 const NodeDefManager *nodedef, u16 protocol_version)
1486 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1490 u32 length of the next item
1491 zlib-compressed serialized NodeDefManager
1493 std::ostringstream tmp_os(std::ios::binary);
1494 nodedef->serialize(tmp_os, protocol_version);
1495 std::ostringstream tmp_os2(std::ios::binary);
1496 compressZlib(tmp_os.str(), tmp_os2);
1498 pkt.putLongString(tmp_os2.str());
1501 verbosestream << "Server: Sending node definitions to id(" << peer_id
1502 << "): size=" << pkt.getSize() << std::endl;
1508 Non-static send methods
1511 void Server::SendInventory(PlayerSAO* playerSAO)
1513 UpdateCrafting(playerSAO->getPlayer());
1519 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1521 std::ostringstream os;
1522 playerSAO->getInventory()->serialize(os);
1524 std::string s = os.str();
1526 pkt.putRawString(s.c_str(), s.size());
1530 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1532 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1534 u8 type = message.type;
1535 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1537 if (peer_id != PEER_ID_INEXISTENT) {
1538 RemotePlayer *player = m_env->getPlayer(peer_id);
1544 m_clients.sendToAll(&pkt);
1548 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1549 const std::string &formname)
1551 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1552 if (formspec.empty()){
1553 //the client should close the formspec
1554 m_formspec_state_data.erase(peer_id);
1555 pkt.putLongString("");
1557 m_formspec_state_data[peer_id] = formname;
1558 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1565 // Spawns a particle on peer with peer_id
1566 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1567 v3f pos, v3f velocity, v3f acceleration,
1568 float expirationtime, float size, bool collisiondetection,
1569 bool collision_removal,
1570 bool vertical, const std::string &texture,
1571 const struct TileAnimationParams &animation, u8 glow)
1573 static thread_local const float radius =
1574 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1576 if (peer_id == PEER_ID_INEXISTENT) {
1577 std::vector<session_t> clients = m_clients.getClientIDs();
1579 for (const session_t client_id : clients) {
1580 RemotePlayer *player = m_env->getPlayer(client_id);
1584 PlayerSAO *sao = player->getPlayerSAO();
1588 // Do not send to distant clients
1589 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1592 SendSpawnParticle(client_id, player->protocol_version,
1593 pos, velocity, acceleration,
1594 expirationtime, size, collisiondetection,
1595 collision_removal, vertical, texture, animation, glow);
1600 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1602 pkt << pos << velocity << acceleration << expirationtime
1603 << size << collisiondetection;
1604 pkt.putLongString(texture);
1606 pkt << collision_removal;
1607 // This is horrible but required (why are there two ways to serialize pkts?)
1608 std::ostringstream os(std::ios_base::binary);
1609 animation.serialize(os, protocol_version);
1610 pkt.putRawString(os.str());
1616 // Adds a ParticleSpawner on peer with peer_id
1617 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1618 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1619 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1620 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1621 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1622 const struct TileAnimationParams &animation, u8 glow)
1624 if (peer_id == PEER_ID_INEXISTENT) {
1625 // This sucks and should be replaced:
1626 std::vector<session_t> clients = m_clients.getClientIDs();
1627 for (const session_t client_id : clients) {
1628 RemotePlayer *player = m_env->getPlayer(client_id);
1631 SendAddParticleSpawner(client_id, player->protocol_version,
1632 amount, spawntime, minpos, maxpos,
1633 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1634 minsize, maxsize, collisiondetection, collision_removal,
1635 attached_id, vertical, texture, id, animation, glow);
1640 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1642 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1643 << minacc << maxacc << minexptime << maxexptime << minsize
1644 << maxsize << collisiondetection;
1646 pkt.putLongString(texture);
1648 pkt << id << vertical;
1649 pkt << collision_removal;
1651 // This is horrible but required
1652 std::ostringstream os(std::ios_base::binary);
1653 animation.serialize(os, protocol_version);
1654 pkt.putRawString(os.str());
1660 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1662 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1664 // Ugly error in this packet
1667 if (peer_id != PEER_ID_INEXISTENT)
1670 m_clients.sendToAll(&pkt);
1674 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1676 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1678 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1679 << form->text << form->number << form->item << form->dir
1680 << form->align << form->offset << form->world_pos << form->size;
1685 void Server::SendHUDRemove(session_t peer_id, u32 id)
1687 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1692 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1694 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1695 pkt << id << (u8) stat;
1699 case HUD_STAT_SCALE:
1700 case HUD_STAT_ALIGN:
1701 case HUD_STAT_OFFSET:
1702 pkt << *(v2f *) value;
1706 pkt << *(std::string *) value;
1708 case HUD_STAT_WORLD_POS:
1709 pkt << *(v3f *) value;
1712 pkt << *(v2s32 *) value;
1714 case HUD_STAT_NUMBER:
1718 pkt << *(u32 *) value;
1725 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1727 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1729 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1731 pkt << flags << mask;
1736 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1738 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1739 pkt << param << value;
1743 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1744 const std::string &type, const std::vector<std::string> ¶ms,
1747 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1748 pkt << bgcolor << type << (u16) params.size();
1750 for (const std::string ¶m : params)
1758 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1760 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1761 pkt << params.density << params.color_bright << params.color_ambient
1762 << params.height << params.thickness << params.speed;
1766 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1769 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1772 pkt << do_override << (u16) (ratio * 65535);
1777 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1779 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1780 pkt << time << time_speed;
1782 if (peer_id == PEER_ID_INEXISTENT) {
1783 m_clients.sendToAll(&pkt);
1790 void Server::SendPlayerHP(session_t peer_id)
1792 PlayerSAO *playersao = getPlayerSAO(peer_id);
1793 // In some rare case if the player is disconnected
1794 // while Lua call l_punch, for example, this can be NULL
1798 SendHP(peer_id, playersao->getHP());
1799 m_script->player_event(playersao,"health_changed");
1801 // Send to other clients
1802 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1803 ActiveObjectMessage aom(playersao->getId(), true, str);
1804 playersao->m_messages_out.push(aom);
1807 void Server::SendPlayerBreath(PlayerSAO *sao)
1811 m_script->player_event(sao, "breath_changed");
1812 SendBreath(sao->getPeerID(), sao->getBreath());
1815 void Server::SendMovePlayer(session_t peer_id)
1817 RemotePlayer *player = m_env->getPlayer(peer_id);
1819 PlayerSAO *sao = player->getPlayerSAO();
1822 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1823 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1826 v3f pos = sao->getBasePosition();
1827 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1828 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1829 << " pitch=" << sao->getPitch()
1830 << " yaw=" << sao->getYaw()
1837 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1838 f32 animation_speed)
1840 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1843 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1844 << animation_frames[3] << animation_speed;
1849 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1851 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1852 pkt << first << third;
1856 void Server::SendPlayerPrivileges(session_t peer_id)
1858 RemotePlayer *player = m_env->getPlayer(peer_id);
1860 if(player->getPeerId() == PEER_ID_INEXISTENT)
1863 std::set<std::string> privs;
1864 m_script->getAuth(player->getName(), NULL, &privs);
1866 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1867 pkt << (u16) privs.size();
1869 for (const std::string &priv : privs) {
1876 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1878 RemotePlayer *player = m_env->getPlayer(peer_id);
1880 if(player->getPeerId() == PEER_ID_INEXISTENT)
1883 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1884 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1888 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1890 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1891 pkt.putRawString(datas.c_str(), datas.size());
1893 return pkt.getSize();
1896 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1899 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1900 datas.size(), peer_id);
1902 pkt.putRawString(datas.c_str(), datas.size());
1904 m_clients.send(pkt.getPeerId(),
1905 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1909 void Server::SendCSMFlavourLimits(session_t peer_id)
1911 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1912 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1913 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1917 s32 Server::playSound(const SimpleSoundSpec &spec,
1918 const ServerSoundParams ¶ms)
1920 // Find out initial position of sound
1921 bool pos_exists = false;
1922 v3f pos = params.getPos(m_env, &pos_exists);
1923 // If position is not found while it should be, cancel sound
1924 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1927 // Filter destination clients
1928 std::vector<session_t> dst_clients;
1929 if(!params.to_player.empty()) {
1930 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1932 infostream<<"Server::playSound: Player \""<<params.to_player
1933 <<"\" not found"<<std::endl;
1936 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1937 infostream<<"Server::playSound: Player \""<<params.to_player
1938 <<"\" not connected"<<std::endl;
1941 dst_clients.push_back(player->getPeerId());
1943 std::vector<session_t> clients = m_clients.getClientIDs();
1945 for (const session_t client_id : clients) {
1946 RemotePlayer *player = m_env->getPlayer(client_id);
1950 PlayerSAO *sao = player->getPlayerSAO();
1955 if(sao->getBasePosition().getDistanceFrom(pos) >
1956 params.max_hear_distance)
1959 dst_clients.push_back(client_id);
1963 if(dst_clients.empty())
1967 s32 id = m_next_sound_id++;
1968 // The sound will exist as a reference in m_playing_sounds
1969 m_playing_sounds[id] = ServerPlayingSound();
1970 ServerPlayingSound &psound = m_playing_sounds[id];
1971 psound.params = params;
1974 float gain = params.gain * spec.gain;
1975 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1976 pkt << id << spec.name << gain
1977 << (u8) params.type << pos << params.object
1978 << params.loop << params.fade << params.pitch;
1980 // Backwards compability
1981 bool play_sound = gain > 0;
1983 for (const u16 dst_client : dst_clients) {
1984 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
1985 psound.clients.insert(dst_client);
1986 m_clients.send(dst_client, 0, &pkt, true);
1991 void Server::stopSound(s32 handle)
1993 // Get sound reference
1994 std::unordered_map<s32, ServerPlayingSound>::iterator i =
1995 m_playing_sounds.find(handle);
1996 if (i == m_playing_sounds.end())
1998 ServerPlayingSound &psound = i->second;
2000 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2003 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2004 si != psound.clients.end(); ++si) {
2006 m_clients.send(*si, 0, &pkt, true);
2008 // Remove sound reference
2009 m_playing_sounds.erase(i);
2012 void Server::fadeSound(s32 handle, float step, float gain)
2014 // Get sound reference
2015 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2016 m_playing_sounds.find(handle);
2017 if (i == m_playing_sounds.end())
2020 ServerPlayingSound &psound = i->second;
2021 psound.params.gain = gain;
2023 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2024 pkt << handle << step << gain;
2026 // Backwards compability
2027 bool play_sound = gain > 0;
2028 ServerPlayingSound compat_psound = psound;
2029 compat_psound.clients.clear();
2031 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2032 compat_pkt << handle;
2034 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2035 it != psound.clients.end();) {
2036 if (m_clients.getProtocolVersion(*it) >= 32) {
2038 m_clients.send(*it, 0, &pkt, true);
2041 compat_psound.clients.insert(*it);
2043 m_clients.send(*it, 0, &compat_pkt, true);
2044 psound.clients.erase(it++);
2048 // Remove sound reference
2049 if (!play_sound || psound.clients.empty())
2050 m_playing_sounds.erase(i);
2052 if (play_sound && !compat_psound.clients.empty()) {
2053 // Play new sound volume on older clients
2054 playSound(compat_psound.spec, compat_psound.params);
2058 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2059 std::vector<u16> *far_players, float far_d_nodes)
2061 float maxd = far_d_nodes*BS;
2062 v3f p_f = intToFloat(p, BS);
2064 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2067 std::vector<session_t> clients = m_clients.getClientIDs();
2068 for (session_t client_id : clients) {
2071 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2072 PlayerSAO *sao = player->getPlayerSAO();
2076 // If player is far away, only set modified blocks not sent
2077 v3f player_pos = sao->getBasePosition();
2078 if (player_pos.getDistanceFrom(p_f) > maxd) {
2079 far_players->push_back(client_id);
2086 m_clients.send(client_id, 0, &pkt, true);
2090 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2091 std::vector<u16> *far_players, float far_d_nodes,
2092 bool remove_metadata)
2094 float maxd = far_d_nodes*BS;
2095 v3f p_f = intToFloat(p, BS);
2097 std::vector<session_t> clients = m_clients.getClientIDs();
2098 for (const session_t client_id : clients) {
2101 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2102 PlayerSAO *sao = player->getPlayerSAO();
2106 // If player is far away, only set modified blocks not sent
2107 v3f player_pos = sao->getBasePosition();
2108 if(player_pos.getDistanceFrom(p_f) > maxd) {
2109 far_players->push_back(client_id);
2115 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2117 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2119 pkt << p << n.param0 << n.param1 << n.param2
2120 << (u8) (remove_metadata ? 0 : 1);
2125 if (pkt.getSize() > 0)
2126 m_clients.send(client_id, 0, &pkt, true);
2130 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2131 u16 net_proto_version)
2134 Create a packet with the block in the right format
2137 std::ostringstream os(std::ios_base::binary);
2138 block->serialize(os, ver, false);
2139 block->serializeNetworkSpecific(os);
2140 std::string s = os.str();
2142 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2144 pkt << block->getPos();
2145 pkt.putRawString(s.c_str(), s.size());
2149 void Server::SendBlocks(float dtime)
2151 MutexAutoLock envlock(m_env_mutex);
2152 //TODO check if one big lock could be faster then multiple small ones
2154 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2156 std::vector<PrioritySortedBlockTransfer> queue;
2158 u32 total_sending = 0;
2161 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2163 std::vector<session_t> clients = m_clients.getClientIDs();
2166 for (const session_t client_id : clients) {
2167 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2172 total_sending += client->getSendingCount();
2173 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2179 // Lowest priority number comes first.
2180 // Lowest is most important.
2181 std::sort(queue.begin(), queue.end());
2185 // Maximal total count calculation
2186 // The per-client block sends is halved with the maximal online users
2187 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2188 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2190 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2191 if (total_sending >= max_blocks_to_send)
2194 MapBlock *block = nullptr;
2196 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2197 } catch (const InvalidPositionException &e) {
2201 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2206 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2207 client->net_proto_version);
2209 client->SentBlock(block_to_send.pos);
2215 void Server::fillMediaCache()
2217 infostream<<"Server: Calculating media file checksums"<<std::endl;
2219 // Collect all media file paths
2220 std::vector<std::string> paths;
2221 m_modmgr->getModsMediaPaths(paths);
2222 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2223 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2225 // Collect media file information from paths into cache
2226 for (const std::string &mediapath : paths) {
2227 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2228 for (const fs::DirListNode &dln : dirlist) {
2229 if (dln.dir) // Ignode dirs
2231 std::string filename = dln.name;
2232 // If name contains illegal characters, ignore the file
2233 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2234 infostream<<"Server: ignoring illegal file name: \""
2235 << filename << "\"" << std::endl;
2238 // If name is not in a supported format, ignore it
2239 const char *supported_ext[] = {
2240 ".png", ".jpg", ".bmp", ".tga",
2241 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2243 ".x", ".b3d", ".md2", ".obj",
2244 // Custom translation file format
2248 if (removeStringEnd(filename, supported_ext).empty()){
2249 infostream << "Server: ignoring unsupported file extension: \""
2250 << filename << "\"" << std::endl;
2253 // Ok, attempt to load the file and add to cache
2254 std::string filepath;
2255 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2258 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2260 errorstream << "Server::fillMediaCache(): Could not open \""
2261 << filename << "\" for reading" << std::endl;
2264 std::ostringstream tmp_os(std::ios_base::binary);
2268 fis.read(buf, 1024);
2269 std::streamsize len = fis.gcount();
2270 tmp_os.write(buf, len);
2279 errorstream<<"Server::fillMediaCache(): Failed to read \""
2280 << filename << "\"" << std::endl;
2283 if(tmp_os.str().length() == 0) {
2284 errorstream << "Server::fillMediaCache(): Empty file \""
2285 << filepath << "\"" << std::endl;
2290 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2292 unsigned char *digest = sha1.getDigest();
2293 std::string sha1_base64 = base64_encode(digest, 20);
2294 std::string sha1_hex = hex_encode((char*)digest, 20);
2298 m_media[filename] = MediaInfo(filepath, sha1_base64);
2299 verbosestream << "Server: " << sha1_hex << " is " << filename
2305 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2307 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2311 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2314 std::string lang_suffix;
2315 lang_suffix.append(".").append(lang_code).append(".tr");
2316 for (const auto &i : m_media) {
2317 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2324 for (const auto &i : m_media) {
2325 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2327 pkt << i.first << i.second.sha1_digest;
2330 pkt << g_settings->get("remote_media");
2334 struct SendableMedia
2340 SendableMedia(const std::string &name_="", const std::string &path_="",
2341 const std::string &data_=""):
2348 void Server::sendRequestedMedia(session_t peer_id,
2349 const std::vector<std::string> &tosend)
2351 verbosestream<<"Server::sendRequestedMedia(): "
2352 <<"Sending files to client"<<std::endl;
2356 // Put 5kB in one bunch (this is not accurate)
2357 u32 bytes_per_bunch = 5000;
2359 std::vector< std::vector<SendableMedia> > file_bunches;
2360 file_bunches.emplace_back();
2362 u32 file_size_bunch_total = 0;
2364 for (const std::string &name : tosend) {
2365 if (m_media.find(name) == m_media.end()) {
2366 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2367 <<"unknown file \""<<(name)<<"\""<<std::endl;
2371 //TODO get path + name
2372 std::string tpath = m_media[name].path;
2375 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2377 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2378 <<tpath<<"\" for reading"<<std::endl;
2381 std::ostringstream tmp_os(std::ios_base::binary);
2385 fis.read(buf, 1024);
2386 std::streamsize len = fis.gcount();
2387 tmp_os.write(buf, len);
2388 file_size_bunch_total += len;
2397 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2398 <<name<<"\""<<std::endl;
2401 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2402 <<tname<<"\""<<std::endl;*/
2404 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2406 // Start next bunch if got enough data
2407 if(file_size_bunch_total >= bytes_per_bunch) {
2408 file_bunches.emplace_back();
2409 file_size_bunch_total = 0;
2414 /* Create and send packets */
2416 u16 num_bunches = file_bunches.size();
2417 for (u16 i = 0; i < num_bunches; i++) {
2420 u16 total number of texture bunches
2421 u16 index of this bunch
2422 u32 number of files in this bunch
2431 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2432 pkt << num_bunches << i << (u32) file_bunches[i].size();
2434 for (const SendableMedia &j : file_bunches[i]) {
2436 pkt.putLongString(j.data);
2439 verbosestream << "Server::sendRequestedMedia(): bunch "
2440 << i << "/" << num_bunches
2441 << " files=" << file_bunches[i].size()
2442 << " size=" << pkt.getSize() << std::endl;
2447 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2449 if(m_detached_inventories.count(name) == 0) {
2450 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2453 Inventory *inv = m_detached_inventories[name];
2454 std::ostringstream os(std::ios_base::binary);
2456 os << serializeString(name);
2460 std::string s = os.str();
2462 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2463 pkt.putRawString(s.c_str(), s.size());
2465 const std::string &check = m_detached_inventories_player[name];
2466 if (peer_id == PEER_ID_INEXISTENT) {
2468 return m_clients.sendToAll(&pkt);
2469 RemotePlayer *p = m_env->getPlayer(check.c_str());
2471 m_clients.send(p->getPeerId(), 0, &pkt, true);
2473 if (check.empty() || getPlayerName(peer_id) == check)
2478 void Server::sendDetachedInventories(session_t peer_id)
2480 for (const auto &detached_inventory : m_detached_inventories) {
2481 const std::string &name = detached_inventory.first;
2482 //Inventory *inv = i->second;
2483 sendDetachedInventory(name, peer_id);
2491 void Server::DiePlayer(session_t peer_id)
2493 PlayerSAO *playersao = getPlayerSAO(peer_id);
2494 // In some rare cases this can be NULL -- if the player is disconnected
2495 // when a Lua function modifies l_punch, for example
2499 infostream << "Server::DiePlayer(): Player "
2500 << playersao->getPlayer()->getName()
2501 << " dies" << std::endl;
2503 playersao->setHP(0);
2505 // Trigger scripted stuff
2506 m_script->on_dieplayer(playersao);
2508 SendPlayerHP(peer_id);
2509 SendDeathscreen(peer_id, false, v3f(0,0,0));
2512 void Server::RespawnPlayer(session_t peer_id)
2514 PlayerSAO *playersao = getPlayerSAO(peer_id);
2517 infostream << "Server::RespawnPlayer(): Player "
2518 << playersao->getPlayer()->getName()
2519 << " respawns" << std::endl;
2521 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2522 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2524 bool repositioned = m_script->on_respawnplayer(playersao);
2525 if (!repositioned) {
2526 // setPos will send the new position to client
2527 playersao->setPos(findSpawnPos());
2530 SendPlayerHP(peer_id);
2534 void Server::DenySudoAccess(session_t peer_id)
2536 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2541 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2542 const std::string &str_reason, bool reconnect)
2544 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2546 m_clients.event(peer_id, CSE_SetDenied);
2547 DisconnectPeer(peer_id);
2551 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2552 const std::string &custom_reason)
2554 SendAccessDenied(peer_id, reason, custom_reason);
2555 m_clients.event(peer_id, CSE_SetDenied);
2556 DisconnectPeer(peer_id);
2559 // 13/03/15: remove this function when protocol version 25 will become
2560 // the minimum version for MT users, maybe in 1 year
2561 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2563 SendAccessDenied_Legacy(peer_id, reason);
2564 m_clients.event(peer_id, CSE_SetDenied);
2565 DisconnectPeer(peer_id);
2568 void Server::DisconnectPeer(session_t peer_id)
2570 m_modchannel_mgr->leaveAllChannels(peer_id);
2571 m_con->DisconnectPeer(peer_id);
2574 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2577 RemoteClient* client = getClient(peer_id, CS_Invalid);
2579 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2581 // Right now, the auth mechs don't change between login and sudo mode.
2582 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2583 client->allowed_sudo_mechs = sudo_auth_mechs;
2585 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2586 << g_settings->getFloat("dedicated_server_step")
2590 m_clients.event(peer_id, CSE_AuthAccept);
2592 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2594 // We only support SRP right now
2595 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2597 resp_pkt << sudo_auth_mechs;
2599 m_clients.event(peer_id, CSE_SudoSuccess);
2603 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2605 std::wstring message;
2608 Clear references to playing sounds
2610 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2611 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2612 ServerPlayingSound &psound = i->second;
2613 psound.clients.erase(peer_id);
2614 if (psound.clients.empty())
2615 m_playing_sounds.erase(i++);
2620 // clear formspec info so the next client can't abuse the current state
2621 m_formspec_state_data.erase(peer_id);
2623 RemotePlayer *player = m_env->getPlayer(peer_id);
2625 /* Run scripts and remove from environment */
2627 PlayerSAO *playersao = player->getPlayerSAO();
2630 // inform connected clients
2631 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2632 // (u16) 1 + std::string represents a vector serialization representation
2633 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2634 m_clients.sendToAll(¬ice);
2636 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2638 playersao->disconnected();
2645 if (player && reason != CDR_DENY) {
2646 std::ostringstream os(std::ios_base::binary);
2647 std::vector<session_t> clients = m_clients.getClientIDs();
2649 for (const session_t client_id : clients) {
2651 RemotePlayer *player = m_env->getPlayer(client_id);
2655 // Get name of player
2656 os << player->getName() << " ";
2659 std::string name = player->getName();
2660 actionstream << name << " "
2661 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2662 << " List of players: " << os.str() << std::endl;
2664 m_admin_chat->outgoing_queue.push_back(
2665 new ChatEventNick(CET_NICK_REMOVE, name));
2669 MutexAutoLock env_lock(m_env_mutex);
2670 m_clients.DeleteClient(peer_id);
2674 // Send leave chat message to all remaining clients
2675 if (!message.empty()) {
2676 SendChatMessage(PEER_ID_INEXISTENT,
2677 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2681 void Server::UpdateCrafting(RemotePlayer *player)
2683 // Get a preview for crafting
2685 InventoryLocation loc;
2686 loc.setPlayer(player->getName());
2687 std::vector<ItemStack> output_replacements;
2688 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2689 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2690 (&player->inventory)->getList("craft"), loc);
2692 // Put the new preview in
2693 InventoryList *plist = player->inventory.getList("craftpreview");
2694 sanity_check(plist);
2695 sanity_check(plist->getSize() >= 1);
2696 plist->changeItem(0, preview);
2699 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2701 if (evt->type == CET_NICK_ADD) {
2702 // The terminal informed us of its nick choice
2703 m_admin_nick = ((ChatEventNick *)evt)->nick;
2704 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2705 errorstream << "You haven't set up an account." << std::endl
2706 << "Please log in using the client as '"
2707 << m_admin_nick << "' with a secure password." << std::endl
2708 << "Until then, you can't execute admin tasks via the console," << std::endl
2709 << "and everybody can claim the user account instead of you," << std::endl
2710 << "giving them full control over this server." << std::endl;
2713 assert(evt->type == CET_CHAT);
2714 handleAdminChat((ChatEventChat *)evt);
2718 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2719 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2721 // If something goes wrong, this player is to blame
2722 RollbackScopeActor rollback_scope(m_rollback,
2723 std::string("player:") + name);
2725 if (g_settings->getBool("strip_color_codes"))
2726 wmessage = unescape_enriched(wmessage);
2729 switch (player->canSendChatMessage()) {
2730 case RPLAYER_CHATRESULT_FLOODING: {
2731 std::wstringstream ws;
2732 ws << L"You cannot send more messages. You are limited to "
2733 << g_settings->getFloat("chat_message_limit_per_10sec")
2734 << L" messages per 10 seconds.";
2737 case RPLAYER_CHATRESULT_KICK:
2738 DenyAccess_Legacy(player->getPeerId(),
2739 L"You have been kicked due to message flooding.");
2741 case RPLAYER_CHATRESULT_OK:
2744 FATAL_ERROR("Unhandled chat filtering result found.");
2748 if (m_max_chatmessage_length > 0
2749 && wmessage.length() > m_max_chatmessage_length) {
2750 return L"Your message exceed the maximum chat message limit set on the server. "
2751 L"It was refused. Send a shorter message";
2754 // Run script hook, exit if script ate the chat message
2755 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2760 // Whether to send line to the player that sent the message, or to all players
2761 bool broadcast_line = true;
2763 if (check_shout_priv && !checkPriv(name, "shout")) {
2764 line += L"-!- You don't have permission to shout.";
2765 broadcast_line = false;
2774 Tell calling method to send the message to sender
2776 if (!broadcast_line)
2780 Send the message to others
2782 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2784 std::vector<session_t> clients = m_clients.getClientIDs();
2787 Send the message back to the inital sender
2788 if they are using protocol version >= 29
2791 session_t peer_id_to_avoid_sending =
2792 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2794 if (player && player->protocol_version >= 29)
2795 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2797 for (u16 cid : clients) {
2798 if (cid != peer_id_to_avoid_sending)
2799 SendChatMessage(cid, ChatMessage(line));
2804 void Server::handleAdminChat(const ChatEventChat *evt)
2806 std::string name = evt->nick;
2807 std::wstring wname = utf8_to_wide(name);
2808 std::wstring wmessage = evt->evt_msg;
2810 std::wstring answer = handleChat(name, wname, wmessage);
2812 // If asked to send answer to sender
2813 if (!answer.empty()) {
2814 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2818 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2820 RemoteClient *client = getClientNoEx(peer_id,state_min);
2822 throw ClientNotFoundException("Client not found");
2826 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2828 return m_clients.getClientNoEx(peer_id, state_min);
2831 std::string Server::getPlayerName(session_t peer_id)
2833 RemotePlayer *player = m_env->getPlayer(peer_id);
2835 return "[id="+itos(peer_id)+"]";
2836 return player->getName();
2839 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2841 RemotePlayer *player = m_env->getPlayer(peer_id);
2844 return player->getPlayerSAO();
2847 std::wstring Server::getStatusString()
2849 std::wostringstream os(std::ios_base::binary);
2852 os<<L"version="<<narrow_to_wide(g_version_string);
2854 os<<L", uptime="<<m_uptime.get();
2856 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2857 // Information about clients
2860 std::vector<session_t> clients = m_clients.getClientIDs();
2861 for (session_t client_id : clients) {
2863 RemotePlayer *player = m_env->getPlayer(client_id);
2864 // Get name of player
2865 std::wstring name = L"unknown";
2867 name = narrow_to_wide(player->getName());
2868 // Add name to information string
2877 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2878 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2880 if (!g_settings->get("motd").empty())
2881 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2885 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2887 std::set<std::string> privs;
2888 m_script->getAuth(name, NULL, &privs);
2892 bool Server::checkPriv(const std::string &name, const std::string &priv)
2894 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2895 return (privs.count(priv) != 0);
2898 void Server::reportPrivsModified(const std::string &name)
2901 std::vector<session_t> clients = m_clients.getClientIDs();
2902 for (const session_t client_id : clients) {
2903 RemotePlayer *player = m_env->getPlayer(client_id);
2904 reportPrivsModified(player->getName());
2907 RemotePlayer *player = m_env->getPlayer(name.c_str());
2910 SendPlayerPrivileges(player->getPeerId());
2911 PlayerSAO *sao = player->getPlayerSAO();
2914 sao->updatePrivileges(
2915 getPlayerEffectivePrivs(name),
2920 void Server::reportInventoryFormspecModified(const std::string &name)
2922 RemotePlayer *player = m_env->getPlayer(name.c_str());
2925 SendPlayerInventoryFormspec(player->getPeerId());
2928 void Server::setIpBanned(const std::string &ip, const std::string &name)
2930 m_banmanager->add(ip, name);
2933 void Server::unsetIpBanned(const std::string &ip_or_name)
2935 m_banmanager->remove(ip_or_name);
2938 std::string Server::getBanDescription(const std::string &ip_or_name)
2940 return m_banmanager->getBanDescription(ip_or_name);
2943 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2945 // m_env will be NULL if the server is initializing
2949 if (m_admin_nick == name && !m_admin_nick.empty()) {
2950 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2953 RemotePlayer *player = m_env->getPlayer(name);
2958 if (player->getPeerId() == PEER_ID_INEXISTENT)
2961 SendChatMessage(player->getPeerId(), ChatMessage(msg));
2964 bool Server::showFormspec(const char *playername, const std::string &formspec,
2965 const std::string &formname)
2967 // m_env will be NULL if the server is initializing
2971 RemotePlayer *player = m_env->getPlayer(playername);
2975 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
2979 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
2984 u32 id = player->addHud(form);
2986 SendHUDAdd(player->getPeerId(), id, form);
2991 bool Server::hudRemove(RemotePlayer *player, u32 id) {
2995 HudElement* todel = player->removeHud(id);
3002 SendHUDRemove(player->getPeerId(), id);
3006 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3011 SendHUDChange(player->getPeerId(), id, stat, data);
3015 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3020 SendHUDSetFlags(player->getPeerId(), flags, mask);
3021 player->hud_flags &= ~mask;
3022 player->hud_flags |= flags;
3024 PlayerSAO* playersao = player->getPlayerSAO();
3029 m_script->player_event(playersao, "hud_changed");
3033 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3038 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3041 player->setHotbarItemcount(hotbar_itemcount);
3042 std::ostringstream os(std::ios::binary);
3043 writeS32(os, hotbar_itemcount);
3044 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3048 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3053 player->setHotbarImage(name);
3054 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3057 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3062 player->setHotbarSelectedImage(name);
3063 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3066 Address Server::getPeerAddress(session_t peer_id)
3068 return m_con->GetPeerAddress(peer_id);
3071 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3072 v2s32 animation_frames[4], f32 frame_speed)
3074 sanity_check(player);
3075 player->setLocalAnimations(animation_frames, frame_speed);
3076 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3079 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3081 sanity_check(player);
3082 player->eye_offset_first = first;
3083 player->eye_offset_third = third;
3084 SendEyeOffset(player->getPeerId(), first, third);
3087 void Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3088 const std::string &type, const std::vector<std::string> ¶ms,
3091 sanity_check(player);
3092 player->setSky(bgcolor, type, params, clouds);
3093 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3096 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3098 sanity_check(player);
3099 player->setCloudParams(params);
3100 SendCloudParams(player->getPeerId(), params);
3103 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3109 player->overrideDayNightRatio(do_override, ratio);
3110 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3114 void Server::notifyPlayers(const std::wstring &msg)
3116 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3119 void Server::spawnParticle(const std::string &playername, v3f pos,
3120 v3f velocity, v3f acceleration,
3121 float expirationtime, float size, bool
3122 collisiondetection, bool collision_removal,
3123 bool vertical, const std::string &texture,
3124 const struct TileAnimationParams &animation, u8 glow)
3126 // m_env will be NULL if the server is initializing
3130 session_t peer_id = PEER_ID_INEXISTENT;
3132 if (!playername.empty()) {
3133 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3136 peer_id = player->getPeerId();
3137 proto_ver = player->protocol_version;
3140 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3141 expirationtime, size, collisiondetection,
3142 collision_removal, vertical, texture, animation, glow);
3145 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3146 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3147 float minexptime, float maxexptime, float minsize, float maxsize,
3148 bool collisiondetection, bool collision_removal,
3149 ServerActiveObject *attached, bool vertical, const std::string &texture,
3150 const std::string &playername, const struct TileAnimationParams &animation,
3153 // m_env will be NULL if the server is initializing
3157 session_t peer_id = PEER_ID_INEXISTENT;
3159 if (!playername.empty()) {
3160 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3163 peer_id = player->getPeerId();
3164 proto_ver = player->protocol_version;
3167 u16 attached_id = attached ? attached->getId() : 0;
3170 if (attached_id == 0)
3171 id = m_env->addParticleSpawner(spawntime);
3173 id = m_env->addParticleSpawner(spawntime, attached_id);
3175 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3176 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3177 minexptime, maxexptime, minsize, maxsize,
3178 collisiondetection, collision_removal, attached_id, vertical,
3179 texture, id, animation, glow);
3184 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3186 // m_env will be NULL if the server is initializing
3188 throw ServerError("Can't delete particle spawners during initialisation!");
3190 session_t peer_id = PEER_ID_INEXISTENT;
3191 if (!playername.empty()) {
3192 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3195 peer_id = player->getPeerId();
3198 m_env->deleteParticleSpawner(id);
3199 SendDeleteParticleSpawner(peer_id, id);
3202 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3204 if(m_detached_inventories.count(name) > 0){
3205 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3206 delete m_detached_inventories[name];
3208 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3210 Inventory *inv = new Inventory(m_itemdef);
3212 m_detached_inventories[name] = inv;
3213 m_detached_inventories_player[name] = player;
3214 //TODO find a better way to do this
3215 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3219 // actions: time-reversed list
3220 // Return value: success/failure
3221 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3222 std::list<std::string> *log)
3224 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3225 ServerMap *map = (ServerMap*)(&m_env->getMap());
3227 // Fail if no actions to handle
3228 if(actions.empty()){
3229 log->push_back("Nothing to do.");
3236 for (const RollbackAction &action : actions) {
3238 bool success = action.applyRevert(map, this, this);
3241 std::ostringstream os;
3242 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3243 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3245 log->push_back(os.str());
3247 std::ostringstream os;
3248 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3249 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3251 log->push_back(os.str());
3255 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3256 <<" failed"<<std::endl;
3258 // Call it done if less than half failed
3259 return num_failed <= num_tried/2;
3262 // IGameDef interface
3264 IItemDefManager *Server::getItemDefManager()
3269 const NodeDefManager *Server::getNodeDefManager()
3274 ICraftDefManager *Server::getCraftDefManager()
3279 u16 Server::allocateUnknownNodeId(const std::string &name)
3281 return m_nodedef->allocateDummy(name);
3284 MtEventManager *Server::getEventManager()
3289 IWritableItemDefManager *Server::getWritableItemDefManager()
3294 NodeDefManager *Server::getWritableNodeDefManager()
3299 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3304 const std::vector<ModSpec> & Server::getMods() const
3306 return m_modmgr->getMods();
3309 const ModSpec *Server::getModSpec(const std::string &modname) const
3311 return m_modmgr->getModSpec(modname);
3314 void Server::getModNames(std::vector<std::string> &modlist)
3316 m_modmgr->getModNames(modlist);
3319 std::string Server::getBuiltinLuaPath()
3321 return porting::path_share + DIR_DELIM + "builtin";
3324 std::string Server::getModStoragePath() const
3326 return m_path_world + DIR_DELIM + "mod_storage";
3329 v3f Server::findSpawnPos()
3331 ServerMap &map = m_env->getServerMap();
3333 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3334 return nodeposf * BS;
3337 bool is_good = false;
3338 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3339 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3341 // Try to find a good place a few times
3342 for(s32 i = 0; i < 4000 && !is_good; i++) {
3343 s32 range = MYMIN(1 + i, range_max);
3344 // We're going to try to throw the player to this position
3345 v2s16 nodepos2d = v2s16(
3346 -range + (myrand() % (range * 2)),
3347 -range + (myrand() % (range * 2)));
3349 // Get spawn level at point
3350 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3351 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3352 // the mapgen to signify an unsuitable spawn position
3353 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3356 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3359 for (s32 i = 0; i < 10; i++) {
3360 v3s16 blockpos = getNodeBlockPos(nodepos);
3361 map.emergeBlock(blockpos, true);
3362 content_t c = map.getNodeNoEx(nodepos).getContent();
3363 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3365 if (air_count >= 2) {
3366 nodeposf = intToFloat(nodepos, BS);
3367 // Don't spawn the player outside map boundaries
3368 if (objectpos_over_limit(nodeposf))
3381 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3383 m_shutdown_timer = delay;
3384 m_shutdown_msg = msg;
3385 m_shutdown_ask_reconnect = reconnect;
3387 if (delay == 0.0f) {
3388 // No delay, shutdown immediately
3389 m_shutdown_requested = true;
3390 // only print to the infostream, a chat message saying
3391 // "Server Shutting Down" is sent when the server destructs.
3392 infostream << "*** Immediate Server shutdown requested." << std::endl;
3393 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3394 // Negative delay, cancel shutdown if requested
3395 m_shutdown_timer = 0.0f;
3396 m_shutdown_msg = "";
3397 m_shutdown_ask_reconnect = false;
3398 m_shutdown_requested = false;
3399 std::wstringstream ws;
3401 ws << L"*** Server shutdown canceled.";
3403 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3404 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3405 } else if (delay > 0.0f) {
3406 // Positive delay, tell the clients when the server will shut down
3407 std::wstringstream ws;
3409 ws << L"*** Server shutting down in "
3410 << duration_to_string(myround(m_shutdown_timer)).c_str()
3413 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3414 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3418 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3421 Try to get an existing player
3423 RemotePlayer *player = m_env->getPlayer(name);
3425 // If player is already connected, cancel
3426 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3427 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3432 If player with the wanted peer_id already exists, cancel.
3434 if (m_env->getPlayer(peer_id)) {
3435 infostream<<"emergePlayer(): Player with wrong name but same"
3436 " peer_id already exists"<<std::endl;
3441 player = new RemotePlayer(name, idef());
3444 bool newplayer = false;
3447 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3449 // Complete init with server parts
3450 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3451 player->protocol_version = proto_version;
3455 m_script->on_newplayer(playersao);
3461 bool Server::registerModStorage(ModMetadata *storage)
3463 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3464 errorstream << "Unable to register same mod storage twice. Storage name: "
3465 << storage->getModName() << std::endl;
3469 m_mod_storages[storage->getModName()] = storage;
3473 void Server::unregisterModStorage(const std::string &name)
3475 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3476 if (it != m_mod_storages.end()) {
3477 // Save unconditionaly on unregistration
3478 it->second->save(getModStoragePath());
3479 m_mod_storages.erase(name);
3483 void dedicated_server_loop(Server &server, bool &kill)
3485 verbosestream<<"dedicated_server_loop()"<<std::endl;
3487 IntervalLimiter m_profiler_interval;
3489 static thread_local const float steplen =
3490 g_settings->getFloat("dedicated_server_step");
3491 static thread_local const float profiler_print_interval =
3492 g_settings->getFloat("profiler_print_interval");
3495 // This is kind of a hack but can be done like this
3496 // because server.step() is very light
3498 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3499 sleep_ms((int)(steplen*1000.0));
3501 server.step(steplen);
3503 if (server.getShutdownRequested() || kill)
3509 if (profiler_print_interval != 0) {
3510 if(m_profiler_interval.step(steplen, profiler_print_interval))
3512 infostream<<"Profiler:"<<std::endl;
3513 g_profiler->print(infostream);
3514 g_profiler->clear();
3519 infostream << "Dedicated server quitting" << std::endl;
3521 if (g_settings->getBool("server_announce"))
3522 ServerList::sendAnnounce(ServerList::AA_DELETE,
3523 server.m_bind_addr.getPort());
3532 bool Server::joinModChannel(const std::string &channel)
3534 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3535 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3538 bool Server::leaveModChannel(const std::string &channel)
3540 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3543 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3545 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3548 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3552 ModChannel* Server::getModChannel(const std::string &channel)
3554 return m_modchannel_mgr->getModChannel(channel);
3557 void Server::broadcastModChannelMessage(const std::string &channel,
3558 const std::string &message, session_t from_peer)
3560 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3564 if (message.size() > STRING_MAX_LEN) {
3565 warningstream << "ModChannel message too long, dropping before sending "
3566 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3567 << channel << ")" << std::endl;
3572 if (from_peer != PEER_ID_SERVER) {
3573 sender = getPlayerName(from_peer);
3576 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3577 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3578 resp_pkt << channel << sender << message;
3579 for (session_t peer_id : peers) {
3581 if (peer_id == from_peer)
3584 Send(peer_id, &resp_pkt);
3587 if (from_peer != PEER_ID_SERVER) {
3588 m_script->on_modchannel_message(channel, sender, message);