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_mods = m_modmgr->getMods();
230 m_modmgr->loadMods(m_script);
232 // Read Textures and calculate sha1 sums
235 // Apply item aliases in the node definition manager
236 m_nodedef->updateAliases(m_itemdef);
238 // Apply texture overrides from texturepack/override.txt
239 std::vector<std::string> paths;
240 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
241 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
242 for (const std::string &path : paths)
243 m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
245 m_nodedef->setNodeRegistrationStatus(true);
247 // Perform pending node name resolutions
248 m_nodedef->runNodeResolveCallbacks();
250 // unmap node names for connected nodeboxes
251 m_nodedef->mapNodeboxConnections();
253 // init the recipe hashes to speed up crafting
254 m_craftdef->initHashes(this);
256 // Initialize Environment
257 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
259 m_clients.setEnv(m_env);
261 if (!servermap->settings_mgr.makeMapgenParams())
262 FATAL_ERROR("Couldn't create any mapgen type");
264 // Initialize mapgens
265 m_emerge->initMapgens(servermap->getMapgenParams());
267 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
268 if (m_enable_rollback_recording) {
269 // Create rollback manager
270 m_rollback = new RollbackManager(m_path_world, this);
273 // Give environment reference to scripting api
274 m_script->initializeEnvironment(m_env);
276 // Register us to receive map edit events
277 servermap->addEventReceiver(this);
279 // If file exists, load environment metadata
280 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
281 infostream << "Server: Loading environment metadata" << std::endl;
284 m_env->loadDefaultMeta();
287 m_liquid_transform_every = g_settings->getFloat("liquid_update");
288 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
289 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
290 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
295 infostream << "Server destructing" << std::endl;
297 // Send shutdown message
298 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
299 L"*** Server shutting down"));
302 MutexAutoLock envlock(m_env_mutex);
304 infostream << "Server: Saving players" << std::endl;
305 m_env->saveLoadedPlayers();
307 infostream << "Server: Kicking players" << std::endl;
308 std::string kick_msg;
309 bool reconnect = false;
310 if (getShutdownRequested()) {
311 reconnect = m_shutdown_ask_reconnect;
312 kick_msg = m_shutdown_msg;
314 if (kick_msg.empty()) {
315 kick_msg = g_settings->get("kick_msg_shutdown");
317 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
318 kick_msg, reconnect);
321 // Do this before stopping the server in case mapgen callbacks need to access
322 // server-controlled resources (like ModStorages). Also do them before
323 // shutdown callbacks since they may modify state that is finalized in a
325 m_emerge->stopThreads();
328 MutexAutoLock envlock(m_env_mutex);
330 // Execute script shutdown hooks
331 infostream << "Executing shutdown hooks" << std::endl;
332 m_script->on_shutdown();
334 infostream << "Server: Saving environment metadata" << std::endl;
342 // Delete things in the reverse order of creation
352 // Deinitialize scripting
353 infostream << "Server: Deinitializing scripting" << std::endl;
356 // Delete detached inventories
357 for (auto &detached_inventory : m_detached_inventories) {
358 delete detached_inventory.second;
364 infostream << "Starting server on " << m_bind_addr.serializeString()
365 << "..." << std::endl;
367 // Stop thread if already running
370 // Initialize connection
371 m_con->SetTimeoutMs(30);
372 m_con->Serve(m_bind_addr);
377 // ASCII art for the win!
379 << " .__ __ __ " << std::endl
380 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
381 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
382 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
383 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
384 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
385 actionstream << "World at [" << m_path_world << "]" << std::endl;
386 actionstream << "Server for gameid=\"" << m_gamespec.id
387 << "\" listening on " << m_bind_addr.serializeString() << ":"
388 << m_bind_addr.getPort() << "." << std::endl;
393 infostream<<"Server: Stopping and waiting threads"<<std::endl;
395 // Stop threads (set run=false first so both start stopping)
397 //m_emergethread.setRun(false);
399 //m_emergethread.stop();
401 infostream<<"Server: Threads stopped"<<std::endl;
404 void Server::step(float dtime)
410 MutexAutoLock lock(m_step_dtime_mutex);
411 m_step_dtime += dtime;
413 // Throw if fatal error occurred in thread
414 std::string async_err = m_async_fatal_error.get();
415 if (!async_err.empty()) {
416 if (!m_simple_singleplayer_mode) {
417 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
418 g_settings->get("kick_msg_crash"),
419 g_settings->getBool("ask_reconnect_on_crash"));
421 throw ServerError("AsyncErr: " + async_err);
425 void Server::AsyncRunStep(bool initial_step)
427 g_profiler->add("Server::AsyncRunStep (num)", 1);
431 MutexAutoLock lock1(m_step_dtime_mutex);
432 dtime = m_step_dtime;
436 // Send blocks to clients
440 if((dtime < 0.001) && !initial_step)
443 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
445 //infostream<<"Server steps "<<dtime<<std::endl;
446 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
449 MutexAutoLock lock1(m_step_dtime_mutex);
450 m_step_dtime -= dtime;
457 m_uptime.set(m_uptime.get() + dtime);
463 Update time of day and overall game time
465 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
468 Send to clients at constant intervals
471 m_time_of_day_send_timer -= dtime;
472 if(m_time_of_day_send_timer < 0.0) {
473 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
474 u16 time = m_env->getTimeOfDay();
475 float time_speed = g_settings->getFloat("time_speed");
476 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
480 MutexAutoLock lock(m_env_mutex);
481 // Figure out and report maximum lag to environment
482 float max_lag = m_env->getMaxLagEstimate();
483 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
485 if(dtime > 0.1 && dtime > max_lag * 2.0)
486 infostream<<"Server: Maximum lag peaked to "<<dtime
490 m_env->reportMaxLagEstimate(max_lag);
492 ScopeProfiler sp(g_profiler, "SEnv step");
493 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
497 static const float map_timer_and_unload_dtime = 2.92;
498 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
500 MutexAutoLock lock(m_env_mutex);
501 // Run Map's timers and unload unused data
502 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
503 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
504 g_settings->getFloat("server_unload_unused_data_timeout"),
509 Listen to the admin chat, if available
512 if (!m_admin_chat->command_queue.empty()) {
513 MutexAutoLock lock(m_env_mutex);
514 while (!m_admin_chat->command_queue.empty()) {
515 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
516 handleChatInterfaceEvent(evt);
520 m_admin_chat->outgoing_queue.push_back(
521 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
528 /* Transform liquids */
529 m_liquid_transform_timer += dtime;
530 if(m_liquid_transform_timer >= m_liquid_transform_every)
532 m_liquid_transform_timer -= m_liquid_transform_every;
534 MutexAutoLock lock(m_env_mutex);
536 ScopeProfiler sp(g_profiler, "Server: liquid transform");
538 std::map<v3s16, MapBlock*> modified_blocks;
539 m_env->getMap().transformLiquids(modified_blocks, m_env);
542 Set the modified blocks unsent for all the clients
544 if (!modified_blocks.empty()) {
545 SetBlocksNotSent(modified_blocks);
548 m_clients.step(dtime);
550 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
552 // send masterserver announce
554 float &counter = m_masterserver_timer;
555 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
556 g_settings->getBool("server_announce")) {
557 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
558 ServerList::AA_START,
559 m_bind_addr.getPort(),
560 m_clients.getPlayerNames(),
562 m_env->getGameTime(),
565 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
575 Check added and deleted active objects
578 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
579 MutexAutoLock envlock(m_env_mutex);
582 const RemoteClientMap &clients = m_clients.getClientList();
583 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
585 // Radius inside which objects are active
586 static thread_local const s16 radius =
587 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
589 // Radius inside which players are active
590 static thread_local const bool is_transfer_limited =
591 g_settings->exists("unlimited_player_transfer_distance") &&
592 !g_settings->getBool("unlimited_player_transfer_distance");
593 static thread_local const s16 player_transfer_dist =
594 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
595 s16 player_radius = player_transfer_dist;
596 if (player_radius == 0 && is_transfer_limited)
597 player_radius = radius;
599 for (const auto &client_it : clients) {
600 RemoteClient *client = client_it.second;
602 // If definitions and textures have not been sent, don't
603 // send objects either
604 if (client->getState() < CS_DefinitionsSent)
607 RemotePlayer *player = m_env->getPlayer(client->peer_id);
609 // This can happen if the client timeouts somehow
613 PlayerSAO *playersao = player->getPlayerSAO();
617 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
618 if (my_radius <= 0) my_radius = radius;
619 //infostream << "Server: Active Radius " << my_radius << std::endl;
621 std::queue<u16> removed_objects;
622 std::queue<u16> added_objects;
623 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
624 client->m_known_objects, removed_objects);
625 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
626 client->m_known_objects, added_objects);
628 // Ignore if nothing happened
629 if (removed_objects.empty() && added_objects.empty()) {
633 std::string data_buffer;
637 // Handle removed objects
638 writeU16((u8*)buf, removed_objects.size());
639 data_buffer.append(buf, 2);
640 while (!removed_objects.empty()) {
642 u16 id = removed_objects.front();
643 ServerActiveObject* obj = m_env->getActiveObject(id);
645 // Add to data buffer for sending
646 writeU16((u8*)buf, id);
647 data_buffer.append(buf, 2);
649 // Remove from known objects
650 client->m_known_objects.erase(id);
652 if(obj && obj->m_known_by_count > 0)
653 obj->m_known_by_count--;
654 removed_objects.pop();
657 // Handle added objects
658 writeU16((u8*)buf, added_objects.size());
659 data_buffer.append(buf, 2);
660 while (!added_objects.empty()) {
662 u16 id = added_objects.front();
663 ServerActiveObject* obj = m_env->getActiveObject(id);
666 u8 type = ACTIVEOBJECT_TYPE_INVALID;
668 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
670 type = obj->getSendType();
672 // Add to data buffer for sending
673 writeU16((u8*)buf, id);
674 data_buffer.append(buf, 2);
675 writeU8((u8*)buf, type);
676 data_buffer.append(buf, 1);
679 data_buffer.append(serializeLongString(
680 obj->getClientInitializationData(client->net_proto_version)));
682 data_buffer.append(serializeLongString(""));
684 // Add to known objects
685 client->m_known_objects.insert(id);
688 obj->m_known_by_count++;
693 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
694 verbosestream << "Server: Sent object remove/add: "
695 << removed_objects.size() << " removed, "
696 << added_objects.size() << " added, "
697 << "packet size is " << pktSize << std::endl;
701 m_mod_storage_save_timer -= dtime;
702 if (m_mod_storage_save_timer <= 0.0f) {
703 infostream << "Saving registered mod storages." << std::endl;
704 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
705 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
706 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
707 if (it->second->isModified()) {
708 it->second->save(getModStoragePath());
718 MutexAutoLock envlock(m_env_mutex);
719 ScopeProfiler sp(g_profiler, "Server: sending object messages");
722 // Value = data sent by object
723 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
725 // Get active object messages from environment
727 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
731 std::vector<ActiveObjectMessage>* message_list = nullptr;
732 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
733 n = buffered_messages.find(aom.id);
734 if (n == buffered_messages.end()) {
735 message_list = new std::vector<ActiveObjectMessage>;
736 buffered_messages[aom.id] = message_list;
739 message_list = n->second;
741 message_list->push_back(aom);
745 const RemoteClientMap &clients = m_clients.getClientList();
746 // Route data to every client
747 for (const auto &client_it : clients) {
748 RemoteClient *client = client_it.second;
749 std::string reliable_data;
750 std::string unreliable_data;
751 // Go through all objects in message buffer
752 for (const auto &buffered_message : buffered_messages) {
753 // If object is not known by client, skip it
754 u16 id = buffered_message.first;
755 if (client->m_known_objects.find(id) == client->m_known_objects.end())
758 // Get message list of object
759 std::vector<ActiveObjectMessage>* list = buffered_message.second;
760 // Go through every message
761 for (const ActiveObjectMessage &aom : *list) {
762 // Compose the full new data with header
763 std::string new_data;
766 writeU16((u8*)&buf[0], aom.id);
767 new_data.append(buf, 2);
769 new_data += serializeString(aom.datastring);
770 // Add data to buffer
772 reliable_data += new_data;
774 unreliable_data += new_data;
778 reliable_data and unreliable_data are now ready.
781 if (!reliable_data.empty()) {
782 SendActiveObjectMessages(client->peer_id, reliable_data);
785 if (!unreliable_data.empty()) {
786 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
791 // Clear buffered_messages
792 for (auto &buffered_message : buffered_messages) {
793 delete buffered_message.second;
798 Send queued-for-sending map edit events.
801 // We will be accessing the environment
802 MutexAutoLock lock(m_env_mutex);
804 // Don't send too many at a time
807 // Single change sending is disabled if queue size is not small
808 bool disable_single_change_sending = false;
809 if(m_unsent_map_edit_queue.size() >= 4)
810 disable_single_change_sending = true;
812 int event_count = m_unsent_map_edit_queue.size();
814 // We'll log the amount of each
817 while (!m_unsent_map_edit_queue.empty()) {
818 MapEditEvent* event = m_unsent_map_edit_queue.front();
819 m_unsent_map_edit_queue.pop();
821 // Players far away from the change are stored here.
822 // Instead of sending the changes, MapBlocks are set not sent
824 std::vector<u16> far_players;
826 switch (event->type) {
829 prof.add("MEET_ADDNODE", 1);
830 sendAddNode(event->p, event->n, event->already_known_by_peer,
831 &far_players, disable_single_change_sending ? 5 : 30,
832 event->type == MEET_ADDNODE);
834 case MEET_REMOVENODE:
835 prof.add("MEET_REMOVENODE", 1);
836 sendRemoveNode(event->p, event->already_known_by_peer,
837 &far_players, disable_single_change_sending ? 5 : 30);
839 case MEET_BLOCK_NODE_METADATA_CHANGED:
840 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
841 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
842 m_clients.markBlockposAsNotSent(event->p);
845 infostream << "Server: MEET_OTHER" << std::endl;
846 prof.add("MEET_OTHER", 1);
847 for (const v3s16 &modified_block : event->modified_blocks) {
848 m_clients.markBlockposAsNotSent(modified_block);
852 prof.add("unknown", 1);
853 warningstream << "Server: Unknown MapEditEvent "
854 << ((u32)event->type) << std::endl;
859 Set blocks not sent to far players
861 if (!far_players.empty()) {
862 // Convert list format to that wanted by SetBlocksNotSent
863 std::map<v3s16, MapBlock*> modified_blocks2;
864 for (const v3s16 &modified_block : event->modified_blocks) {
865 modified_blocks2[modified_block] =
866 m_env->getMap().getBlockNoCreateNoEx(modified_block);
869 // Set blocks not sent
870 for (const u16 far_player : far_players) {
871 if (RemoteClient *client = getClient(far_player))
872 client->SetBlocksNotSent(modified_blocks2);
879 if (event_count >= 5) {
880 infostream << "Server: MapEditEvents:" << std::endl;
881 prof.print(infostream);
882 } else if (event_count != 0) {
883 verbosestream << "Server: MapEditEvents:" << std::endl;
884 prof.print(verbosestream);
890 Trigger emergethread (it somehow gets to a non-triggered but
891 bysy state sometimes)
894 float &counter = m_emergethread_trigger_timer;
896 if (counter >= 2.0) {
899 m_emerge->startThreads();
903 // Save map, players and auth stuff
905 float &counter = m_savemap_timer;
907 static thread_local const float save_interval =
908 g_settings->getFloat("server_map_save_interval");
909 if (counter >= save_interval) {
911 MutexAutoLock lock(m_env_mutex);
913 ScopeProfiler sp(g_profiler, "Server: saving stuff");
916 if (m_banmanager->isModified()) {
917 m_banmanager->save();
920 // Save changed parts of map
921 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
924 m_env->saveLoadedPlayers();
926 // Save environment metadata
932 static const float shutdown_msg_times[] =
934 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
937 if (m_shutdown_timer > 0.0f) {
938 // Automated messages
939 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
940 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
941 // If shutdown timer matches an automessage, shot it
942 if (m_shutdown_timer > shutdown_msg_times[i] &&
943 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
944 std::wstringstream ws;
946 ws << L"*** Server shutting down in "
947 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
950 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
951 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
957 m_shutdown_timer -= dtime;
958 if (m_shutdown_timer < 0.0f) {
959 m_shutdown_timer = 0.0f;
960 m_shutdown_requested = true;
965 void Server::Receive()
970 m_con->Receive(&pkt);
971 peer_id = pkt.getPeerId();
973 } catch (const con::InvalidIncomingDataException &e) {
974 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
975 << e.what() << std::endl;
976 } catch (const SerializationError &e) {
977 infostream << "Server::Receive(): SerializationError: what()="
978 << e.what() << std::endl;
979 } catch (const ClientStateError &e) {
980 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
981 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
982 L"Try reconnecting or updating your client");
983 } catch (const con::PeerNotFoundException &e) {
988 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
990 std::string playername;
991 PlayerSAO *playersao = NULL;
994 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
996 playername = client->getName();
997 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
999 } catch (std::exception &e) {
1005 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1007 // If failed, cancel
1008 if (!playersao || !player) {
1009 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1010 actionstream << "Server: Failed to emerge player \"" << playername
1011 << "\" (player allocated to an another client)" << std::endl;
1012 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1013 L"name. If your client closed unexpectedly, try again in "
1016 errorstream << "Server: " << playername << ": Failed to emerge player"
1018 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1024 Send complete position information
1026 SendMovePlayer(peer_id);
1029 SendPlayerPrivileges(peer_id);
1031 // Send inventory formspec
1032 SendPlayerInventoryFormspec(peer_id);
1035 SendInventory(playersao);
1037 // Send HP or death screen
1038 if (playersao->isDead())
1039 SendDeathscreen(peer_id, false, v3f(0,0,0));
1041 SendPlayerHPOrDie(playersao);
1044 SendPlayerBreath(playersao);
1046 // Note things in chat if not in simple singleplayer mode
1047 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1048 // Send information about server to player in chat
1049 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1051 Address addr = getPeerAddress(player->getPeerId());
1052 std::string ip_str = addr.serializeString();
1053 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1058 const std::vector<std::string> &names = m_clients.getPlayerNames();
1060 actionstream << player->getName() << " joins game. List of players: ";
1062 for (const std::string &name : names) {
1063 actionstream << name << " ";
1066 actionstream << player->getName() <<std::endl;
1071 inline void Server::handleCommand(NetworkPacket* pkt)
1073 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1074 (this->*opHandle.handler)(pkt);
1077 void Server::ProcessData(NetworkPacket *pkt)
1079 // Environment is locked first.
1080 MutexAutoLock envlock(m_env_mutex);
1082 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1083 u32 peer_id = pkt->getPeerId();
1086 Address address = getPeerAddress(peer_id);
1087 std::string addr_s = address.serializeString();
1089 if(m_banmanager->isIpBanned(addr_s)) {
1090 std::string ban_name = m_banmanager->getBanName(addr_s);
1091 infostream << "Server: A banned client tried to connect from "
1092 << addr_s << "; banned name was "
1093 << ban_name << std::endl;
1094 // This actually doesn't seem to transfer to the client
1095 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1096 + utf8_to_wide(ban_name));
1100 catch(con::PeerNotFoundException &e) {
1102 * no peer for this packet found
1103 * most common reason is peer timeout, e.g. peer didn't
1104 * respond for some time, your server was overloaded or
1107 infostream << "Server::ProcessData(): Canceling: peer "
1108 << peer_id << " not found" << std::endl;
1113 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1115 // Command must be handled into ToServerCommandHandler
1116 if (command >= TOSERVER_NUM_MSG_TYPES) {
1117 infostream << "Server: Ignoring unknown command "
1118 << command << std::endl;
1122 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1127 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1129 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1130 errorstream << "Server::ProcessData(): Cancelling: Peer"
1131 " serialization format invalid or not initialized."
1132 " Skipping incoming command=" << command << std::endl;
1136 /* Handle commands related to client startup */
1137 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1142 if (m_clients.getClientState(peer_id) < CS_Active) {
1143 if (command == TOSERVER_PLAYERPOS) return;
1145 errorstream << "Got packet command: " << command << " for peer id "
1146 << peer_id << " but client isn't active yet. Dropping packet "
1152 } catch (SendFailedException &e) {
1153 errorstream << "Server::ProcessData(): SendFailedException: "
1154 << "what=" << e.what()
1156 } catch (PacketError &e) {
1157 actionstream << "Server::ProcessData(): PacketError: "
1158 << "what=" << e.what()
1163 void Server::setTimeOfDay(u32 time)
1165 m_env->setTimeOfDay(time);
1166 m_time_of_day_send_timer = 0;
1169 void Server::onMapEditEvent(MapEditEvent *event)
1171 if(m_ignore_map_edit_events)
1173 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1175 MapEditEvent *e = event->clone();
1176 m_unsent_map_edit_queue.push(e);
1179 Inventory* Server::getInventory(const InventoryLocation &loc)
1182 case InventoryLocation::UNDEFINED:
1183 case InventoryLocation::CURRENT_PLAYER:
1185 case InventoryLocation::PLAYER:
1187 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1190 PlayerSAO *playersao = player->getPlayerSAO();
1193 return playersao->getInventory();
1196 case InventoryLocation::NODEMETA:
1198 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1201 return meta->getInventory();
1204 case InventoryLocation::DETACHED:
1206 if(m_detached_inventories.count(loc.name) == 0)
1208 return m_detached_inventories[loc.name];
1212 sanity_check(false); // abort
1217 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1220 case InventoryLocation::UNDEFINED:
1222 case InventoryLocation::PLAYER:
1227 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1232 PlayerSAO *playersao = player->getPlayerSAO();
1236 SendInventory(playersao);
1239 case InventoryLocation::NODEMETA:
1241 v3s16 blockpos = getNodeBlockPos(loc.p);
1243 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1245 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1247 m_clients.markBlockposAsNotSent(blockpos);
1250 case InventoryLocation::DETACHED:
1252 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1256 sanity_check(false); // abort
1261 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1263 std::vector<session_t> clients = m_clients.getClientIDs();
1265 // Set the modified blocks unsent for all the clients
1266 for (const session_t client_id : clients) {
1267 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1268 client->SetBlocksNotSent(block);
1273 void Server::peerAdded(con::Peer *peer)
1275 verbosestream<<"Server::peerAdded(): peer->id="
1276 <<peer->id<<std::endl;
1278 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1281 void Server::deletingPeer(con::Peer *peer, bool timeout)
1283 verbosestream<<"Server::deletingPeer(): peer->id="
1284 <<peer->id<<", timeout="<<timeout<<std::endl;
1286 m_clients.event(peer->id, CSE_Disconnect);
1287 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1290 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1292 *retval = m_con->getPeerStat(peer_id,type);
1293 return *retval != -1;
1296 bool Server::getClientInfo(
1305 std::string* vers_string
1308 *state = m_clients.getClientState(peer_id);
1310 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1317 *uptime = client->uptime();
1318 *ser_vers = client->serialization_version;
1319 *prot_vers = client->net_proto_version;
1321 *major = client->getMajor();
1322 *minor = client->getMinor();
1323 *patch = client->getPatch();
1324 *vers_string = client->getPatch();
1331 void Server::handlePeerChanges()
1333 while(!m_peer_change_queue.empty())
1335 con::PeerChange c = m_peer_change_queue.front();
1336 m_peer_change_queue.pop();
1338 verbosestream<<"Server: Handling peer change: "
1339 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1344 case con::PEER_ADDED:
1345 m_clients.CreateClient(c.peer_id);
1348 case con::PEER_REMOVED:
1349 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1353 FATAL_ERROR("Invalid peer change event received!");
1359 void Server::printToConsoleOnly(const std::string &text)
1362 m_admin_chat->outgoing_queue.push_back(
1363 new ChatEventChat("", utf8_to_wide(text)));
1365 std::cout << text << std::endl;
1369 void Server::Send(NetworkPacket *pkt)
1371 Send(pkt->getPeerId(), pkt);
1374 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1376 m_clients.send(peer_id,
1377 clientCommandFactoryTable[pkt->getCommand()].channel,
1379 clientCommandFactoryTable[pkt->getCommand()].reliable);
1382 void Server::SendMovement(session_t peer_id)
1384 std::ostringstream os(std::ios_base::binary);
1386 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1388 pkt << g_settings->getFloat("movement_acceleration_default");
1389 pkt << g_settings->getFloat("movement_acceleration_air");
1390 pkt << g_settings->getFloat("movement_acceleration_fast");
1391 pkt << g_settings->getFloat("movement_speed_walk");
1392 pkt << g_settings->getFloat("movement_speed_crouch");
1393 pkt << g_settings->getFloat("movement_speed_fast");
1394 pkt << g_settings->getFloat("movement_speed_climb");
1395 pkt << g_settings->getFloat("movement_speed_jump");
1396 pkt << g_settings->getFloat("movement_liquid_fluidity");
1397 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1398 pkt << g_settings->getFloat("movement_liquid_sink");
1399 pkt << g_settings->getFloat("movement_gravity");
1404 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1406 if (!g_settings->getBool("enable_damage"))
1409 session_t peer_id = playersao->getPeerID();
1410 bool is_alive = playersao->getHP() > 0;
1413 SendPlayerHP(peer_id);
1418 void Server::SendHP(session_t peer_id, u16 hp)
1420 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1425 void Server::SendBreath(session_t peer_id, u16 breath)
1427 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1428 pkt << (u16) breath;
1432 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1433 const std::string &custom_reason, bool reconnect)
1435 assert(reason < SERVER_ACCESSDENIED_MAX);
1437 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1439 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1440 pkt << custom_reason;
1441 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1442 reason == SERVER_ACCESSDENIED_CRASH)
1443 pkt << custom_reason << (u8)reconnect;
1447 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1449 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1454 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1455 v3f camera_point_target)
1457 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1458 pkt << set_camera_point_target << camera_point_target;
1462 void Server::SendItemDef(session_t peer_id,
1463 IItemDefManager *itemdef, u16 protocol_version)
1465 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1469 u32 length of the next item
1470 zlib-compressed serialized ItemDefManager
1472 std::ostringstream tmp_os(std::ios::binary);
1473 itemdef->serialize(tmp_os, protocol_version);
1474 std::ostringstream tmp_os2(std::ios::binary);
1475 compressZlib(tmp_os.str(), tmp_os2);
1476 pkt.putLongString(tmp_os2.str());
1479 verbosestream << "Server: Sending item definitions to id(" << peer_id
1480 << "): size=" << pkt.getSize() << std::endl;
1485 void Server::SendNodeDef(session_t peer_id,
1486 const NodeDefManager *nodedef, u16 protocol_version)
1488 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1492 u32 length of the next item
1493 zlib-compressed serialized NodeDefManager
1495 std::ostringstream tmp_os(std::ios::binary);
1496 nodedef->serialize(tmp_os, protocol_version);
1497 std::ostringstream tmp_os2(std::ios::binary);
1498 compressZlib(tmp_os.str(), tmp_os2);
1500 pkt.putLongString(tmp_os2.str());
1503 verbosestream << "Server: Sending node definitions to id(" << peer_id
1504 << "): size=" << pkt.getSize() << std::endl;
1510 Non-static send methods
1513 void Server::SendInventory(PlayerSAO* playerSAO)
1515 UpdateCrafting(playerSAO->getPlayer());
1521 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1523 std::ostringstream os;
1524 playerSAO->getInventory()->serialize(os);
1526 std::string s = os.str();
1528 pkt.putRawString(s.c_str(), s.size());
1532 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1534 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1536 u8 type = message.type;
1537 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1539 if (peer_id != PEER_ID_INEXISTENT) {
1540 RemotePlayer *player = m_env->getPlayer(peer_id);
1546 m_clients.sendToAll(&pkt);
1550 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1551 const std::string &formname)
1553 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1554 if (formspec.empty()){
1555 //the client should close the formspec
1556 m_formspec_state_data.erase(peer_id);
1557 pkt.putLongString("");
1559 m_formspec_state_data[peer_id] = formname;
1560 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1567 // Spawns a particle on peer with peer_id
1568 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1569 v3f pos, v3f velocity, v3f acceleration,
1570 float expirationtime, float size, bool collisiondetection,
1571 bool collision_removal,
1572 bool vertical, const std::string &texture,
1573 const struct TileAnimationParams &animation, u8 glow)
1575 static thread_local const float radius =
1576 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1578 if (peer_id == PEER_ID_INEXISTENT) {
1579 std::vector<session_t> clients = m_clients.getClientIDs();
1581 for (const session_t client_id : clients) {
1582 RemotePlayer *player = m_env->getPlayer(client_id);
1586 PlayerSAO *sao = player->getPlayerSAO();
1590 // Do not send to distant clients
1591 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1594 SendSpawnParticle(client_id, player->protocol_version,
1595 pos, velocity, acceleration,
1596 expirationtime, size, collisiondetection,
1597 collision_removal, vertical, texture, animation, glow);
1602 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1604 pkt << pos << velocity << acceleration << expirationtime
1605 << size << collisiondetection;
1606 pkt.putLongString(texture);
1608 pkt << collision_removal;
1609 // This is horrible but required (why are there two ways to serialize pkts?)
1610 std::ostringstream os(std::ios_base::binary);
1611 animation.serialize(os, protocol_version);
1612 pkt.putRawString(os.str());
1618 // Adds a ParticleSpawner on peer with peer_id
1619 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1620 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1621 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1622 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1623 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1624 const struct TileAnimationParams &animation, u8 glow)
1626 if (peer_id == PEER_ID_INEXISTENT) {
1627 // This sucks and should be replaced:
1628 std::vector<session_t> clients = m_clients.getClientIDs();
1629 for (const session_t client_id : clients) {
1630 RemotePlayer *player = m_env->getPlayer(client_id);
1633 SendAddParticleSpawner(client_id, player->protocol_version,
1634 amount, spawntime, minpos, maxpos,
1635 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1636 minsize, maxsize, collisiondetection, collision_removal,
1637 attached_id, vertical, texture, id, animation, glow);
1642 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1644 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1645 << minacc << maxacc << minexptime << maxexptime << minsize
1646 << maxsize << collisiondetection;
1648 pkt.putLongString(texture);
1650 pkt << id << vertical;
1651 pkt << collision_removal;
1653 // This is horrible but required
1654 std::ostringstream os(std::ios_base::binary);
1655 animation.serialize(os, protocol_version);
1656 pkt.putRawString(os.str());
1662 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1664 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1666 // Ugly error in this packet
1669 if (peer_id != PEER_ID_INEXISTENT)
1672 m_clients.sendToAll(&pkt);
1676 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1678 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1680 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1681 << form->text << form->number << form->item << form->dir
1682 << form->align << form->offset << form->world_pos << form->size;
1687 void Server::SendHUDRemove(session_t peer_id, u32 id)
1689 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1694 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1696 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1697 pkt << id << (u8) stat;
1701 case HUD_STAT_SCALE:
1702 case HUD_STAT_ALIGN:
1703 case HUD_STAT_OFFSET:
1704 pkt << *(v2f *) value;
1708 pkt << *(std::string *) value;
1710 case HUD_STAT_WORLD_POS:
1711 pkt << *(v3f *) value;
1714 pkt << *(v2s32 *) value;
1716 case HUD_STAT_NUMBER:
1720 pkt << *(u32 *) value;
1727 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1729 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1731 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1733 pkt << flags << mask;
1738 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1740 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1741 pkt << param << value;
1745 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1746 const std::string &type, const std::vector<std::string> ¶ms,
1749 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1750 pkt << bgcolor << type << (u16) params.size();
1752 for (const std::string ¶m : params)
1760 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1762 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1763 pkt << params.density << params.color_bright << params.color_ambient
1764 << params.height << params.thickness << params.speed;
1768 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1771 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1774 pkt << do_override << (u16) (ratio * 65535);
1779 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1781 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1782 pkt << time << time_speed;
1784 if (peer_id == PEER_ID_INEXISTENT) {
1785 m_clients.sendToAll(&pkt);
1792 void Server::SendPlayerHP(session_t peer_id)
1794 PlayerSAO *playersao = getPlayerSAO(peer_id);
1795 // In some rare case if the player is disconnected
1796 // while Lua call l_punch, for example, this can be NULL
1800 SendHP(peer_id, playersao->getHP());
1801 m_script->player_event(playersao,"health_changed");
1803 // Send to other clients
1804 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1805 ActiveObjectMessage aom(playersao->getId(), true, str);
1806 playersao->m_messages_out.push(aom);
1809 void Server::SendPlayerBreath(PlayerSAO *sao)
1813 m_script->player_event(sao, "breath_changed");
1814 SendBreath(sao->getPeerID(), sao->getBreath());
1817 void Server::SendMovePlayer(session_t peer_id)
1819 RemotePlayer *player = m_env->getPlayer(peer_id);
1821 PlayerSAO *sao = player->getPlayerSAO();
1824 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1825 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1828 v3f pos = sao->getBasePosition();
1829 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1830 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1831 << " pitch=" << sao->getPitch()
1832 << " yaw=" << sao->getYaw()
1839 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1840 f32 animation_speed)
1842 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1845 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1846 << animation_frames[3] << animation_speed;
1851 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1853 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1854 pkt << first << third;
1858 void Server::SendPlayerPrivileges(session_t peer_id)
1860 RemotePlayer *player = m_env->getPlayer(peer_id);
1862 if(player->getPeerId() == PEER_ID_INEXISTENT)
1865 std::set<std::string> privs;
1866 m_script->getAuth(player->getName(), NULL, &privs);
1868 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1869 pkt << (u16) privs.size();
1871 for (const std::string &priv : privs) {
1878 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1880 RemotePlayer *player = m_env->getPlayer(peer_id);
1882 if(player->getPeerId() == PEER_ID_INEXISTENT)
1885 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1886 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1890 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1892 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1893 pkt.putRawString(datas.c_str(), datas.size());
1895 return pkt.getSize();
1898 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1901 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1902 datas.size(), peer_id);
1904 pkt.putRawString(datas.c_str(), datas.size());
1906 m_clients.send(pkt.getPeerId(),
1907 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1911 void Server::SendCSMFlavourLimits(session_t peer_id)
1913 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1914 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1915 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1919 s32 Server::playSound(const SimpleSoundSpec &spec,
1920 const ServerSoundParams ¶ms)
1922 // Find out initial position of sound
1923 bool pos_exists = false;
1924 v3f pos = params.getPos(m_env, &pos_exists);
1925 // If position is not found while it should be, cancel sound
1926 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1929 // Filter destination clients
1930 std::vector<session_t> dst_clients;
1931 if(!params.to_player.empty()) {
1932 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1934 infostream<<"Server::playSound: Player \""<<params.to_player
1935 <<"\" not found"<<std::endl;
1938 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1939 infostream<<"Server::playSound: Player \""<<params.to_player
1940 <<"\" not connected"<<std::endl;
1943 dst_clients.push_back(player->getPeerId());
1945 std::vector<session_t> clients = m_clients.getClientIDs();
1947 for (const session_t client_id : clients) {
1948 RemotePlayer *player = m_env->getPlayer(client_id);
1952 PlayerSAO *sao = player->getPlayerSAO();
1957 if(sao->getBasePosition().getDistanceFrom(pos) >
1958 params.max_hear_distance)
1961 dst_clients.push_back(client_id);
1965 if(dst_clients.empty())
1969 s32 id = m_next_sound_id++;
1970 // The sound will exist as a reference in m_playing_sounds
1971 m_playing_sounds[id] = ServerPlayingSound();
1972 ServerPlayingSound &psound = m_playing_sounds[id];
1973 psound.params = params;
1976 float gain = params.gain * spec.gain;
1977 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1978 pkt << id << spec.name << gain
1979 << (u8) params.type << pos << params.object
1980 << params.loop << params.fade << params.pitch;
1982 // Backwards compability
1983 bool play_sound = gain > 0;
1985 for (const u16 dst_client : dst_clients) {
1986 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
1987 psound.clients.insert(dst_client);
1988 m_clients.send(dst_client, 0, &pkt, true);
1993 void Server::stopSound(s32 handle)
1995 // Get sound reference
1996 std::unordered_map<s32, ServerPlayingSound>::iterator i =
1997 m_playing_sounds.find(handle);
1998 if (i == m_playing_sounds.end())
2000 ServerPlayingSound &psound = i->second;
2002 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2005 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2006 si != psound.clients.end(); ++si) {
2008 m_clients.send(*si, 0, &pkt, true);
2010 // Remove sound reference
2011 m_playing_sounds.erase(i);
2014 void Server::fadeSound(s32 handle, float step, float gain)
2016 // Get sound reference
2017 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2018 m_playing_sounds.find(handle);
2019 if (i == m_playing_sounds.end())
2022 ServerPlayingSound &psound = i->second;
2023 psound.params.gain = gain;
2025 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2026 pkt << handle << step << gain;
2028 // Backwards compability
2029 bool play_sound = gain > 0;
2030 ServerPlayingSound compat_psound = psound;
2031 compat_psound.clients.clear();
2033 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2034 compat_pkt << handle;
2036 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2037 it != psound.clients.end();) {
2038 if (m_clients.getProtocolVersion(*it) >= 32) {
2040 m_clients.send(*it, 0, &pkt, true);
2043 compat_psound.clients.insert(*it);
2045 m_clients.send(*it, 0, &compat_pkt, true);
2046 psound.clients.erase(it++);
2050 // Remove sound reference
2051 if (!play_sound || psound.clients.empty())
2052 m_playing_sounds.erase(i);
2054 if (play_sound && !compat_psound.clients.empty()) {
2055 // Play new sound volume on older clients
2056 playSound(compat_psound.spec, compat_psound.params);
2060 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2061 std::vector<u16> *far_players, float far_d_nodes)
2063 float maxd = far_d_nodes*BS;
2064 v3f p_f = intToFloat(p, BS);
2066 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2069 std::vector<session_t> clients = m_clients.getClientIDs();
2070 for (session_t client_id : clients) {
2073 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2074 PlayerSAO *sao = player->getPlayerSAO();
2078 // If player is far away, only set modified blocks not sent
2079 v3f player_pos = sao->getBasePosition();
2080 if (player_pos.getDistanceFrom(p_f) > maxd) {
2081 far_players->push_back(client_id);
2088 m_clients.send(client_id, 0, &pkt, true);
2092 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2093 std::vector<u16> *far_players, float far_d_nodes,
2094 bool remove_metadata)
2096 float maxd = far_d_nodes*BS;
2097 v3f p_f = intToFloat(p, BS);
2099 std::vector<session_t> clients = m_clients.getClientIDs();
2100 for (const session_t client_id : clients) {
2103 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2104 PlayerSAO *sao = player->getPlayerSAO();
2108 // If player is far away, only set modified blocks not sent
2109 v3f player_pos = sao->getBasePosition();
2110 if(player_pos.getDistanceFrom(p_f) > maxd) {
2111 far_players->push_back(client_id);
2117 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2119 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2121 pkt << p << n.param0 << n.param1 << n.param2
2122 << (u8) (remove_metadata ? 0 : 1);
2127 if (pkt.getSize() > 0)
2128 m_clients.send(client_id, 0, &pkt, true);
2132 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2133 u16 net_proto_version)
2136 Create a packet with the block in the right format
2139 std::ostringstream os(std::ios_base::binary);
2140 block->serialize(os, ver, false);
2141 block->serializeNetworkSpecific(os);
2142 std::string s = os.str();
2144 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2146 pkt << block->getPos();
2147 pkt.putRawString(s.c_str(), s.size());
2151 void Server::SendBlocks(float dtime)
2153 MutexAutoLock envlock(m_env_mutex);
2154 //TODO check if one big lock could be faster then multiple small ones
2156 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2158 std::vector<PrioritySortedBlockTransfer> queue;
2160 u32 total_sending = 0;
2163 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2165 std::vector<session_t> clients = m_clients.getClientIDs();
2168 for (const session_t client_id : clients) {
2169 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2174 total_sending += client->getSendingCount();
2175 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2181 // Lowest priority number comes first.
2182 // Lowest is most important.
2183 std::sort(queue.begin(), queue.end());
2187 // Maximal total count calculation
2188 // The per-client block sends is halved with the maximal online users
2189 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2190 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2192 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2193 if (total_sending >= max_blocks_to_send)
2196 MapBlock *block = nullptr;
2198 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2199 } catch (const InvalidPositionException &e) {
2203 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2208 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2209 client->net_proto_version);
2211 client->SentBlock(block_to_send.pos);
2217 void Server::fillMediaCache()
2219 infostream<<"Server: Calculating media file checksums"<<std::endl;
2221 // Collect all media file paths
2222 std::vector<std::string> paths;
2223 m_modmgr->getModsMediaPaths(paths);
2224 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2225 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2227 // Collect media file information from paths into cache
2228 for (const std::string &mediapath : paths) {
2229 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2230 for (const fs::DirListNode &dln : dirlist) {
2231 if (dln.dir) // Ignode dirs
2233 std::string filename = dln.name;
2234 // If name contains illegal characters, ignore the file
2235 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2236 infostream<<"Server: ignoring illegal file name: \""
2237 << filename << "\"" << std::endl;
2240 // If name is not in a supported format, ignore it
2241 const char *supported_ext[] = {
2242 ".png", ".jpg", ".bmp", ".tga",
2243 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2245 ".x", ".b3d", ".md2", ".obj",
2246 // Custom translation file format
2250 if (removeStringEnd(filename, supported_ext).empty()){
2251 infostream << "Server: ignoring unsupported file extension: \""
2252 << filename << "\"" << std::endl;
2255 // Ok, attempt to load the file and add to cache
2256 std::string filepath;
2257 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2260 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2262 errorstream << "Server::fillMediaCache(): Could not open \""
2263 << filename << "\" for reading" << std::endl;
2266 std::ostringstream tmp_os(std::ios_base::binary);
2270 fis.read(buf, 1024);
2271 std::streamsize len = fis.gcount();
2272 tmp_os.write(buf, len);
2281 errorstream<<"Server::fillMediaCache(): Failed to read \""
2282 << filename << "\"" << std::endl;
2285 if(tmp_os.str().length() == 0) {
2286 errorstream << "Server::fillMediaCache(): Empty file \""
2287 << filepath << "\"" << std::endl;
2292 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2294 unsigned char *digest = sha1.getDigest();
2295 std::string sha1_base64 = base64_encode(digest, 20);
2296 std::string sha1_hex = hex_encode((char*)digest, 20);
2300 m_media[filename] = MediaInfo(filepath, sha1_base64);
2301 verbosestream << "Server: " << sha1_hex << " is " << filename
2307 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2309 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2313 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2316 std::string lang_suffix;
2317 lang_suffix.append(".").append(lang_code).append(".tr");
2318 for (const auto &i : m_media) {
2319 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2326 for (const auto &i : m_media) {
2327 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2329 pkt << i.first << i.second.sha1_digest;
2332 pkt << g_settings->get("remote_media");
2336 struct SendableMedia
2342 SendableMedia(const std::string &name_="", const std::string &path_="",
2343 const std::string &data_=""):
2350 void Server::sendRequestedMedia(session_t peer_id,
2351 const std::vector<std::string> &tosend)
2353 verbosestream<<"Server::sendRequestedMedia(): "
2354 <<"Sending files to client"<<std::endl;
2358 // Put 5kB in one bunch (this is not accurate)
2359 u32 bytes_per_bunch = 5000;
2361 std::vector< std::vector<SendableMedia> > file_bunches;
2362 file_bunches.emplace_back();
2364 u32 file_size_bunch_total = 0;
2366 for (const std::string &name : tosend) {
2367 if (m_media.find(name) == m_media.end()) {
2368 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2369 <<"unknown file \""<<(name)<<"\""<<std::endl;
2373 //TODO get path + name
2374 std::string tpath = m_media[name].path;
2377 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2379 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2380 <<tpath<<"\" for reading"<<std::endl;
2383 std::ostringstream tmp_os(std::ios_base::binary);
2387 fis.read(buf, 1024);
2388 std::streamsize len = fis.gcount();
2389 tmp_os.write(buf, len);
2390 file_size_bunch_total += len;
2399 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2400 <<name<<"\""<<std::endl;
2403 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2404 <<tname<<"\""<<std::endl;*/
2406 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2408 // Start next bunch if got enough data
2409 if(file_size_bunch_total >= bytes_per_bunch) {
2410 file_bunches.emplace_back();
2411 file_size_bunch_total = 0;
2416 /* Create and send packets */
2418 u16 num_bunches = file_bunches.size();
2419 for (u16 i = 0; i < num_bunches; i++) {
2422 u16 total number of texture bunches
2423 u16 index of this bunch
2424 u32 number of files in this bunch
2433 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2434 pkt << num_bunches << i << (u32) file_bunches[i].size();
2436 for (const SendableMedia &j : file_bunches[i]) {
2438 pkt.putLongString(j.data);
2441 verbosestream << "Server::sendRequestedMedia(): bunch "
2442 << i << "/" << num_bunches
2443 << " files=" << file_bunches[i].size()
2444 << " size=" << pkt.getSize() << std::endl;
2449 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2451 if(m_detached_inventories.count(name) == 0) {
2452 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2455 Inventory *inv = m_detached_inventories[name];
2456 std::ostringstream os(std::ios_base::binary);
2458 os << serializeString(name);
2462 std::string s = os.str();
2464 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2465 pkt.putRawString(s.c_str(), s.size());
2467 const std::string &check = m_detached_inventories_player[name];
2468 if (peer_id == PEER_ID_INEXISTENT) {
2470 return m_clients.sendToAll(&pkt);
2471 RemotePlayer *p = m_env->getPlayer(check.c_str());
2473 m_clients.send(p->getPeerId(), 0, &pkt, true);
2475 if (check.empty() || getPlayerName(peer_id) == check)
2480 void Server::sendDetachedInventories(session_t peer_id)
2482 for (const auto &detached_inventory : m_detached_inventories) {
2483 const std::string &name = detached_inventory.first;
2484 //Inventory *inv = i->second;
2485 sendDetachedInventory(name, peer_id);
2493 void Server::DiePlayer(session_t peer_id)
2495 PlayerSAO *playersao = getPlayerSAO(peer_id);
2496 // In some rare cases this can be NULL -- if the player is disconnected
2497 // when a Lua function modifies l_punch, for example
2501 infostream << "Server::DiePlayer(): Player "
2502 << playersao->getPlayer()->getName()
2503 << " dies" << std::endl;
2505 playersao->setHP(0);
2507 // Trigger scripted stuff
2508 m_script->on_dieplayer(playersao);
2510 SendPlayerHP(peer_id);
2511 SendDeathscreen(peer_id, false, v3f(0,0,0));
2514 void Server::RespawnPlayer(session_t peer_id)
2516 PlayerSAO *playersao = getPlayerSAO(peer_id);
2519 infostream << "Server::RespawnPlayer(): Player "
2520 << playersao->getPlayer()->getName()
2521 << " respawns" << std::endl;
2523 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2524 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2526 bool repositioned = m_script->on_respawnplayer(playersao);
2527 if (!repositioned) {
2528 // setPos will send the new position to client
2529 playersao->setPos(findSpawnPos());
2532 SendPlayerHP(peer_id);
2536 void Server::DenySudoAccess(session_t peer_id)
2538 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2543 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2544 const std::string &str_reason, bool reconnect)
2546 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2548 m_clients.event(peer_id, CSE_SetDenied);
2549 DisconnectPeer(peer_id);
2553 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2554 const std::string &custom_reason)
2556 SendAccessDenied(peer_id, reason, custom_reason);
2557 m_clients.event(peer_id, CSE_SetDenied);
2558 DisconnectPeer(peer_id);
2561 // 13/03/15: remove this function when protocol version 25 will become
2562 // the minimum version for MT users, maybe in 1 year
2563 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2565 SendAccessDenied_Legacy(peer_id, reason);
2566 m_clients.event(peer_id, CSE_SetDenied);
2567 DisconnectPeer(peer_id);
2570 void Server::DisconnectPeer(session_t peer_id)
2572 m_modchannel_mgr->leaveAllChannels(peer_id);
2573 m_con->DisconnectPeer(peer_id);
2576 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2579 RemoteClient* client = getClient(peer_id, CS_Invalid);
2581 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2583 // Right now, the auth mechs don't change between login and sudo mode.
2584 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2585 client->allowed_sudo_mechs = sudo_auth_mechs;
2587 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2588 << g_settings->getFloat("dedicated_server_step")
2592 m_clients.event(peer_id, CSE_AuthAccept);
2594 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2596 // We only support SRP right now
2597 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2599 resp_pkt << sudo_auth_mechs;
2601 m_clients.event(peer_id, CSE_SudoSuccess);
2605 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2607 std::wstring message;
2610 Clear references to playing sounds
2612 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2613 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2614 ServerPlayingSound &psound = i->second;
2615 psound.clients.erase(peer_id);
2616 if (psound.clients.empty())
2617 m_playing_sounds.erase(i++);
2622 // clear formspec info so the next client can't abuse the current state
2623 m_formspec_state_data.erase(peer_id);
2625 RemotePlayer *player = m_env->getPlayer(peer_id);
2627 /* Run scripts and remove from environment */
2629 PlayerSAO *playersao = player->getPlayerSAO();
2632 // inform connected clients
2633 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2634 // (u16) 1 + std::string represents a vector serialization representation
2635 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2636 m_clients.sendToAll(¬ice);
2638 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2640 playersao->disconnected();
2647 if (player && reason != CDR_DENY) {
2648 std::ostringstream os(std::ios_base::binary);
2649 std::vector<session_t> clients = m_clients.getClientIDs();
2651 for (const session_t client_id : clients) {
2653 RemotePlayer *player = m_env->getPlayer(client_id);
2657 // Get name of player
2658 os << player->getName() << " ";
2661 std::string name = player->getName();
2662 actionstream << name << " "
2663 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2664 << " List of players: " << os.str() << std::endl;
2666 m_admin_chat->outgoing_queue.push_back(
2667 new ChatEventNick(CET_NICK_REMOVE, name));
2671 MutexAutoLock env_lock(m_env_mutex);
2672 m_clients.DeleteClient(peer_id);
2676 // Send leave chat message to all remaining clients
2677 if (!message.empty()) {
2678 SendChatMessage(PEER_ID_INEXISTENT,
2679 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2683 void Server::UpdateCrafting(RemotePlayer *player)
2685 // Get a preview for crafting
2687 InventoryLocation loc;
2688 loc.setPlayer(player->getName());
2689 std::vector<ItemStack> output_replacements;
2690 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2691 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2692 (&player->inventory)->getList("craft"), loc);
2694 // Put the new preview in
2695 InventoryList *plist = player->inventory.getList("craftpreview");
2696 sanity_check(plist);
2697 sanity_check(plist->getSize() >= 1);
2698 plist->changeItem(0, preview);
2701 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2703 if (evt->type == CET_NICK_ADD) {
2704 // The terminal informed us of its nick choice
2705 m_admin_nick = ((ChatEventNick *)evt)->nick;
2706 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2707 errorstream << "You haven't set up an account." << std::endl
2708 << "Please log in using the client as '"
2709 << m_admin_nick << "' with a secure password." << std::endl
2710 << "Until then, you can't execute admin tasks via the console," << std::endl
2711 << "and everybody can claim the user account instead of you," << std::endl
2712 << "giving them full control over this server." << std::endl;
2715 assert(evt->type == CET_CHAT);
2716 handleAdminChat((ChatEventChat *)evt);
2720 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2721 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2723 // If something goes wrong, this player is to blame
2724 RollbackScopeActor rollback_scope(m_rollback,
2725 std::string("player:") + name);
2727 if (g_settings->getBool("strip_color_codes"))
2728 wmessage = unescape_enriched(wmessage);
2731 switch (player->canSendChatMessage()) {
2732 case RPLAYER_CHATRESULT_FLOODING: {
2733 std::wstringstream ws;
2734 ws << L"You cannot send more messages. You are limited to "
2735 << g_settings->getFloat("chat_message_limit_per_10sec")
2736 << L" messages per 10 seconds.";
2739 case RPLAYER_CHATRESULT_KICK:
2740 DenyAccess_Legacy(player->getPeerId(),
2741 L"You have been kicked due to message flooding.");
2743 case RPLAYER_CHATRESULT_OK:
2746 FATAL_ERROR("Unhandled chat filtering result found.");
2750 if (m_max_chatmessage_length > 0
2751 && wmessage.length() > m_max_chatmessage_length) {
2752 return L"Your message exceed the maximum chat message limit set on the server. "
2753 L"It was refused. Send a shorter message";
2756 // Run script hook, exit if script ate the chat message
2757 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2762 // Whether to send line to the player that sent the message, or to all players
2763 bool broadcast_line = true;
2765 if (check_shout_priv && !checkPriv(name, "shout")) {
2766 line += L"-!- You don't have permission to shout.";
2767 broadcast_line = false;
2776 Tell calling method to send the message to sender
2778 if (!broadcast_line)
2782 Send the message to others
2784 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2786 std::vector<session_t> clients = m_clients.getClientIDs();
2789 Send the message back to the inital sender
2790 if they are using protocol version >= 29
2793 session_t peer_id_to_avoid_sending =
2794 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2796 if (player && player->protocol_version >= 29)
2797 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2799 for (u16 cid : clients) {
2800 if (cid != peer_id_to_avoid_sending)
2801 SendChatMessage(cid, ChatMessage(line));
2806 void Server::handleAdminChat(const ChatEventChat *evt)
2808 std::string name = evt->nick;
2809 std::wstring wname = utf8_to_wide(name);
2810 std::wstring wmessage = evt->evt_msg;
2812 std::wstring answer = handleChat(name, wname, wmessage);
2814 // If asked to send answer to sender
2815 if (!answer.empty()) {
2816 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2820 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2822 RemoteClient *client = getClientNoEx(peer_id,state_min);
2824 throw ClientNotFoundException("Client not found");
2828 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2830 return m_clients.getClientNoEx(peer_id, state_min);
2833 std::string Server::getPlayerName(session_t peer_id)
2835 RemotePlayer *player = m_env->getPlayer(peer_id);
2837 return "[id="+itos(peer_id)+"]";
2838 return player->getName();
2841 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2843 RemotePlayer *player = m_env->getPlayer(peer_id);
2846 return player->getPlayerSAO();
2849 std::wstring Server::getStatusString()
2851 std::wostringstream os(std::ios_base::binary);
2854 os<<L"version="<<narrow_to_wide(g_version_string);
2856 os<<L", uptime="<<m_uptime.get();
2858 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2859 // Information about clients
2862 std::vector<session_t> clients = m_clients.getClientIDs();
2863 for (session_t client_id : clients) {
2865 RemotePlayer *player = m_env->getPlayer(client_id);
2866 // Get name of player
2867 std::wstring name = L"unknown";
2869 name = narrow_to_wide(player->getName());
2870 // Add name to information string
2879 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2880 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2882 if (!g_settings->get("motd").empty())
2883 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2887 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2889 std::set<std::string> privs;
2890 m_script->getAuth(name, NULL, &privs);
2894 bool Server::checkPriv(const std::string &name, const std::string &priv)
2896 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2897 return (privs.count(priv) != 0);
2900 void Server::reportPrivsModified(const std::string &name)
2903 std::vector<session_t> clients = m_clients.getClientIDs();
2904 for (const session_t client_id : clients) {
2905 RemotePlayer *player = m_env->getPlayer(client_id);
2906 reportPrivsModified(player->getName());
2909 RemotePlayer *player = m_env->getPlayer(name.c_str());
2912 SendPlayerPrivileges(player->getPeerId());
2913 PlayerSAO *sao = player->getPlayerSAO();
2916 sao->updatePrivileges(
2917 getPlayerEffectivePrivs(name),
2922 void Server::reportInventoryFormspecModified(const std::string &name)
2924 RemotePlayer *player = m_env->getPlayer(name.c_str());
2927 SendPlayerInventoryFormspec(player->getPeerId());
2930 void Server::setIpBanned(const std::string &ip, const std::string &name)
2932 m_banmanager->add(ip, name);
2935 void Server::unsetIpBanned(const std::string &ip_or_name)
2937 m_banmanager->remove(ip_or_name);
2940 std::string Server::getBanDescription(const std::string &ip_or_name)
2942 return m_banmanager->getBanDescription(ip_or_name);
2945 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2947 // m_env will be NULL if the server is initializing
2951 if (m_admin_nick == name && !m_admin_nick.empty()) {
2952 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2955 RemotePlayer *player = m_env->getPlayer(name);
2960 if (player->getPeerId() == PEER_ID_INEXISTENT)
2963 SendChatMessage(player->getPeerId(), ChatMessage(msg));
2966 bool Server::showFormspec(const char *playername, const std::string &formspec,
2967 const std::string &formname)
2969 // m_env will be NULL if the server is initializing
2973 RemotePlayer *player = m_env->getPlayer(playername);
2977 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
2981 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
2986 u32 id = player->addHud(form);
2988 SendHUDAdd(player->getPeerId(), id, form);
2993 bool Server::hudRemove(RemotePlayer *player, u32 id) {
2997 HudElement* todel = player->removeHud(id);
3004 SendHUDRemove(player->getPeerId(), id);
3008 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3013 SendHUDChange(player->getPeerId(), id, stat, data);
3017 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3022 SendHUDSetFlags(player->getPeerId(), flags, mask);
3023 player->hud_flags &= ~mask;
3024 player->hud_flags |= flags;
3026 PlayerSAO* playersao = player->getPlayerSAO();
3031 m_script->player_event(playersao, "hud_changed");
3035 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3040 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3043 player->setHotbarItemcount(hotbar_itemcount);
3044 std::ostringstream os(std::ios::binary);
3045 writeS32(os, hotbar_itemcount);
3046 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3050 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3055 player->setHotbarImage(name);
3056 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3059 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3064 player->setHotbarSelectedImage(name);
3065 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3068 Address Server::getPeerAddress(session_t peer_id)
3070 return m_con->GetPeerAddress(peer_id);
3073 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3074 v2s32 animation_frames[4], f32 frame_speed)
3076 sanity_check(player);
3077 player->setLocalAnimations(animation_frames, frame_speed);
3078 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3081 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3083 sanity_check(player);
3084 player->eye_offset_first = first;
3085 player->eye_offset_third = third;
3086 SendEyeOffset(player->getPeerId(), first, third);
3089 void Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3090 const std::string &type, const std::vector<std::string> ¶ms,
3093 sanity_check(player);
3094 player->setSky(bgcolor, type, params, clouds);
3095 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3098 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3100 sanity_check(player);
3101 player->setCloudParams(params);
3102 SendCloudParams(player->getPeerId(), params);
3105 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3111 player->overrideDayNightRatio(do_override, ratio);
3112 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3116 void Server::notifyPlayers(const std::wstring &msg)
3118 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3121 void Server::spawnParticle(const std::string &playername, v3f pos,
3122 v3f velocity, v3f acceleration,
3123 float expirationtime, float size, bool
3124 collisiondetection, bool collision_removal,
3125 bool vertical, const std::string &texture,
3126 const struct TileAnimationParams &animation, u8 glow)
3128 // m_env will be NULL if the server is initializing
3132 session_t peer_id = PEER_ID_INEXISTENT;
3134 if (!playername.empty()) {
3135 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3138 peer_id = player->getPeerId();
3139 proto_ver = player->protocol_version;
3142 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3143 expirationtime, size, collisiondetection,
3144 collision_removal, vertical, texture, animation, glow);
3147 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3148 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3149 float minexptime, float maxexptime, float minsize, float maxsize,
3150 bool collisiondetection, bool collision_removal,
3151 ServerActiveObject *attached, bool vertical, const std::string &texture,
3152 const std::string &playername, const struct TileAnimationParams &animation,
3155 // m_env will be NULL if the server is initializing
3159 session_t peer_id = PEER_ID_INEXISTENT;
3161 if (!playername.empty()) {
3162 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3165 peer_id = player->getPeerId();
3166 proto_ver = player->protocol_version;
3169 u16 attached_id = attached ? attached->getId() : 0;
3172 if (attached_id == 0)
3173 id = m_env->addParticleSpawner(spawntime);
3175 id = m_env->addParticleSpawner(spawntime, attached_id);
3177 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3178 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3179 minexptime, maxexptime, minsize, maxsize,
3180 collisiondetection, collision_removal, attached_id, vertical,
3181 texture, id, animation, glow);
3186 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3188 // m_env will be NULL if the server is initializing
3190 throw ServerError("Can't delete particle spawners during initialisation!");
3192 session_t peer_id = PEER_ID_INEXISTENT;
3193 if (!playername.empty()) {
3194 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3197 peer_id = player->getPeerId();
3200 m_env->deleteParticleSpawner(id);
3201 SendDeleteParticleSpawner(peer_id, id);
3204 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3206 if(m_detached_inventories.count(name) > 0){
3207 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3208 delete m_detached_inventories[name];
3210 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3212 Inventory *inv = new Inventory(m_itemdef);
3214 m_detached_inventories[name] = inv;
3215 m_detached_inventories_player[name] = player;
3216 //TODO find a better way to do this
3217 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3221 // actions: time-reversed list
3222 // Return value: success/failure
3223 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3224 std::list<std::string> *log)
3226 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3227 ServerMap *map = (ServerMap*)(&m_env->getMap());
3229 // Fail if no actions to handle
3230 if(actions.empty()){
3231 log->push_back("Nothing to do.");
3238 for (const RollbackAction &action : actions) {
3240 bool success = action.applyRevert(map, this, this);
3243 std::ostringstream os;
3244 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3245 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3247 log->push_back(os.str());
3249 std::ostringstream os;
3250 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3251 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3253 log->push_back(os.str());
3257 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3258 <<" failed"<<std::endl;
3260 // Call it done if less than half failed
3261 return num_failed <= num_tried/2;
3264 // IGameDef interface
3266 IItemDefManager *Server::getItemDefManager()
3271 const NodeDefManager *Server::getNodeDefManager()
3276 ICraftDefManager *Server::getCraftDefManager()
3281 u16 Server::allocateUnknownNodeId(const std::string &name)
3283 return m_nodedef->allocateDummy(name);
3286 MtEventManager *Server::getEventManager()
3291 IWritableItemDefManager *Server::getWritableItemDefManager()
3296 NodeDefManager *Server::getWritableNodeDefManager()
3301 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3306 const std::vector<ModSpec> & Server::getMods() const
3308 return m_modmgr->getMods();
3311 const ModSpec *Server::getModSpec(const std::string &modname) const
3313 return m_modmgr->getModSpec(modname);
3316 void Server::getModNames(std::vector<std::string> &modlist)
3318 m_modmgr->getModNames(modlist);
3321 std::string Server::getBuiltinLuaPath()
3323 return porting::path_share + DIR_DELIM + "builtin";
3326 std::string Server::getModStoragePath() const
3328 return m_path_world + DIR_DELIM + "mod_storage";
3331 v3f Server::findSpawnPos()
3333 ServerMap &map = m_env->getServerMap();
3335 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3336 return nodeposf * BS;
3339 bool is_good = false;
3340 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3341 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3343 // Try to find a good place a few times
3344 for(s32 i = 0; i < 4000 && !is_good; i++) {
3345 s32 range = MYMIN(1 + i, range_max);
3346 // We're going to try to throw the player to this position
3347 v2s16 nodepos2d = v2s16(
3348 -range + (myrand() % (range * 2)),
3349 -range + (myrand() % (range * 2)));
3351 // Get spawn level at point
3352 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3353 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3354 // the mapgen to signify an unsuitable spawn position
3355 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3358 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3361 for (s32 i = 0; i < 10; i++) {
3362 v3s16 blockpos = getNodeBlockPos(nodepos);
3363 map.emergeBlock(blockpos, true);
3364 content_t c = map.getNodeNoEx(nodepos).getContent();
3365 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3367 if (air_count >= 2) {
3368 nodeposf = intToFloat(nodepos, BS);
3369 // Don't spawn the player outside map boundaries
3370 if (objectpos_over_limit(nodeposf))
3383 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3385 m_shutdown_timer = delay;
3386 m_shutdown_msg = msg;
3387 m_shutdown_ask_reconnect = reconnect;
3389 if (delay == 0.0f) {
3390 // No delay, shutdown immediately
3391 m_shutdown_requested = true;
3392 // only print to the infostream, a chat message saying
3393 // "Server Shutting Down" is sent when the server destructs.
3394 infostream << "*** Immediate Server shutdown requested." << std::endl;
3395 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3396 // Negative delay, cancel shutdown if requested
3397 m_shutdown_timer = 0.0f;
3398 m_shutdown_msg = "";
3399 m_shutdown_ask_reconnect = false;
3400 m_shutdown_requested = false;
3401 std::wstringstream ws;
3403 ws << L"*** Server shutdown canceled.";
3405 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3406 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3407 } else if (delay > 0.0f) {
3408 // Positive delay, tell the clients when the server will shut down
3409 std::wstringstream ws;
3411 ws << L"*** Server shutting down in "
3412 << duration_to_string(myround(m_shutdown_timer)).c_str()
3415 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3416 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3420 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3423 Try to get an existing player
3425 RemotePlayer *player = m_env->getPlayer(name);
3427 // If player is already connected, cancel
3428 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3429 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3434 If player with the wanted peer_id already exists, cancel.
3436 if (m_env->getPlayer(peer_id)) {
3437 infostream<<"emergePlayer(): Player with wrong name but same"
3438 " peer_id already exists"<<std::endl;
3443 player = new RemotePlayer(name, idef());
3446 bool newplayer = false;
3449 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3451 // Complete init with server parts
3452 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3453 player->protocol_version = proto_version;
3457 m_script->on_newplayer(playersao);
3463 bool Server::registerModStorage(ModMetadata *storage)
3465 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3466 errorstream << "Unable to register same mod storage twice. Storage name: "
3467 << storage->getModName() << std::endl;
3471 m_mod_storages[storage->getModName()] = storage;
3475 void Server::unregisterModStorage(const std::string &name)
3477 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3478 if (it != m_mod_storages.end()) {
3479 // Save unconditionaly on unregistration
3480 it->second->save(getModStoragePath());
3481 m_mod_storages.erase(name);
3485 void dedicated_server_loop(Server &server, bool &kill)
3487 verbosestream<<"dedicated_server_loop()"<<std::endl;
3489 IntervalLimiter m_profiler_interval;
3491 static thread_local const float steplen =
3492 g_settings->getFloat("dedicated_server_step");
3493 static thread_local const float profiler_print_interval =
3494 g_settings->getFloat("profiler_print_interval");
3497 // This is kind of a hack but can be done like this
3498 // because server.step() is very light
3500 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3501 sleep_ms((int)(steplen*1000.0));
3503 server.step(steplen);
3505 if (server.getShutdownRequested() || kill)
3511 if (profiler_print_interval != 0) {
3512 if(m_profiler_interval.step(steplen, profiler_print_interval))
3514 infostream<<"Profiler:"<<std::endl;
3515 g_profiler->print(infostream);
3516 g_profiler->clear();
3521 infostream << "Dedicated server quitting" << std::endl;
3523 if (g_settings->getBool("server_announce"))
3524 ServerList::sendAnnounce(ServerList::AA_DELETE,
3525 server.m_bind_addr.getPort());
3534 bool Server::joinModChannel(const std::string &channel)
3536 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3537 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3540 bool Server::leaveModChannel(const std::string &channel)
3542 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3545 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3547 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3550 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3554 ModChannel* Server::getModChannel(const std::string &channel)
3556 return m_modchannel_mgr->getModChannel(channel);
3559 void Server::broadcastModChannelMessage(const std::string &channel,
3560 const std::string &message, session_t from_peer)
3562 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3566 if (message.size() > STRING_MAX_LEN) {
3567 warningstream << "ModChannel message too long, dropping before sending "
3568 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3569 << channel << ")" << std::endl;
3574 if (from_peer != PEER_ID_SERVER) {
3575 sender = getPlayerName(from_peer);
3578 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3579 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3580 resp_pkt << channel << sender << message;
3581 for (session_t peer_id : peers) {
3583 if (peer_id == from_peer)
3586 Send(peer_id, &resp_pkt);
3589 if (from_peer != PEER_ID_SERVER) {
3590 m_script->on_modchannel_message(channel, sender, message);