3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "network/connection.h"
25 #include "network/networkprotocol.h"
26 #include "network/serveropcodes.h"
28 #include "environment.h"
30 #include "threading/mutex_auto_lock.h"
31 #include "constants.h"
37 #include "serverobject.h"
38 #include "genericobject.h"
42 #include "scripting_server.h"
47 #include "mapgen/mapgen.h"
48 #include "mapgen/mg_biome.h"
49 #include "content_mapnode.h"
50 #include "content_nodemeta.h"
51 #include "content_sao.h"
53 #include "event_manager.h"
54 #include "modchannels.h"
55 #include "serverlist.h"
56 #include "util/string.h"
58 #include "util/serialize.h"
59 #include "util/thread.h"
60 #include "defaultsettings.h"
61 #include "server/mods.h"
62 #include "util/base64.h"
63 #include "util/sha1.h"
65 #include "database/database.h"
66 #include "chatmessage.h"
67 #include "chat_interface.h"
68 #include "remoteplayer.h"
70 class ClientNotFoundException : public BaseException
73 ClientNotFoundException(const char *s):
78 class ServerThread : public Thread
82 ServerThread(Server *server):
93 void *ServerThread::run()
95 BEGIN_DEBUG_EXCEPTION_HANDLER
97 m_server->AsyncRunStep(true);
99 while (!stopRequested()) {
101 m_server->AsyncRunStep();
105 } catch (con::NoIncomingDataException &e) {
106 } catch (con::PeerNotFoundException &e) {
107 infostream<<"Server: PeerNotFoundException"<<std::endl;
108 } catch (ClientNotFoundException &e) {
109 } catch (con::ConnectionBindFailed &e) {
110 m_server->setAsyncFatalError(e.what());
111 } catch (LuaError &e) {
112 m_server->setAsyncFatalError(
113 "ServerThread::run Lua: " + std::string(e.what()));
117 END_DEBUG_EXCEPTION_HANDLER
122 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
124 if(pos_exists) *pos_exists = false;
129 if(pos_exists) *pos_exists = true;
134 ServerActiveObject *sao = env->getActiveObject(object);
137 if(pos_exists) *pos_exists = true;
138 return sao->getBasePosition(); }
150 const std::string &path_world,
151 const SubgameSpec &gamespec,
152 bool simple_singleplayer_mode,
157 m_bind_addr(bind_addr),
158 m_path_world(path_world),
159 m_gamespec(gamespec),
160 m_simple_singleplayer_mode(simple_singleplayer_mode),
161 m_dedicated(dedicated),
162 m_async_fatal_error(""),
163 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
166 m_bind_addr.isIPv6(),
168 m_itemdef(createItemDefManager()),
169 m_nodedef(createNodeDefManager()),
170 m_craftdef(createCraftDefManager()),
171 m_event(new EventManager()),
175 m_modchannel_mgr(new ModChannelMgr())
177 m_lag = g_settings->getFloat("dedicated_server_step");
179 if (path_world.empty())
180 throw ServerError("Supplied empty world path");
182 if(!gamespec.isValid())
183 throw ServerError("Supplied invalid gamespec");
185 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
186 if(m_simple_singleplayer_mode)
187 infostream<<" in simple singleplayer mode"<<std::endl;
189 infostream<<std::endl;
190 infostream<<"- world: "<<m_path_world<<std::endl;
191 infostream<<"- game: "<<m_gamespec.path<<std::endl;
193 // Create world if it doesn't exist
194 if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
195 throw ServerError("Failed to initialize world");
197 // Create server thread
198 m_thread = new ServerThread(this);
200 // Create emerge manager
201 m_emerge = new EmergeManager(this);
203 // Create ban manager
204 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
205 m_banmanager = new BanManager(ban_path);
207 m_modmgr = std::unique_ptr<ServerModManager>(new ServerModManager(
209 std::vector<ModSpec> unsatisfied_mods = m_modmgr->getUnsatisfiedMods();
210 // complain about mods with unsatisfied dependencies
211 if (!m_modmgr->isConsistent()) {
212 m_modmgr->printUnsatisfiedModsError();
216 MutexAutoLock envlock(m_env_mutex);
218 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
219 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
221 // Initialize scripting
222 infostream<<"Server: Initializing Lua"<<std::endl;
224 m_script = new ServerScripting(this);
226 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
228 m_modmgr->loadMods(m_script);
230 // Read Textures and calculate sha1 sums
233 // Apply item aliases in the node definition manager
234 m_nodedef->updateAliases(m_itemdef);
236 // Apply texture overrides from texturepack/override.txt
237 std::vector<std::string> paths;
238 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
239 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
240 for (const std::string &path : paths)
241 m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
243 m_nodedef->setNodeRegistrationStatus(true);
245 // Perform pending node name resolutions
246 m_nodedef->runNodeResolveCallbacks();
248 // unmap node names for connected nodeboxes
249 m_nodedef->mapNodeboxConnections();
251 // init the recipe hashes to speed up crafting
252 m_craftdef->initHashes(this);
254 // Initialize Environment
255 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
257 m_clients.setEnv(m_env);
259 if (!servermap->settings_mgr.makeMapgenParams())
260 FATAL_ERROR("Couldn't create any mapgen type");
262 // Initialize mapgens
263 m_emerge->initMapgens(servermap->getMapgenParams());
265 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
266 if (m_enable_rollback_recording) {
267 // Create rollback manager
268 m_rollback = new RollbackManager(m_path_world, this);
271 // Give environment reference to scripting api
272 m_script->initializeEnvironment(m_env);
274 // Register us to receive map edit events
275 servermap->addEventReceiver(this);
279 m_liquid_transform_every = g_settings->getFloat("liquid_update");
280 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
281 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
282 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
287 infostream << "Server destructing" << std::endl;
289 // Send shutdown message
290 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
291 L"*** Server shutting down"));
294 MutexAutoLock envlock(m_env_mutex);
296 infostream << "Server: Saving players" << std::endl;
297 m_env->saveLoadedPlayers();
299 infostream << "Server: Kicking players" << std::endl;
300 std::string kick_msg;
301 bool reconnect = false;
302 if (getShutdownRequested()) {
303 reconnect = m_shutdown_ask_reconnect;
304 kick_msg = m_shutdown_msg;
306 if (kick_msg.empty()) {
307 kick_msg = g_settings->get("kick_msg_shutdown");
309 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
310 kick_msg, reconnect);
313 // Do this before stopping the server in case mapgen callbacks need to access
314 // server-controlled resources (like ModStorages). Also do them before
315 // shutdown callbacks since they may modify state that is finalized in a
317 m_emerge->stopThreads();
320 MutexAutoLock envlock(m_env_mutex);
322 // Execute script shutdown hooks
323 infostream << "Executing shutdown hooks" << std::endl;
324 m_script->on_shutdown();
326 infostream << "Server: Saving environment metadata" << std::endl;
334 // Delete things in the reverse order of creation
344 // Deinitialize scripting
345 infostream << "Server: Deinitializing scripting" << std::endl;
348 // Delete detached inventories
349 for (auto &detached_inventory : m_detached_inventories) {
350 delete detached_inventory.second;
356 infostream << "Starting server on " << m_bind_addr.serializeString()
357 << "..." << std::endl;
359 // Stop thread if already running
362 // Initialize connection
363 m_con->SetTimeoutMs(30);
364 m_con->Serve(m_bind_addr);
369 // ASCII art for the win!
371 << " .__ __ __ " << std::endl
372 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
373 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
374 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
375 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
376 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
377 actionstream << "World at [" << m_path_world << "]" << std::endl;
378 actionstream << "Server for gameid=\"" << m_gamespec.id
379 << "\" listening on " << m_bind_addr.serializeString() << ":"
380 << m_bind_addr.getPort() << "." << std::endl;
385 infostream<<"Server: Stopping and waiting threads"<<std::endl;
387 // Stop threads (set run=false first so both start stopping)
389 //m_emergethread.setRun(false);
391 //m_emergethread.stop();
393 infostream<<"Server: Threads stopped"<<std::endl;
396 void Server::step(float dtime)
402 MutexAutoLock lock(m_step_dtime_mutex);
403 m_step_dtime += dtime;
405 // Throw if fatal error occurred in thread
406 std::string async_err = m_async_fatal_error.get();
407 if (!async_err.empty()) {
408 if (!m_simple_singleplayer_mode) {
409 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
410 g_settings->get("kick_msg_crash"),
411 g_settings->getBool("ask_reconnect_on_crash"));
413 throw ServerError("AsyncErr: " + async_err);
417 void Server::AsyncRunStep(bool initial_step)
419 g_profiler->add("Server::AsyncRunStep (num)", 1);
423 MutexAutoLock lock1(m_step_dtime_mutex);
424 dtime = m_step_dtime;
428 // Send blocks to clients
432 if((dtime < 0.001) && !initial_step)
435 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
437 //infostream<<"Server steps "<<dtime<<std::endl;
438 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
441 MutexAutoLock lock1(m_step_dtime_mutex);
442 m_step_dtime -= dtime;
449 m_uptime.set(m_uptime.get() + dtime);
455 Update time of day and overall game time
457 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
460 Send to clients at constant intervals
463 m_time_of_day_send_timer -= dtime;
464 if(m_time_of_day_send_timer < 0.0) {
465 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
466 u16 time = m_env->getTimeOfDay();
467 float time_speed = g_settings->getFloat("time_speed");
468 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
472 MutexAutoLock lock(m_env_mutex);
473 // Figure out and report maximum lag to environment
474 float max_lag = m_env->getMaxLagEstimate();
475 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
477 if(dtime > 0.1 && dtime > max_lag * 2.0)
478 infostream<<"Server: Maximum lag peaked to "<<dtime
482 m_env->reportMaxLagEstimate(max_lag);
484 ScopeProfiler sp(g_profiler, "SEnv step");
485 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
489 static const float map_timer_and_unload_dtime = 2.92;
490 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
492 MutexAutoLock lock(m_env_mutex);
493 // Run Map's timers and unload unused data
494 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
495 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
496 g_settings->getFloat("server_unload_unused_data_timeout"),
501 Listen to the admin chat, if available
504 if (!m_admin_chat->command_queue.empty()) {
505 MutexAutoLock lock(m_env_mutex);
506 while (!m_admin_chat->command_queue.empty()) {
507 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
508 handleChatInterfaceEvent(evt);
512 m_admin_chat->outgoing_queue.push_back(
513 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
520 /* Transform liquids */
521 m_liquid_transform_timer += dtime;
522 if(m_liquid_transform_timer >= m_liquid_transform_every)
524 m_liquid_transform_timer -= m_liquid_transform_every;
526 MutexAutoLock lock(m_env_mutex);
528 ScopeProfiler sp(g_profiler, "Server: liquid transform");
530 std::map<v3s16, MapBlock*> modified_blocks;
531 m_env->getMap().transformLiquids(modified_blocks, m_env);
534 Set the modified blocks unsent for all the clients
536 if (!modified_blocks.empty()) {
537 SetBlocksNotSent(modified_blocks);
540 m_clients.step(dtime);
542 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
544 // send masterserver announce
546 float &counter = m_masterserver_timer;
547 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
548 g_settings->getBool("server_announce")) {
549 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
550 ServerList::AA_START,
551 m_bind_addr.getPort(),
552 m_clients.getPlayerNames(),
554 m_env->getGameTime(),
557 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
567 Check added and deleted active objects
570 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
571 MutexAutoLock envlock(m_env_mutex);
574 const RemoteClientMap &clients = m_clients.getClientList();
575 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
577 // Radius inside which objects are active
578 static thread_local const s16 radius =
579 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
581 // Radius inside which players are active
582 static thread_local const bool is_transfer_limited =
583 g_settings->exists("unlimited_player_transfer_distance") &&
584 !g_settings->getBool("unlimited_player_transfer_distance");
585 static thread_local const s16 player_transfer_dist =
586 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
587 s16 player_radius = player_transfer_dist;
588 if (player_radius == 0 && is_transfer_limited)
589 player_radius = radius;
591 for (const auto &client_it : clients) {
592 RemoteClient *client = client_it.second;
594 // If definitions and textures have not been sent, don't
595 // send objects either
596 if (client->getState() < CS_DefinitionsSent)
599 RemotePlayer *player = m_env->getPlayer(client->peer_id);
601 // This can happen if the client timeouts somehow
605 PlayerSAO *playersao = player->getPlayerSAO();
609 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
610 if (my_radius <= 0) my_radius = radius;
611 //infostream << "Server: Active Radius " << my_radius << std::endl;
613 std::queue<u16> removed_objects;
614 std::queue<u16> added_objects;
615 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
616 client->m_known_objects, removed_objects);
617 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
618 client->m_known_objects, added_objects);
620 // Ignore if nothing happened
621 if (removed_objects.empty() && added_objects.empty()) {
625 std::string data_buffer;
629 // Handle removed objects
630 writeU16((u8*)buf, removed_objects.size());
631 data_buffer.append(buf, 2);
632 while (!removed_objects.empty()) {
634 u16 id = removed_objects.front();
635 ServerActiveObject* obj = m_env->getActiveObject(id);
637 // Add to data buffer for sending
638 writeU16((u8*)buf, id);
639 data_buffer.append(buf, 2);
641 // Remove from known objects
642 client->m_known_objects.erase(id);
644 if(obj && obj->m_known_by_count > 0)
645 obj->m_known_by_count--;
646 removed_objects.pop();
649 // Handle added objects
650 writeU16((u8*)buf, added_objects.size());
651 data_buffer.append(buf, 2);
652 while (!added_objects.empty()) {
654 u16 id = added_objects.front();
655 ServerActiveObject* obj = m_env->getActiveObject(id);
658 u8 type = ACTIVEOBJECT_TYPE_INVALID;
660 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
662 type = obj->getSendType();
664 // Add to data buffer for sending
665 writeU16((u8*)buf, id);
666 data_buffer.append(buf, 2);
667 writeU8((u8*)buf, type);
668 data_buffer.append(buf, 1);
671 data_buffer.append(serializeLongString(
672 obj->getClientInitializationData(client->net_proto_version)));
674 data_buffer.append(serializeLongString(""));
676 // Add to known objects
677 client->m_known_objects.insert(id);
680 obj->m_known_by_count++;
685 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
686 verbosestream << "Server: Sent object remove/add: "
687 << removed_objects.size() << " removed, "
688 << added_objects.size() << " added, "
689 << "packet size is " << pktSize << std::endl;
693 m_mod_storage_save_timer -= dtime;
694 if (m_mod_storage_save_timer <= 0.0f) {
695 infostream << "Saving registered mod storages." << std::endl;
696 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
697 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
698 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
699 if (it->second->isModified()) {
700 it->second->save(getModStoragePath());
710 MutexAutoLock envlock(m_env_mutex);
711 ScopeProfiler sp(g_profiler, "Server: sending object messages");
714 // Value = data sent by object
715 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
717 // Get active object messages from environment
719 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
723 std::vector<ActiveObjectMessage>* message_list = nullptr;
724 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
725 n = buffered_messages.find(aom.id);
726 if (n == buffered_messages.end()) {
727 message_list = new std::vector<ActiveObjectMessage>;
728 buffered_messages[aom.id] = message_list;
731 message_list = n->second;
733 message_list->push_back(aom);
737 const RemoteClientMap &clients = m_clients.getClientList();
738 // Route data to every client
739 for (const auto &client_it : clients) {
740 RemoteClient *client = client_it.second;
741 std::string reliable_data;
742 std::string unreliable_data;
743 // Go through all objects in message buffer
744 for (const auto &buffered_message : buffered_messages) {
745 // If object is not known by client, skip it
746 u16 id = buffered_message.first;
747 if (client->m_known_objects.find(id) == client->m_known_objects.end())
750 // Get message list of object
751 std::vector<ActiveObjectMessage>* list = buffered_message.second;
752 // Go through every message
753 for (const ActiveObjectMessage &aom : *list) {
754 // Compose the full new data with header
755 std::string new_data;
758 writeU16((u8*)&buf[0], aom.id);
759 new_data.append(buf, 2);
761 new_data += serializeString(aom.datastring);
762 // Add data to buffer
764 reliable_data += new_data;
766 unreliable_data += new_data;
770 reliable_data and unreliable_data are now ready.
773 if (!reliable_data.empty()) {
774 SendActiveObjectMessages(client->peer_id, reliable_data);
777 if (!unreliable_data.empty()) {
778 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
783 // Clear buffered_messages
784 for (auto &buffered_message : buffered_messages) {
785 delete buffered_message.second;
790 Send queued-for-sending map edit events.
793 // We will be accessing the environment
794 MutexAutoLock lock(m_env_mutex);
796 // Don't send too many at a time
799 // Single change sending is disabled if queue size is not small
800 bool disable_single_change_sending = false;
801 if(m_unsent_map_edit_queue.size() >= 4)
802 disable_single_change_sending = true;
804 int event_count = m_unsent_map_edit_queue.size();
806 // We'll log the amount of each
809 while (!m_unsent_map_edit_queue.empty()) {
810 MapEditEvent* event = m_unsent_map_edit_queue.front();
811 m_unsent_map_edit_queue.pop();
813 // Players far away from the change are stored here.
814 // Instead of sending the changes, MapBlocks are set not sent
816 std::vector<u16> far_players;
818 switch (event->type) {
821 prof.add("MEET_ADDNODE", 1);
822 sendAddNode(event->p, event->n, event->already_known_by_peer,
823 &far_players, disable_single_change_sending ? 5 : 30,
824 event->type == MEET_ADDNODE);
826 case MEET_REMOVENODE:
827 prof.add("MEET_REMOVENODE", 1);
828 sendRemoveNode(event->p, event->already_known_by_peer,
829 &far_players, disable_single_change_sending ? 5 : 30);
831 case MEET_BLOCK_NODE_METADATA_CHANGED:
832 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
833 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
834 m_clients.markBlockposAsNotSent(event->p);
837 infostream << "Server: MEET_OTHER" << std::endl;
838 prof.add("MEET_OTHER", 1);
839 for (const v3s16 &modified_block : event->modified_blocks) {
840 m_clients.markBlockposAsNotSent(modified_block);
844 prof.add("unknown", 1);
845 warningstream << "Server: Unknown MapEditEvent "
846 << ((u32)event->type) << std::endl;
851 Set blocks not sent to far players
853 if (!far_players.empty()) {
854 // Convert list format to that wanted by SetBlocksNotSent
855 std::map<v3s16, MapBlock*> modified_blocks2;
856 for (const v3s16 &modified_block : event->modified_blocks) {
857 modified_blocks2[modified_block] =
858 m_env->getMap().getBlockNoCreateNoEx(modified_block);
861 // Set blocks not sent
862 for (const u16 far_player : far_players) {
863 if (RemoteClient *client = getClient(far_player))
864 client->SetBlocksNotSent(modified_blocks2);
871 if (event_count >= 5) {
872 infostream << "Server: MapEditEvents:" << std::endl;
873 prof.print(infostream);
874 } else if (event_count != 0) {
875 verbosestream << "Server: MapEditEvents:" << std::endl;
876 prof.print(verbosestream);
882 Trigger emergethread (it somehow gets to a non-triggered but
883 bysy state sometimes)
886 float &counter = m_emergethread_trigger_timer;
888 if (counter >= 2.0) {
891 m_emerge->startThreads();
895 // Save map, players and auth stuff
897 float &counter = m_savemap_timer;
899 static thread_local const float save_interval =
900 g_settings->getFloat("server_map_save_interval");
901 if (counter >= save_interval) {
903 MutexAutoLock lock(m_env_mutex);
905 ScopeProfiler sp(g_profiler, "Server: saving stuff");
908 if (m_banmanager->isModified()) {
909 m_banmanager->save();
912 // Save changed parts of map
913 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
916 m_env->saveLoadedPlayers();
918 // Save environment metadata
924 static const float shutdown_msg_times[] =
926 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
929 if (m_shutdown_timer > 0.0f) {
930 // Automated messages
931 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
932 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
933 // If shutdown timer matches an automessage, shot it
934 if (m_shutdown_timer > shutdown_msg_times[i] &&
935 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
936 std::wstringstream ws;
938 ws << L"*** Server shutting down in "
939 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
942 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
943 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
949 m_shutdown_timer -= dtime;
950 if (m_shutdown_timer < 0.0f) {
951 m_shutdown_timer = 0.0f;
952 m_shutdown_requested = true;
957 void Server::Receive()
962 m_con->Receive(&pkt);
963 peer_id = pkt.getPeerId();
965 } catch (const con::InvalidIncomingDataException &e) {
966 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
967 << e.what() << std::endl;
968 } catch (const SerializationError &e) {
969 infostream << "Server::Receive(): SerializationError: what()="
970 << e.what() << std::endl;
971 } catch (const ClientStateError &e) {
972 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
973 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
974 L"Try reconnecting or updating your client");
975 } catch (const con::PeerNotFoundException &e) {
980 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
982 std::string playername;
983 PlayerSAO *playersao = NULL;
986 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
988 playername = client->getName();
989 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
991 } catch (std::exception &e) {
997 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1000 if (!playersao || !player) {
1001 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1002 actionstream << "Server: Failed to emerge player \"" << playername
1003 << "\" (player allocated to an another client)" << std::endl;
1004 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1005 L"name. If your client closed unexpectedly, try again in "
1008 errorstream << "Server: " << playername << ": Failed to emerge player"
1010 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1016 Send complete position information
1018 SendMovePlayer(peer_id);
1021 SendPlayerPrivileges(peer_id);
1023 // Send inventory formspec
1024 SendPlayerInventoryFormspec(peer_id);
1027 SendInventory(playersao);
1029 // Send HP or death screen
1030 if (playersao->isDead())
1031 SendDeathscreen(peer_id, false, v3f(0,0,0));
1033 SendPlayerHPOrDie(playersao);
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)
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);
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 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1884 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1885 pkt.putRawString(datas.c_str(), datas.size());
1887 return pkt.getSize();
1890 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1893 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1894 datas.size(), peer_id);
1896 pkt.putRawString(datas.c_str(), datas.size());
1898 m_clients.send(pkt.getPeerId(),
1899 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1903 void Server::SendCSMFlavourLimits(session_t peer_id)
1905 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1906 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1907 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1911 s32 Server::playSound(const SimpleSoundSpec &spec,
1912 const ServerSoundParams ¶ms)
1914 // Find out initial position of sound
1915 bool pos_exists = false;
1916 v3f pos = params.getPos(m_env, &pos_exists);
1917 // If position is not found while it should be, cancel sound
1918 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1921 // Filter destination clients
1922 std::vector<session_t> dst_clients;
1923 if(!params.to_player.empty()) {
1924 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1926 infostream<<"Server::playSound: Player \""<<params.to_player
1927 <<"\" not found"<<std::endl;
1930 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1931 infostream<<"Server::playSound: Player \""<<params.to_player
1932 <<"\" not connected"<<std::endl;
1935 dst_clients.push_back(player->getPeerId());
1937 std::vector<session_t> clients = m_clients.getClientIDs();
1939 for (const session_t client_id : clients) {
1940 RemotePlayer *player = m_env->getPlayer(client_id);
1944 PlayerSAO *sao = player->getPlayerSAO();
1949 if(sao->getBasePosition().getDistanceFrom(pos) >
1950 params.max_hear_distance)
1953 dst_clients.push_back(client_id);
1957 if(dst_clients.empty())
1961 s32 id = m_next_sound_id++;
1962 // The sound will exist as a reference in m_playing_sounds
1963 m_playing_sounds[id] = ServerPlayingSound();
1964 ServerPlayingSound &psound = m_playing_sounds[id];
1965 psound.params = params;
1968 float gain = params.gain * spec.gain;
1969 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1970 pkt << id << spec.name << gain
1971 << (u8) params.type << pos << params.object
1972 << params.loop << params.fade << params.pitch;
1974 // Backwards compability
1975 bool play_sound = gain > 0;
1977 for (const u16 dst_client : dst_clients) {
1978 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
1979 psound.clients.insert(dst_client);
1980 m_clients.send(dst_client, 0, &pkt, true);
1985 void Server::stopSound(s32 handle)
1987 // Get sound reference
1988 std::unordered_map<s32, ServerPlayingSound>::iterator i =
1989 m_playing_sounds.find(handle);
1990 if (i == m_playing_sounds.end())
1992 ServerPlayingSound &psound = i->second;
1994 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
1997 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
1998 si != psound.clients.end(); ++si) {
2000 m_clients.send(*si, 0, &pkt, true);
2002 // Remove sound reference
2003 m_playing_sounds.erase(i);
2006 void Server::fadeSound(s32 handle, float step, float gain)
2008 // Get sound reference
2009 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2010 m_playing_sounds.find(handle);
2011 if (i == m_playing_sounds.end())
2014 ServerPlayingSound &psound = i->second;
2015 psound.params.gain = gain;
2017 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2018 pkt << handle << step << gain;
2020 // Backwards compability
2021 bool play_sound = gain > 0;
2022 ServerPlayingSound compat_psound = psound;
2023 compat_psound.clients.clear();
2025 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2026 compat_pkt << handle;
2028 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2029 it != psound.clients.end();) {
2030 if (m_clients.getProtocolVersion(*it) >= 32) {
2032 m_clients.send(*it, 0, &pkt, true);
2035 compat_psound.clients.insert(*it);
2037 m_clients.send(*it, 0, &compat_pkt, true);
2038 psound.clients.erase(it++);
2042 // Remove sound reference
2043 if (!play_sound || psound.clients.empty())
2044 m_playing_sounds.erase(i);
2046 if (play_sound && !compat_psound.clients.empty()) {
2047 // Play new sound volume on older clients
2048 playSound(compat_psound.spec, compat_psound.params);
2052 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2053 std::vector<u16> *far_players, float far_d_nodes)
2055 float maxd = far_d_nodes*BS;
2056 v3f p_f = intToFloat(p, BS);
2058 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2061 std::vector<session_t> clients = m_clients.getClientIDs();
2062 for (session_t client_id : clients) {
2065 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2066 PlayerSAO *sao = player->getPlayerSAO();
2070 // If player is far away, only set modified blocks not sent
2071 v3f player_pos = sao->getBasePosition();
2072 if (player_pos.getDistanceFrom(p_f) > maxd) {
2073 far_players->push_back(client_id);
2080 m_clients.send(client_id, 0, &pkt, true);
2084 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2085 std::vector<u16> *far_players, float far_d_nodes,
2086 bool remove_metadata)
2088 float maxd = far_d_nodes*BS;
2089 v3f p_f = intToFloat(p, BS);
2091 std::vector<session_t> clients = m_clients.getClientIDs();
2092 for (const session_t client_id : clients) {
2095 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2096 PlayerSAO *sao = player->getPlayerSAO();
2100 // If player is far away, only set modified blocks not sent
2101 v3f player_pos = sao->getBasePosition();
2102 if(player_pos.getDistanceFrom(p_f) > maxd) {
2103 far_players->push_back(client_id);
2109 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2111 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2113 pkt << p << n.param0 << n.param1 << n.param2
2114 << (u8) (remove_metadata ? 0 : 1);
2119 if (pkt.getSize() > 0)
2120 m_clients.send(client_id, 0, &pkt, true);
2124 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2125 u16 net_proto_version)
2128 Create a packet with the block in the right format
2131 std::ostringstream os(std::ios_base::binary);
2132 block->serialize(os, ver, false);
2133 block->serializeNetworkSpecific(os);
2134 std::string s = os.str();
2136 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2138 pkt << block->getPos();
2139 pkt.putRawString(s.c_str(), s.size());
2143 void Server::SendBlocks(float dtime)
2145 MutexAutoLock envlock(m_env_mutex);
2146 //TODO check if one big lock could be faster then multiple small ones
2148 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2150 std::vector<PrioritySortedBlockTransfer> queue;
2152 u32 total_sending = 0;
2155 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2157 std::vector<session_t> clients = m_clients.getClientIDs();
2160 for (const session_t client_id : clients) {
2161 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2166 total_sending += client->getSendingCount();
2167 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2173 // Lowest priority number comes first.
2174 // Lowest is most important.
2175 std::sort(queue.begin(), queue.end());
2179 // Maximal total count calculation
2180 // The per-client block sends is halved with the maximal online users
2181 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2182 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2184 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2185 if (total_sending >= max_blocks_to_send)
2188 MapBlock *block = nullptr;
2190 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2191 } catch (const InvalidPositionException &e) {
2195 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2200 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2201 client->net_proto_version);
2203 client->SentBlock(block_to_send.pos);
2209 void Server::fillMediaCache()
2211 infostream<<"Server: Calculating media file checksums"<<std::endl;
2213 // Collect all media file paths
2214 std::vector<std::string> paths;
2215 m_modmgr->getModsMediaPaths(paths);
2216 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2217 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2219 // Collect media file information from paths into cache
2220 for (const std::string &mediapath : paths) {
2221 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2222 for (const fs::DirListNode &dln : dirlist) {
2223 if (dln.dir) // Ignode dirs
2225 std::string filename = dln.name;
2226 // If name contains illegal characters, ignore the file
2227 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2228 infostream<<"Server: ignoring illegal file name: \""
2229 << filename << "\"" << std::endl;
2232 // If name is not in a supported format, ignore it
2233 const char *supported_ext[] = {
2234 ".png", ".jpg", ".bmp", ".tga",
2235 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2237 ".x", ".b3d", ".md2", ".obj",
2238 // Custom translation file format
2242 if (removeStringEnd(filename, supported_ext).empty()){
2243 infostream << "Server: ignoring unsupported file extension: \""
2244 << filename << "\"" << std::endl;
2247 // Ok, attempt to load the file and add to cache
2248 std::string filepath;
2249 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2252 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2254 errorstream << "Server::fillMediaCache(): Could not open \""
2255 << filename << "\" for reading" << std::endl;
2258 std::ostringstream tmp_os(std::ios_base::binary);
2262 fis.read(buf, 1024);
2263 std::streamsize len = fis.gcount();
2264 tmp_os.write(buf, len);
2273 errorstream<<"Server::fillMediaCache(): Failed to read \""
2274 << filename << "\"" << std::endl;
2277 if(tmp_os.str().length() == 0) {
2278 errorstream << "Server::fillMediaCache(): Empty file \""
2279 << filepath << "\"" << std::endl;
2284 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2286 unsigned char *digest = sha1.getDigest();
2287 std::string sha1_base64 = base64_encode(digest, 20);
2288 std::string sha1_hex = hex_encode((char*)digest, 20);
2292 m_media[filename] = MediaInfo(filepath, sha1_base64);
2293 verbosestream << "Server: " << sha1_hex << " is " << filename
2299 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2301 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2305 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2308 std::string lang_suffix;
2309 lang_suffix.append(".").append(lang_code).append(".tr");
2310 for (const auto &i : m_media) {
2311 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2318 for (const auto &i : m_media) {
2319 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2321 pkt << i.first << i.second.sha1_digest;
2324 pkt << g_settings->get("remote_media");
2328 struct SendableMedia
2334 SendableMedia(const std::string &name_="", const std::string &path_="",
2335 const std::string &data_=""):
2342 void Server::sendRequestedMedia(session_t peer_id,
2343 const std::vector<std::string> &tosend)
2345 verbosestream<<"Server::sendRequestedMedia(): "
2346 <<"Sending files to client"<<std::endl;
2350 // Put 5kB in one bunch (this is not accurate)
2351 u32 bytes_per_bunch = 5000;
2353 std::vector< std::vector<SendableMedia> > file_bunches;
2354 file_bunches.emplace_back();
2356 u32 file_size_bunch_total = 0;
2358 for (const std::string &name : tosend) {
2359 if (m_media.find(name) == m_media.end()) {
2360 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2361 <<"unknown file \""<<(name)<<"\""<<std::endl;
2365 //TODO get path + name
2366 std::string tpath = m_media[name].path;
2369 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2371 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2372 <<tpath<<"\" for reading"<<std::endl;
2375 std::ostringstream tmp_os(std::ios_base::binary);
2379 fis.read(buf, 1024);
2380 std::streamsize len = fis.gcount();
2381 tmp_os.write(buf, len);
2382 file_size_bunch_total += len;
2391 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2392 <<name<<"\""<<std::endl;
2395 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2396 <<tname<<"\""<<std::endl;*/
2398 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2400 // Start next bunch if got enough data
2401 if(file_size_bunch_total >= bytes_per_bunch) {
2402 file_bunches.emplace_back();
2403 file_size_bunch_total = 0;
2408 /* Create and send packets */
2410 u16 num_bunches = file_bunches.size();
2411 for (u16 i = 0; i < num_bunches; i++) {
2414 u16 total number of texture bunches
2415 u16 index of this bunch
2416 u32 number of files in this bunch
2425 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2426 pkt << num_bunches << i << (u32) file_bunches[i].size();
2428 for (const SendableMedia &j : file_bunches[i]) {
2430 pkt.putLongString(j.data);
2433 verbosestream << "Server::sendRequestedMedia(): bunch "
2434 << i << "/" << num_bunches
2435 << " files=" << file_bunches[i].size()
2436 << " size=" << pkt.getSize() << std::endl;
2441 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2443 if(m_detached_inventories.count(name) == 0) {
2444 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2447 Inventory *inv = m_detached_inventories[name];
2448 std::ostringstream os(std::ios_base::binary);
2450 os << serializeString(name);
2454 std::string s = os.str();
2456 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2457 pkt.putRawString(s.c_str(), s.size());
2459 const std::string &check = m_detached_inventories_player[name];
2460 if (peer_id == PEER_ID_INEXISTENT) {
2462 return m_clients.sendToAll(&pkt);
2463 RemotePlayer *p = m_env->getPlayer(check.c_str());
2465 m_clients.send(p->getPeerId(), 0, &pkt, true);
2467 if (check.empty() || getPlayerName(peer_id) == check)
2472 void Server::sendDetachedInventories(session_t peer_id)
2474 for (const auto &detached_inventory : m_detached_inventories) {
2475 const std::string &name = detached_inventory.first;
2476 //Inventory *inv = i->second;
2477 sendDetachedInventory(name, peer_id);
2485 void Server::DiePlayer(session_t peer_id)
2487 PlayerSAO *playersao = getPlayerSAO(peer_id);
2488 // In some rare cases this can be NULL -- if the player is disconnected
2489 // when a Lua function modifies l_punch, for example
2493 infostream << "Server::DiePlayer(): Player "
2494 << playersao->getPlayer()->getName()
2495 << " dies" << std::endl;
2497 playersao->setHP(0);
2499 // Trigger scripted stuff
2500 m_script->on_dieplayer(playersao);
2502 SendPlayerHP(peer_id);
2503 SendDeathscreen(peer_id, false, v3f(0,0,0));
2506 void Server::RespawnPlayer(session_t peer_id)
2508 PlayerSAO *playersao = getPlayerSAO(peer_id);
2511 infostream << "Server::RespawnPlayer(): Player "
2512 << playersao->getPlayer()->getName()
2513 << " respawns" << std::endl;
2515 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2516 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2518 bool repositioned = m_script->on_respawnplayer(playersao);
2519 if (!repositioned) {
2520 // setPos will send the new position to client
2521 playersao->setPos(findSpawnPos());
2524 SendPlayerHP(peer_id);
2528 void Server::DenySudoAccess(session_t peer_id)
2530 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2535 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2536 const std::string &str_reason, bool reconnect)
2538 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2540 m_clients.event(peer_id, CSE_SetDenied);
2541 DisconnectPeer(peer_id);
2545 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2546 const std::string &custom_reason)
2548 SendAccessDenied(peer_id, reason, custom_reason);
2549 m_clients.event(peer_id, CSE_SetDenied);
2550 DisconnectPeer(peer_id);
2553 // 13/03/15: remove this function when protocol version 25 will become
2554 // the minimum version for MT users, maybe in 1 year
2555 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2557 SendAccessDenied_Legacy(peer_id, reason);
2558 m_clients.event(peer_id, CSE_SetDenied);
2559 DisconnectPeer(peer_id);
2562 void Server::DisconnectPeer(session_t peer_id)
2564 m_modchannel_mgr->leaveAllChannels(peer_id);
2565 m_con->DisconnectPeer(peer_id);
2568 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2571 RemoteClient* client = getClient(peer_id, CS_Invalid);
2573 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2575 // Right now, the auth mechs don't change between login and sudo mode.
2576 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2577 client->allowed_sudo_mechs = sudo_auth_mechs;
2579 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2580 << g_settings->getFloat("dedicated_server_step")
2584 m_clients.event(peer_id, CSE_AuthAccept);
2586 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2588 // We only support SRP right now
2589 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2591 resp_pkt << sudo_auth_mechs;
2593 m_clients.event(peer_id, CSE_SudoSuccess);
2597 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2599 std::wstring message;
2602 Clear references to playing sounds
2604 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2605 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2606 ServerPlayingSound &psound = i->second;
2607 psound.clients.erase(peer_id);
2608 if (psound.clients.empty())
2609 m_playing_sounds.erase(i++);
2614 // clear formspec info so the next client can't abuse the current state
2615 m_formspec_state_data.erase(peer_id);
2617 RemotePlayer *player = m_env->getPlayer(peer_id);
2619 /* Run scripts and remove from environment */
2621 PlayerSAO *playersao = player->getPlayerSAO();
2624 // inform connected clients
2625 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2626 // (u16) 1 + std::string represents a vector serialization representation
2627 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2628 m_clients.sendToAll(¬ice);
2630 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2632 playersao->disconnected();
2639 if (player && reason != CDR_DENY) {
2640 std::ostringstream os(std::ios_base::binary);
2641 std::vector<session_t> clients = m_clients.getClientIDs();
2643 for (const session_t client_id : clients) {
2645 RemotePlayer *player = m_env->getPlayer(client_id);
2649 // Get name of player
2650 os << player->getName() << " ";
2653 std::string name = player->getName();
2654 actionstream << name << " "
2655 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2656 << " List of players: " << os.str() << std::endl;
2658 m_admin_chat->outgoing_queue.push_back(
2659 new ChatEventNick(CET_NICK_REMOVE, name));
2663 MutexAutoLock env_lock(m_env_mutex);
2664 m_clients.DeleteClient(peer_id);
2668 // Send leave chat message to all remaining clients
2669 if (!message.empty()) {
2670 SendChatMessage(PEER_ID_INEXISTENT,
2671 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2675 void Server::UpdateCrafting(RemotePlayer *player)
2677 // Get a preview for crafting
2679 InventoryLocation loc;
2680 loc.setPlayer(player->getName());
2681 std::vector<ItemStack> output_replacements;
2682 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2683 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2684 (&player->inventory)->getList("craft"), loc);
2686 // Put the new preview in
2687 InventoryList *plist = player->inventory.getList("craftpreview");
2688 sanity_check(plist);
2689 sanity_check(plist->getSize() >= 1);
2690 plist->changeItem(0, preview);
2693 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2695 if (evt->type == CET_NICK_ADD) {
2696 // The terminal informed us of its nick choice
2697 m_admin_nick = ((ChatEventNick *)evt)->nick;
2698 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2699 errorstream << "You haven't set up an account." << std::endl
2700 << "Please log in using the client as '"
2701 << m_admin_nick << "' with a secure password." << std::endl
2702 << "Until then, you can't execute admin tasks via the console," << std::endl
2703 << "and everybody can claim the user account instead of you," << std::endl
2704 << "giving them full control over this server." << std::endl;
2707 assert(evt->type == CET_CHAT);
2708 handleAdminChat((ChatEventChat *)evt);
2712 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2713 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2715 // If something goes wrong, this player is to blame
2716 RollbackScopeActor rollback_scope(m_rollback,
2717 std::string("player:") + name);
2719 if (g_settings->getBool("strip_color_codes"))
2720 wmessage = unescape_enriched(wmessage);
2723 switch (player->canSendChatMessage()) {
2724 case RPLAYER_CHATRESULT_FLOODING: {
2725 std::wstringstream ws;
2726 ws << L"You cannot send more messages. You are limited to "
2727 << g_settings->getFloat("chat_message_limit_per_10sec")
2728 << L" messages per 10 seconds.";
2731 case RPLAYER_CHATRESULT_KICK:
2732 DenyAccess_Legacy(player->getPeerId(),
2733 L"You have been kicked due to message flooding.");
2735 case RPLAYER_CHATRESULT_OK:
2738 FATAL_ERROR("Unhandled chat filtering result found.");
2742 if (m_max_chatmessage_length > 0
2743 && wmessage.length() > m_max_chatmessage_length) {
2744 return L"Your message exceed the maximum chat message limit set on the server. "
2745 L"It was refused. Send a shorter message";
2748 // Run script hook, exit if script ate the chat message
2749 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2754 // Whether to send line to the player that sent the message, or to all players
2755 bool broadcast_line = true;
2757 if (check_shout_priv && !checkPriv(name, "shout")) {
2758 line += L"-!- You don't have permission to shout.";
2759 broadcast_line = false;
2768 Tell calling method to send the message to sender
2770 if (!broadcast_line)
2774 Send the message to others
2776 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2778 std::vector<session_t> clients = m_clients.getClientIDs();
2781 Send the message back to the inital sender
2782 if they are using protocol version >= 29
2785 session_t peer_id_to_avoid_sending =
2786 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2788 if (player && player->protocol_version >= 29)
2789 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2791 for (u16 cid : clients) {
2792 if (cid != peer_id_to_avoid_sending)
2793 SendChatMessage(cid, ChatMessage(line));
2798 void Server::handleAdminChat(const ChatEventChat *evt)
2800 std::string name = evt->nick;
2801 std::wstring wname = utf8_to_wide(name);
2802 std::wstring wmessage = evt->evt_msg;
2804 std::wstring answer = handleChat(name, wname, wmessage);
2806 // If asked to send answer to sender
2807 if (!answer.empty()) {
2808 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2812 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2814 RemoteClient *client = getClientNoEx(peer_id,state_min);
2816 throw ClientNotFoundException("Client not found");
2820 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2822 return m_clients.getClientNoEx(peer_id, state_min);
2825 std::string Server::getPlayerName(session_t peer_id)
2827 RemotePlayer *player = m_env->getPlayer(peer_id);
2829 return "[id="+itos(peer_id)+"]";
2830 return player->getName();
2833 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2835 RemotePlayer *player = m_env->getPlayer(peer_id);
2838 return player->getPlayerSAO();
2841 std::wstring Server::getStatusString()
2843 std::wostringstream os(std::ios_base::binary);
2846 os<<L"version="<<narrow_to_wide(g_version_string);
2848 os<<L", uptime="<<m_uptime.get();
2850 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2851 // Information about clients
2854 std::vector<session_t> clients = m_clients.getClientIDs();
2855 for (session_t client_id : clients) {
2857 RemotePlayer *player = m_env->getPlayer(client_id);
2858 // Get name of player
2859 std::wstring name = L"unknown";
2861 name = narrow_to_wide(player->getName());
2862 // Add name to information string
2871 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2872 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2874 if (!g_settings->get("motd").empty())
2875 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2879 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2881 std::set<std::string> privs;
2882 m_script->getAuth(name, NULL, &privs);
2886 bool Server::checkPriv(const std::string &name, const std::string &priv)
2888 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2889 return (privs.count(priv) != 0);
2892 void Server::reportPrivsModified(const std::string &name)
2895 std::vector<session_t> clients = m_clients.getClientIDs();
2896 for (const session_t client_id : clients) {
2897 RemotePlayer *player = m_env->getPlayer(client_id);
2898 reportPrivsModified(player->getName());
2901 RemotePlayer *player = m_env->getPlayer(name.c_str());
2904 SendPlayerPrivileges(player->getPeerId());
2905 PlayerSAO *sao = player->getPlayerSAO();
2908 sao->updatePrivileges(
2909 getPlayerEffectivePrivs(name),
2914 void Server::reportInventoryFormspecModified(const std::string &name)
2916 RemotePlayer *player = m_env->getPlayer(name.c_str());
2919 SendPlayerInventoryFormspec(player->getPeerId());
2922 void Server::setIpBanned(const std::string &ip, const std::string &name)
2924 m_banmanager->add(ip, name);
2927 void Server::unsetIpBanned(const std::string &ip_or_name)
2929 m_banmanager->remove(ip_or_name);
2932 std::string Server::getBanDescription(const std::string &ip_or_name)
2934 return m_banmanager->getBanDescription(ip_or_name);
2937 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2939 // m_env will be NULL if the server is initializing
2943 if (m_admin_nick == name && !m_admin_nick.empty()) {
2944 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2947 RemotePlayer *player = m_env->getPlayer(name);
2952 if (player->getPeerId() == PEER_ID_INEXISTENT)
2955 SendChatMessage(player->getPeerId(), ChatMessage(msg));
2958 bool Server::showFormspec(const char *playername, const std::string &formspec,
2959 const std::string &formname)
2961 // m_env will be NULL if the server is initializing
2965 RemotePlayer *player = m_env->getPlayer(playername);
2969 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
2973 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
2978 u32 id = player->addHud(form);
2980 SendHUDAdd(player->getPeerId(), id, form);
2985 bool Server::hudRemove(RemotePlayer *player, u32 id) {
2989 HudElement* todel = player->removeHud(id);
2996 SendHUDRemove(player->getPeerId(), id);
3000 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3005 SendHUDChange(player->getPeerId(), id, stat, data);
3009 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3014 SendHUDSetFlags(player->getPeerId(), flags, mask);
3015 player->hud_flags &= ~mask;
3016 player->hud_flags |= flags;
3018 PlayerSAO* playersao = player->getPlayerSAO();
3023 m_script->player_event(playersao, "hud_changed");
3027 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3032 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3035 player->setHotbarItemcount(hotbar_itemcount);
3036 std::ostringstream os(std::ios::binary);
3037 writeS32(os, hotbar_itemcount);
3038 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3042 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3047 player->setHotbarImage(name);
3048 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3051 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3056 player->setHotbarSelectedImage(name);
3057 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3060 Address Server::getPeerAddress(session_t peer_id)
3062 return m_con->GetPeerAddress(peer_id);
3065 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3066 v2s32 animation_frames[4], f32 frame_speed)
3068 sanity_check(player);
3069 player->setLocalAnimations(animation_frames, frame_speed);
3070 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3073 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3075 sanity_check(player);
3076 player->eye_offset_first = first;
3077 player->eye_offset_third = third;
3078 SendEyeOffset(player->getPeerId(), first, third);
3081 void Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3082 const std::string &type, const std::vector<std::string> ¶ms,
3085 sanity_check(player);
3086 player->setSky(bgcolor, type, params, clouds);
3087 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3090 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3092 sanity_check(player);
3093 player->setCloudParams(params);
3094 SendCloudParams(player->getPeerId(), params);
3097 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3103 player->overrideDayNightRatio(do_override, ratio);
3104 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3108 void Server::notifyPlayers(const std::wstring &msg)
3110 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3113 void Server::spawnParticle(const std::string &playername, v3f pos,
3114 v3f velocity, v3f acceleration,
3115 float expirationtime, float size, bool
3116 collisiondetection, bool collision_removal,
3117 bool vertical, const std::string &texture,
3118 const struct TileAnimationParams &animation, u8 glow)
3120 // m_env will be NULL if the server is initializing
3124 session_t peer_id = PEER_ID_INEXISTENT;
3126 if (!playername.empty()) {
3127 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3130 peer_id = player->getPeerId();
3131 proto_ver = player->protocol_version;
3134 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3135 expirationtime, size, collisiondetection,
3136 collision_removal, vertical, texture, animation, glow);
3139 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3140 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3141 float minexptime, float maxexptime, float minsize, float maxsize,
3142 bool collisiondetection, bool collision_removal,
3143 ServerActiveObject *attached, bool vertical, const std::string &texture,
3144 const std::string &playername, const struct TileAnimationParams &animation,
3147 // m_env will be NULL if the server is initializing
3151 session_t peer_id = PEER_ID_INEXISTENT;
3153 if (!playername.empty()) {
3154 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3157 peer_id = player->getPeerId();
3158 proto_ver = player->protocol_version;
3161 u16 attached_id = attached ? attached->getId() : 0;
3164 if (attached_id == 0)
3165 id = m_env->addParticleSpawner(spawntime);
3167 id = m_env->addParticleSpawner(spawntime, attached_id);
3169 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3170 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3171 minexptime, maxexptime, minsize, maxsize,
3172 collisiondetection, collision_removal, attached_id, vertical,
3173 texture, id, animation, glow);
3178 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3180 // m_env will be NULL if the server is initializing
3182 throw ServerError("Can't delete particle spawners during initialisation!");
3184 session_t peer_id = PEER_ID_INEXISTENT;
3185 if (!playername.empty()) {
3186 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3189 peer_id = player->getPeerId();
3192 m_env->deleteParticleSpawner(id);
3193 SendDeleteParticleSpawner(peer_id, id);
3196 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3198 if(m_detached_inventories.count(name) > 0){
3199 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3200 delete m_detached_inventories[name];
3202 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3204 Inventory *inv = new Inventory(m_itemdef);
3206 m_detached_inventories[name] = inv;
3207 m_detached_inventories_player[name] = player;
3208 //TODO find a better way to do this
3209 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3213 // actions: time-reversed list
3214 // Return value: success/failure
3215 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3216 std::list<std::string> *log)
3218 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3219 ServerMap *map = (ServerMap*)(&m_env->getMap());
3221 // Fail if no actions to handle
3222 if(actions.empty()){
3223 log->push_back("Nothing to do.");
3230 for (const RollbackAction &action : actions) {
3232 bool success = action.applyRevert(map, this, this);
3235 std::ostringstream os;
3236 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3237 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3239 log->push_back(os.str());
3241 std::ostringstream os;
3242 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3243 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3245 log->push_back(os.str());
3249 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3250 <<" failed"<<std::endl;
3252 // Call it done if less than half failed
3253 return num_failed <= num_tried/2;
3256 // IGameDef interface
3258 IItemDefManager *Server::getItemDefManager()
3263 const NodeDefManager *Server::getNodeDefManager()
3268 ICraftDefManager *Server::getCraftDefManager()
3273 u16 Server::allocateUnknownNodeId(const std::string &name)
3275 return m_nodedef->allocateDummy(name);
3278 MtEventManager *Server::getEventManager()
3283 IWritableItemDefManager *Server::getWritableItemDefManager()
3288 NodeDefManager *Server::getWritableNodeDefManager()
3293 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3298 const std::vector<ModSpec> & Server::getMods() const
3300 return m_modmgr->getMods();
3303 const ModSpec *Server::getModSpec(const std::string &modname) const
3305 return m_modmgr->getModSpec(modname);
3308 void Server::getModNames(std::vector<std::string> &modlist)
3310 m_modmgr->getModNames(modlist);
3313 std::string Server::getBuiltinLuaPath()
3315 return porting::path_share + DIR_DELIM + "builtin";
3318 std::string Server::getModStoragePath() const
3320 return m_path_world + DIR_DELIM + "mod_storage";
3323 v3f Server::findSpawnPos()
3325 ServerMap &map = m_env->getServerMap();
3327 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3328 return nodeposf * BS;
3331 bool is_good = false;
3332 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3333 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3335 // Try to find a good place a few times
3336 for(s32 i = 0; i < 4000 && !is_good; i++) {
3337 s32 range = MYMIN(1 + i, range_max);
3338 // We're going to try to throw the player to this position
3339 v2s16 nodepos2d = v2s16(
3340 -range + (myrand() % (range * 2)),
3341 -range + (myrand() % (range * 2)));
3343 // Get spawn level at point
3344 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3345 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3346 // the mapgen to signify an unsuitable spawn position
3347 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3350 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3353 for (s32 i = 0; i < 10; i++) {
3354 v3s16 blockpos = getNodeBlockPos(nodepos);
3355 map.emergeBlock(blockpos, true);
3356 content_t c = map.getNodeNoEx(nodepos).getContent();
3357 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3359 if (air_count >= 2) {
3360 nodeposf = intToFloat(nodepos, BS);
3361 // Don't spawn the player outside map boundaries
3362 if (objectpos_over_limit(nodeposf))
3375 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3377 m_shutdown_timer = delay;
3378 m_shutdown_msg = msg;
3379 m_shutdown_ask_reconnect = reconnect;
3381 if (delay == 0.0f) {
3382 // No delay, shutdown immediately
3383 m_shutdown_requested = true;
3384 // only print to the infostream, a chat message saying
3385 // "Server Shutting Down" is sent when the server destructs.
3386 infostream << "*** Immediate Server shutdown requested." << std::endl;
3387 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3388 // Negative delay, cancel shutdown if requested
3389 m_shutdown_timer = 0.0f;
3390 m_shutdown_msg = "";
3391 m_shutdown_ask_reconnect = false;
3392 m_shutdown_requested = false;
3393 std::wstringstream ws;
3395 ws << L"*** Server shutdown canceled.";
3397 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3398 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3399 } else if (delay > 0.0f) {
3400 // Positive delay, tell the clients when the server will shut down
3401 std::wstringstream ws;
3403 ws << L"*** Server shutting down in "
3404 << duration_to_string(myround(m_shutdown_timer)).c_str()
3407 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3408 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3412 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3415 Try to get an existing player
3417 RemotePlayer *player = m_env->getPlayer(name);
3419 // If player is already connected, cancel
3420 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3421 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3426 If player with the wanted peer_id already exists, cancel.
3428 if (m_env->getPlayer(peer_id)) {
3429 infostream<<"emergePlayer(): Player with wrong name but same"
3430 " peer_id already exists"<<std::endl;
3435 player = new RemotePlayer(name, idef());
3438 bool newplayer = false;
3441 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3443 // Complete init with server parts
3444 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3445 player->protocol_version = proto_version;
3449 m_script->on_newplayer(playersao);
3455 bool Server::registerModStorage(ModMetadata *storage)
3457 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3458 errorstream << "Unable to register same mod storage twice. Storage name: "
3459 << storage->getModName() << std::endl;
3463 m_mod_storages[storage->getModName()] = storage;
3467 void Server::unregisterModStorage(const std::string &name)
3469 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3470 if (it != m_mod_storages.end()) {
3471 // Save unconditionaly on unregistration
3472 it->second->save(getModStoragePath());
3473 m_mod_storages.erase(name);
3477 void dedicated_server_loop(Server &server, bool &kill)
3479 verbosestream<<"dedicated_server_loop()"<<std::endl;
3481 IntervalLimiter m_profiler_interval;
3483 static thread_local const float steplen =
3484 g_settings->getFloat("dedicated_server_step");
3485 static thread_local const float profiler_print_interval =
3486 g_settings->getFloat("profiler_print_interval");
3489 // This is kind of a hack but can be done like this
3490 // because server.step() is very light
3492 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3493 sleep_ms((int)(steplen*1000.0));
3495 server.step(steplen);
3497 if (server.getShutdownRequested() || kill)
3503 if (profiler_print_interval != 0) {
3504 if(m_profiler_interval.step(steplen, profiler_print_interval))
3506 infostream<<"Profiler:"<<std::endl;
3507 g_profiler->print(infostream);
3508 g_profiler->clear();
3513 infostream << "Dedicated server quitting" << std::endl;
3515 if (g_settings->getBool("server_announce"))
3516 ServerList::sendAnnounce(ServerList::AA_DELETE,
3517 server.m_bind_addr.getPort());
3526 bool Server::joinModChannel(const std::string &channel)
3528 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3529 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3532 bool Server::leaveModChannel(const std::string &channel)
3534 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3537 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3539 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3542 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3546 ModChannel* Server::getModChannel(const std::string &channel)
3548 return m_modchannel_mgr->getModChannel(channel);
3551 void Server::broadcastModChannelMessage(const std::string &channel,
3552 const std::string &message, session_t from_peer)
3554 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3558 if (message.size() > STRING_MAX_LEN) {
3559 warningstream << "ModChannel message too long, dropping before sending "
3560 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3561 << channel << ")" << std::endl;
3566 if (from_peer != PEER_ID_SERVER) {
3567 sender = getPlayerName(from_peer);
3570 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3571 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3572 resp_pkt << channel << sender << message;
3573 for (session_t peer_id : peers) {
3575 if (peer_id == from_peer)
3578 Send(peer_id, &resp_pkt);
3581 if (from_peer != PEER_ID_SERVER) {
3582 m_script->on_modchannel_message(channel, sender, message);