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 if (g_settings->getBool("enable_rollback_recording")) {
266 // Create rollback manager
267 m_rollback = new RollbackManager(m_path_world, this);
270 // Give environment reference to scripting api
271 m_script->initializeEnvironment(m_env);
273 // Register us to receive map edit events
274 servermap->addEventReceiver(this);
278 m_liquid_transform_every = g_settings->getFloat("liquid_update");
279 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
280 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
281 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
286 infostream << "Server destructing" << std::endl;
288 // Send shutdown message
289 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
290 L"*** Server shutting down"));
293 MutexAutoLock envlock(m_env_mutex);
295 infostream << "Server: Saving players" << std::endl;
296 m_env->saveLoadedPlayers();
298 infostream << "Server: Kicking players" << std::endl;
299 std::string kick_msg;
300 bool reconnect = false;
301 if (getShutdownRequested()) {
302 reconnect = m_shutdown_ask_reconnect;
303 kick_msg = m_shutdown_msg;
305 if (kick_msg.empty()) {
306 kick_msg = g_settings->get("kick_msg_shutdown");
308 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
309 kick_msg, reconnect);
312 // Do this before stopping the server in case mapgen callbacks need to access
313 // server-controlled resources (like ModStorages). Also do them before
314 // shutdown callbacks since they may modify state that is finalized in a
316 m_emerge->stopThreads();
319 MutexAutoLock envlock(m_env_mutex);
321 // Execute script shutdown hooks
322 infostream << "Executing shutdown hooks" << std::endl;
323 m_script->on_shutdown();
325 infostream << "Server: Saving environment metadata" << std::endl;
333 // Delete things in the reverse order of creation
343 // Deinitialize scripting
344 infostream << "Server: Deinitializing scripting" << std::endl;
347 // Delete detached inventories
348 for (auto &detached_inventory : m_detached_inventories) {
349 delete detached_inventory.second;
355 infostream << "Starting server on " << m_bind_addr.serializeString()
356 << "..." << std::endl;
358 // Stop thread if already running
361 // Initialize connection
362 m_con->SetTimeoutMs(30);
363 m_con->Serve(m_bind_addr);
368 // ASCII art for the win!
370 << " .__ __ __ " << std::endl
371 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
372 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
373 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
374 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
375 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
376 actionstream << "World at [" << m_path_world << "]" << std::endl;
377 actionstream << "Server for gameid=\"" << m_gamespec.id
378 << "\" listening on " << m_bind_addr.serializeString() << ":"
379 << m_bind_addr.getPort() << "." << std::endl;
384 infostream<<"Server: Stopping and waiting threads"<<std::endl;
386 // Stop threads (set run=false first so both start stopping)
388 //m_emergethread.setRun(false);
390 //m_emergethread.stop();
392 infostream<<"Server: Threads stopped"<<std::endl;
395 void Server::step(float dtime)
401 MutexAutoLock lock(m_step_dtime_mutex);
402 m_step_dtime += dtime;
404 // Throw if fatal error occurred in thread
405 std::string async_err = m_async_fatal_error.get();
406 if (!async_err.empty()) {
407 if (!m_simple_singleplayer_mode) {
408 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
409 g_settings->get("kick_msg_crash"),
410 g_settings->getBool("ask_reconnect_on_crash"));
412 throw ServerError("AsyncErr: " + async_err);
416 void Server::AsyncRunStep(bool initial_step)
418 g_profiler->add("Server::AsyncRunStep (num)", 1);
422 MutexAutoLock lock1(m_step_dtime_mutex);
423 dtime = m_step_dtime;
427 // Send blocks to clients
431 if((dtime < 0.001) && !initial_step)
434 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
436 //infostream<<"Server steps "<<dtime<<std::endl;
437 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
440 MutexAutoLock lock1(m_step_dtime_mutex);
441 m_step_dtime -= dtime;
448 m_uptime.set(m_uptime.get() + dtime);
454 Update time of day and overall game time
456 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
459 Send to clients at constant intervals
462 m_time_of_day_send_timer -= dtime;
463 if(m_time_of_day_send_timer < 0.0) {
464 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
465 u16 time = m_env->getTimeOfDay();
466 float time_speed = g_settings->getFloat("time_speed");
467 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
471 MutexAutoLock lock(m_env_mutex);
472 // Figure out and report maximum lag to environment
473 float max_lag = m_env->getMaxLagEstimate();
474 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
476 if(dtime > 0.1 && dtime > max_lag * 2.0)
477 infostream<<"Server: Maximum lag peaked to "<<dtime
481 m_env->reportMaxLagEstimate(max_lag);
483 ScopeProfiler sp(g_profiler, "SEnv step");
484 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
488 static const float map_timer_and_unload_dtime = 2.92;
489 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
491 MutexAutoLock lock(m_env_mutex);
492 // Run Map's timers and unload unused data
493 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
494 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
495 g_settings->getFloat("server_unload_unused_data_timeout"),
500 Listen to the admin chat, if available
503 if (!m_admin_chat->command_queue.empty()) {
504 MutexAutoLock lock(m_env_mutex);
505 while (!m_admin_chat->command_queue.empty()) {
506 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
507 handleChatInterfaceEvent(evt);
511 m_admin_chat->outgoing_queue.push_back(
512 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
519 /* Transform liquids */
520 m_liquid_transform_timer += dtime;
521 if(m_liquid_transform_timer >= m_liquid_transform_every)
523 m_liquid_transform_timer -= m_liquid_transform_every;
525 MutexAutoLock lock(m_env_mutex);
527 ScopeProfiler sp(g_profiler, "Server: liquid transform");
529 std::map<v3s16, MapBlock*> modified_blocks;
530 m_env->getMap().transformLiquids(modified_blocks, m_env);
533 Set the modified blocks unsent for all the clients
535 if (!modified_blocks.empty()) {
536 SetBlocksNotSent(modified_blocks);
539 m_clients.step(dtime);
541 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
543 // send masterserver announce
545 float &counter = m_masterserver_timer;
546 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
547 g_settings->getBool("server_announce")) {
548 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
549 ServerList::AA_START,
550 m_bind_addr.getPort(),
551 m_clients.getPlayerNames(),
553 m_env->getGameTime(),
556 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
566 Check added and deleted active objects
569 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
570 MutexAutoLock envlock(m_env_mutex);
573 const RemoteClientMap &clients = m_clients.getClientList();
574 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
576 // Radius inside which objects are active
577 static thread_local const s16 radius =
578 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
580 // Radius inside which players are active
581 static thread_local const bool is_transfer_limited =
582 g_settings->exists("unlimited_player_transfer_distance") &&
583 !g_settings->getBool("unlimited_player_transfer_distance");
584 static thread_local const s16 player_transfer_dist =
585 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
586 s16 player_radius = player_transfer_dist;
587 if (player_radius == 0 && is_transfer_limited)
588 player_radius = radius;
590 for (const auto &client_it : clients) {
591 RemoteClient *client = client_it.second;
593 // If definitions and textures have not been sent, don't
594 // send objects either
595 if (client->getState() < CS_DefinitionsSent)
598 RemotePlayer *player = m_env->getPlayer(client->peer_id);
600 // This can happen if the client timeouts somehow
604 PlayerSAO *playersao = player->getPlayerSAO();
608 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
609 if (my_radius <= 0) my_radius = radius;
610 //infostream << "Server: Active Radius " << my_radius << std::endl;
612 std::queue<u16> removed_objects;
613 std::queue<u16> added_objects;
614 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
615 client->m_known_objects, removed_objects);
616 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
617 client->m_known_objects, added_objects);
619 // Ignore if nothing happened
620 if (removed_objects.empty() && added_objects.empty()) {
624 std::string data_buffer;
628 // Handle removed objects
629 writeU16((u8*)buf, removed_objects.size());
630 data_buffer.append(buf, 2);
631 while (!removed_objects.empty()) {
633 u16 id = removed_objects.front();
634 ServerActiveObject* obj = m_env->getActiveObject(id);
636 // Add to data buffer for sending
637 writeU16((u8*)buf, id);
638 data_buffer.append(buf, 2);
640 // Remove from known objects
641 client->m_known_objects.erase(id);
643 if(obj && obj->m_known_by_count > 0)
644 obj->m_known_by_count--;
645 removed_objects.pop();
648 // Handle added objects
649 writeU16((u8*)buf, added_objects.size());
650 data_buffer.append(buf, 2);
651 while (!added_objects.empty()) {
653 u16 id = added_objects.front();
654 ServerActiveObject* obj = m_env->getActiveObject(id);
657 u8 type = ACTIVEOBJECT_TYPE_INVALID;
659 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
661 type = obj->getSendType();
663 // Add to data buffer for sending
664 writeU16((u8*)buf, id);
665 data_buffer.append(buf, 2);
666 writeU8((u8*)buf, type);
667 data_buffer.append(buf, 1);
670 data_buffer.append(serializeLongString(
671 obj->getClientInitializationData(client->net_proto_version)));
673 data_buffer.append(serializeLongString(""));
675 // Add to known objects
676 client->m_known_objects.insert(id);
679 obj->m_known_by_count++;
684 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
685 verbosestream << "Server: Sent object remove/add: "
686 << removed_objects.size() << " removed, "
687 << added_objects.size() << " added, "
688 << "packet size is " << pktSize << std::endl;
692 m_mod_storage_save_timer -= dtime;
693 if (m_mod_storage_save_timer <= 0.0f) {
694 infostream << "Saving registered mod storages." << std::endl;
695 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
696 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
697 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
698 if (it->second->isModified()) {
699 it->second->save(getModStoragePath());
709 MutexAutoLock envlock(m_env_mutex);
710 ScopeProfiler sp(g_profiler, "Server: sending object messages");
713 // Value = data sent by object
714 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
716 // Get active object messages from environment
718 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
722 std::vector<ActiveObjectMessage>* message_list = nullptr;
723 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
724 n = buffered_messages.find(aom.id);
725 if (n == buffered_messages.end()) {
726 message_list = new std::vector<ActiveObjectMessage>;
727 buffered_messages[aom.id] = message_list;
730 message_list = n->second;
732 message_list->push_back(aom);
736 const RemoteClientMap &clients = m_clients.getClientList();
737 // Route data to every client
738 for (const auto &client_it : clients) {
739 RemoteClient *client = client_it.second;
740 std::string reliable_data;
741 std::string unreliable_data;
742 // Go through all objects in message buffer
743 for (const auto &buffered_message : buffered_messages) {
744 // If object is not known by client, skip it
745 u16 id = buffered_message.first;
746 if (client->m_known_objects.find(id) == client->m_known_objects.end())
749 // Get message list of object
750 std::vector<ActiveObjectMessage>* list = buffered_message.second;
751 // Go through every message
752 for (const ActiveObjectMessage &aom : *list) {
753 // Compose the full new data with header
754 std::string new_data;
757 writeU16((u8*)&buf[0], aom.id);
758 new_data.append(buf, 2);
760 new_data += serializeString(aom.datastring);
761 // Add data to buffer
763 reliable_data += new_data;
765 unreliable_data += new_data;
769 reliable_data and unreliable_data are now ready.
772 if (!reliable_data.empty()) {
773 SendActiveObjectMessages(client->peer_id, reliable_data);
776 if (!unreliable_data.empty()) {
777 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
782 // Clear buffered_messages
783 for (auto &buffered_message : buffered_messages) {
784 delete buffered_message.second;
789 Send queued-for-sending map edit events.
792 // We will be accessing the environment
793 MutexAutoLock lock(m_env_mutex);
795 // Don't send too many at a time
798 // Single change sending is disabled if queue size is not small
799 bool disable_single_change_sending = false;
800 if(m_unsent_map_edit_queue.size() >= 4)
801 disable_single_change_sending = true;
803 int event_count = m_unsent_map_edit_queue.size();
805 // We'll log the amount of each
808 while (!m_unsent_map_edit_queue.empty()) {
809 MapEditEvent* event = m_unsent_map_edit_queue.front();
810 m_unsent_map_edit_queue.pop();
812 // Players far away from the change are stored here.
813 // Instead of sending the changes, MapBlocks are set not sent
815 std::vector<u16> far_players;
817 switch (event->type) {
820 prof.add("MEET_ADDNODE", 1);
821 sendAddNode(event->p, event->n, event->already_known_by_peer,
822 &far_players, disable_single_change_sending ? 5 : 30,
823 event->type == MEET_ADDNODE);
825 case MEET_REMOVENODE:
826 prof.add("MEET_REMOVENODE", 1);
827 sendRemoveNode(event->p, event->already_known_by_peer,
828 &far_players, disable_single_change_sending ? 5 : 30);
830 case MEET_BLOCK_NODE_METADATA_CHANGED:
831 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
832 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
833 m_clients.markBlockposAsNotSent(event->p);
836 infostream << "Server: MEET_OTHER" << std::endl;
837 prof.add("MEET_OTHER", 1);
838 for (const v3s16 &modified_block : event->modified_blocks) {
839 m_clients.markBlockposAsNotSent(modified_block);
843 prof.add("unknown", 1);
844 warningstream << "Server: Unknown MapEditEvent "
845 << ((u32)event->type) << std::endl;
850 Set blocks not sent to far players
852 if (!far_players.empty()) {
853 // Convert list format to that wanted by SetBlocksNotSent
854 std::map<v3s16, MapBlock*> modified_blocks2;
855 for (const v3s16 &modified_block : event->modified_blocks) {
856 modified_blocks2[modified_block] =
857 m_env->getMap().getBlockNoCreateNoEx(modified_block);
860 // Set blocks not sent
861 for (const u16 far_player : far_players) {
862 if (RemoteClient *client = getClient(far_player))
863 client->SetBlocksNotSent(modified_blocks2);
870 if (event_count >= 5) {
871 infostream << "Server: MapEditEvents:" << std::endl;
872 prof.print(infostream);
873 } else if (event_count != 0) {
874 verbosestream << "Server: MapEditEvents:" << std::endl;
875 prof.print(verbosestream);
881 Trigger emergethread (it somehow gets to a non-triggered but
882 bysy state sometimes)
885 float &counter = m_emergethread_trigger_timer;
887 if (counter >= 2.0) {
890 m_emerge->startThreads();
894 // Save map, players and auth stuff
896 float &counter = m_savemap_timer;
898 static thread_local const float save_interval =
899 g_settings->getFloat("server_map_save_interval");
900 if (counter >= save_interval) {
902 MutexAutoLock lock(m_env_mutex);
904 ScopeProfiler sp(g_profiler, "Server: saving stuff");
907 if (m_banmanager->isModified()) {
908 m_banmanager->save();
911 // Save changed parts of map
912 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
915 m_env->saveLoadedPlayers();
917 // Save environment metadata
923 static const float shutdown_msg_times[] =
925 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
928 if (m_shutdown_timer > 0.0f) {
929 // Automated messages
930 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
931 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
932 // If shutdown timer matches an automessage, shot it
933 if (m_shutdown_timer > shutdown_msg_times[i] &&
934 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
935 std::wstringstream ws;
937 ws << L"*** Server shutting down in "
938 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
941 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
942 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
948 m_shutdown_timer -= dtime;
949 if (m_shutdown_timer < 0.0f) {
950 m_shutdown_timer = 0.0f;
951 m_shutdown_requested = true;
956 void Server::Receive()
961 m_con->Receive(&pkt);
962 peer_id = pkt.getPeerId();
964 } catch (const con::InvalidIncomingDataException &e) {
965 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
966 << e.what() << std::endl;
967 } catch (const SerializationError &e) {
968 infostream << "Server::Receive(): SerializationError: what()="
969 << e.what() << std::endl;
970 } catch (const ClientStateError &e) {
971 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
972 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
973 L"Try reconnecting or updating your client");
974 } catch (const con::PeerNotFoundException &e) {
979 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
981 std::string playername;
982 PlayerSAO *playersao = NULL;
985 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
987 playername = client->getName();
988 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
990 } catch (std::exception &e) {
996 RemotePlayer *player = m_env->getPlayer(playername.c_str());
999 if (!playersao || !player) {
1000 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1001 actionstream << "Server: Failed to emerge player \"" << playername
1002 << "\" (player allocated to an another client)" << std::endl;
1003 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1004 L"name. If your client closed unexpectedly, try again in "
1007 errorstream << "Server: " << playername << ": Failed to emerge player"
1009 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1015 Send complete position information
1017 SendMovePlayer(peer_id);
1020 SendPlayerPrivileges(peer_id);
1022 // Send inventory formspec
1023 SendPlayerInventoryFormspec(peer_id);
1026 SendInventory(playersao);
1028 // Send HP or death screen
1029 if (playersao->isDead())
1030 SendDeathscreen(peer_id, false, v3f(0,0,0));
1032 SendPlayerHPOrDie(playersao,
1033 PlayerHPChangeReason(PlayerHPChangeReason::SET_HP));
1036 SendPlayerBreath(playersao);
1038 // Note things in chat if not in simple singleplayer mode
1039 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1040 // Send information about server to player in chat
1041 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1043 Address addr = getPeerAddress(player->getPeerId());
1044 std::string ip_str = addr.serializeString();
1045 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1050 const std::vector<std::string> &names = m_clients.getPlayerNames();
1052 actionstream << player->getName() << " joins game. List of players: ";
1054 for (const std::string &name : names) {
1055 actionstream << name << " ";
1058 actionstream << player->getName() <<std::endl;
1063 inline void Server::handleCommand(NetworkPacket* pkt)
1065 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1066 (this->*opHandle.handler)(pkt);
1069 void Server::ProcessData(NetworkPacket *pkt)
1071 // Environment is locked first.
1072 MutexAutoLock envlock(m_env_mutex);
1074 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1075 u32 peer_id = pkt->getPeerId();
1078 Address address = getPeerAddress(peer_id);
1079 std::string addr_s = address.serializeString();
1081 if(m_banmanager->isIpBanned(addr_s)) {
1082 std::string ban_name = m_banmanager->getBanName(addr_s);
1083 infostream << "Server: A banned client tried to connect from "
1084 << addr_s << "; banned name was "
1085 << ban_name << std::endl;
1086 // This actually doesn't seem to transfer to the client
1087 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1088 + utf8_to_wide(ban_name));
1092 catch(con::PeerNotFoundException &e) {
1094 * no peer for this packet found
1095 * most common reason is peer timeout, e.g. peer didn't
1096 * respond for some time, your server was overloaded or
1099 infostream << "Server::ProcessData(): Canceling: peer "
1100 << peer_id << " not found" << std::endl;
1105 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1107 // Command must be handled into ToServerCommandHandler
1108 if (command >= TOSERVER_NUM_MSG_TYPES) {
1109 infostream << "Server: Ignoring unknown command "
1110 << command << std::endl;
1114 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1119 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1121 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1122 errorstream << "Server::ProcessData(): Cancelling: Peer"
1123 " serialization format invalid or not initialized."
1124 " Skipping incoming command=" << command << std::endl;
1128 /* Handle commands related to client startup */
1129 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1134 if (m_clients.getClientState(peer_id) < CS_Active) {
1135 if (command == TOSERVER_PLAYERPOS) return;
1137 errorstream << "Got packet command: " << command << " for peer id "
1138 << peer_id << " but client isn't active yet. Dropping packet "
1144 } catch (SendFailedException &e) {
1145 errorstream << "Server::ProcessData(): SendFailedException: "
1146 << "what=" << e.what()
1148 } catch (PacketError &e) {
1149 actionstream << "Server::ProcessData(): PacketError: "
1150 << "what=" << e.what()
1155 void Server::setTimeOfDay(u32 time)
1157 m_env->setTimeOfDay(time);
1158 m_time_of_day_send_timer = 0;
1161 void Server::onMapEditEvent(MapEditEvent *event)
1163 if(m_ignore_map_edit_events)
1165 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1167 MapEditEvent *e = event->clone();
1168 m_unsent_map_edit_queue.push(e);
1171 Inventory* Server::getInventory(const InventoryLocation &loc)
1174 case InventoryLocation::UNDEFINED:
1175 case InventoryLocation::CURRENT_PLAYER:
1177 case InventoryLocation::PLAYER:
1179 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1182 PlayerSAO *playersao = player->getPlayerSAO();
1185 return playersao->getInventory();
1188 case InventoryLocation::NODEMETA:
1190 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1193 return meta->getInventory();
1196 case InventoryLocation::DETACHED:
1198 if(m_detached_inventories.count(loc.name) == 0)
1200 return m_detached_inventories[loc.name];
1204 sanity_check(false); // abort
1209 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1212 case InventoryLocation::UNDEFINED:
1214 case InventoryLocation::PLAYER:
1219 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1224 PlayerSAO *playersao = player->getPlayerSAO();
1228 SendInventory(playersao);
1231 case InventoryLocation::NODEMETA:
1233 v3s16 blockpos = getNodeBlockPos(loc.p);
1235 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1237 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1239 m_clients.markBlockposAsNotSent(blockpos);
1242 case InventoryLocation::DETACHED:
1244 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1248 sanity_check(false); // abort
1253 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1255 std::vector<session_t> clients = m_clients.getClientIDs();
1257 // Set the modified blocks unsent for all the clients
1258 for (const session_t client_id : clients) {
1259 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1260 client->SetBlocksNotSent(block);
1265 void Server::peerAdded(con::Peer *peer)
1267 verbosestream<<"Server::peerAdded(): peer->id="
1268 <<peer->id<<std::endl;
1270 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1273 void Server::deletingPeer(con::Peer *peer, bool timeout)
1275 verbosestream<<"Server::deletingPeer(): peer->id="
1276 <<peer->id<<", timeout="<<timeout<<std::endl;
1278 m_clients.event(peer->id, CSE_Disconnect);
1279 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1282 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1284 *retval = m_con->getPeerStat(peer_id,type);
1285 return *retval != -1;
1288 bool Server::getClientInfo(
1297 std::string* vers_string
1300 *state = m_clients.getClientState(peer_id);
1302 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1309 *uptime = client->uptime();
1310 *ser_vers = client->serialization_version;
1311 *prot_vers = client->net_proto_version;
1313 *major = client->getMajor();
1314 *minor = client->getMinor();
1315 *patch = client->getPatch();
1316 *vers_string = client->getPatch();
1323 void Server::handlePeerChanges()
1325 while(!m_peer_change_queue.empty())
1327 con::PeerChange c = m_peer_change_queue.front();
1328 m_peer_change_queue.pop();
1330 verbosestream<<"Server: Handling peer change: "
1331 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1336 case con::PEER_ADDED:
1337 m_clients.CreateClient(c.peer_id);
1340 case con::PEER_REMOVED:
1341 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1345 FATAL_ERROR("Invalid peer change event received!");
1351 void Server::printToConsoleOnly(const std::string &text)
1354 m_admin_chat->outgoing_queue.push_back(
1355 new ChatEventChat("", utf8_to_wide(text)));
1357 std::cout << text << std::endl;
1361 void Server::Send(NetworkPacket *pkt)
1363 Send(pkt->getPeerId(), pkt);
1366 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1368 m_clients.send(peer_id,
1369 clientCommandFactoryTable[pkt->getCommand()].channel,
1371 clientCommandFactoryTable[pkt->getCommand()].reliable);
1374 void Server::SendMovement(session_t peer_id)
1376 std::ostringstream os(std::ios_base::binary);
1378 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1380 pkt << g_settings->getFloat("movement_acceleration_default");
1381 pkt << g_settings->getFloat("movement_acceleration_air");
1382 pkt << g_settings->getFloat("movement_acceleration_fast");
1383 pkt << g_settings->getFloat("movement_speed_walk");
1384 pkt << g_settings->getFloat("movement_speed_crouch");
1385 pkt << g_settings->getFloat("movement_speed_fast");
1386 pkt << g_settings->getFloat("movement_speed_climb");
1387 pkt << g_settings->getFloat("movement_speed_jump");
1388 pkt << g_settings->getFloat("movement_liquid_fluidity");
1389 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1390 pkt << g_settings->getFloat("movement_liquid_sink");
1391 pkt << g_settings->getFloat("movement_gravity");
1396 void Server::SendPlayerHPOrDie(PlayerSAO *playersao, const PlayerHPChangeReason &reason)
1398 if (!g_settings->getBool("enable_damage"))
1401 session_t peer_id = playersao->getPeerID();
1402 bool is_alive = playersao->getHP() > 0;
1405 SendPlayerHP(peer_id);
1407 DiePlayer(peer_id, reason);
1410 void Server::SendHP(session_t peer_id, u16 hp)
1412 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1417 void Server::SendBreath(session_t peer_id, u16 breath)
1419 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1420 pkt << (u16) breath;
1424 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1425 const std::string &custom_reason, bool reconnect)
1427 assert(reason < SERVER_ACCESSDENIED_MAX);
1429 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1431 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1432 pkt << custom_reason;
1433 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1434 reason == SERVER_ACCESSDENIED_CRASH)
1435 pkt << custom_reason << (u8)reconnect;
1439 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1441 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1446 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1447 v3f camera_point_target)
1449 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1450 pkt << set_camera_point_target << camera_point_target;
1454 void Server::SendItemDef(session_t peer_id,
1455 IItemDefManager *itemdef, u16 protocol_version)
1457 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1461 u32 length of the next item
1462 zlib-compressed serialized ItemDefManager
1464 std::ostringstream tmp_os(std::ios::binary);
1465 itemdef->serialize(tmp_os, protocol_version);
1466 std::ostringstream tmp_os2(std::ios::binary);
1467 compressZlib(tmp_os.str(), tmp_os2);
1468 pkt.putLongString(tmp_os2.str());
1471 verbosestream << "Server: Sending item definitions to id(" << peer_id
1472 << "): size=" << pkt.getSize() << std::endl;
1477 void Server::SendNodeDef(session_t peer_id,
1478 const NodeDefManager *nodedef, u16 protocol_version)
1480 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1484 u32 length of the next item
1485 zlib-compressed serialized NodeDefManager
1487 std::ostringstream tmp_os(std::ios::binary);
1488 nodedef->serialize(tmp_os, protocol_version);
1489 std::ostringstream tmp_os2(std::ios::binary);
1490 compressZlib(tmp_os.str(), tmp_os2);
1492 pkt.putLongString(tmp_os2.str());
1495 verbosestream << "Server: Sending node definitions to id(" << peer_id
1496 << "): size=" << pkt.getSize() << std::endl;
1502 Non-static send methods
1505 void Server::SendInventory(PlayerSAO* playerSAO)
1507 UpdateCrafting(playerSAO->getPlayer());
1513 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1515 std::ostringstream os;
1516 playerSAO->getInventory()->serialize(os);
1518 std::string s = os.str();
1520 pkt.putRawString(s.c_str(), s.size());
1524 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1526 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1528 u8 type = message.type;
1529 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1531 if (peer_id != PEER_ID_INEXISTENT) {
1532 RemotePlayer *player = m_env->getPlayer(peer_id);
1538 m_clients.sendToAll(&pkt);
1542 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1543 const std::string &formname)
1545 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1546 if (formspec.empty()){
1547 //the client should close the formspec
1548 m_formspec_state_data.erase(peer_id);
1549 pkt.putLongString("");
1551 m_formspec_state_data[peer_id] = formname;
1552 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1559 // Spawns a particle on peer with peer_id
1560 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1561 v3f pos, v3f velocity, v3f acceleration,
1562 float expirationtime, float size, bool collisiondetection,
1563 bool collision_removal,
1564 bool vertical, const std::string &texture,
1565 const struct TileAnimationParams &animation, u8 glow)
1567 static thread_local const float radius =
1568 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1570 if (peer_id == PEER_ID_INEXISTENT) {
1571 std::vector<session_t> clients = m_clients.getClientIDs();
1573 for (const session_t client_id : clients) {
1574 RemotePlayer *player = m_env->getPlayer(client_id);
1578 PlayerSAO *sao = player->getPlayerSAO();
1582 // Do not send to distant clients
1583 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1586 SendSpawnParticle(client_id, player->protocol_version,
1587 pos, velocity, acceleration,
1588 expirationtime, size, collisiondetection,
1589 collision_removal, vertical, texture, animation, glow);
1594 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1596 pkt << pos << velocity << acceleration << expirationtime
1597 << size << collisiondetection;
1598 pkt.putLongString(texture);
1600 pkt << collision_removal;
1601 // This is horrible but required (why are there two ways to serialize pkts?)
1602 std::ostringstream os(std::ios_base::binary);
1603 animation.serialize(os, protocol_version);
1604 pkt.putRawString(os.str());
1610 // Adds a ParticleSpawner on peer with peer_id
1611 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1612 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1613 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1614 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1615 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1616 const struct TileAnimationParams &animation, u8 glow)
1618 if (peer_id == PEER_ID_INEXISTENT) {
1619 // This sucks and should be replaced:
1620 std::vector<session_t> clients = m_clients.getClientIDs();
1621 for (const session_t client_id : clients) {
1622 RemotePlayer *player = m_env->getPlayer(client_id);
1625 SendAddParticleSpawner(client_id, player->protocol_version,
1626 amount, spawntime, minpos, maxpos,
1627 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1628 minsize, maxsize, collisiondetection, collision_removal,
1629 attached_id, vertical, texture, id, animation, glow);
1634 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1636 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1637 << minacc << maxacc << minexptime << maxexptime << minsize
1638 << maxsize << collisiondetection;
1640 pkt.putLongString(texture);
1642 pkt << id << vertical;
1643 pkt << collision_removal;
1645 // This is horrible but required
1646 std::ostringstream os(std::ios_base::binary);
1647 animation.serialize(os, protocol_version);
1648 pkt.putRawString(os.str());
1654 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1656 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1658 // Ugly error in this packet
1661 if (peer_id != PEER_ID_INEXISTENT)
1664 m_clients.sendToAll(&pkt);
1668 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1670 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1672 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1673 << form->text << form->number << form->item << form->dir
1674 << form->align << form->offset << form->world_pos << form->size;
1679 void Server::SendHUDRemove(session_t peer_id, u32 id)
1681 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1686 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1688 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1689 pkt << id << (u8) stat;
1693 case HUD_STAT_SCALE:
1694 case HUD_STAT_ALIGN:
1695 case HUD_STAT_OFFSET:
1696 pkt << *(v2f *) value;
1700 pkt << *(std::string *) value;
1702 case HUD_STAT_WORLD_POS:
1703 pkt << *(v3f *) value;
1706 pkt << *(v2s32 *) value;
1708 case HUD_STAT_NUMBER:
1712 pkt << *(u32 *) value;
1719 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1721 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1723 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1725 pkt << flags << mask;
1730 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1732 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1733 pkt << param << value;
1737 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1738 const std::string &type, const std::vector<std::string> ¶ms,
1741 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1742 pkt << bgcolor << type << (u16) params.size();
1744 for (const std::string ¶m : params)
1752 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1754 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1755 pkt << params.density << params.color_bright << params.color_ambient
1756 << params.height << params.thickness << params.speed;
1760 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1763 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1766 pkt << do_override << (u16) (ratio * 65535);
1771 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1773 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1774 pkt << time << time_speed;
1776 if (peer_id == PEER_ID_INEXISTENT) {
1777 m_clients.sendToAll(&pkt);
1784 void Server::SendPlayerHP(session_t peer_id)
1786 PlayerSAO *playersao = getPlayerSAO(peer_id);
1787 // In some rare case if the player is disconnected
1788 // while Lua call l_punch, for example, this can be NULL
1792 SendHP(peer_id, playersao->getHP());
1793 m_script->player_event(playersao,"health_changed");
1795 // Send to other clients
1796 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1797 ActiveObjectMessage aom(playersao->getId(), true, str);
1798 playersao->m_messages_out.push(aom);
1801 void Server::SendPlayerBreath(PlayerSAO *sao)
1805 m_script->player_event(sao, "breath_changed");
1806 SendBreath(sao->getPeerID(), sao->getBreath());
1809 void Server::SendMovePlayer(session_t peer_id)
1811 RemotePlayer *player = m_env->getPlayer(peer_id);
1813 PlayerSAO *sao = player->getPlayerSAO();
1816 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1817 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1820 v3f pos = sao->getBasePosition();
1821 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1822 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1823 << " pitch=" << sao->getPitch()
1824 << " yaw=" << sao->getYaw()
1831 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1832 f32 animation_speed)
1834 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1837 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1838 << animation_frames[3] << animation_speed;
1843 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1845 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1846 pkt << first << third;
1850 void Server::SendPlayerPrivileges(session_t peer_id)
1852 RemotePlayer *player = m_env->getPlayer(peer_id);
1854 if(player->getPeerId() == PEER_ID_INEXISTENT)
1857 std::set<std::string> privs;
1858 m_script->getAuth(player->getName(), NULL, &privs);
1860 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1861 pkt << (u16) privs.size();
1863 for (const std::string &priv : privs) {
1870 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1872 RemotePlayer *player = m_env->getPlayer(peer_id);
1874 if (player->getPeerId() == PEER_ID_INEXISTENT)
1877 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1878 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1882 void Server::SendPlayerFormspecPrepend(session_t peer_id)
1884 RemotePlayer *player = m_env->getPlayer(peer_id);
1886 if (player->getPeerId() == PEER_ID_INEXISTENT)
1889 NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
1890 pkt << FORMSPEC_VERSION_STRING + player->formspec_prepend;
1894 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1896 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1897 pkt.putRawString(datas.c_str(), datas.size());
1899 return pkt.getSize();
1902 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1905 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1906 datas.size(), peer_id);
1908 pkt.putRawString(datas.c_str(), datas.size());
1910 m_clients.send(pkt.getPeerId(),
1911 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1915 void Server::SendCSMFlavourLimits(session_t peer_id)
1917 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1918 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1919 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1923 s32 Server::playSound(const SimpleSoundSpec &spec,
1924 const ServerSoundParams ¶ms)
1926 // Find out initial position of sound
1927 bool pos_exists = false;
1928 v3f pos = params.getPos(m_env, &pos_exists);
1929 // If position is not found while it should be, cancel sound
1930 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1933 // Filter destination clients
1934 std::vector<session_t> dst_clients;
1935 if(!params.to_player.empty()) {
1936 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1938 infostream<<"Server::playSound: Player \""<<params.to_player
1939 <<"\" not found"<<std::endl;
1942 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1943 infostream<<"Server::playSound: Player \""<<params.to_player
1944 <<"\" not connected"<<std::endl;
1947 dst_clients.push_back(player->getPeerId());
1949 std::vector<session_t> clients = m_clients.getClientIDs();
1951 for (const session_t client_id : clients) {
1952 RemotePlayer *player = m_env->getPlayer(client_id);
1956 PlayerSAO *sao = player->getPlayerSAO();
1961 if(sao->getBasePosition().getDistanceFrom(pos) >
1962 params.max_hear_distance)
1965 dst_clients.push_back(client_id);
1969 if(dst_clients.empty())
1973 s32 id = m_next_sound_id++;
1974 // The sound will exist as a reference in m_playing_sounds
1975 m_playing_sounds[id] = ServerPlayingSound();
1976 ServerPlayingSound &psound = m_playing_sounds[id];
1977 psound.params = params;
1980 float gain = params.gain * spec.gain;
1981 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1982 pkt << id << spec.name << gain
1983 << (u8) params.type << pos << params.object
1984 << params.loop << params.fade << params.pitch;
1986 // Backwards compability
1987 bool play_sound = gain > 0;
1989 for (const u16 dst_client : dst_clients) {
1990 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
1991 psound.clients.insert(dst_client);
1992 m_clients.send(dst_client, 0, &pkt, true);
1997 void Server::stopSound(s32 handle)
1999 // Get sound reference
2000 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2001 m_playing_sounds.find(handle);
2002 if (i == m_playing_sounds.end())
2004 ServerPlayingSound &psound = i->second;
2006 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2009 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2010 si != psound.clients.end(); ++si) {
2012 m_clients.send(*si, 0, &pkt, true);
2014 // Remove sound reference
2015 m_playing_sounds.erase(i);
2018 void Server::fadeSound(s32 handle, float step, float gain)
2020 // Get sound reference
2021 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2022 m_playing_sounds.find(handle);
2023 if (i == m_playing_sounds.end())
2026 ServerPlayingSound &psound = i->second;
2027 psound.params.gain = gain;
2029 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2030 pkt << handle << step << gain;
2032 // Backwards compability
2033 bool play_sound = gain > 0;
2034 ServerPlayingSound compat_psound = psound;
2035 compat_psound.clients.clear();
2037 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2038 compat_pkt << handle;
2040 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2041 it != psound.clients.end();) {
2042 if (m_clients.getProtocolVersion(*it) >= 32) {
2044 m_clients.send(*it, 0, &pkt, true);
2047 compat_psound.clients.insert(*it);
2049 m_clients.send(*it, 0, &compat_pkt, true);
2050 psound.clients.erase(it++);
2054 // Remove sound reference
2055 if (!play_sound || psound.clients.empty())
2056 m_playing_sounds.erase(i);
2058 if (play_sound && !compat_psound.clients.empty()) {
2059 // Play new sound volume on older clients
2060 playSound(compat_psound.spec, compat_psound.params);
2064 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2065 std::vector<u16> *far_players, float far_d_nodes)
2067 float maxd = far_d_nodes*BS;
2068 v3f p_f = intToFloat(p, BS);
2070 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2073 std::vector<session_t> clients = m_clients.getClientIDs();
2074 for (session_t client_id : clients) {
2077 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2078 PlayerSAO *sao = player->getPlayerSAO();
2082 // If player is far away, only set modified blocks not sent
2083 v3f player_pos = sao->getBasePosition();
2084 if (player_pos.getDistanceFrom(p_f) > maxd) {
2085 far_players->push_back(client_id);
2092 m_clients.send(client_id, 0, &pkt, true);
2096 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2097 std::vector<u16> *far_players, float far_d_nodes,
2098 bool remove_metadata)
2100 float maxd = far_d_nodes*BS;
2101 v3f p_f = intToFloat(p, BS);
2103 std::vector<session_t> clients = m_clients.getClientIDs();
2104 for (const session_t client_id : clients) {
2107 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2108 PlayerSAO *sao = player->getPlayerSAO();
2112 // If player is far away, only set modified blocks not sent
2113 v3f player_pos = sao->getBasePosition();
2114 if(player_pos.getDistanceFrom(p_f) > maxd) {
2115 far_players->push_back(client_id);
2121 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2123 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2125 pkt << p << n.param0 << n.param1 << n.param2
2126 << (u8) (remove_metadata ? 0 : 1);
2131 if (pkt.getSize() > 0)
2132 m_clients.send(client_id, 0, &pkt, true);
2136 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2137 u16 net_proto_version)
2140 Create a packet with the block in the right format
2143 std::ostringstream os(std::ios_base::binary);
2144 block->serialize(os, ver, false);
2145 block->serializeNetworkSpecific(os);
2146 std::string s = os.str();
2148 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2150 pkt << block->getPos();
2151 pkt.putRawString(s.c_str(), s.size());
2155 void Server::SendBlocks(float dtime)
2157 MutexAutoLock envlock(m_env_mutex);
2158 //TODO check if one big lock could be faster then multiple small ones
2160 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2162 std::vector<PrioritySortedBlockTransfer> queue;
2164 u32 total_sending = 0;
2167 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2169 std::vector<session_t> clients = m_clients.getClientIDs();
2172 for (const session_t client_id : clients) {
2173 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2178 total_sending += client->getSendingCount();
2179 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2185 // Lowest priority number comes first.
2186 // Lowest is most important.
2187 std::sort(queue.begin(), queue.end());
2191 // Maximal total count calculation
2192 // The per-client block sends is halved with the maximal online users
2193 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2194 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2196 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2197 if (total_sending >= max_blocks_to_send)
2200 MapBlock *block = nullptr;
2202 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2203 } catch (const InvalidPositionException &e) {
2207 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2212 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2213 client->net_proto_version);
2215 client->SentBlock(block_to_send.pos);
2221 void Server::fillMediaCache()
2223 infostream<<"Server: Calculating media file checksums"<<std::endl;
2225 // Collect all media file paths
2226 std::vector<std::string> paths;
2227 m_modmgr->getModsMediaPaths(paths);
2228 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2229 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2231 // Collect media file information from paths into cache
2232 for (const std::string &mediapath : paths) {
2233 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2234 for (const fs::DirListNode &dln : dirlist) {
2235 if (dln.dir) // Ignode dirs
2237 std::string filename = dln.name;
2238 // If name contains illegal characters, ignore the file
2239 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2240 infostream<<"Server: ignoring illegal file name: \""
2241 << filename << "\"" << std::endl;
2244 // If name is not in a supported format, ignore it
2245 const char *supported_ext[] = {
2246 ".png", ".jpg", ".bmp", ".tga",
2247 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2249 ".x", ".b3d", ".md2", ".obj",
2250 // Custom translation file format
2254 if (removeStringEnd(filename, supported_ext).empty()){
2255 infostream << "Server: ignoring unsupported file extension: \""
2256 << filename << "\"" << std::endl;
2259 // Ok, attempt to load the file and add to cache
2260 std::string filepath;
2261 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2264 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2266 errorstream << "Server::fillMediaCache(): Could not open \""
2267 << filename << "\" for reading" << std::endl;
2270 std::ostringstream tmp_os(std::ios_base::binary);
2274 fis.read(buf, 1024);
2275 std::streamsize len = fis.gcount();
2276 tmp_os.write(buf, len);
2285 errorstream<<"Server::fillMediaCache(): Failed to read \""
2286 << filename << "\"" << std::endl;
2289 if(tmp_os.str().length() == 0) {
2290 errorstream << "Server::fillMediaCache(): Empty file \""
2291 << filepath << "\"" << std::endl;
2296 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2298 unsigned char *digest = sha1.getDigest();
2299 std::string sha1_base64 = base64_encode(digest, 20);
2300 std::string sha1_hex = hex_encode((char*)digest, 20);
2304 m_media[filename] = MediaInfo(filepath, sha1_base64);
2305 verbosestream << "Server: " << sha1_hex << " is " << filename
2311 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2313 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2317 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2320 std::string lang_suffix;
2321 lang_suffix.append(".").append(lang_code).append(".tr");
2322 for (const auto &i : m_media) {
2323 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2330 for (const auto &i : m_media) {
2331 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2333 pkt << i.first << i.second.sha1_digest;
2336 pkt << g_settings->get("remote_media");
2340 struct SendableMedia
2346 SendableMedia(const std::string &name_="", const std::string &path_="",
2347 const std::string &data_=""):
2354 void Server::sendRequestedMedia(session_t peer_id,
2355 const std::vector<std::string> &tosend)
2357 verbosestream<<"Server::sendRequestedMedia(): "
2358 <<"Sending files to client"<<std::endl;
2362 // Put 5kB in one bunch (this is not accurate)
2363 u32 bytes_per_bunch = 5000;
2365 std::vector< std::vector<SendableMedia> > file_bunches;
2366 file_bunches.emplace_back();
2368 u32 file_size_bunch_total = 0;
2370 for (const std::string &name : tosend) {
2371 if (m_media.find(name) == m_media.end()) {
2372 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2373 <<"unknown file \""<<(name)<<"\""<<std::endl;
2377 //TODO get path + name
2378 std::string tpath = m_media[name].path;
2381 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2383 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2384 <<tpath<<"\" for reading"<<std::endl;
2387 std::ostringstream tmp_os(std::ios_base::binary);
2391 fis.read(buf, 1024);
2392 std::streamsize len = fis.gcount();
2393 tmp_os.write(buf, len);
2394 file_size_bunch_total += len;
2403 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2404 <<name<<"\""<<std::endl;
2407 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2408 <<tname<<"\""<<std::endl;*/
2410 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2412 // Start next bunch if got enough data
2413 if(file_size_bunch_total >= bytes_per_bunch) {
2414 file_bunches.emplace_back();
2415 file_size_bunch_total = 0;
2420 /* Create and send packets */
2422 u16 num_bunches = file_bunches.size();
2423 for (u16 i = 0; i < num_bunches; i++) {
2426 u16 total number of texture bunches
2427 u16 index of this bunch
2428 u32 number of files in this bunch
2437 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2438 pkt << num_bunches << i << (u32) file_bunches[i].size();
2440 for (const SendableMedia &j : file_bunches[i]) {
2442 pkt.putLongString(j.data);
2445 verbosestream << "Server::sendRequestedMedia(): bunch "
2446 << i << "/" << num_bunches
2447 << " files=" << file_bunches[i].size()
2448 << " size=" << pkt.getSize() << std::endl;
2453 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2455 if(m_detached_inventories.count(name) == 0) {
2456 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2459 Inventory *inv = m_detached_inventories[name];
2460 std::ostringstream os(std::ios_base::binary);
2462 os << serializeString(name);
2466 std::string s = os.str();
2468 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2469 pkt.putRawString(s.c_str(), s.size());
2471 const std::string &check = m_detached_inventories_player[name];
2472 if (peer_id == PEER_ID_INEXISTENT) {
2474 return m_clients.sendToAll(&pkt);
2475 RemotePlayer *p = m_env->getPlayer(check.c_str());
2477 m_clients.send(p->getPeerId(), 0, &pkt, true);
2479 if (check.empty() || getPlayerName(peer_id) == check)
2484 void Server::sendDetachedInventories(session_t peer_id)
2486 for (const auto &detached_inventory : m_detached_inventories) {
2487 const std::string &name = detached_inventory.first;
2488 //Inventory *inv = i->second;
2489 sendDetachedInventory(name, peer_id);
2497 void Server::DiePlayer(session_t peer_id, const PlayerHPChangeReason &reason)
2499 PlayerSAO *playersao = getPlayerSAO(peer_id);
2500 // In some rare cases this can be NULL -- if the player is disconnected
2501 // when a Lua function modifies l_punch, for example
2505 infostream << "Server::DiePlayer(): Player "
2506 << playersao->getPlayer()->getName()
2507 << " dies" << std::endl;
2509 playersao->setHP(0, reason);
2511 // Trigger scripted stuff
2512 m_script->on_dieplayer(playersao, reason);
2514 SendPlayerHP(peer_id);
2515 SendDeathscreen(peer_id, false, v3f(0,0,0));
2518 void Server::RespawnPlayer(session_t peer_id)
2520 PlayerSAO *playersao = getPlayerSAO(peer_id);
2523 infostream << "Server::RespawnPlayer(): Player "
2524 << playersao->getPlayer()->getName()
2525 << " respawns" << std::endl;
2527 playersao->setHP(playersao->accessObjectProperties()->hp_max,
2528 PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN));
2529 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2531 bool repositioned = m_script->on_respawnplayer(playersao);
2532 if (!repositioned) {
2533 // setPos will send the new position to client
2534 playersao->setPos(findSpawnPos());
2537 SendPlayerHP(peer_id);
2541 void Server::DenySudoAccess(session_t peer_id)
2543 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2548 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2549 const std::string &str_reason, bool reconnect)
2551 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2553 m_clients.event(peer_id, CSE_SetDenied);
2554 DisconnectPeer(peer_id);
2558 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2559 const std::string &custom_reason)
2561 SendAccessDenied(peer_id, reason, custom_reason);
2562 m_clients.event(peer_id, CSE_SetDenied);
2563 DisconnectPeer(peer_id);
2566 // 13/03/15: remove this function when protocol version 25 will become
2567 // the minimum version for MT users, maybe in 1 year
2568 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2570 SendAccessDenied_Legacy(peer_id, reason);
2571 m_clients.event(peer_id, CSE_SetDenied);
2572 DisconnectPeer(peer_id);
2575 void Server::DisconnectPeer(session_t peer_id)
2577 m_modchannel_mgr->leaveAllChannels(peer_id);
2578 m_con->DisconnectPeer(peer_id);
2581 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2584 RemoteClient* client = getClient(peer_id, CS_Invalid);
2586 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2588 // Right now, the auth mechs don't change between login and sudo mode.
2589 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2590 client->allowed_sudo_mechs = sudo_auth_mechs;
2592 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2593 << g_settings->getFloat("dedicated_server_step")
2597 m_clients.event(peer_id, CSE_AuthAccept);
2599 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2601 // We only support SRP right now
2602 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2604 resp_pkt << sudo_auth_mechs;
2606 m_clients.event(peer_id, CSE_SudoSuccess);
2610 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2612 std::wstring message;
2615 Clear references to playing sounds
2617 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2618 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2619 ServerPlayingSound &psound = i->second;
2620 psound.clients.erase(peer_id);
2621 if (psound.clients.empty())
2622 m_playing_sounds.erase(i++);
2627 // clear formspec info so the next client can't abuse the current state
2628 m_formspec_state_data.erase(peer_id);
2630 RemotePlayer *player = m_env->getPlayer(peer_id);
2632 /* Run scripts and remove from environment */
2634 PlayerSAO *playersao = player->getPlayerSAO();
2637 // inform connected clients
2638 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2639 // (u16) 1 + std::string represents a vector serialization representation
2640 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2641 m_clients.sendToAll(¬ice);
2643 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2645 playersao->disconnected();
2652 if (player && reason != CDR_DENY) {
2653 std::ostringstream os(std::ios_base::binary);
2654 std::vector<session_t> clients = m_clients.getClientIDs();
2656 for (const session_t client_id : clients) {
2658 RemotePlayer *player = m_env->getPlayer(client_id);
2662 // Get name of player
2663 os << player->getName() << " ";
2666 std::string name = player->getName();
2667 actionstream << name << " "
2668 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2669 << " List of players: " << os.str() << std::endl;
2671 m_admin_chat->outgoing_queue.push_back(
2672 new ChatEventNick(CET_NICK_REMOVE, name));
2676 MutexAutoLock env_lock(m_env_mutex);
2677 m_clients.DeleteClient(peer_id);
2681 // Send leave chat message to all remaining clients
2682 if (!message.empty()) {
2683 SendChatMessage(PEER_ID_INEXISTENT,
2684 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2688 void Server::UpdateCrafting(RemotePlayer *player)
2690 // Get a preview for crafting
2692 InventoryLocation loc;
2693 loc.setPlayer(player->getName());
2694 std::vector<ItemStack> output_replacements;
2695 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2696 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2697 (&player->inventory)->getList("craft"), loc);
2699 // Put the new preview in
2700 InventoryList *plist = player->inventory.getList("craftpreview");
2701 sanity_check(plist);
2702 sanity_check(plist->getSize() >= 1);
2703 plist->changeItem(0, preview);
2706 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2708 if (evt->type == CET_NICK_ADD) {
2709 // The terminal informed us of its nick choice
2710 m_admin_nick = ((ChatEventNick *)evt)->nick;
2711 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2712 errorstream << "You haven't set up an account." << std::endl
2713 << "Please log in using the client as '"
2714 << m_admin_nick << "' with a secure password." << std::endl
2715 << "Until then, you can't execute admin tasks via the console," << std::endl
2716 << "and everybody can claim the user account instead of you," << std::endl
2717 << "giving them full control over this server." << std::endl;
2720 assert(evt->type == CET_CHAT);
2721 handleAdminChat((ChatEventChat *)evt);
2725 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2726 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2728 // If something goes wrong, this player is to blame
2729 RollbackScopeActor rollback_scope(m_rollback,
2730 std::string("player:") + name);
2732 if (g_settings->getBool("strip_color_codes"))
2733 wmessage = unescape_enriched(wmessage);
2736 switch (player->canSendChatMessage()) {
2737 case RPLAYER_CHATRESULT_FLOODING: {
2738 std::wstringstream ws;
2739 ws << L"You cannot send more messages. You are limited to "
2740 << g_settings->getFloat("chat_message_limit_per_10sec")
2741 << L" messages per 10 seconds.";
2744 case RPLAYER_CHATRESULT_KICK:
2745 DenyAccess_Legacy(player->getPeerId(),
2746 L"You have been kicked due to message flooding.");
2748 case RPLAYER_CHATRESULT_OK:
2751 FATAL_ERROR("Unhandled chat filtering result found.");
2755 if (m_max_chatmessage_length > 0
2756 && wmessage.length() > m_max_chatmessage_length) {
2757 return L"Your message exceed the maximum chat message limit set on the server. "
2758 L"It was refused. Send a shorter message";
2761 // Run script hook, exit if script ate the chat message
2762 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2767 // Whether to send line to the player that sent the message, or to all players
2768 bool broadcast_line = true;
2770 if (check_shout_priv && !checkPriv(name, "shout")) {
2771 line += L"-!- You don't have permission to shout.";
2772 broadcast_line = false;
2781 Tell calling method to send the message to sender
2783 if (!broadcast_line)
2787 Send the message to others
2789 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2791 std::vector<session_t> clients = m_clients.getClientIDs();
2794 Send the message back to the inital sender
2795 if they are using protocol version >= 29
2798 session_t peer_id_to_avoid_sending =
2799 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2801 if (player && player->protocol_version >= 29)
2802 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2804 for (u16 cid : clients) {
2805 if (cid != peer_id_to_avoid_sending)
2806 SendChatMessage(cid, ChatMessage(line));
2811 void Server::handleAdminChat(const ChatEventChat *evt)
2813 std::string name = evt->nick;
2814 std::wstring wname = utf8_to_wide(name);
2815 std::wstring wmessage = evt->evt_msg;
2817 std::wstring answer = handleChat(name, wname, wmessage);
2819 // If asked to send answer to sender
2820 if (!answer.empty()) {
2821 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2825 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2827 RemoteClient *client = getClientNoEx(peer_id,state_min);
2829 throw ClientNotFoundException("Client not found");
2833 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2835 return m_clients.getClientNoEx(peer_id, state_min);
2838 std::string Server::getPlayerName(session_t peer_id)
2840 RemotePlayer *player = m_env->getPlayer(peer_id);
2842 return "[id="+itos(peer_id)+"]";
2843 return player->getName();
2846 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2848 RemotePlayer *player = m_env->getPlayer(peer_id);
2851 return player->getPlayerSAO();
2854 std::wstring Server::getStatusString()
2856 std::wostringstream os(std::ios_base::binary);
2859 os<<L"version="<<narrow_to_wide(g_version_string);
2861 os<<L", uptime="<<m_uptime.get();
2863 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2864 // Information about clients
2867 std::vector<session_t> clients = m_clients.getClientIDs();
2868 for (session_t client_id : clients) {
2870 RemotePlayer *player = m_env->getPlayer(client_id);
2871 // Get name of player
2872 std::wstring name = L"unknown";
2874 name = narrow_to_wide(player->getName());
2875 // Add name to information string
2884 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2885 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2887 if (!g_settings->get("motd").empty())
2888 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2892 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2894 std::set<std::string> privs;
2895 m_script->getAuth(name, NULL, &privs);
2899 bool Server::checkPriv(const std::string &name, const std::string &priv)
2901 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2902 return (privs.count(priv) != 0);
2905 void Server::reportPrivsModified(const std::string &name)
2908 std::vector<session_t> clients = m_clients.getClientIDs();
2909 for (const session_t client_id : clients) {
2910 RemotePlayer *player = m_env->getPlayer(client_id);
2911 reportPrivsModified(player->getName());
2914 RemotePlayer *player = m_env->getPlayer(name.c_str());
2917 SendPlayerPrivileges(player->getPeerId());
2918 PlayerSAO *sao = player->getPlayerSAO();
2921 sao->updatePrivileges(
2922 getPlayerEffectivePrivs(name),
2927 void Server::reportInventoryFormspecModified(const std::string &name)
2929 RemotePlayer *player = m_env->getPlayer(name.c_str());
2932 SendPlayerInventoryFormspec(player->getPeerId());
2935 void Server::reportFormspecPrependModified(const std::string &name)
2937 RemotePlayer *player = m_env->getPlayer(name.c_str());
2940 SendPlayerFormspecPrepend(player->getPeerId());
2943 void Server::setIpBanned(const std::string &ip, const std::string &name)
2945 m_banmanager->add(ip, name);
2948 void Server::unsetIpBanned(const std::string &ip_or_name)
2950 m_banmanager->remove(ip_or_name);
2953 std::string Server::getBanDescription(const std::string &ip_or_name)
2955 return m_banmanager->getBanDescription(ip_or_name);
2958 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2960 // m_env will be NULL if the server is initializing
2964 if (m_admin_nick == name && !m_admin_nick.empty()) {
2965 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2968 RemotePlayer *player = m_env->getPlayer(name);
2973 if (player->getPeerId() == PEER_ID_INEXISTENT)
2976 SendChatMessage(player->getPeerId(), ChatMessage(msg));
2979 bool Server::showFormspec(const char *playername, const std::string &formspec,
2980 const std::string &formname)
2982 // m_env will be NULL if the server is initializing
2986 RemotePlayer *player = m_env->getPlayer(playername);
2990 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
2994 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
2999 u32 id = player->addHud(form);
3001 SendHUDAdd(player->getPeerId(), id, form);
3006 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3010 HudElement* todel = player->removeHud(id);
3017 SendHUDRemove(player->getPeerId(), id);
3021 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3026 SendHUDChange(player->getPeerId(), id, stat, data);
3030 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3035 SendHUDSetFlags(player->getPeerId(), flags, mask);
3036 player->hud_flags &= ~mask;
3037 player->hud_flags |= flags;
3039 PlayerSAO* playersao = player->getPlayerSAO();
3044 m_script->player_event(playersao, "hud_changed");
3048 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3053 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3056 player->setHotbarItemcount(hotbar_itemcount);
3057 std::ostringstream os(std::ios::binary);
3058 writeS32(os, hotbar_itemcount);
3059 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3063 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3068 player->setHotbarImage(name);
3069 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3072 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3077 player->setHotbarSelectedImage(name);
3078 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3081 Address Server::getPeerAddress(session_t peer_id)
3083 return m_con->GetPeerAddress(peer_id);
3086 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3087 v2s32 animation_frames[4], f32 frame_speed)
3089 sanity_check(player);
3090 player->setLocalAnimations(animation_frames, frame_speed);
3091 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3094 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3096 sanity_check(player);
3097 player->eye_offset_first = first;
3098 player->eye_offset_third = third;
3099 SendEyeOffset(player->getPeerId(), first, third);
3102 void Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3103 const std::string &type, const std::vector<std::string> ¶ms,
3106 sanity_check(player);
3107 player->setSky(bgcolor, type, params, clouds);
3108 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3111 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3113 sanity_check(player);
3114 player->setCloudParams(params);
3115 SendCloudParams(player->getPeerId(), params);
3118 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3124 player->overrideDayNightRatio(do_override, ratio);
3125 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3129 void Server::notifyPlayers(const std::wstring &msg)
3131 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3134 void Server::spawnParticle(const std::string &playername, v3f pos,
3135 v3f velocity, v3f acceleration,
3136 float expirationtime, float size, bool
3137 collisiondetection, bool collision_removal,
3138 bool vertical, const std::string &texture,
3139 const struct TileAnimationParams &animation, u8 glow)
3141 // m_env will be NULL if the server is initializing
3145 session_t peer_id = PEER_ID_INEXISTENT;
3147 if (!playername.empty()) {
3148 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3151 peer_id = player->getPeerId();
3152 proto_ver = player->protocol_version;
3155 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3156 expirationtime, size, collisiondetection,
3157 collision_removal, vertical, texture, animation, glow);
3160 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3161 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3162 float minexptime, float maxexptime, float minsize, float maxsize,
3163 bool collisiondetection, bool collision_removal,
3164 ServerActiveObject *attached, bool vertical, const std::string &texture,
3165 const std::string &playername, const struct TileAnimationParams &animation,
3168 // m_env will be NULL if the server is initializing
3172 session_t peer_id = PEER_ID_INEXISTENT;
3174 if (!playername.empty()) {
3175 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3178 peer_id = player->getPeerId();
3179 proto_ver = player->protocol_version;
3182 u16 attached_id = attached ? attached->getId() : 0;
3185 if (attached_id == 0)
3186 id = m_env->addParticleSpawner(spawntime);
3188 id = m_env->addParticleSpawner(spawntime, attached_id);
3190 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3191 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3192 minexptime, maxexptime, minsize, maxsize,
3193 collisiondetection, collision_removal, attached_id, vertical,
3194 texture, id, animation, glow);
3199 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3201 // m_env will be NULL if the server is initializing
3203 throw ServerError("Can't delete particle spawners during initialisation!");
3205 session_t peer_id = PEER_ID_INEXISTENT;
3206 if (!playername.empty()) {
3207 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3210 peer_id = player->getPeerId();
3213 m_env->deleteParticleSpawner(id);
3214 SendDeleteParticleSpawner(peer_id, id);
3217 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3219 if(m_detached_inventories.count(name) > 0){
3220 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3221 delete m_detached_inventories[name];
3223 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3225 Inventory *inv = new Inventory(m_itemdef);
3227 m_detached_inventories[name] = inv;
3228 m_detached_inventories_player[name] = player;
3229 //TODO find a better way to do this
3230 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3234 // actions: time-reversed list
3235 // Return value: success/failure
3236 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3237 std::list<std::string> *log)
3239 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3240 ServerMap *map = (ServerMap*)(&m_env->getMap());
3242 // Fail if no actions to handle
3243 if(actions.empty()){
3244 log->push_back("Nothing to do.");
3251 for (const RollbackAction &action : actions) {
3253 bool success = action.applyRevert(map, this, this);
3256 std::ostringstream os;
3257 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3258 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3260 log->push_back(os.str());
3262 std::ostringstream os;
3263 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3264 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3266 log->push_back(os.str());
3270 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3271 <<" failed"<<std::endl;
3273 // Call it done if less than half failed
3274 return num_failed <= num_tried/2;
3277 // IGameDef interface
3279 IItemDefManager *Server::getItemDefManager()
3284 const NodeDefManager *Server::getNodeDefManager()
3289 ICraftDefManager *Server::getCraftDefManager()
3294 u16 Server::allocateUnknownNodeId(const std::string &name)
3296 return m_nodedef->allocateDummy(name);
3299 MtEventManager *Server::getEventManager()
3304 IWritableItemDefManager *Server::getWritableItemDefManager()
3309 NodeDefManager *Server::getWritableNodeDefManager()
3314 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3319 const std::vector<ModSpec> & Server::getMods() const
3321 return m_modmgr->getMods();
3324 const ModSpec *Server::getModSpec(const std::string &modname) const
3326 return m_modmgr->getModSpec(modname);
3329 void Server::getModNames(std::vector<std::string> &modlist)
3331 m_modmgr->getModNames(modlist);
3334 std::string Server::getBuiltinLuaPath()
3336 return porting::path_share + DIR_DELIM + "builtin";
3339 std::string Server::getModStoragePath() const
3341 return m_path_world + DIR_DELIM + "mod_storage";
3344 v3f Server::findSpawnPos()
3346 ServerMap &map = m_env->getServerMap();
3348 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3349 return nodeposf * BS;
3352 bool is_good = false;
3353 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3354 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3356 // Try to find a good place a few times
3357 for(s32 i = 0; i < 4000 && !is_good; i++) {
3358 s32 range = MYMIN(1 + i, range_max);
3359 // We're going to try to throw the player to this position
3360 v2s16 nodepos2d = v2s16(
3361 -range + (myrand() % (range * 2)),
3362 -range + (myrand() % (range * 2)));
3364 // Get spawn level at point
3365 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3366 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3367 // the mapgen to signify an unsuitable spawn position
3368 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3371 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3374 for (s32 i = 0; i < 10; i++) {
3375 v3s16 blockpos = getNodeBlockPos(nodepos);
3376 map.emergeBlock(blockpos, true);
3377 content_t c = map.getNodeNoEx(nodepos).getContent();
3378 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3380 if (air_count >= 2) {
3381 nodeposf = intToFloat(nodepos, BS);
3382 // Don't spawn the player outside map boundaries
3383 if (objectpos_over_limit(nodeposf))
3396 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3398 m_shutdown_timer = delay;
3399 m_shutdown_msg = msg;
3400 m_shutdown_ask_reconnect = reconnect;
3402 if (delay == 0.0f) {
3403 // No delay, shutdown immediately
3404 m_shutdown_requested = true;
3405 // only print to the infostream, a chat message saying
3406 // "Server Shutting Down" is sent when the server destructs.
3407 infostream << "*** Immediate Server shutdown requested." << std::endl;
3408 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3409 // Negative delay, cancel shutdown if requested
3410 m_shutdown_timer = 0.0f;
3411 m_shutdown_msg = "";
3412 m_shutdown_ask_reconnect = false;
3413 m_shutdown_requested = false;
3414 std::wstringstream ws;
3416 ws << L"*** Server shutdown canceled.";
3418 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3419 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3420 } else if (delay > 0.0f) {
3421 // Positive delay, tell the clients when the server will shut down
3422 std::wstringstream ws;
3424 ws << L"*** Server shutting down in "
3425 << duration_to_string(myround(m_shutdown_timer)).c_str()
3428 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3429 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3433 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3436 Try to get an existing player
3438 RemotePlayer *player = m_env->getPlayer(name);
3440 // If player is already connected, cancel
3441 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3442 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3447 If player with the wanted peer_id already exists, cancel.
3449 if (m_env->getPlayer(peer_id)) {
3450 infostream<<"emergePlayer(): Player with wrong name but same"
3451 " peer_id already exists"<<std::endl;
3456 player = new RemotePlayer(name, idef());
3459 bool newplayer = false;
3462 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3464 // Complete init with server parts
3465 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3466 player->protocol_version = proto_version;
3470 m_script->on_newplayer(playersao);
3476 bool Server::registerModStorage(ModMetadata *storage)
3478 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3479 errorstream << "Unable to register same mod storage twice. Storage name: "
3480 << storage->getModName() << std::endl;
3484 m_mod_storages[storage->getModName()] = storage;
3488 void Server::unregisterModStorage(const std::string &name)
3490 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3491 if (it != m_mod_storages.end()) {
3492 // Save unconditionaly on unregistration
3493 it->second->save(getModStoragePath());
3494 m_mod_storages.erase(name);
3498 void dedicated_server_loop(Server &server, bool &kill)
3500 verbosestream<<"dedicated_server_loop()"<<std::endl;
3502 IntervalLimiter m_profiler_interval;
3504 static thread_local const float steplen =
3505 g_settings->getFloat("dedicated_server_step");
3506 static thread_local const float profiler_print_interval =
3507 g_settings->getFloat("profiler_print_interval");
3510 // This is kind of a hack but can be done like this
3511 // because server.step() is very light
3513 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3514 sleep_ms((int)(steplen*1000.0));
3516 server.step(steplen);
3518 if (server.getShutdownRequested() || kill)
3524 if (profiler_print_interval != 0) {
3525 if(m_profiler_interval.step(steplen, profiler_print_interval))
3527 infostream<<"Profiler:"<<std::endl;
3528 g_profiler->print(infostream);
3529 g_profiler->clear();
3534 infostream << "Dedicated server quitting" << std::endl;
3536 if (g_settings->getBool("server_announce"))
3537 ServerList::sendAnnounce(ServerList::AA_DELETE,
3538 server.m_bind_addr.getPort());
3547 bool Server::joinModChannel(const std::string &channel)
3549 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3550 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3553 bool Server::leaveModChannel(const std::string &channel)
3555 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3558 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3560 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3563 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3567 ModChannel* Server::getModChannel(const std::string &channel)
3569 return m_modchannel_mgr->getModChannel(channel);
3572 void Server::broadcastModChannelMessage(const std::string &channel,
3573 const std::string &message, session_t from_peer)
3575 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3579 if (message.size() > STRING_MAX_LEN) {
3580 warningstream << "ModChannel message too long, dropping before sending "
3581 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3582 << channel << ")" << std::endl;
3587 if (from_peer != PEER_ID_SERVER) {
3588 sender = getPlayerName(from_peer);
3591 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3592 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3593 resp_pkt << channel << sender << message;
3594 for (session_t peer_id : peers) {
3596 if (peer_id == from_peer)
3599 Send(peer_id, &resp_pkt);
3602 if (from_peer != PEER_ID_SERVER) {
3603 m_script->on_modchannel_message(channel, sender, message);