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);
1035 SendPlayerBreath(playersao);
1037 // Note things in chat if not in simple singleplayer mode
1038 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1039 // Send information about server to player in chat
1040 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1042 Address addr = getPeerAddress(player->getPeerId());
1043 std::string ip_str = addr.serializeString();
1044 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1049 const std::vector<std::string> &names = m_clients.getPlayerNames();
1051 actionstream << player->getName() << " joins game. List of players: ";
1053 for (const std::string &name : names) {
1054 actionstream << name << " ";
1057 actionstream << player->getName() <<std::endl;
1062 inline void Server::handleCommand(NetworkPacket* pkt)
1064 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1065 (this->*opHandle.handler)(pkt);
1068 void Server::ProcessData(NetworkPacket *pkt)
1070 // Environment is locked first.
1071 MutexAutoLock envlock(m_env_mutex);
1073 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1074 u32 peer_id = pkt->getPeerId();
1077 Address address = getPeerAddress(peer_id);
1078 std::string addr_s = address.serializeString();
1080 if(m_banmanager->isIpBanned(addr_s)) {
1081 std::string ban_name = m_banmanager->getBanName(addr_s);
1082 infostream << "Server: A banned client tried to connect from "
1083 << addr_s << "; banned name was "
1084 << ban_name << std::endl;
1085 // This actually doesn't seem to transfer to the client
1086 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1087 + utf8_to_wide(ban_name));
1091 catch(con::PeerNotFoundException &e) {
1093 * no peer for this packet found
1094 * most common reason is peer timeout, e.g. peer didn't
1095 * respond for some time, your server was overloaded or
1098 infostream << "Server::ProcessData(): Canceling: peer "
1099 << peer_id << " not found" << std::endl;
1104 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1106 // Command must be handled into ToServerCommandHandler
1107 if (command >= TOSERVER_NUM_MSG_TYPES) {
1108 infostream << "Server: Ignoring unknown command "
1109 << command << std::endl;
1113 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1118 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1120 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1121 errorstream << "Server::ProcessData(): Cancelling: Peer"
1122 " serialization format invalid or not initialized."
1123 " Skipping incoming command=" << command << std::endl;
1127 /* Handle commands related to client startup */
1128 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1133 if (m_clients.getClientState(peer_id) < CS_Active) {
1134 if (command == TOSERVER_PLAYERPOS) return;
1136 errorstream << "Got packet command: " << command << " for peer id "
1137 << peer_id << " but client isn't active yet. Dropping packet "
1143 } catch (SendFailedException &e) {
1144 errorstream << "Server::ProcessData(): SendFailedException: "
1145 << "what=" << e.what()
1147 } catch (PacketError &e) {
1148 actionstream << "Server::ProcessData(): PacketError: "
1149 << "what=" << e.what()
1154 void Server::setTimeOfDay(u32 time)
1156 m_env->setTimeOfDay(time);
1157 m_time_of_day_send_timer = 0;
1160 void Server::onMapEditEvent(MapEditEvent *event)
1162 if(m_ignore_map_edit_events)
1164 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1166 MapEditEvent *e = event->clone();
1167 m_unsent_map_edit_queue.push(e);
1170 Inventory* Server::getInventory(const InventoryLocation &loc)
1173 case InventoryLocation::UNDEFINED:
1174 case InventoryLocation::CURRENT_PLAYER:
1176 case InventoryLocation::PLAYER:
1178 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1181 PlayerSAO *playersao = player->getPlayerSAO();
1184 return playersao->getInventory();
1187 case InventoryLocation::NODEMETA:
1189 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1192 return meta->getInventory();
1195 case InventoryLocation::DETACHED:
1197 if(m_detached_inventories.count(loc.name) == 0)
1199 return m_detached_inventories[loc.name];
1203 sanity_check(false); // abort
1208 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1211 case InventoryLocation::UNDEFINED:
1213 case InventoryLocation::PLAYER:
1218 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1223 PlayerSAO *playersao = player->getPlayerSAO();
1227 SendInventory(playersao);
1230 case InventoryLocation::NODEMETA:
1232 v3s16 blockpos = getNodeBlockPos(loc.p);
1234 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1236 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1238 m_clients.markBlockposAsNotSent(blockpos);
1241 case InventoryLocation::DETACHED:
1243 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1247 sanity_check(false); // abort
1252 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1254 std::vector<session_t> clients = m_clients.getClientIDs();
1256 // Set the modified blocks unsent for all the clients
1257 for (const session_t client_id : clients) {
1258 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1259 client->SetBlocksNotSent(block);
1264 void Server::peerAdded(con::Peer *peer)
1266 verbosestream<<"Server::peerAdded(): peer->id="
1267 <<peer->id<<std::endl;
1269 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1272 void Server::deletingPeer(con::Peer *peer, bool timeout)
1274 verbosestream<<"Server::deletingPeer(): peer->id="
1275 <<peer->id<<", timeout="<<timeout<<std::endl;
1277 m_clients.event(peer->id, CSE_Disconnect);
1278 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1281 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1283 *retval = m_con->getPeerStat(peer_id,type);
1284 return *retval != -1;
1287 bool Server::getClientInfo(
1296 std::string* vers_string
1299 *state = m_clients.getClientState(peer_id);
1301 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1308 *uptime = client->uptime();
1309 *ser_vers = client->serialization_version;
1310 *prot_vers = client->net_proto_version;
1312 *major = client->getMajor();
1313 *minor = client->getMinor();
1314 *patch = client->getPatch();
1315 *vers_string = client->getPatch();
1322 void Server::handlePeerChanges()
1324 while(!m_peer_change_queue.empty())
1326 con::PeerChange c = m_peer_change_queue.front();
1327 m_peer_change_queue.pop();
1329 verbosestream<<"Server: Handling peer change: "
1330 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1335 case con::PEER_ADDED:
1336 m_clients.CreateClient(c.peer_id);
1339 case con::PEER_REMOVED:
1340 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1344 FATAL_ERROR("Invalid peer change event received!");
1350 void Server::printToConsoleOnly(const std::string &text)
1353 m_admin_chat->outgoing_queue.push_back(
1354 new ChatEventChat("", utf8_to_wide(text)));
1356 std::cout << text << std::endl;
1360 void Server::Send(NetworkPacket *pkt)
1362 Send(pkt->getPeerId(), pkt);
1365 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1367 m_clients.send(peer_id,
1368 clientCommandFactoryTable[pkt->getCommand()].channel,
1370 clientCommandFactoryTable[pkt->getCommand()].reliable);
1373 void Server::SendMovement(session_t peer_id)
1375 std::ostringstream os(std::ios_base::binary);
1377 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1379 pkt << g_settings->getFloat("movement_acceleration_default");
1380 pkt << g_settings->getFloat("movement_acceleration_air");
1381 pkt << g_settings->getFloat("movement_acceleration_fast");
1382 pkt << g_settings->getFloat("movement_speed_walk");
1383 pkt << g_settings->getFloat("movement_speed_crouch");
1384 pkt << g_settings->getFloat("movement_speed_fast");
1385 pkt << g_settings->getFloat("movement_speed_climb");
1386 pkt << g_settings->getFloat("movement_speed_jump");
1387 pkt << g_settings->getFloat("movement_liquid_fluidity");
1388 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1389 pkt << g_settings->getFloat("movement_liquid_sink");
1390 pkt << g_settings->getFloat("movement_gravity");
1395 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1397 if (!g_settings->getBool("enable_damage"))
1400 session_t peer_id = playersao->getPeerID();
1401 bool is_alive = playersao->getHP() > 0;
1404 SendPlayerHP(peer_id);
1409 void Server::SendHP(session_t peer_id, u16 hp)
1411 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1416 void Server::SendBreath(session_t peer_id, u16 breath)
1418 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1419 pkt << (u16) breath;
1423 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1424 const std::string &custom_reason, bool reconnect)
1426 assert(reason < SERVER_ACCESSDENIED_MAX);
1428 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1430 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1431 pkt << custom_reason;
1432 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1433 reason == SERVER_ACCESSDENIED_CRASH)
1434 pkt << custom_reason << (u8)reconnect;
1438 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1440 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1445 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1446 v3f camera_point_target)
1448 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1449 pkt << set_camera_point_target << camera_point_target;
1453 void Server::SendItemDef(session_t peer_id,
1454 IItemDefManager *itemdef, u16 protocol_version)
1456 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1460 u32 length of the next item
1461 zlib-compressed serialized ItemDefManager
1463 std::ostringstream tmp_os(std::ios::binary);
1464 itemdef->serialize(tmp_os, protocol_version);
1465 std::ostringstream tmp_os2(std::ios::binary);
1466 compressZlib(tmp_os.str(), tmp_os2);
1467 pkt.putLongString(tmp_os2.str());
1470 verbosestream << "Server: Sending item definitions to id(" << peer_id
1471 << "): size=" << pkt.getSize() << std::endl;
1476 void Server::SendNodeDef(session_t peer_id,
1477 const NodeDefManager *nodedef, u16 protocol_version)
1479 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1483 u32 length of the next item
1484 zlib-compressed serialized NodeDefManager
1486 std::ostringstream tmp_os(std::ios::binary);
1487 nodedef->serialize(tmp_os, protocol_version);
1488 std::ostringstream tmp_os2(std::ios::binary);
1489 compressZlib(tmp_os.str(), tmp_os2);
1491 pkt.putLongString(tmp_os2.str());
1494 verbosestream << "Server: Sending node definitions to id(" << peer_id
1495 << "): size=" << pkt.getSize() << std::endl;
1501 Non-static send methods
1504 void Server::SendInventory(PlayerSAO* playerSAO)
1506 UpdateCrafting(playerSAO->getPlayer());
1512 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1514 std::ostringstream os;
1515 playerSAO->getInventory()->serialize(os);
1517 std::string s = os.str();
1519 pkt.putRawString(s.c_str(), s.size());
1523 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1525 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1527 u8 type = message.type;
1528 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1530 if (peer_id != PEER_ID_INEXISTENT) {
1531 RemotePlayer *player = m_env->getPlayer(peer_id);
1537 m_clients.sendToAll(&pkt);
1541 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1542 const std::string &formname)
1544 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1545 if (formspec.empty()){
1546 //the client should close the formspec
1547 m_formspec_state_data.erase(peer_id);
1548 pkt.putLongString("");
1550 m_formspec_state_data[peer_id] = formname;
1551 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1558 // Spawns a particle on peer with peer_id
1559 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1560 v3f pos, v3f velocity, v3f acceleration,
1561 float expirationtime, float size, bool collisiondetection,
1562 bool collision_removal,
1563 bool vertical, const std::string &texture,
1564 const struct TileAnimationParams &animation, u8 glow)
1566 static thread_local const float radius =
1567 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1569 if (peer_id == PEER_ID_INEXISTENT) {
1570 std::vector<session_t> clients = m_clients.getClientIDs();
1572 for (const session_t client_id : clients) {
1573 RemotePlayer *player = m_env->getPlayer(client_id);
1577 PlayerSAO *sao = player->getPlayerSAO();
1581 // Do not send to distant clients
1582 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1585 SendSpawnParticle(client_id, player->protocol_version,
1586 pos, velocity, acceleration,
1587 expirationtime, size, collisiondetection,
1588 collision_removal, vertical, texture, animation, glow);
1593 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1595 pkt << pos << velocity << acceleration << expirationtime
1596 << size << collisiondetection;
1597 pkt.putLongString(texture);
1599 pkt << collision_removal;
1600 // This is horrible but required (why are there two ways to serialize pkts?)
1601 std::ostringstream os(std::ios_base::binary);
1602 animation.serialize(os, protocol_version);
1603 pkt.putRawString(os.str());
1609 // Adds a ParticleSpawner on peer with peer_id
1610 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1611 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1612 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1613 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1614 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1615 const struct TileAnimationParams &animation, u8 glow)
1617 if (peer_id == PEER_ID_INEXISTENT) {
1618 // This sucks and should be replaced:
1619 std::vector<session_t> clients = m_clients.getClientIDs();
1620 for (const session_t client_id : clients) {
1621 RemotePlayer *player = m_env->getPlayer(client_id);
1624 SendAddParticleSpawner(client_id, player->protocol_version,
1625 amount, spawntime, minpos, maxpos,
1626 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1627 minsize, maxsize, collisiondetection, collision_removal,
1628 attached_id, vertical, texture, id, animation, glow);
1633 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1635 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1636 << minacc << maxacc << minexptime << maxexptime << minsize
1637 << maxsize << collisiondetection;
1639 pkt.putLongString(texture);
1641 pkt << id << vertical;
1642 pkt << collision_removal;
1644 // This is horrible but required
1645 std::ostringstream os(std::ios_base::binary);
1646 animation.serialize(os, protocol_version);
1647 pkt.putRawString(os.str());
1653 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1655 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1657 // Ugly error in this packet
1660 if (peer_id != PEER_ID_INEXISTENT)
1663 m_clients.sendToAll(&pkt);
1667 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1669 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1671 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1672 << form->text << form->number << form->item << form->dir
1673 << form->align << form->offset << form->world_pos << form->size;
1678 void Server::SendHUDRemove(session_t peer_id, u32 id)
1680 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1685 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1687 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1688 pkt << id << (u8) stat;
1692 case HUD_STAT_SCALE:
1693 case HUD_STAT_ALIGN:
1694 case HUD_STAT_OFFSET:
1695 pkt << *(v2f *) value;
1699 pkt << *(std::string *) value;
1701 case HUD_STAT_WORLD_POS:
1702 pkt << *(v3f *) value;
1705 pkt << *(v2s32 *) value;
1707 case HUD_STAT_NUMBER:
1711 pkt << *(u32 *) value;
1718 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1720 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1722 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1724 pkt << flags << mask;
1729 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1731 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1732 pkt << param << value;
1736 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1737 const std::string &type, const std::vector<std::string> ¶ms,
1740 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1741 pkt << bgcolor << type << (u16) params.size();
1743 for (const std::string ¶m : params)
1751 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1753 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1754 pkt << params.density << params.color_bright << params.color_ambient
1755 << params.height << params.thickness << params.speed;
1759 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1762 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1765 pkt << do_override << (u16) (ratio * 65535);
1770 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1772 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1773 pkt << time << time_speed;
1775 if (peer_id == PEER_ID_INEXISTENT) {
1776 m_clients.sendToAll(&pkt);
1783 void Server::SendPlayerHP(session_t peer_id)
1785 PlayerSAO *playersao = getPlayerSAO(peer_id);
1786 // In some rare case if the player is disconnected
1787 // while Lua call l_punch, for example, this can be NULL
1791 SendHP(peer_id, playersao->getHP());
1792 m_script->player_event(playersao,"health_changed");
1794 // Send to other clients
1795 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1796 ActiveObjectMessage aom(playersao->getId(), true, str);
1797 playersao->m_messages_out.push(aom);
1800 void Server::SendPlayerBreath(PlayerSAO *sao)
1804 m_script->player_event(sao, "breath_changed");
1805 SendBreath(sao->getPeerID(), sao->getBreath());
1808 void Server::SendMovePlayer(session_t peer_id)
1810 RemotePlayer *player = m_env->getPlayer(peer_id);
1812 PlayerSAO *sao = player->getPlayerSAO();
1815 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1816 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1819 v3f pos = sao->getBasePosition();
1820 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1821 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1822 << " pitch=" << sao->getPitch()
1823 << " yaw=" << sao->getYaw()
1830 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1831 f32 animation_speed)
1833 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1836 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1837 << animation_frames[3] << animation_speed;
1842 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1844 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1845 pkt << first << third;
1849 void Server::SendPlayerPrivileges(session_t peer_id)
1851 RemotePlayer *player = m_env->getPlayer(peer_id);
1853 if(player->getPeerId() == PEER_ID_INEXISTENT)
1856 std::set<std::string> privs;
1857 m_script->getAuth(player->getName(), NULL, &privs);
1859 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1860 pkt << (u16) privs.size();
1862 for (const std::string &priv : privs) {
1869 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1871 RemotePlayer *player = m_env->getPlayer(peer_id);
1873 if(player->getPeerId() == PEER_ID_INEXISTENT)
1876 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1877 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1881 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1883 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1884 pkt.putRawString(datas.c_str(), datas.size());
1886 return pkt.getSize();
1889 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1892 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1893 datas.size(), peer_id);
1895 pkt.putRawString(datas.c_str(), datas.size());
1897 m_clients.send(pkt.getPeerId(),
1898 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1902 void Server::SendCSMFlavourLimits(session_t peer_id)
1904 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1905 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1906 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1910 s32 Server::playSound(const SimpleSoundSpec &spec,
1911 const ServerSoundParams ¶ms)
1913 // Find out initial position of sound
1914 bool pos_exists = false;
1915 v3f pos = params.getPos(m_env, &pos_exists);
1916 // If position is not found while it should be, cancel sound
1917 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1920 // Filter destination clients
1921 std::vector<session_t> dst_clients;
1922 if(!params.to_player.empty()) {
1923 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1925 infostream<<"Server::playSound: Player \""<<params.to_player
1926 <<"\" not found"<<std::endl;
1929 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1930 infostream<<"Server::playSound: Player \""<<params.to_player
1931 <<"\" not connected"<<std::endl;
1934 dst_clients.push_back(player->getPeerId());
1936 std::vector<session_t> clients = m_clients.getClientIDs();
1938 for (const session_t client_id : clients) {
1939 RemotePlayer *player = m_env->getPlayer(client_id);
1943 PlayerSAO *sao = player->getPlayerSAO();
1948 if(sao->getBasePosition().getDistanceFrom(pos) >
1949 params.max_hear_distance)
1952 dst_clients.push_back(client_id);
1956 if(dst_clients.empty())
1960 s32 id = m_next_sound_id++;
1961 // The sound will exist as a reference in m_playing_sounds
1962 m_playing_sounds[id] = ServerPlayingSound();
1963 ServerPlayingSound &psound = m_playing_sounds[id];
1964 psound.params = params;
1967 float gain = params.gain * spec.gain;
1968 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1969 pkt << id << spec.name << gain
1970 << (u8) params.type << pos << params.object
1971 << params.loop << params.fade << params.pitch;
1973 // Backwards compability
1974 bool play_sound = gain > 0;
1976 for (const u16 dst_client : dst_clients) {
1977 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
1978 psound.clients.insert(dst_client);
1979 m_clients.send(dst_client, 0, &pkt, true);
1984 void Server::stopSound(s32 handle)
1986 // Get sound reference
1987 std::unordered_map<s32, ServerPlayingSound>::iterator i =
1988 m_playing_sounds.find(handle);
1989 if (i == m_playing_sounds.end())
1991 ServerPlayingSound &psound = i->second;
1993 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
1996 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
1997 si != psound.clients.end(); ++si) {
1999 m_clients.send(*si, 0, &pkt, true);
2001 // Remove sound reference
2002 m_playing_sounds.erase(i);
2005 void Server::fadeSound(s32 handle, float step, float gain)
2007 // Get sound reference
2008 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2009 m_playing_sounds.find(handle);
2010 if (i == m_playing_sounds.end())
2013 ServerPlayingSound &psound = i->second;
2014 psound.params.gain = gain;
2016 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2017 pkt << handle << step << gain;
2019 // Backwards compability
2020 bool play_sound = gain > 0;
2021 ServerPlayingSound compat_psound = psound;
2022 compat_psound.clients.clear();
2024 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2025 compat_pkt << handle;
2027 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2028 it != psound.clients.end();) {
2029 if (m_clients.getProtocolVersion(*it) >= 32) {
2031 m_clients.send(*it, 0, &pkt, true);
2034 compat_psound.clients.insert(*it);
2036 m_clients.send(*it, 0, &compat_pkt, true);
2037 psound.clients.erase(it++);
2041 // Remove sound reference
2042 if (!play_sound || psound.clients.empty())
2043 m_playing_sounds.erase(i);
2045 if (play_sound && !compat_psound.clients.empty()) {
2046 // Play new sound volume on older clients
2047 playSound(compat_psound.spec, compat_psound.params);
2051 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2052 std::vector<u16> *far_players, float far_d_nodes)
2054 float maxd = far_d_nodes*BS;
2055 v3f p_f = intToFloat(p, BS);
2057 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2060 std::vector<session_t> clients = m_clients.getClientIDs();
2061 for (session_t client_id : clients) {
2064 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2065 PlayerSAO *sao = player->getPlayerSAO();
2069 // If player is far away, only set modified blocks not sent
2070 v3f player_pos = sao->getBasePosition();
2071 if (player_pos.getDistanceFrom(p_f) > maxd) {
2072 far_players->push_back(client_id);
2079 m_clients.send(client_id, 0, &pkt, true);
2083 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2084 std::vector<u16> *far_players, float far_d_nodes,
2085 bool remove_metadata)
2087 float maxd = far_d_nodes*BS;
2088 v3f p_f = intToFloat(p, BS);
2090 std::vector<session_t> clients = m_clients.getClientIDs();
2091 for (const session_t client_id : clients) {
2094 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2095 PlayerSAO *sao = player->getPlayerSAO();
2099 // If player is far away, only set modified blocks not sent
2100 v3f player_pos = sao->getBasePosition();
2101 if(player_pos.getDistanceFrom(p_f) > maxd) {
2102 far_players->push_back(client_id);
2108 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2110 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2112 pkt << p << n.param0 << n.param1 << n.param2
2113 << (u8) (remove_metadata ? 0 : 1);
2118 if (pkt.getSize() > 0)
2119 m_clients.send(client_id, 0, &pkt, true);
2123 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2124 u16 net_proto_version)
2127 Create a packet with the block in the right format
2130 std::ostringstream os(std::ios_base::binary);
2131 block->serialize(os, ver, false);
2132 block->serializeNetworkSpecific(os);
2133 std::string s = os.str();
2135 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2137 pkt << block->getPos();
2138 pkt.putRawString(s.c_str(), s.size());
2142 void Server::SendBlocks(float dtime)
2144 MutexAutoLock envlock(m_env_mutex);
2145 //TODO check if one big lock could be faster then multiple small ones
2147 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2149 std::vector<PrioritySortedBlockTransfer> queue;
2151 u32 total_sending = 0;
2154 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2156 std::vector<session_t> clients = m_clients.getClientIDs();
2159 for (const session_t client_id : clients) {
2160 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2165 total_sending += client->getSendingCount();
2166 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2172 // Lowest priority number comes first.
2173 // Lowest is most important.
2174 std::sort(queue.begin(), queue.end());
2178 // Maximal total count calculation
2179 // The per-client block sends is halved with the maximal online users
2180 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2181 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2183 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2184 if (total_sending >= max_blocks_to_send)
2187 MapBlock *block = nullptr;
2189 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2190 } catch (const InvalidPositionException &e) {
2194 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2199 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2200 client->net_proto_version);
2202 client->SentBlock(block_to_send.pos);
2208 void Server::fillMediaCache()
2210 infostream<<"Server: Calculating media file checksums"<<std::endl;
2212 // Collect all media file paths
2213 std::vector<std::string> paths;
2214 m_modmgr->getModsMediaPaths(paths);
2215 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2216 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2218 // Collect media file information from paths into cache
2219 for (const std::string &mediapath : paths) {
2220 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2221 for (const fs::DirListNode &dln : dirlist) {
2222 if (dln.dir) // Ignode dirs
2224 std::string filename = dln.name;
2225 // If name contains illegal characters, ignore the file
2226 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2227 infostream<<"Server: ignoring illegal file name: \""
2228 << filename << "\"" << std::endl;
2231 // If name is not in a supported format, ignore it
2232 const char *supported_ext[] = {
2233 ".png", ".jpg", ".bmp", ".tga",
2234 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2236 ".x", ".b3d", ".md2", ".obj",
2237 // Custom translation file format
2241 if (removeStringEnd(filename, supported_ext).empty()){
2242 infostream << "Server: ignoring unsupported file extension: \""
2243 << filename << "\"" << std::endl;
2246 // Ok, attempt to load the file and add to cache
2247 std::string filepath;
2248 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2251 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2253 errorstream << "Server::fillMediaCache(): Could not open \""
2254 << filename << "\" for reading" << std::endl;
2257 std::ostringstream tmp_os(std::ios_base::binary);
2261 fis.read(buf, 1024);
2262 std::streamsize len = fis.gcount();
2263 tmp_os.write(buf, len);
2272 errorstream<<"Server::fillMediaCache(): Failed to read \""
2273 << filename << "\"" << std::endl;
2276 if(tmp_os.str().length() == 0) {
2277 errorstream << "Server::fillMediaCache(): Empty file \""
2278 << filepath << "\"" << std::endl;
2283 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2285 unsigned char *digest = sha1.getDigest();
2286 std::string sha1_base64 = base64_encode(digest, 20);
2287 std::string sha1_hex = hex_encode((char*)digest, 20);
2291 m_media[filename] = MediaInfo(filepath, sha1_base64);
2292 verbosestream << "Server: " << sha1_hex << " is " << filename
2298 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2300 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2304 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2307 std::string lang_suffix;
2308 lang_suffix.append(".").append(lang_code).append(".tr");
2309 for (const auto &i : m_media) {
2310 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2317 for (const auto &i : m_media) {
2318 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2320 pkt << i.first << i.second.sha1_digest;
2323 pkt << g_settings->get("remote_media");
2327 struct SendableMedia
2333 SendableMedia(const std::string &name_="", const std::string &path_="",
2334 const std::string &data_=""):
2341 void Server::sendRequestedMedia(session_t peer_id,
2342 const std::vector<std::string> &tosend)
2344 verbosestream<<"Server::sendRequestedMedia(): "
2345 <<"Sending files to client"<<std::endl;
2349 // Put 5kB in one bunch (this is not accurate)
2350 u32 bytes_per_bunch = 5000;
2352 std::vector< std::vector<SendableMedia> > file_bunches;
2353 file_bunches.emplace_back();
2355 u32 file_size_bunch_total = 0;
2357 for (const std::string &name : tosend) {
2358 if (m_media.find(name) == m_media.end()) {
2359 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2360 <<"unknown file \""<<(name)<<"\""<<std::endl;
2364 //TODO get path + name
2365 std::string tpath = m_media[name].path;
2368 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2370 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2371 <<tpath<<"\" for reading"<<std::endl;
2374 std::ostringstream tmp_os(std::ios_base::binary);
2378 fis.read(buf, 1024);
2379 std::streamsize len = fis.gcount();
2380 tmp_os.write(buf, len);
2381 file_size_bunch_total += len;
2390 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2391 <<name<<"\""<<std::endl;
2394 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2395 <<tname<<"\""<<std::endl;*/
2397 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2399 // Start next bunch if got enough data
2400 if(file_size_bunch_total >= bytes_per_bunch) {
2401 file_bunches.emplace_back();
2402 file_size_bunch_total = 0;
2407 /* Create and send packets */
2409 u16 num_bunches = file_bunches.size();
2410 for (u16 i = 0; i < num_bunches; i++) {
2413 u16 total number of texture bunches
2414 u16 index of this bunch
2415 u32 number of files in this bunch
2424 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2425 pkt << num_bunches << i << (u32) file_bunches[i].size();
2427 for (const SendableMedia &j : file_bunches[i]) {
2429 pkt.putLongString(j.data);
2432 verbosestream << "Server::sendRequestedMedia(): bunch "
2433 << i << "/" << num_bunches
2434 << " files=" << file_bunches[i].size()
2435 << " size=" << pkt.getSize() << std::endl;
2440 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2442 if(m_detached_inventories.count(name) == 0) {
2443 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2446 Inventory *inv = m_detached_inventories[name];
2447 std::ostringstream os(std::ios_base::binary);
2449 os << serializeString(name);
2453 std::string s = os.str();
2455 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2456 pkt.putRawString(s.c_str(), s.size());
2458 const std::string &check = m_detached_inventories_player[name];
2459 if (peer_id == PEER_ID_INEXISTENT) {
2461 return m_clients.sendToAll(&pkt);
2462 RemotePlayer *p = m_env->getPlayer(check.c_str());
2464 m_clients.send(p->getPeerId(), 0, &pkt, true);
2466 if (check.empty() || getPlayerName(peer_id) == check)
2471 void Server::sendDetachedInventories(session_t peer_id)
2473 for (const auto &detached_inventory : m_detached_inventories) {
2474 const std::string &name = detached_inventory.first;
2475 //Inventory *inv = i->second;
2476 sendDetachedInventory(name, peer_id);
2484 void Server::DiePlayer(session_t peer_id)
2486 PlayerSAO *playersao = getPlayerSAO(peer_id);
2487 // In some rare cases this can be NULL -- if the player is disconnected
2488 // when a Lua function modifies l_punch, for example
2492 infostream << "Server::DiePlayer(): Player "
2493 << playersao->getPlayer()->getName()
2494 << " dies" << std::endl;
2496 playersao->setHP(0);
2498 // Trigger scripted stuff
2499 m_script->on_dieplayer(playersao);
2501 SendPlayerHP(peer_id);
2502 SendDeathscreen(peer_id, false, v3f(0,0,0));
2505 void Server::RespawnPlayer(session_t peer_id)
2507 PlayerSAO *playersao = getPlayerSAO(peer_id);
2510 infostream << "Server::RespawnPlayer(): Player "
2511 << playersao->getPlayer()->getName()
2512 << " respawns" << std::endl;
2514 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2515 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2517 bool repositioned = m_script->on_respawnplayer(playersao);
2518 if (!repositioned) {
2519 // setPos will send the new position to client
2520 playersao->setPos(findSpawnPos());
2523 SendPlayerHP(peer_id);
2527 void Server::DenySudoAccess(session_t peer_id)
2529 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2534 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2535 const std::string &str_reason, bool reconnect)
2537 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2539 m_clients.event(peer_id, CSE_SetDenied);
2540 DisconnectPeer(peer_id);
2544 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2545 const std::string &custom_reason)
2547 SendAccessDenied(peer_id, reason, custom_reason);
2548 m_clients.event(peer_id, CSE_SetDenied);
2549 DisconnectPeer(peer_id);
2552 // 13/03/15: remove this function when protocol version 25 will become
2553 // the minimum version for MT users, maybe in 1 year
2554 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2556 SendAccessDenied_Legacy(peer_id, reason);
2557 m_clients.event(peer_id, CSE_SetDenied);
2558 DisconnectPeer(peer_id);
2561 void Server::DisconnectPeer(session_t peer_id)
2563 m_modchannel_mgr->leaveAllChannels(peer_id);
2564 m_con->DisconnectPeer(peer_id);
2567 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2570 RemoteClient* client = getClient(peer_id, CS_Invalid);
2572 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2574 // Right now, the auth mechs don't change between login and sudo mode.
2575 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2576 client->allowed_sudo_mechs = sudo_auth_mechs;
2578 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2579 << g_settings->getFloat("dedicated_server_step")
2583 m_clients.event(peer_id, CSE_AuthAccept);
2585 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2587 // We only support SRP right now
2588 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2590 resp_pkt << sudo_auth_mechs;
2592 m_clients.event(peer_id, CSE_SudoSuccess);
2596 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2598 std::wstring message;
2601 Clear references to playing sounds
2603 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2604 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2605 ServerPlayingSound &psound = i->second;
2606 psound.clients.erase(peer_id);
2607 if (psound.clients.empty())
2608 m_playing_sounds.erase(i++);
2613 // clear formspec info so the next client can't abuse the current state
2614 m_formspec_state_data.erase(peer_id);
2616 RemotePlayer *player = m_env->getPlayer(peer_id);
2618 /* Run scripts and remove from environment */
2620 PlayerSAO *playersao = player->getPlayerSAO();
2623 // inform connected clients
2624 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2625 // (u16) 1 + std::string represents a vector serialization representation
2626 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2627 m_clients.sendToAll(¬ice);
2629 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2631 playersao->disconnected();
2638 if (player && reason != CDR_DENY) {
2639 std::ostringstream os(std::ios_base::binary);
2640 std::vector<session_t> clients = m_clients.getClientIDs();
2642 for (const session_t client_id : clients) {
2644 RemotePlayer *player = m_env->getPlayer(client_id);
2648 // Get name of player
2649 os << player->getName() << " ";
2652 std::string name = player->getName();
2653 actionstream << name << " "
2654 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2655 << " List of players: " << os.str() << std::endl;
2657 m_admin_chat->outgoing_queue.push_back(
2658 new ChatEventNick(CET_NICK_REMOVE, name));
2662 MutexAutoLock env_lock(m_env_mutex);
2663 m_clients.DeleteClient(peer_id);
2667 // Send leave chat message to all remaining clients
2668 if (!message.empty()) {
2669 SendChatMessage(PEER_ID_INEXISTENT,
2670 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2674 void Server::UpdateCrafting(RemotePlayer *player)
2676 // Get a preview for crafting
2678 InventoryLocation loc;
2679 loc.setPlayer(player->getName());
2680 std::vector<ItemStack> output_replacements;
2681 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2682 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2683 (&player->inventory)->getList("craft"), loc);
2685 // Put the new preview in
2686 InventoryList *plist = player->inventory.getList("craftpreview");
2687 sanity_check(plist);
2688 sanity_check(plist->getSize() >= 1);
2689 plist->changeItem(0, preview);
2692 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2694 if (evt->type == CET_NICK_ADD) {
2695 // The terminal informed us of its nick choice
2696 m_admin_nick = ((ChatEventNick *)evt)->nick;
2697 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2698 errorstream << "You haven't set up an account." << std::endl
2699 << "Please log in using the client as '"
2700 << m_admin_nick << "' with a secure password." << std::endl
2701 << "Until then, you can't execute admin tasks via the console," << std::endl
2702 << "and everybody can claim the user account instead of you," << std::endl
2703 << "giving them full control over this server." << std::endl;
2706 assert(evt->type == CET_CHAT);
2707 handleAdminChat((ChatEventChat *)evt);
2711 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2712 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2714 // If something goes wrong, this player is to blame
2715 RollbackScopeActor rollback_scope(m_rollback,
2716 std::string("player:") + name);
2718 if (g_settings->getBool("strip_color_codes"))
2719 wmessage = unescape_enriched(wmessage);
2722 switch (player->canSendChatMessage()) {
2723 case RPLAYER_CHATRESULT_FLOODING: {
2724 std::wstringstream ws;
2725 ws << L"You cannot send more messages. You are limited to "
2726 << g_settings->getFloat("chat_message_limit_per_10sec")
2727 << L" messages per 10 seconds.";
2730 case RPLAYER_CHATRESULT_KICK:
2731 DenyAccess_Legacy(player->getPeerId(),
2732 L"You have been kicked due to message flooding.");
2734 case RPLAYER_CHATRESULT_OK:
2737 FATAL_ERROR("Unhandled chat filtering result found.");
2741 if (m_max_chatmessage_length > 0
2742 && wmessage.length() > m_max_chatmessage_length) {
2743 return L"Your message exceed the maximum chat message limit set on the server. "
2744 L"It was refused. Send a shorter message";
2747 // Run script hook, exit if script ate the chat message
2748 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2753 // Whether to send line to the player that sent the message, or to all players
2754 bool broadcast_line = true;
2756 if (check_shout_priv && !checkPriv(name, "shout")) {
2757 line += L"-!- You don't have permission to shout.";
2758 broadcast_line = false;
2767 Tell calling method to send the message to sender
2769 if (!broadcast_line)
2773 Send the message to others
2775 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2777 std::vector<session_t> clients = m_clients.getClientIDs();
2780 Send the message back to the inital sender
2781 if they are using protocol version >= 29
2784 session_t peer_id_to_avoid_sending =
2785 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2787 if (player && player->protocol_version >= 29)
2788 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2790 for (u16 cid : clients) {
2791 if (cid != peer_id_to_avoid_sending)
2792 SendChatMessage(cid, ChatMessage(line));
2797 void Server::handleAdminChat(const ChatEventChat *evt)
2799 std::string name = evt->nick;
2800 std::wstring wname = utf8_to_wide(name);
2801 std::wstring wmessage = evt->evt_msg;
2803 std::wstring answer = handleChat(name, wname, wmessage);
2805 // If asked to send answer to sender
2806 if (!answer.empty()) {
2807 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2811 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2813 RemoteClient *client = getClientNoEx(peer_id,state_min);
2815 throw ClientNotFoundException("Client not found");
2819 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2821 return m_clients.getClientNoEx(peer_id, state_min);
2824 std::string Server::getPlayerName(session_t peer_id)
2826 RemotePlayer *player = m_env->getPlayer(peer_id);
2828 return "[id="+itos(peer_id)+"]";
2829 return player->getName();
2832 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2834 RemotePlayer *player = m_env->getPlayer(peer_id);
2837 return player->getPlayerSAO();
2840 std::wstring Server::getStatusString()
2842 std::wostringstream os(std::ios_base::binary);
2845 os<<L"version="<<narrow_to_wide(g_version_string);
2847 os<<L", uptime="<<m_uptime.get();
2849 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2850 // Information about clients
2853 std::vector<session_t> clients = m_clients.getClientIDs();
2854 for (session_t client_id : clients) {
2856 RemotePlayer *player = m_env->getPlayer(client_id);
2857 // Get name of player
2858 std::wstring name = L"unknown";
2860 name = narrow_to_wide(player->getName());
2861 // Add name to information string
2870 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2871 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2873 if (!g_settings->get("motd").empty())
2874 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2878 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2880 std::set<std::string> privs;
2881 m_script->getAuth(name, NULL, &privs);
2885 bool Server::checkPriv(const std::string &name, const std::string &priv)
2887 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2888 return (privs.count(priv) != 0);
2891 void Server::reportPrivsModified(const std::string &name)
2894 std::vector<session_t> clients = m_clients.getClientIDs();
2895 for (const session_t client_id : clients) {
2896 RemotePlayer *player = m_env->getPlayer(client_id);
2897 reportPrivsModified(player->getName());
2900 RemotePlayer *player = m_env->getPlayer(name.c_str());
2903 SendPlayerPrivileges(player->getPeerId());
2904 PlayerSAO *sao = player->getPlayerSAO();
2907 sao->updatePrivileges(
2908 getPlayerEffectivePrivs(name),
2913 void Server::reportInventoryFormspecModified(const std::string &name)
2915 RemotePlayer *player = m_env->getPlayer(name.c_str());
2918 SendPlayerInventoryFormspec(player->getPeerId());
2921 void Server::setIpBanned(const std::string &ip, const std::string &name)
2923 m_banmanager->add(ip, name);
2926 void Server::unsetIpBanned(const std::string &ip_or_name)
2928 m_banmanager->remove(ip_or_name);
2931 std::string Server::getBanDescription(const std::string &ip_or_name)
2933 return m_banmanager->getBanDescription(ip_or_name);
2936 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2938 // m_env will be NULL if the server is initializing
2942 if (m_admin_nick == name && !m_admin_nick.empty()) {
2943 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2946 RemotePlayer *player = m_env->getPlayer(name);
2951 if (player->getPeerId() == PEER_ID_INEXISTENT)
2954 SendChatMessage(player->getPeerId(), ChatMessage(msg));
2957 bool Server::showFormspec(const char *playername, const std::string &formspec,
2958 const std::string &formname)
2960 // m_env will be NULL if the server is initializing
2964 RemotePlayer *player = m_env->getPlayer(playername);
2968 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
2972 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
2977 u32 id = player->addHud(form);
2979 SendHUDAdd(player->getPeerId(), id, form);
2984 bool Server::hudRemove(RemotePlayer *player, u32 id) {
2988 HudElement* todel = player->removeHud(id);
2995 SendHUDRemove(player->getPeerId(), id);
2999 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3004 SendHUDChange(player->getPeerId(), id, stat, data);
3008 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3013 SendHUDSetFlags(player->getPeerId(), flags, mask);
3014 player->hud_flags &= ~mask;
3015 player->hud_flags |= flags;
3017 PlayerSAO* playersao = player->getPlayerSAO();
3022 m_script->player_event(playersao, "hud_changed");
3026 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3031 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3034 player->setHotbarItemcount(hotbar_itemcount);
3035 std::ostringstream os(std::ios::binary);
3036 writeS32(os, hotbar_itemcount);
3037 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3041 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3046 player->setHotbarImage(name);
3047 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3050 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3055 player->setHotbarSelectedImage(name);
3056 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3059 Address Server::getPeerAddress(session_t peer_id)
3061 return m_con->GetPeerAddress(peer_id);
3064 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3065 v2s32 animation_frames[4], f32 frame_speed)
3067 sanity_check(player);
3068 player->setLocalAnimations(animation_frames, frame_speed);
3069 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3072 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3074 sanity_check(player);
3075 player->eye_offset_first = first;
3076 player->eye_offset_third = third;
3077 SendEyeOffset(player->getPeerId(), first, third);
3080 void Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3081 const std::string &type, const std::vector<std::string> ¶ms,
3084 sanity_check(player);
3085 player->setSky(bgcolor, type, params, clouds);
3086 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3089 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3091 sanity_check(player);
3092 player->setCloudParams(params);
3093 SendCloudParams(player->getPeerId(), params);
3096 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3102 player->overrideDayNightRatio(do_override, ratio);
3103 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3107 void Server::notifyPlayers(const std::wstring &msg)
3109 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3112 void Server::spawnParticle(const std::string &playername, v3f pos,
3113 v3f velocity, v3f acceleration,
3114 float expirationtime, float size, bool
3115 collisiondetection, bool collision_removal,
3116 bool vertical, const std::string &texture,
3117 const struct TileAnimationParams &animation, u8 glow)
3119 // m_env will be NULL if the server is initializing
3123 session_t peer_id = PEER_ID_INEXISTENT;
3125 if (!playername.empty()) {
3126 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3129 peer_id = player->getPeerId();
3130 proto_ver = player->protocol_version;
3133 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3134 expirationtime, size, collisiondetection,
3135 collision_removal, vertical, texture, animation, glow);
3138 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3139 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3140 float minexptime, float maxexptime, float minsize, float maxsize,
3141 bool collisiondetection, bool collision_removal,
3142 ServerActiveObject *attached, bool vertical, const std::string &texture,
3143 const std::string &playername, const struct TileAnimationParams &animation,
3146 // m_env will be NULL if the server is initializing
3150 session_t peer_id = PEER_ID_INEXISTENT;
3152 if (!playername.empty()) {
3153 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3156 peer_id = player->getPeerId();
3157 proto_ver = player->protocol_version;
3160 u16 attached_id = attached ? attached->getId() : 0;
3163 if (attached_id == 0)
3164 id = m_env->addParticleSpawner(spawntime);
3166 id = m_env->addParticleSpawner(spawntime, attached_id);
3168 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3169 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3170 minexptime, maxexptime, minsize, maxsize,
3171 collisiondetection, collision_removal, attached_id, vertical,
3172 texture, id, animation, glow);
3177 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3179 // m_env will be NULL if the server is initializing
3181 throw ServerError("Can't delete particle spawners during initialisation!");
3183 session_t peer_id = PEER_ID_INEXISTENT;
3184 if (!playername.empty()) {
3185 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3188 peer_id = player->getPeerId();
3191 m_env->deleteParticleSpawner(id);
3192 SendDeleteParticleSpawner(peer_id, id);
3195 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3197 if(m_detached_inventories.count(name) > 0){
3198 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3199 delete m_detached_inventories[name];
3201 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3203 Inventory *inv = new Inventory(m_itemdef);
3205 m_detached_inventories[name] = inv;
3206 m_detached_inventories_player[name] = player;
3207 //TODO find a better way to do this
3208 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3212 // actions: time-reversed list
3213 // Return value: success/failure
3214 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3215 std::list<std::string> *log)
3217 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3218 ServerMap *map = (ServerMap*)(&m_env->getMap());
3220 // Fail if no actions to handle
3221 if(actions.empty()){
3222 log->push_back("Nothing to do.");
3229 for (const RollbackAction &action : actions) {
3231 bool success = action.applyRevert(map, this, this);
3234 std::ostringstream os;
3235 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3236 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3238 log->push_back(os.str());
3240 std::ostringstream os;
3241 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3242 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3244 log->push_back(os.str());
3248 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3249 <<" failed"<<std::endl;
3251 // Call it done if less than half failed
3252 return num_failed <= num_tried/2;
3255 // IGameDef interface
3257 IItemDefManager *Server::getItemDefManager()
3262 const NodeDefManager *Server::getNodeDefManager()
3267 ICraftDefManager *Server::getCraftDefManager()
3272 u16 Server::allocateUnknownNodeId(const std::string &name)
3274 return m_nodedef->allocateDummy(name);
3277 MtEventManager *Server::getEventManager()
3282 IWritableItemDefManager *Server::getWritableItemDefManager()
3287 NodeDefManager *Server::getWritableNodeDefManager()
3292 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3297 const std::vector<ModSpec> & Server::getMods() const
3299 return m_modmgr->getMods();
3302 const ModSpec *Server::getModSpec(const std::string &modname) const
3304 return m_modmgr->getModSpec(modname);
3307 void Server::getModNames(std::vector<std::string> &modlist)
3309 m_modmgr->getModNames(modlist);
3312 std::string Server::getBuiltinLuaPath()
3314 return porting::path_share + DIR_DELIM + "builtin";
3317 std::string Server::getModStoragePath() const
3319 return m_path_world + DIR_DELIM + "mod_storage";
3322 v3f Server::findSpawnPos()
3324 ServerMap &map = m_env->getServerMap();
3326 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3327 return nodeposf * BS;
3330 bool is_good = false;
3331 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3332 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3334 // Try to find a good place a few times
3335 for(s32 i = 0; i < 4000 && !is_good; i++) {
3336 s32 range = MYMIN(1 + i, range_max);
3337 // We're going to try to throw the player to this position
3338 v2s16 nodepos2d = v2s16(
3339 -range + (myrand() % (range * 2)),
3340 -range + (myrand() % (range * 2)));
3342 // Get spawn level at point
3343 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3344 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3345 // the mapgen to signify an unsuitable spawn position
3346 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3349 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3352 for (s32 i = 0; i < 10; i++) {
3353 v3s16 blockpos = getNodeBlockPos(nodepos);
3354 map.emergeBlock(blockpos, true);
3355 content_t c = map.getNodeNoEx(nodepos).getContent();
3356 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3358 if (air_count >= 2) {
3359 nodeposf = intToFloat(nodepos, BS);
3360 // Don't spawn the player outside map boundaries
3361 if (objectpos_over_limit(nodeposf))
3374 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3376 m_shutdown_timer = delay;
3377 m_shutdown_msg = msg;
3378 m_shutdown_ask_reconnect = reconnect;
3380 if (delay == 0.0f) {
3381 // No delay, shutdown immediately
3382 m_shutdown_requested = true;
3383 // only print to the infostream, a chat message saying
3384 // "Server Shutting Down" is sent when the server destructs.
3385 infostream << "*** Immediate Server shutdown requested." << std::endl;
3386 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3387 // Negative delay, cancel shutdown if requested
3388 m_shutdown_timer = 0.0f;
3389 m_shutdown_msg = "";
3390 m_shutdown_ask_reconnect = false;
3391 m_shutdown_requested = false;
3392 std::wstringstream ws;
3394 ws << L"*** Server shutdown canceled.";
3396 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3397 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3398 } else if (delay > 0.0f) {
3399 // Positive delay, tell the clients when the server will shut down
3400 std::wstringstream ws;
3402 ws << L"*** Server shutting down in "
3403 << duration_to_string(myround(m_shutdown_timer)).c_str()
3406 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3407 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3411 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3414 Try to get an existing player
3416 RemotePlayer *player = m_env->getPlayer(name);
3418 // If player is already connected, cancel
3419 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3420 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3425 If player with the wanted peer_id already exists, cancel.
3427 if (m_env->getPlayer(peer_id)) {
3428 infostream<<"emergePlayer(): Player with wrong name but same"
3429 " peer_id already exists"<<std::endl;
3434 player = new RemotePlayer(name, idef());
3437 bool newplayer = false;
3440 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3442 // Complete init with server parts
3443 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3444 player->protocol_version = proto_version;
3448 m_script->on_newplayer(playersao);
3454 bool Server::registerModStorage(ModMetadata *storage)
3456 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3457 errorstream << "Unable to register same mod storage twice. Storage name: "
3458 << storage->getModName() << std::endl;
3462 m_mod_storages[storage->getModName()] = storage;
3466 void Server::unregisterModStorage(const std::string &name)
3468 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3469 if (it != m_mod_storages.end()) {
3470 // Save unconditionaly on unregistration
3471 it->second->save(getModStoragePath());
3472 m_mod_storages.erase(name);
3476 void dedicated_server_loop(Server &server, bool &kill)
3478 verbosestream<<"dedicated_server_loop()"<<std::endl;
3480 IntervalLimiter m_profiler_interval;
3482 static thread_local const float steplen =
3483 g_settings->getFloat("dedicated_server_step");
3484 static thread_local const float profiler_print_interval =
3485 g_settings->getFloat("profiler_print_interval");
3488 // This is kind of a hack but can be done like this
3489 // because server.step() is very light
3491 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3492 sleep_ms((int)(steplen*1000.0));
3494 server.step(steplen);
3496 if (server.getShutdownRequested() || kill)
3502 if (profiler_print_interval != 0) {
3503 if(m_profiler_interval.step(steplen, profiler_print_interval))
3505 infostream<<"Profiler:"<<std::endl;
3506 g_profiler->print(infostream);
3507 g_profiler->clear();
3512 infostream << "Dedicated server quitting" << std::endl;
3514 if (g_settings->getBool("server_announce"))
3515 ServerList::sendAnnounce(ServerList::AA_DELETE,
3516 server.m_bind_addr.getPort());
3525 bool Server::joinModChannel(const std::string &channel)
3527 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3528 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3531 bool Server::leaveModChannel(const std::string &channel)
3533 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3536 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3538 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3541 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3545 ModChannel* Server::getModChannel(const std::string &channel)
3547 return m_modchannel_mgr->getModChannel(channel);
3550 void Server::broadcastModChannelMessage(const std::string &channel,
3551 const std::string &message, session_t from_peer)
3553 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3557 if (message.size() > STRING_MAX_LEN) {
3558 warningstream << "ModChannel message too long, dropping before sending "
3559 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3560 << channel << ")" << std::endl;
3565 if (from_peer != PEER_ID_SERVER) {
3566 sender = getPlayerName(from_peer);
3569 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3570 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3571 resp_pkt << channel << sender << message;
3572 for (session_t peer_id : peers) {
3574 if (peer_id == from_peer)
3577 Send(peer_id, &resp_pkt);
3580 if (from_peer != PEER_ID_SERVER) {
3581 m_script->on_modchannel_message(channel, sender, message);