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"
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 "util/base64.h"
62 #include "util/sha1.h"
65 #include "chatmessage.h"
66 #include "chat_interface.h"
67 #include "remoteplayer.h"
69 class ClientNotFoundException : public BaseException
72 ClientNotFoundException(const char *s):
77 class ServerThread : public Thread
81 ServerThread(Server *server):
92 void *ServerThread::run()
94 BEGIN_DEBUG_EXCEPTION_HANDLER
96 m_server->AsyncRunStep(true);
98 while (!stopRequested()) {
100 m_server->AsyncRunStep();
104 } catch (con::NoIncomingDataException &e) {
105 } catch (con::PeerNotFoundException &e) {
106 infostream<<"Server: PeerNotFoundException"<<std::endl;
107 } catch (ClientNotFoundException &e) {
108 } catch (con::ConnectionBindFailed &e) {
109 m_server->setAsyncFatalError(e.what());
110 } catch (LuaError &e) {
111 m_server->setAsyncFatalError(
112 "ServerThread::run Lua: " + std::string(e.what()));
116 END_DEBUG_EXCEPTION_HANDLER
121 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
123 if(pos_exists) *pos_exists = false;
128 if(pos_exists) *pos_exists = true;
133 ServerActiveObject *sao = env->getActiveObject(object);
136 if(pos_exists) *pos_exists = true;
137 return sao->getBasePosition(); }
149 const std::string &path_world,
150 const SubgameSpec &gamespec,
151 bool simple_singleplayer_mode,
156 m_bind_addr(bind_addr),
157 m_path_world(path_world),
158 m_gamespec(gamespec),
159 m_simple_singleplayer_mode(simple_singleplayer_mode),
160 m_dedicated(dedicated),
161 m_async_fatal_error(""),
162 m_con(std::make_shared<con::Connection>(PROTOCOL_ID,
165 m_bind_addr.isIPv6(),
167 m_itemdef(createItemDefManager()),
168 m_nodedef(createNodeDefManager()),
169 m_craftdef(createCraftDefManager()),
170 m_event(new EventManager()),
174 m_modchannel_mgr(new ModChannelMgr())
176 m_lag = g_settings->getFloat("dedicated_server_step");
178 if (path_world.empty())
179 throw ServerError("Supplied empty world path");
181 if(!gamespec.isValid())
182 throw ServerError("Supplied invalid gamespec");
184 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
185 if(m_simple_singleplayer_mode)
186 infostream<<" in simple singleplayer mode"<<std::endl;
188 infostream<<std::endl;
189 infostream<<"- world: "<<m_path_world<<std::endl;
190 infostream<<"- game: "<<m_gamespec.path<<std::endl;
192 // Create world if it doesn't exist
193 if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
194 throw ServerError("Failed to initialize world");
196 // Create server thread
197 m_thread = new ServerThread(this);
199 // Create emerge manager
200 m_emerge = new EmergeManager(this);
202 // Create ban manager
203 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
204 m_banmanager = new BanManager(ban_path);
206 ServerModConfiguration modconf(m_path_world);
207 m_mods = modconf.getMods();
208 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
209 // complain about mods with unsatisfied dependencies
210 if (!modconf.isConsistent()) {
211 modconf.printUnsatisfiedModsError();
215 MutexAutoLock envlock(m_env_mutex);
217 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
218 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
220 // Initialize scripting
221 infostream<<"Server: Initializing Lua"<<std::endl;
223 m_script = new ServerScripting(this);
225 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
228 infostream << "Server: Loading mods: ";
229 for (std::vector<ModSpec>::const_iterator i = m_mods.begin();
230 i != m_mods.end(); ++i) {
231 infostream << (*i).name << " ";
233 infostream << std::endl;
234 // Load and run "mod" scripts
235 for (std::vector<ModSpec>::const_iterator it = m_mods.begin();
236 it != m_mods.end(); ++it) {
237 const ModSpec &mod = *it;
238 if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
239 throw ModError("Error loading mod \"" + mod.name +
240 "\": Mod name does not follow naming conventions: "
241 "Only characters [a-z0-9_] are allowed.");
243 std::string script_path = mod.path + DIR_DELIM + "init.lua";
244 infostream << " [" << padStringRight(mod.name, 12) << "] [\""
245 << script_path << "\"]" << std::endl;
246 m_script->loadMod(script_path, mod.name);
249 // Read Textures and calculate sha1 sums
252 // Apply item aliases in the node definition manager
253 m_nodedef->updateAliases(m_itemdef);
255 // Apply texture overrides from texturepack/override.txt
256 std::string texture_path = g_settings->get("texture_path");
257 if (!texture_path.empty() && fs::IsDir(texture_path))
258 m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
260 m_nodedef->setNodeRegistrationStatus(true);
262 // Perform pending node name resolutions
263 m_nodedef->runNodeResolveCallbacks();
265 // unmap node names for connected nodeboxes
266 m_nodedef->mapNodeboxConnections();
268 // init the recipe hashes to speed up crafting
269 m_craftdef->initHashes(this);
271 // Initialize Environment
272 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
274 m_clients.setEnv(m_env);
276 if (!servermap->settings_mgr.makeMapgenParams())
277 FATAL_ERROR("Couldn't create any mapgen type");
279 // Initialize mapgens
280 m_emerge->initMapgens(servermap->getMapgenParams());
282 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
283 if (m_enable_rollback_recording) {
284 // Create rollback manager
285 m_rollback = new RollbackManager(m_path_world, this);
288 // Give environment reference to scripting api
289 m_script->initializeEnvironment(m_env);
291 // Register us to receive map edit events
292 servermap->addEventReceiver(this);
294 // If file exists, load environment metadata
295 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
296 infostream << "Server: Loading environment metadata" << std::endl;
299 m_env->loadDefaultMeta();
302 m_liquid_transform_every = g_settings->getFloat("liquid_update");
303 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
304 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
305 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
310 infostream<<"Server destructing"<<std::endl;
312 // Send shutdown message
313 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
314 L"*** Server shutting down"));
317 MutexAutoLock envlock(m_env_mutex);
319 // Execute script shutdown hooks
320 m_script->on_shutdown();
322 infostream << "Server: Saving players" << std::endl;
323 m_env->saveLoadedPlayers();
325 infostream << "Server: Kicking players" << std::endl;
326 std::string kick_msg;
327 bool reconnect = false;
328 if (getShutdownRequested()) {
329 reconnect = m_shutdown_ask_reconnect;
330 kick_msg = m_shutdown_msg;
332 if (kick_msg.empty()) {
333 kick_msg = g_settings->get("kick_msg_shutdown");
335 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
336 kick_msg, reconnect);
338 infostream << "Server: Saving environment metadata" << std::endl;
346 // stop all emerge threads before deleting players that may have
347 // requested blocks to be emerged
348 m_emerge->stopThreads();
350 // Delete things in the reverse order of creation
360 // Deinitialize scripting
361 infostream<<"Server: Deinitializing scripting"<<std::endl;
364 // Delete detached inventories
365 for (auto &detached_inventory : m_detached_inventories) {
366 delete detached_inventory.second;
372 infostream << "Starting server on " << m_bind_addr.serializeString()
373 << "..." << std::endl;
375 // Stop thread if already running
378 // Initialize connection
379 m_con->SetTimeoutMs(30);
380 m_con->Serve(m_bind_addr);
385 // ASCII art for the win!
387 << " .__ __ __ " << std::endl
388 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
389 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
390 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
391 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
392 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
393 actionstream << "World at [" << m_path_world << "]" << std::endl;
394 actionstream << "Server for gameid=\"" << m_gamespec.id
395 << "\" listening on " << m_bind_addr.serializeString() << ":"
396 << m_bind_addr.getPort() << "." << std::endl;
401 infostream<<"Server: Stopping and waiting threads"<<std::endl;
403 // Stop threads (set run=false first so both start stopping)
405 //m_emergethread.setRun(false);
407 //m_emergethread.stop();
409 infostream<<"Server: Threads stopped"<<std::endl;
412 void Server::step(float dtime)
418 MutexAutoLock lock(m_step_dtime_mutex);
419 m_step_dtime += dtime;
421 // Throw if fatal error occurred in thread
422 std::string async_err = m_async_fatal_error.get();
423 if (!async_err.empty()) {
424 if (!m_simple_singleplayer_mode) {
425 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
426 g_settings->get("kick_msg_crash"),
427 g_settings->getBool("ask_reconnect_on_crash"));
429 throw ServerError("AsyncErr: " + async_err);
433 void Server::AsyncRunStep(bool initial_step)
435 g_profiler->add("Server::AsyncRunStep (num)", 1);
439 MutexAutoLock lock1(m_step_dtime_mutex);
440 dtime = m_step_dtime;
444 // Send blocks to clients
448 if((dtime < 0.001) && !initial_step)
451 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
453 //infostream<<"Server steps "<<dtime<<std::endl;
454 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
457 MutexAutoLock lock1(m_step_dtime_mutex);
458 m_step_dtime -= dtime;
465 m_uptime.set(m_uptime.get() + dtime);
471 Update time of day and overall game time
473 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
476 Send to clients at constant intervals
479 m_time_of_day_send_timer -= dtime;
480 if(m_time_of_day_send_timer < 0.0) {
481 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
482 u16 time = m_env->getTimeOfDay();
483 float time_speed = g_settings->getFloat("time_speed");
484 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
488 MutexAutoLock lock(m_env_mutex);
489 // Figure out and report maximum lag to environment
490 float max_lag = m_env->getMaxLagEstimate();
491 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
493 if(dtime > 0.1 && dtime > max_lag * 2.0)
494 infostream<<"Server: Maximum lag peaked to "<<dtime
498 m_env->reportMaxLagEstimate(max_lag);
500 ScopeProfiler sp(g_profiler, "SEnv step");
501 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
505 static const float map_timer_and_unload_dtime = 2.92;
506 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
508 MutexAutoLock lock(m_env_mutex);
509 // Run Map's timers and unload unused data
510 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
511 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
512 g_settings->getFloat("server_unload_unused_data_timeout"),
517 Listen to the admin chat, if available
520 if (!m_admin_chat->command_queue.empty()) {
521 MutexAutoLock lock(m_env_mutex);
522 while (!m_admin_chat->command_queue.empty()) {
523 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
524 handleChatInterfaceEvent(evt);
528 m_admin_chat->outgoing_queue.push_back(
529 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
536 /* Transform liquids */
537 m_liquid_transform_timer += dtime;
538 if(m_liquid_transform_timer >= m_liquid_transform_every)
540 m_liquid_transform_timer -= m_liquid_transform_every;
542 MutexAutoLock lock(m_env_mutex);
544 ScopeProfiler sp(g_profiler, "Server: liquid transform");
546 std::map<v3s16, MapBlock*> modified_blocks;
547 m_env->getMap().transformLiquids(modified_blocks, m_env);
550 Set the modified blocks unsent for all the clients
552 if(!modified_blocks.empty())
554 SetBlocksNotSent(modified_blocks);
557 m_clients.step(dtime);
559 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
561 // send masterserver announce
563 float &counter = m_masterserver_timer;
564 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
565 g_settings->getBool("server_announce")) {
566 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
567 ServerList::AA_START,
568 m_bind_addr.getPort(),
569 m_clients.getPlayerNames(),
571 m_env->getGameTime(),
574 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
584 Check added and deleted active objects
587 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
588 MutexAutoLock envlock(m_env_mutex);
591 const RemoteClientMap &clients = m_clients.getClientList();
592 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
594 // Radius inside which objects are active
595 static thread_local const s16 radius =
596 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
598 // Radius inside which players are active
599 static thread_local const bool is_transfer_limited =
600 g_settings->exists("unlimited_player_transfer_distance") &&
601 !g_settings->getBool("unlimited_player_transfer_distance");
602 static thread_local const s16 player_transfer_dist =
603 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
604 s16 player_radius = player_transfer_dist;
605 if (player_radius == 0 && is_transfer_limited)
606 player_radius = radius;
608 for (const auto &client_it : clients) {
609 RemoteClient *client = client_it.second;
611 // If definitions and textures have not been sent, don't
612 // send objects either
613 if (client->getState() < CS_DefinitionsSent)
616 RemotePlayer *player = m_env->getPlayer(client->peer_id);
618 // This can happen if the client timeouts somehow
622 PlayerSAO *playersao = player->getPlayerSAO();
626 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
627 if (my_radius <= 0) my_radius = radius;
628 //infostream << "Server: Active Radius " << my_radius << std::endl;
630 std::queue<u16> removed_objects;
631 std::queue<u16> added_objects;
632 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
633 client->m_known_objects, removed_objects);
634 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
635 client->m_known_objects, added_objects);
637 // Ignore if nothing happened
638 if (removed_objects.empty() && added_objects.empty()) {
642 std::string data_buffer;
646 // Handle removed objects
647 writeU16((u8*)buf, removed_objects.size());
648 data_buffer.append(buf, 2);
649 while (!removed_objects.empty()) {
651 u16 id = removed_objects.front();
652 ServerActiveObject* obj = m_env->getActiveObject(id);
654 // Add to data buffer for sending
655 writeU16((u8*)buf, id);
656 data_buffer.append(buf, 2);
658 // Remove from known objects
659 client->m_known_objects.erase(id);
661 if(obj && obj->m_known_by_count > 0)
662 obj->m_known_by_count--;
663 removed_objects.pop();
666 // Handle added objects
667 writeU16((u8*)buf, added_objects.size());
668 data_buffer.append(buf, 2);
669 while (!added_objects.empty()) {
671 u16 id = added_objects.front();
672 ServerActiveObject* obj = m_env->getActiveObject(id);
675 u8 type = ACTIVEOBJECT_TYPE_INVALID;
677 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
679 type = obj->getSendType();
681 // Add to data buffer for sending
682 writeU16((u8*)buf, id);
683 data_buffer.append(buf, 2);
684 writeU8((u8*)buf, type);
685 data_buffer.append(buf, 1);
688 data_buffer.append(serializeLongString(
689 obj->getClientInitializationData(client->net_proto_version)));
691 data_buffer.append(serializeLongString(""));
693 // Add to known objects
694 client->m_known_objects.insert(id);
697 obj->m_known_by_count++;
702 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
703 verbosestream << "Server: Sent object remove/add: "
704 << removed_objects.size() << " removed, "
705 << added_objects.size() << " added, "
706 << "packet size is " << pktSize << std::endl;
710 m_mod_storage_save_timer -= dtime;
711 if (m_mod_storage_save_timer <= 0.0f) {
712 infostream << "Saving registered mod storages." << std::endl;
713 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
714 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
715 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
716 if (it->second->isModified()) {
717 it->second->save(getModStoragePath());
727 MutexAutoLock envlock(m_env_mutex);
728 ScopeProfiler sp(g_profiler, "Server: sending object messages");
731 // Value = data sent by object
732 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
734 // Get active object messages from environment
736 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
740 std::vector<ActiveObjectMessage>* message_list = nullptr;
741 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
742 n = buffered_messages.find(aom.id);
743 if (n == buffered_messages.end()) {
744 message_list = new std::vector<ActiveObjectMessage>;
745 buffered_messages[aom.id] = message_list;
748 message_list = n->second;
750 message_list->push_back(aom);
754 const RemoteClientMap &clients = m_clients.getClientList();
755 // Route data to every client
756 for (const auto &client_it : clients) {
757 RemoteClient *client = client_it.second;
758 std::string reliable_data;
759 std::string unreliable_data;
760 // Go through all objects in message buffer
761 for (const auto &buffered_message : buffered_messages) {
762 // If object is not known by client, skip it
763 u16 id = buffered_message.first;
764 if (client->m_known_objects.find(id) == client->m_known_objects.end())
767 // Get message list of object
768 std::vector<ActiveObjectMessage>* list = buffered_message.second;
769 // Go through every message
770 for (const ActiveObjectMessage &aom : *list) {
771 // Compose the full new data with header
772 std::string new_data;
775 writeU16((u8*)&buf[0], aom.id);
776 new_data.append(buf, 2);
778 new_data += serializeString(aom.datastring);
779 // Add data to buffer
781 reliable_data += new_data;
783 unreliable_data += new_data;
787 reliable_data and unreliable_data are now ready.
790 if (!reliable_data.empty()) {
791 SendActiveObjectMessages(client->peer_id, reliable_data);
794 if (!unreliable_data.empty()) {
795 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
800 // Clear buffered_messages
801 for (auto &buffered_message : buffered_messages) {
802 delete buffered_message.second;
807 Send queued-for-sending map edit events.
810 // We will be accessing the environment
811 MutexAutoLock lock(m_env_mutex);
813 // Don't send too many at a time
816 // Single change sending is disabled if queue size is not small
817 bool disable_single_change_sending = false;
818 if(m_unsent_map_edit_queue.size() >= 4)
819 disable_single_change_sending = true;
821 int event_count = m_unsent_map_edit_queue.size();
823 // We'll log the amount of each
826 while (!m_unsent_map_edit_queue.empty()) {
827 MapEditEvent* event = m_unsent_map_edit_queue.front();
828 m_unsent_map_edit_queue.pop();
830 // Players far away from the change are stored here.
831 // Instead of sending the changes, MapBlocks are set not sent
833 std::vector<u16> far_players;
835 switch (event->type) {
838 prof.add("MEET_ADDNODE", 1);
839 sendAddNode(event->p, event->n, event->already_known_by_peer,
840 &far_players, disable_single_change_sending ? 5 : 30,
841 event->type == MEET_ADDNODE);
843 case MEET_REMOVENODE:
844 prof.add("MEET_REMOVENODE", 1);
845 sendRemoveNode(event->p, event->already_known_by_peer,
846 &far_players, disable_single_change_sending ? 5 : 30);
848 case MEET_BLOCK_NODE_METADATA_CHANGED:
849 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
850 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
851 setBlockNotSent(event->p);
854 infostream << "Server: MEET_OTHER" << std::endl;
855 prof.add("MEET_OTHER", 1);
856 for (const v3s16 &modified_block : event->modified_blocks) {
857 setBlockNotSent(modified_block);
861 prof.add("unknown", 1);
862 warningstream << "Server: Unknown MapEditEvent "
863 << ((u32)event->type) << std::endl;
868 Set blocks not sent to far players
870 if (!far_players.empty()) {
871 // Convert list format to that wanted by SetBlocksNotSent
872 std::map<v3s16, MapBlock*> modified_blocks2;
873 for (const v3s16 &modified_block : event->modified_blocks) {
874 modified_blocks2[modified_block] =
875 m_env->getMap().getBlockNoCreateNoEx(modified_block);
878 // Set blocks not sent
879 for (const u16 far_player : far_players) {
880 if (RemoteClient *client = getClient(far_player))
881 client->SetBlocksNotSent(modified_blocks2);
888 if (event_count >= 5) {
889 infostream << "Server: MapEditEvents:" << std::endl;
890 prof.print(infostream);
891 } else if (event_count != 0) {
892 verbosestream << "Server: MapEditEvents:" << std::endl;
893 prof.print(verbosestream);
899 Trigger emergethread (it somehow gets to a non-triggered but
900 bysy state sometimes)
903 float &counter = m_emergethread_trigger_timer;
905 if (counter >= 2.0) {
908 m_emerge->startThreads();
912 // Save map, players and auth stuff
914 float &counter = m_savemap_timer;
916 static thread_local const float save_interval =
917 g_settings->getFloat("server_map_save_interval");
918 if (counter >= save_interval) {
920 MutexAutoLock lock(m_env_mutex);
922 ScopeProfiler sp(g_profiler, "Server: saving stuff");
925 if (m_banmanager->isModified()) {
926 m_banmanager->save();
929 // Save changed parts of map
930 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
933 m_env->saveLoadedPlayers();
935 // Save environment metadata
941 static const float shutdown_msg_times[] =
943 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
946 if (m_shutdown_timer > 0.0f) {
947 // Automated messages
948 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
949 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
950 // If shutdown timer matches an automessage, shot it
951 if (m_shutdown_timer > shutdown_msg_times[i] &&
952 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
953 std::wstringstream ws;
955 ws << L"*** Server shutting down in "
956 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
959 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
960 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
966 m_shutdown_timer -= dtime;
967 if (m_shutdown_timer < 0.0f) {
968 m_shutdown_timer = 0.0f;
969 m_shutdown_requested = true;
974 void Server::Receive()
979 m_con->Receive(&pkt);
980 peer_id = pkt.getPeerId();
982 } catch (const con::InvalidIncomingDataException &e) {
983 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
984 << e.what() << std::endl;
985 } catch (const SerializationError &e) {
986 infostream << "Server::Receive(): SerializationError: what()="
987 << e.what() << std::endl;
988 } catch (const ClientStateError &e) {
989 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
990 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
991 L"Try reconnecting or updating your client");
992 } catch (const con::PeerNotFoundException &e) {
997 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
999 std::string playername;
1000 PlayerSAO *playersao = NULL;
1003 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1005 playername = client->getName();
1006 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1008 } catch (std::exception &e) {
1014 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1016 // If failed, cancel
1017 if (!playersao || !player) {
1018 if (player && player->peer_id != 0) {
1019 actionstream << "Server: Failed to emerge player \"" << playername
1020 << "\" (player allocated to an another client)" << std::endl;
1021 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1022 L"name. If your client closed unexpectedly, try again in "
1025 errorstream << "Server: " << playername << ": Failed to emerge player"
1027 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1033 Send complete position information
1035 SendMovePlayer(peer_id);
1038 SendPlayerPrivileges(peer_id);
1040 // Send inventory formspec
1041 SendPlayerInventoryFormspec(peer_id);
1044 SendInventory(playersao);
1046 // Send HP or death screen
1047 if (playersao->isDead())
1048 SendDeathscreen(peer_id, false, v3f(0,0,0));
1050 SendPlayerHPOrDie(playersao);
1053 SendPlayerBreath(playersao);
1055 // Note things in chat if not in simple singleplayer mode
1056 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1057 // Send information about server to player in chat
1058 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1060 Address addr = getPeerAddress(player->peer_id);
1061 std::string ip_str = addr.serializeString();
1062 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1067 const std::vector<std::string> &names = m_clients.getPlayerNames();
1069 actionstream << player->getName() << " joins game. List of players: ";
1071 for (const std::string &name : names) {
1072 actionstream << name << " ";
1075 actionstream << player->getName() <<std::endl;
1080 inline void Server::handleCommand(NetworkPacket* pkt)
1082 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1083 (this->*opHandle.handler)(pkt);
1086 void Server::ProcessData(NetworkPacket *pkt)
1088 // Environment is locked first.
1089 MutexAutoLock envlock(m_env_mutex);
1091 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1092 u32 peer_id = pkt->getPeerId();
1095 Address address = getPeerAddress(peer_id);
1096 std::string addr_s = address.serializeString();
1098 if(m_banmanager->isIpBanned(addr_s)) {
1099 std::string ban_name = m_banmanager->getBanName(addr_s);
1100 infostream << "Server: A banned client tried to connect from "
1101 << addr_s << "; banned name was "
1102 << ban_name << std::endl;
1103 // This actually doesn't seem to transfer to the client
1104 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1105 + utf8_to_wide(ban_name));
1109 catch(con::PeerNotFoundException &e) {
1111 * no peer for this packet found
1112 * most common reason is peer timeout, e.g. peer didn't
1113 * respond for some time, your server was overloaded or
1116 infostream << "Server::ProcessData(): Canceling: peer "
1117 << peer_id << " not found" << std::endl;
1122 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1124 // Command must be handled into ToServerCommandHandler
1125 if (command >= TOSERVER_NUM_MSG_TYPES) {
1126 infostream << "Server: Ignoring unknown command "
1127 << command << std::endl;
1131 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1136 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1138 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1139 errorstream << "Server::ProcessData(): Cancelling: Peer"
1140 " serialization format invalid or not initialized."
1141 " Skipping incoming command=" << command << std::endl;
1145 /* Handle commands related to client startup */
1146 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1151 if (m_clients.getClientState(peer_id) < CS_Active) {
1152 if (command == TOSERVER_PLAYERPOS) return;
1154 errorstream << "Got packet command: " << command << " for peer id "
1155 << peer_id << " but client isn't active yet. Dropping packet "
1161 } catch (SendFailedException &e) {
1162 errorstream << "Server::ProcessData(): SendFailedException: "
1163 << "what=" << e.what()
1165 } catch (PacketError &e) {
1166 actionstream << "Server::ProcessData(): PacketError: "
1167 << "what=" << e.what()
1172 void Server::setTimeOfDay(u32 time)
1174 m_env->setTimeOfDay(time);
1175 m_time_of_day_send_timer = 0;
1178 void Server::onMapEditEvent(MapEditEvent *event)
1180 if(m_ignore_map_edit_events)
1182 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1184 MapEditEvent *e = event->clone();
1185 m_unsent_map_edit_queue.push(e);
1188 Inventory* Server::getInventory(const InventoryLocation &loc)
1191 case InventoryLocation::UNDEFINED:
1192 case InventoryLocation::CURRENT_PLAYER:
1194 case InventoryLocation::PLAYER:
1196 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1199 PlayerSAO *playersao = player->getPlayerSAO();
1202 return playersao->getInventory();
1205 case InventoryLocation::NODEMETA:
1207 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1210 return meta->getInventory();
1213 case InventoryLocation::DETACHED:
1215 if(m_detached_inventories.count(loc.name) == 0)
1217 return m_detached_inventories[loc.name];
1221 sanity_check(false); // abort
1226 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1229 case InventoryLocation::UNDEFINED:
1231 case InventoryLocation::PLAYER:
1236 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1241 PlayerSAO *playersao = player->getPlayerSAO();
1245 SendInventory(playersao);
1248 case InventoryLocation::NODEMETA:
1250 v3s16 blockpos = getNodeBlockPos(loc.p);
1252 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1254 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1256 setBlockNotSent(blockpos);
1259 case InventoryLocation::DETACHED:
1261 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1265 sanity_check(false); // abort
1270 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1272 std::vector<session_t> clients = m_clients.getClientIDs();
1274 // Set the modified blocks unsent for all the clients
1275 for (const session_t client_id : clients) {
1276 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1277 client->SetBlocksNotSent(block);
1282 void Server::peerAdded(con::Peer *peer)
1284 verbosestream<<"Server::peerAdded(): peer->id="
1285 <<peer->id<<std::endl;
1287 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1290 void Server::deletingPeer(con::Peer *peer, bool timeout)
1292 verbosestream<<"Server::deletingPeer(): peer->id="
1293 <<peer->id<<", timeout="<<timeout<<std::endl;
1295 m_clients.event(peer->id, CSE_Disconnect);
1296 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1299 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1301 *retval = m_con->getPeerStat(peer_id,type);
1302 return *retval != -1;
1305 bool Server::getClientInfo(
1314 std::string* vers_string
1317 *state = m_clients.getClientState(peer_id);
1319 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1326 *uptime = client->uptime();
1327 *ser_vers = client->serialization_version;
1328 *prot_vers = client->net_proto_version;
1330 *major = client->getMajor();
1331 *minor = client->getMinor();
1332 *patch = client->getPatch();
1333 *vers_string = client->getPatch();
1340 void Server::handlePeerChanges()
1342 while(!m_peer_change_queue.empty())
1344 con::PeerChange c = m_peer_change_queue.front();
1345 m_peer_change_queue.pop();
1347 verbosestream<<"Server: Handling peer change: "
1348 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1353 case con::PEER_ADDED:
1354 m_clients.CreateClient(c.peer_id);
1357 case con::PEER_REMOVED:
1358 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1362 FATAL_ERROR("Invalid peer change event received!");
1368 void Server::printToConsoleOnly(const std::string &text)
1371 m_admin_chat->outgoing_queue.push_back(
1372 new ChatEventChat("", utf8_to_wide(text)));
1374 std::cout << text << std::endl;
1378 void Server::Send(NetworkPacket *pkt)
1380 Send(pkt->getPeerId(), pkt);
1383 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1385 m_clients.send(peer_id,
1386 clientCommandFactoryTable[pkt->getCommand()].channel,
1388 clientCommandFactoryTable[pkt->getCommand()].reliable);
1391 void Server::SendMovement(session_t peer_id)
1393 std::ostringstream os(std::ios_base::binary);
1395 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1397 pkt << g_settings->getFloat("movement_acceleration_default");
1398 pkt << g_settings->getFloat("movement_acceleration_air");
1399 pkt << g_settings->getFloat("movement_acceleration_fast");
1400 pkt << g_settings->getFloat("movement_speed_walk");
1401 pkt << g_settings->getFloat("movement_speed_crouch");
1402 pkt << g_settings->getFloat("movement_speed_fast");
1403 pkt << g_settings->getFloat("movement_speed_climb");
1404 pkt << g_settings->getFloat("movement_speed_jump");
1405 pkt << g_settings->getFloat("movement_liquid_fluidity");
1406 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1407 pkt << g_settings->getFloat("movement_liquid_sink");
1408 pkt << g_settings->getFloat("movement_gravity");
1413 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1415 if (!g_settings->getBool("enable_damage"))
1418 session_t peer_id = playersao->getPeerID();
1419 bool is_alive = playersao->getHP() > 0;
1422 SendPlayerHP(peer_id);
1427 void Server::SendHP(session_t peer_id, u16 hp)
1429 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1434 void Server::SendBreath(session_t peer_id, u16 breath)
1436 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1437 pkt << (u16) breath;
1441 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1442 const std::string &custom_reason, bool reconnect)
1444 assert(reason < SERVER_ACCESSDENIED_MAX);
1446 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1448 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1449 pkt << custom_reason;
1450 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1451 reason == SERVER_ACCESSDENIED_CRASH)
1452 pkt << custom_reason << (u8)reconnect;
1456 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1458 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1463 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1464 v3f camera_point_target)
1466 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1467 pkt << set_camera_point_target << camera_point_target;
1471 void Server::SendItemDef(session_t peer_id,
1472 IItemDefManager *itemdef, u16 protocol_version)
1474 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1478 u32 length of the next item
1479 zlib-compressed serialized ItemDefManager
1481 std::ostringstream tmp_os(std::ios::binary);
1482 itemdef->serialize(tmp_os, protocol_version);
1483 std::ostringstream tmp_os2(std::ios::binary);
1484 compressZlib(tmp_os.str(), tmp_os2);
1485 pkt.putLongString(tmp_os2.str());
1488 verbosestream << "Server: Sending item definitions to id(" << peer_id
1489 << "): size=" << pkt.getSize() << std::endl;
1494 void Server::SendNodeDef(session_t peer_id,
1495 INodeDefManager *nodedef, u16 protocol_version)
1497 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1501 u32 length of the next item
1502 zlib-compressed serialized NodeDefManager
1504 std::ostringstream tmp_os(std::ios::binary);
1505 nodedef->serialize(tmp_os, protocol_version);
1506 std::ostringstream tmp_os2(std::ios::binary);
1507 compressZlib(tmp_os.str(), tmp_os2);
1509 pkt.putLongString(tmp_os2.str());
1512 verbosestream << "Server: Sending node definitions to id(" << peer_id
1513 << "): size=" << pkt.getSize() << std::endl;
1519 Non-static send methods
1522 void Server::SendInventory(PlayerSAO* playerSAO)
1524 UpdateCrafting(playerSAO->getPlayer());
1530 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1532 std::ostringstream os;
1533 playerSAO->getInventory()->serialize(os);
1535 std::string s = os.str();
1537 pkt.putRawString(s.c_str(), s.size());
1541 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1543 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1545 u8 type = message.type;
1546 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1548 if (peer_id != PEER_ID_INEXISTENT) {
1549 RemotePlayer *player = m_env->getPlayer(peer_id);
1555 m_clients.sendToAll(&pkt);
1559 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1560 const std::string &formname)
1562 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1563 if (formspec.empty()){
1564 //the client should close the formspec
1565 pkt.putLongString("");
1567 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1574 // Spawns a particle on peer with peer_id
1575 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1576 v3f pos, v3f velocity, v3f acceleration,
1577 float expirationtime, float size, bool collisiondetection,
1578 bool collision_removal,
1579 bool vertical, const std::string &texture,
1580 const struct TileAnimationParams &animation, u8 glow)
1582 static thread_local const float radius =
1583 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1585 if (peer_id == PEER_ID_INEXISTENT) {
1586 std::vector<session_t> clients = m_clients.getClientIDs();
1588 for (const session_t client_id : clients) {
1589 RemotePlayer *player = m_env->getPlayer(client_id);
1593 PlayerSAO *sao = player->getPlayerSAO();
1597 // Do not send to distant clients
1598 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1601 SendSpawnParticle(client_id, player->protocol_version,
1602 pos, velocity, acceleration,
1603 expirationtime, size, collisiondetection,
1604 collision_removal, vertical, texture, animation, glow);
1609 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1611 pkt << pos << velocity << acceleration << expirationtime
1612 << size << collisiondetection;
1613 pkt.putLongString(texture);
1615 pkt << collision_removal;
1616 // This is horrible but required (why are there two ways to serialize pkts?)
1617 std::ostringstream os(std::ios_base::binary);
1618 animation.serialize(os, protocol_version);
1619 pkt.putRawString(os.str());
1625 // Adds a ParticleSpawner on peer with peer_id
1626 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1627 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1628 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1629 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1630 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1631 const struct TileAnimationParams &animation, u8 glow)
1633 if (peer_id == PEER_ID_INEXISTENT) {
1634 // This sucks and should be replaced:
1635 std::vector<session_t> clients = m_clients.getClientIDs();
1636 for (const session_t client_id : clients) {
1637 RemotePlayer *player = m_env->getPlayer(client_id);
1640 SendAddParticleSpawner(client_id, player->protocol_version,
1641 amount, spawntime, minpos, maxpos,
1642 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1643 minsize, maxsize, collisiondetection, collision_removal,
1644 attached_id, vertical, texture, id, animation, glow);
1649 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1651 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1652 << minacc << maxacc << minexptime << maxexptime << minsize
1653 << maxsize << collisiondetection;
1655 pkt.putLongString(texture);
1657 pkt << id << vertical;
1658 pkt << collision_removal;
1660 // This is horrible but required
1661 std::ostringstream os(std::ios_base::binary);
1662 animation.serialize(os, protocol_version);
1663 pkt.putRawString(os.str());
1669 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1671 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1673 // Ugly error in this packet
1676 if (peer_id != PEER_ID_INEXISTENT)
1679 m_clients.sendToAll(&pkt);
1683 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1685 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1687 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1688 << form->text << form->number << form->item << form->dir
1689 << form->align << form->offset << form->world_pos << form->size;
1694 void Server::SendHUDRemove(session_t peer_id, u32 id)
1696 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1701 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1703 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1704 pkt << id << (u8) stat;
1708 case HUD_STAT_SCALE:
1709 case HUD_STAT_ALIGN:
1710 case HUD_STAT_OFFSET:
1711 pkt << *(v2f *) value;
1715 pkt << *(std::string *) value;
1717 case HUD_STAT_WORLD_POS:
1718 pkt << *(v3f *) value;
1721 pkt << *(v2s32 *) value;
1723 case HUD_STAT_NUMBER:
1727 pkt << *(u32 *) value;
1734 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1736 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1738 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1740 pkt << flags << mask;
1745 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1747 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1748 pkt << param << value;
1752 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1753 const std::string &type, const std::vector<std::string> ¶ms,
1756 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1757 pkt << bgcolor << type << (u16) params.size();
1759 for (const std::string ¶m : params)
1767 void Server::SendCloudParams(session_t peer_id, float density,
1768 const video::SColor &color_bright,
1769 const video::SColor &color_ambient,
1774 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1775 pkt << density << color_bright << color_ambient
1776 << height << thickness << speed;
1781 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1784 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1787 pkt << do_override << (u16) (ratio * 65535);
1792 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1794 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1795 pkt << time << time_speed;
1797 if (peer_id == PEER_ID_INEXISTENT) {
1798 m_clients.sendToAll(&pkt);
1805 void Server::SendPlayerHP(session_t peer_id)
1807 PlayerSAO *playersao = getPlayerSAO(peer_id);
1808 // In some rare case if the player is disconnected
1809 // while Lua call l_punch, for example, this can be NULL
1813 SendHP(peer_id, playersao->getHP());
1814 m_script->player_event(playersao,"health_changed");
1816 // Send to other clients
1817 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1818 ActiveObjectMessage aom(playersao->getId(), true, str);
1819 playersao->m_messages_out.push(aom);
1822 void Server::SendPlayerBreath(PlayerSAO *sao)
1826 m_script->player_event(sao, "breath_changed");
1827 SendBreath(sao->getPeerID(), sao->getBreath());
1830 void Server::SendMovePlayer(session_t peer_id)
1832 RemotePlayer *player = m_env->getPlayer(peer_id);
1834 PlayerSAO *sao = player->getPlayerSAO();
1837 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1838 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1841 v3f pos = sao->getBasePosition();
1842 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1843 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1844 << " pitch=" << sao->getPitch()
1845 << " yaw=" << sao->getYaw()
1852 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1853 f32 animation_speed)
1855 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1858 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1859 << animation_frames[3] << animation_speed;
1864 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1866 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1867 pkt << first << third;
1871 void Server::SendPlayerPrivileges(session_t peer_id)
1873 RemotePlayer *player = m_env->getPlayer(peer_id);
1875 if(player->peer_id == PEER_ID_INEXISTENT)
1878 std::set<std::string> privs;
1879 m_script->getAuth(player->getName(), NULL, &privs);
1881 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1882 pkt << (u16) privs.size();
1884 for (const std::string &priv : privs) {
1891 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1893 RemotePlayer *player = m_env->getPlayer(peer_id);
1895 if(player->peer_id == PEER_ID_INEXISTENT)
1898 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1899 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1903 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1905 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1906 pkt.putRawString(datas.c_str(), datas.size());
1908 return pkt.getSize();
1911 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1914 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1915 datas.size(), peer_id);
1917 pkt.putRawString(datas.c_str(), datas.size());
1919 m_clients.send(pkt.getPeerId(),
1920 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1924 void Server::SendCSMFlavourLimits(session_t peer_id)
1926 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1927 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1928 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1932 s32 Server::playSound(const SimpleSoundSpec &spec,
1933 const ServerSoundParams ¶ms)
1935 // Find out initial position of sound
1936 bool pos_exists = false;
1937 v3f pos = params.getPos(m_env, &pos_exists);
1938 // If position is not found while it should be, cancel sound
1939 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1942 // Filter destination clients
1943 std::vector<session_t> dst_clients;
1944 if(!params.to_player.empty()) {
1945 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1947 infostream<<"Server::playSound: Player \""<<params.to_player
1948 <<"\" not found"<<std::endl;
1951 if(player->peer_id == PEER_ID_INEXISTENT){
1952 infostream<<"Server::playSound: Player \""<<params.to_player
1953 <<"\" not connected"<<std::endl;
1956 dst_clients.push_back(player->peer_id);
1958 std::vector<session_t> clients = m_clients.getClientIDs();
1960 for (const session_t client_id : clients) {
1961 RemotePlayer *player = m_env->getPlayer(client_id);
1965 PlayerSAO *sao = player->getPlayerSAO();
1970 if(sao->getBasePosition().getDistanceFrom(pos) >
1971 params.max_hear_distance)
1974 dst_clients.push_back(client_id);
1978 if(dst_clients.empty())
1982 s32 id = m_next_sound_id++;
1983 // The sound will exist as a reference in m_playing_sounds
1984 m_playing_sounds[id] = ServerPlayingSound();
1985 ServerPlayingSound &psound = m_playing_sounds[id];
1986 psound.params = params;
1989 float gain = params.gain * spec.gain;
1990 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1991 pkt << id << spec.name << gain
1992 << (u8) params.type << pos << params.object
1993 << params.loop << params.fade << params.pitch;
1995 // Backwards compability
1996 bool play_sound = gain > 0;
1998 for (const u16 dst_client : dst_clients) {
1999 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2000 psound.clients.insert(dst_client);
2001 m_clients.send(dst_client, 0, &pkt, true);
2006 void Server::stopSound(s32 handle)
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())
2013 ServerPlayingSound &psound = i->second;
2015 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2018 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2019 si != psound.clients.end(); ++si) {
2021 m_clients.send(*si, 0, &pkt, true);
2023 // Remove sound reference
2024 m_playing_sounds.erase(i);
2027 void Server::fadeSound(s32 handle, float step, float gain)
2029 // Get sound reference
2030 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2031 m_playing_sounds.find(handle);
2032 if (i == m_playing_sounds.end())
2035 ServerPlayingSound &psound = i->second;
2036 psound.params.gain = gain;
2038 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2039 pkt << handle << step << gain;
2041 // Backwards compability
2042 bool play_sound = gain > 0;
2043 ServerPlayingSound compat_psound = psound;
2044 compat_psound.clients.clear();
2046 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2047 compat_pkt << handle;
2049 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2050 it != psound.clients.end();) {
2051 if (m_clients.getProtocolVersion(*it) >= 32) {
2053 m_clients.send(*it, 0, &pkt, true);
2056 compat_psound.clients.insert(*it);
2058 m_clients.send(*it, 0, &compat_pkt, true);
2059 psound.clients.erase(it++);
2063 // Remove sound reference
2064 if (!play_sound || psound.clients.empty())
2065 m_playing_sounds.erase(i);
2067 if (play_sound && !compat_psound.clients.empty()) {
2068 // Play new sound volume on older clients
2069 playSound(compat_psound.spec, compat_psound.params);
2073 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2074 std::vector<u16> *far_players, float far_d_nodes)
2076 float maxd = far_d_nodes*BS;
2077 v3f p_f = intToFloat(p, BS);
2079 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2082 std::vector<session_t> clients = m_clients.getClientIDs();
2083 for (session_t client_id : clients) {
2086 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2087 PlayerSAO *sao = player->getPlayerSAO();
2091 // If player is far away, only set modified blocks not sent
2092 v3f player_pos = sao->getBasePosition();
2093 if (player_pos.getDistanceFrom(p_f) > maxd) {
2094 far_players->push_back(client_id);
2101 m_clients.send(client_id, 0, &pkt, true);
2105 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2106 std::vector<u16> *far_players, float far_d_nodes,
2107 bool remove_metadata)
2109 float maxd = far_d_nodes*BS;
2110 v3f p_f = intToFloat(p, BS);
2112 std::vector<session_t> clients = m_clients.getClientIDs();
2113 for (const session_t client_id : clients) {
2116 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2117 PlayerSAO *sao = player->getPlayerSAO();
2121 // If player is far away, only set modified blocks not sent
2122 v3f player_pos = sao->getBasePosition();
2123 if(player_pos.getDistanceFrom(p_f) > maxd) {
2124 far_players->push_back(client_id);
2130 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2132 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2134 pkt << p << n.param0 << n.param1 << n.param2
2135 << (u8) (remove_metadata ? 0 : 1);
2140 if (pkt.getSize() > 0)
2141 m_clients.send(client_id, 0, &pkt, true);
2145 void Server::setBlockNotSent(v3s16 p)
2147 std::vector<session_t> clients = m_clients.getClientIDs();
2149 for (const session_t i : clients) {
2150 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2151 client->SetBlockNotSent(p);
2156 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2157 u16 net_proto_version)
2159 v3s16 p = block->getPos();
2162 Create a packet with the block in the right format
2165 std::ostringstream os(std::ios_base::binary);
2166 block->serialize(os, ver, false);
2167 block->serializeNetworkSpecific(os);
2168 std::string s = os.str();
2170 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2173 pkt.putRawString(s.c_str(), s.size());
2177 void Server::SendBlocks(float dtime)
2179 MutexAutoLock envlock(m_env_mutex);
2180 //TODO check if one big lock could be faster then multiple small ones
2182 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2184 std::vector<PrioritySortedBlockTransfer> queue;
2186 u32 total_sending = 0;
2189 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2191 std::vector<session_t> clients = m_clients.getClientIDs();
2194 for (const session_t client_id : clients) {
2195 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2200 total_sending += client->getSendingCount();
2201 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2207 // Lowest priority number comes first.
2208 // Lowest is most important.
2209 std::sort(queue.begin(), queue.end());
2213 // Maximal total count calculation
2214 // The per-client block sends is halved with the maximal online users
2215 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2216 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2218 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2219 if (total_sending >= max_blocks_to_send)
2222 MapBlock *block = nullptr;
2224 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2225 } catch (const InvalidPositionException &e) {
2229 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2234 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2235 client->net_proto_version);
2237 client->SentBlock(block_to_send.pos);
2243 void Server::fillMediaCache()
2245 infostream<<"Server: Calculating media file checksums"<<std::endl;
2247 // Collect all media file paths
2248 std::vector<std::string> paths;
2249 for (const ModSpec &mod : m_mods) {
2250 paths.push_back(mod.path + DIR_DELIM + "textures");
2251 paths.push_back(mod.path + DIR_DELIM + "sounds");
2252 paths.push_back(mod.path + DIR_DELIM + "media");
2253 paths.push_back(mod.path + DIR_DELIM + "models");
2254 paths.push_back(mod.path + DIR_DELIM + "locale");
2256 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2258 // Collect media file information from paths into cache
2259 for (const std::string &mediapath : paths) {
2260 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2261 for (const fs::DirListNode &dln : dirlist) {
2262 if (dln.dir) // Ignode dirs
2264 std::string filename = dln.name;
2265 // If name contains illegal characters, ignore the file
2266 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2267 infostream<<"Server: ignoring illegal file name: \""
2268 << filename << "\"" << std::endl;
2271 // If name is not in a supported format, ignore it
2272 const char *supported_ext[] = {
2273 ".png", ".jpg", ".bmp", ".tga",
2274 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2276 ".x", ".b3d", ".md2", ".obj",
2277 // Custom translation file format
2281 if (removeStringEnd(filename, supported_ext).empty()){
2282 infostream << "Server: ignoring unsupported file extension: \""
2283 << filename << "\"" << std::endl;
2286 // Ok, attempt to load the file and add to cache
2287 std::string filepath;
2288 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2291 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2293 errorstream << "Server::fillMediaCache(): Could not open \""
2294 << filename << "\" for reading" << std::endl;
2297 std::ostringstream tmp_os(std::ios_base::binary);
2301 fis.read(buf, 1024);
2302 std::streamsize len = fis.gcount();
2303 tmp_os.write(buf, len);
2312 errorstream<<"Server::fillMediaCache(): Failed to read \""
2313 << filename << "\"" << std::endl;
2316 if(tmp_os.str().length() == 0) {
2317 errorstream << "Server::fillMediaCache(): Empty file \""
2318 << filepath << "\"" << std::endl;
2323 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2325 unsigned char *digest = sha1.getDigest();
2326 std::string sha1_base64 = base64_encode(digest, 20);
2327 std::string sha1_hex = hex_encode((char*)digest, 20);
2331 m_media[filename] = MediaInfo(filepath, sha1_base64);
2332 verbosestream << "Server: " << sha1_hex << " is " << filename
2338 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2340 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2344 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2347 std::string lang_suffix;
2348 lang_suffix.append(".").append(lang_code).append(".tr");
2349 for (const auto &i : m_media) {
2350 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2357 for (const auto &i : m_media) {
2358 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2360 pkt << i.first << i.second.sha1_digest;
2363 pkt << g_settings->get("remote_media");
2367 struct SendableMedia
2373 SendableMedia(const std::string &name_="", const std::string &path_="",
2374 const std::string &data_=""):
2381 void Server::sendRequestedMedia(session_t peer_id,
2382 const std::vector<std::string> &tosend)
2384 verbosestream<<"Server::sendRequestedMedia(): "
2385 <<"Sending files to client"<<std::endl;
2389 // Put 5kB in one bunch (this is not accurate)
2390 u32 bytes_per_bunch = 5000;
2392 std::vector< std::vector<SendableMedia> > file_bunches;
2393 file_bunches.emplace_back();
2395 u32 file_size_bunch_total = 0;
2397 for (const std::string &name : tosend) {
2398 if (m_media.find(name) == m_media.end()) {
2399 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2400 <<"unknown file \""<<(name)<<"\""<<std::endl;
2404 //TODO get path + name
2405 std::string tpath = m_media[name].path;
2408 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2410 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2411 <<tpath<<"\" for reading"<<std::endl;
2414 std::ostringstream tmp_os(std::ios_base::binary);
2418 fis.read(buf, 1024);
2419 std::streamsize len = fis.gcount();
2420 tmp_os.write(buf, len);
2421 file_size_bunch_total += len;
2430 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2431 <<name<<"\""<<std::endl;
2434 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2435 <<tname<<"\""<<std::endl;*/
2437 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2439 // Start next bunch if got enough data
2440 if(file_size_bunch_total >= bytes_per_bunch) {
2441 file_bunches.emplace_back();
2442 file_size_bunch_total = 0;
2447 /* Create and send packets */
2449 u16 num_bunches = file_bunches.size();
2450 for (u16 i = 0; i < num_bunches; i++) {
2453 u16 total number of texture bunches
2454 u16 index of this bunch
2455 u32 number of files in this bunch
2464 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2465 pkt << num_bunches << i << (u32) file_bunches[i].size();
2467 for (const SendableMedia &j : file_bunches[i]) {
2469 pkt.putLongString(j.data);
2472 verbosestream << "Server::sendRequestedMedia(): bunch "
2473 << i << "/" << num_bunches
2474 << " files=" << file_bunches[i].size()
2475 << " size=" << pkt.getSize() << std::endl;
2480 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2482 if(m_detached_inventories.count(name) == 0) {
2483 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2486 Inventory *inv = m_detached_inventories[name];
2487 std::ostringstream os(std::ios_base::binary);
2489 os << serializeString(name);
2493 std::string s = os.str();
2495 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2496 pkt.putRawString(s.c_str(), s.size());
2498 const std::string &check = m_detached_inventories_player[name];
2499 if (peer_id == PEER_ID_INEXISTENT) {
2501 return m_clients.sendToAll(&pkt);
2502 RemotePlayer *p = m_env->getPlayer(check.c_str());
2504 m_clients.send(p->peer_id, 0, &pkt, true);
2506 if (check.empty() || getPlayerName(peer_id) == check)
2511 void Server::sendDetachedInventories(session_t peer_id)
2513 for (const auto &detached_inventory : m_detached_inventories) {
2514 const std::string &name = detached_inventory.first;
2515 //Inventory *inv = i->second;
2516 sendDetachedInventory(name, peer_id);
2524 void Server::DiePlayer(session_t peer_id)
2526 PlayerSAO *playersao = getPlayerSAO(peer_id);
2527 // In some rare cases this can be NULL -- if the player is disconnected
2528 // when a Lua function modifies l_punch, for example
2532 infostream << "Server::DiePlayer(): Player "
2533 << playersao->getPlayer()->getName()
2534 << " dies" << std::endl;
2536 playersao->setHP(0);
2538 // Trigger scripted stuff
2539 m_script->on_dieplayer(playersao);
2541 SendPlayerHP(peer_id);
2542 SendDeathscreen(peer_id, false, v3f(0,0,0));
2545 void Server::RespawnPlayer(session_t peer_id)
2547 PlayerSAO *playersao = getPlayerSAO(peer_id);
2550 infostream << "Server::RespawnPlayer(): Player "
2551 << playersao->getPlayer()->getName()
2552 << " respawns" << std::endl;
2554 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2555 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2557 bool repositioned = m_script->on_respawnplayer(playersao);
2558 if (!repositioned) {
2559 // setPos will send the new position to client
2560 playersao->setPos(findSpawnPos());
2563 SendPlayerHP(peer_id);
2567 void Server::DenySudoAccess(session_t peer_id)
2569 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2574 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2575 const std::string &str_reason, bool reconnect)
2577 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2579 m_clients.event(peer_id, CSE_SetDenied);
2580 DisconnectPeer(peer_id);
2584 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2585 const std::string &custom_reason)
2587 SendAccessDenied(peer_id, reason, custom_reason);
2588 m_clients.event(peer_id, CSE_SetDenied);
2589 DisconnectPeer(peer_id);
2592 // 13/03/15: remove this function when protocol version 25 will become
2593 // the minimum version for MT users, maybe in 1 year
2594 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2596 SendAccessDenied_Legacy(peer_id, reason);
2597 m_clients.event(peer_id, CSE_SetDenied);
2598 DisconnectPeer(peer_id);
2601 void Server::DisconnectPeer(session_t peer_id)
2603 m_modchannel_mgr->leaveAllChannels(peer_id);
2604 m_con->DisconnectPeer(peer_id);
2607 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2610 RemoteClient* client = getClient(peer_id, CS_Invalid);
2612 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2614 // Right now, the auth mechs don't change between login and sudo mode.
2615 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2616 client->allowed_sudo_mechs = sudo_auth_mechs;
2618 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2619 << g_settings->getFloat("dedicated_server_step")
2623 m_clients.event(peer_id, CSE_AuthAccept);
2625 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2627 // We only support SRP right now
2628 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2630 resp_pkt << sudo_auth_mechs;
2632 m_clients.event(peer_id, CSE_SudoSuccess);
2636 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2638 std::wstring message;
2641 Clear references to playing sounds
2643 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2644 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2645 ServerPlayingSound &psound = i->second;
2646 psound.clients.erase(peer_id);
2647 if (psound.clients.empty())
2648 m_playing_sounds.erase(i++);
2653 RemotePlayer *player = m_env->getPlayer(peer_id);
2655 /* Run scripts and remove from environment */
2657 PlayerSAO *playersao = player->getPlayerSAO();
2660 // inform connected clients
2661 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2662 // (u16) 1 + std::string represents a vector serialization representation
2663 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2664 m_clients.sendToAll(¬ice);
2666 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2668 playersao->disconnected();
2675 if (player && reason != CDR_DENY) {
2676 std::ostringstream os(std::ios_base::binary);
2677 std::vector<session_t> clients = m_clients.getClientIDs();
2679 for (const session_t client_id : clients) {
2681 RemotePlayer *player = m_env->getPlayer(client_id);
2685 // Get name of player
2686 os << player->getName() << " ";
2689 std::string name = player->getName();
2690 actionstream << name << " "
2691 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2692 << " List of players: " << os.str() << std::endl;
2694 m_admin_chat->outgoing_queue.push_back(
2695 new ChatEventNick(CET_NICK_REMOVE, name));
2699 MutexAutoLock env_lock(m_env_mutex);
2700 m_clients.DeleteClient(peer_id);
2704 // Send leave chat message to all remaining clients
2705 if (!message.empty()) {
2706 SendChatMessage(PEER_ID_INEXISTENT,
2707 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2711 void Server::UpdateCrafting(RemotePlayer *player)
2713 // Get a preview for crafting
2715 InventoryLocation loc;
2716 loc.setPlayer(player->getName());
2717 std::vector<ItemStack> output_replacements;
2718 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2719 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2720 (&player->inventory)->getList("craft"), loc);
2722 // Put the new preview in
2723 InventoryList *plist = player->inventory.getList("craftpreview");
2724 sanity_check(plist);
2725 sanity_check(plist->getSize() >= 1);
2726 plist->changeItem(0, preview);
2729 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2731 if (evt->type == CET_NICK_ADD) {
2732 // The terminal informed us of its nick choice
2733 m_admin_nick = ((ChatEventNick *)evt)->nick;
2734 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2735 errorstream << "You haven't set up an account." << std::endl
2736 << "Please log in using the client as '"
2737 << m_admin_nick << "' with a secure password." << std::endl
2738 << "Until then, you can't execute admin tasks via the console," << std::endl
2739 << "and everybody can claim the user account instead of you," << std::endl
2740 << "giving them full control over this server." << std::endl;
2743 assert(evt->type == CET_CHAT);
2744 handleAdminChat((ChatEventChat *)evt);
2748 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2749 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2751 // If something goes wrong, this player is to blame
2752 RollbackScopeActor rollback_scope(m_rollback,
2753 std::string("player:") + name);
2755 if (g_settings->getBool("strip_color_codes"))
2756 wmessage = unescape_enriched(wmessage);
2759 switch (player->canSendChatMessage()) {
2760 case RPLAYER_CHATRESULT_FLOODING: {
2761 std::wstringstream ws;
2762 ws << L"You cannot send more messages. You are limited to "
2763 << g_settings->getFloat("chat_message_limit_per_10sec")
2764 << L" messages per 10 seconds.";
2767 case RPLAYER_CHATRESULT_KICK:
2768 DenyAccess_Legacy(player->peer_id,
2769 L"You have been kicked due to message flooding.");
2771 case RPLAYER_CHATRESULT_OK:
2774 FATAL_ERROR("Unhandled chat filtering result found.");
2778 if (m_max_chatmessage_length > 0
2779 && wmessage.length() > m_max_chatmessage_length) {
2780 return L"Your message exceed the maximum chat message limit set on the server. "
2781 L"It was refused. Send a shorter message";
2784 // Run script hook, exit if script ate the chat message
2785 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2790 // Whether to send line to the player that sent the message, or to all players
2791 bool broadcast_line = true;
2793 if (check_shout_priv && !checkPriv(name, "shout")) {
2794 line += L"-!- You don't have permission to shout.";
2795 broadcast_line = false;
2804 Tell calling method to send the message to sender
2806 if (!broadcast_line)
2810 Send the message to others
2812 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2814 std::vector<session_t> clients = m_clients.getClientIDs();
2817 Send the message back to the inital sender
2818 if they are using protocol version >= 29
2821 session_t peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2822 if (player && player->protocol_version >= 29)
2823 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2825 for (u16 cid : clients) {
2826 if (cid != peer_id_to_avoid_sending)
2827 SendChatMessage(cid, ChatMessage(line));
2832 void Server::handleAdminChat(const ChatEventChat *evt)
2834 std::string name = evt->nick;
2835 std::wstring wname = utf8_to_wide(name);
2836 std::wstring wmessage = evt->evt_msg;
2838 std::wstring answer = handleChat(name, wname, wmessage);
2840 // If asked to send answer to sender
2841 if (!answer.empty()) {
2842 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2846 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2848 RemoteClient *client = getClientNoEx(peer_id,state_min);
2850 throw ClientNotFoundException("Client not found");
2854 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2856 return m_clients.getClientNoEx(peer_id, state_min);
2859 std::string Server::getPlayerName(session_t peer_id)
2861 RemotePlayer *player = m_env->getPlayer(peer_id);
2863 return "[id="+itos(peer_id)+"]";
2864 return player->getName();
2867 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2869 RemotePlayer *player = m_env->getPlayer(peer_id);
2872 return player->getPlayerSAO();
2875 std::wstring Server::getStatusString()
2877 std::wostringstream os(std::ios_base::binary);
2880 os<<L"version="<<narrow_to_wide(g_version_string);
2882 os<<L", uptime="<<m_uptime.get();
2884 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2885 // Information about clients
2888 std::vector<session_t> clients = m_clients.getClientIDs();
2889 for (session_t client_id : clients) {
2891 RemotePlayer *player = m_env->getPlayer(client_id);
2892 // Get name of player
2893 std::wstring name = L"unknown";
2895 name = narrow_to_wide(player->getName());
2896 // Add name to information string
2905 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2906 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2908 if (!g_settings->get("motd").empty())
2909 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2913 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2915 std::set<std::string> privs;
2916 m_script->getAuth(name, NULL, &privs);
2920 bool Server::checkPriv(const std::string &name, const std::string &priv)
2922 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2923 return (privs.count(priv) != 0);
2926 void Server::reportPrivsModified(const std::string &name)
2929 std::vector<session_t> clients = m_clients.getClientIDs();
2930 for (const session_t client_id : clients) {
2931 RemotePlayer *player = m_env->getPlayer(client_id);
2932 reportPrivsModified(player->getName());
2935 RemotePlayer *player = m_env->getPlayer(name.c_str());
2938 SendPlayerPrivileges(player->peer_id);
2939 PlayerSAO *sao = player->getPlayerSAO();
2942 sao->updatePrivileges(
2943 getPlayerEffectivePrivs(name),
2948 void Server::reportInventoryFormspecModified(const std::string &name)
2950 RemotePlayer *player = m_env->getPlayer(name.c_str());
2953 SendPlayerInventoryFormspec(player->peer_id);
2956 void Server::setIpBanned(const std::string &ip, const std::string &name)
2958 m_banmanager->add(ip, name);
2961 void Server::unsetIpBanned(const std::string &ip_or_name)
2963 m_banmanager->remove(ip_or_name);
2966 std::string Server::getBanDescription(const std::string &ip_or_name)
2968 return m_banmanager->getBanDescription(ip_or_name);
2971 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2973 // m_env will be NULL if the server is initializing
2977 if (m_admin_nick == name && !m_admin_nick.empty()) {
2978 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2981 RemotePlayer *player = m_env->getPlayer(name);
2986 if (player->peer_id == PEER_ID_INEXISTENT)
2989 SendChatMessage(player->peer_id, ChatMessage(msg));
2992 bool Server::showFormspec(const char *playername, const std::string &formspec,
2993 const std::string &formname)
2995 // m_env will be NULL if the server is initializing
2999 RemotePlayer *player = m_env->getPlayer(playername);
3003 SendShowFormspecMessage(player->peer_id, formspec, formname);
3007 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3012 u32 id = player->addHud(form);
3014 SendHUDAdd(player->peer_id, id, form);
3019 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3023 HudElement* todel = player->removeHud(id);
3030 SendHUDRemove(player->peer_id, id);
3034 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3039 SendHUDChange(player->peer_id, id, stat, data);
3043 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3048 SendHUDSetFlags(player->peer_id, flags, mask);
3049 player->hud_flags &= ~mask;
3050 player->hud_flags |= flags;
3052 PlayerSAO* playersao = player->getPlayerSAO();
3057 m_script->player_event(playersao, "hud_changed");
3061 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3066 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3069 player->setHotbarItemcount(hotbar_itemcount);
3070 std::ostringstream os(std::ios::binary);
3071 writeS32(os, hotbar_itemcount);
3072 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3076 s32 Server::hudGetHotbarItemcount(RemotePlayer *player) const
3078 return player->getHotbarItemcount();
3081 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3086 player->setHotbarImage(name);
3087 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3090 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3094 return player->getHotbarImage();
3097 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3102 player->setHotbarSelectedImage(name);
3103 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3106 const std::string& Server::hudGetHotbarSelectedImage(RemotePlayer *player) const
3108 return player->getHotbarSelectedImage();
3111 Address Server::getPeerAddress(session_t peer_id)
3113 return m_con->GetPeerAddress(peer_id);
3116 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3117 v2s32 animation_frames[4], f32 frame_speed)
3122 player->setLocalAnimations(animation_frames, frame_speed);
3123 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3127 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3132 player->eye_offset_first = first;
3133 player->eye_offset_third = third;
3134 SendEyeOffset(player->peer_id, first, third);
3138 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3139 const std::string &type, const std::vector<std::string> ¶ms,
3145 player->setSky(bgcolor, type, params, clouds);
3146 SendSetSky(player->peer_id, bgcolor, type, params, clouds);
3150 bool Server::setClouds(RemotePlayer *player, float density,
3151 const video::SColor &color_bright,
3152 const video::SColor &color_ambient,
3160 SendCloudParams(player->peer_id, density,
3161 color_bright, color_ambient, height,
3166 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3172 player->overrideDayNightRatio(do_override, ratio);
3173 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3177 void Server::notifyPlayers(const std::wstring &msg)
3179 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3182 void Server::spawnParticle(const std::string &playername, v3f pos,
3183 v3f velocity, v3f acceleration,
3184 float expirationtime, float size, bool
3185 collisiondetection, bool collision_removal,
3186 bool vertical, const std::string &texture,
3187 const struct TileAnimationParams &animation, u8 glow)
3189 // m_env will be NULL if the server is initializing
3193 session_t peer_id = PEER_ID_INEXISTENT;
3195 if (!playername.empty()) {
3196 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3199 peer_id = player->peer_id;
3200 proto_ver = player->protocol_version;
3203 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3204 expirationtime, size, collisiondetection,
3205 collision_removal, vertical, texture, animation, glow);
3208 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3209 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3210 float minexptime, float maxexptime, float minsize, float maxsize,
3211 bool collisiondetection, bool collision_removal,
3212 ServerActiveObject *attached, bool vertical, const std::string &texture,
3213 const std::string &playername, const struct TileAnimationParams &animation,
3216 // m_env will be NULL if the server is initializing
3220 session_t peer_id = PEER_ID_INEXISTENT;
3222 if (!playername.empty()) {
3223 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3226 peer_id = player->peer_id;
3227 proto_ver = player->protocol_version;
3230 u16 attached_id = attached ? attached->getId() : 0;
3233 if (attached_id == 0)
3234 id = m_env->addParticleSpawner(spawntime);
3236 id = m_env->addParticleSpawner(spawntime, attached_id);
3238 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3239 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3240 minexptime, maxexptime, minsize, maxsize,
3241 collisiondetection, collision_removal, attached_id, vertical,
3242 texture, id, animation, glow);
3247 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3249 // m_env will be NULL if the server is initializing
3251 throw ServerError("Can't delete particle spawners during initialisation!");
3253 session_t peer_id = PEER_ID_INEXISTENT;
3254 if (!playername.empty()) {
3255 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3258 peer_id = player->peer_id;
3261 m_env->deleteParticleSpawner(id);
3262 SendDeleteParticleSpawner(peer_id, id);
3265 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3267 if(m_detached_inventories.count(name) > 0){
3268 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3269 delete m_detached_inventories[name];
3271 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3273 Inventory *inv = new Inventory(m_itemdef);
3275 m_detached_inventories[name] = inv;
3276 m_detached_inventories_player[name] = player;
3277 //TODO find a better way to do this
3278 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3282 // actions: time-reversed list
3283 // Return value: success/failure
3284 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3285 std::list<std::string> *log)
3287 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3288 ServerMap *map = (ServerMap*)(&m_env->getMap());
3290 // Fail if no actions to handle
3291 if(actions.empty()){
3292 log->push_back("Nothing to do.");
3299 for (const RollbackAction &action : actions) {
3301 bool success = action.applyRevert(map, this, this);
3304 std::ostringstream os;
3305 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3306 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3308 log->push_back(os.str());
3310 std::ostringstream os;
3311 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3312 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3314 log->push_back(os.str());
3318 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3319 <<" failed"<<std::endl;
3321 // Call it done if less than half failed
3322 return num_failed <= num_tried/2;
3325 // IGameDef interface
3327 IItemDefManager *Server::getItemDefManager()
3332 INodeDefManager *Server::getNodeDefManager()
3337 ICraftDefManager *Server::getCraftDefManager()
3342 u16 Server::allocateUnknownNodeId(const std::string &name)
3344 return m_nodedef->allocateDummy(name);
3347 MtEventManager *Server::getEventManager()
3352 IWritableItemDefManager *Server::getWritableItemDefManager()
3357 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3362 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3367 const ModSpec *Server::getModSpec(const std::string &modname) const
3369 std::vector<ModSpec>::const_iterator it;
3370 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3371 const ModSpec &mod = *it;
3372 if (mod.name == modname)
3378 void Server::getModNames(std::vector<std::string> &modlist)
3380 std::vector<ModSpec>::iterator it;
3381 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3382 modlist.push_back(it->name);
3385 std::string Server::getBuiltinLuaPath()
3387 return porting::path_share + DIR_DELIM + "builtin";
3390 std::string Server::getModStoragePath() const
3392 return m_path_world + DIR_DELIM + "mod_storage";
3395 v3f Server::findSpawnPos()
3397 ServerMap &map = m_env->getServerMap();
3399 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3400 return nodeposf * BS;
3403 bool is_good = false;
3404 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3405 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3407 // Try to find a good place a few times
3408 for(s32 i = 0; i < 4000 && !is_good; i++) {
3409 s32 range = MYMIN(1 + i, range_max);
3410 // We're going to try to throw the player to this position
3411 v2s16 nodepos2d = v2s16(
3412 -range + (myrand() % (range * 2)),
3413 -range + (myrand() % (range * 2)));
3415 // Get spawn level at point
3416 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3417 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3418 // the mapgen to signify an unsuitable spawn position
3419 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3422 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3425 for (s32 i = 0; i < 10; i++) {
3426 v3s16 blockpos = getNodeBlockPos(nodepos);
3427 map.emergeBlock(blockpos, true);
3428 content_t c = map.getNodeNoEx(nodepos).getContent();
3429 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3431 if (air_count >= 2) {
3432 nodeposf = intToFloat(nodepos, BS);
3433 // Don't spawn the player outside map boundaries
3434 if (objectpos_over_limit(nodeposf))
3447 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3449 m_shutdown_timer = delay;
3450 m_shutdown_msg = msg;
3451 m_shutdown_ask_reconnect = reconnect;
3453 if (delay == 0.0f) {
3454 // No delay, shutdown immediately
3455 m_shutdown_requested = true;
3456 // only print to the infostream, a chat message saying
3457 // "Server Shutting Down" is sent when the server destructs.
3458 infostream << "*** Immediate Server shutdown requested." << std::endl;
3459 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3460 // Negative delay, cancel shutdown if requested
3461 m_shutdown_timer = 0.0f;
3462 m_shutdown_msg = "";
3463 m_shutdown_ask_reconnect = false;
3464 m_shutdown_requested = false;
3465 std::wstringstream ws;
3467 ws << L"*** Server shutdown canceled.";
3469 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3470 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3471 } else if (delay > 0.0f) {
3472 // Positive delay, tell the clients when the server will shut down
3473 std::wstringstream ws;
3475 ws << L"*** Server shutting down in "
3476 << duration_to_string(myround(m_shutdown_timer)).c_str()
3479 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3480 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3484 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3487 Try to get an existing player
3489 RemotePlayer *player = m_env->getPlayer(name);
3491 // If player is already connected, cancel
3492 if (player && player->peer_id != 0) {
3493 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3498 If player with the wanted peer_id already exists, cancel.
3500 if (m_env->getPlayer(peer_id)) {
3501 infostream<<"emergePlayer(): Player with wrong name but same"
3502 " peer_id already exists"<<std::endl;
3507 player = new RemotePlayer(name, idef());
3510 bool newplayer = false;
3513 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3515 // Complete init with server parts
3516 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3517 player->protocol_version = proto_version;
3521 m_script->on_newplayer(playersao);
3527 bool Server::registerModStorage(ModMetadata *storage)
3529 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3530 errorstream << "Unable to register same mod storage twice. Storage name: "
3531 << storage->getModName() << std::endl;
3535 m_mod_storages[storage->getModName()] = storage;
3539 void Server::unregisterModStorage(const std::string &name)
3541 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3542 if (it != m_mod_storages.end()) {
3543 // Save unconditionaly on unregistration
3544 it->second->save(getModStoragePath());
3545 m_mod_storages.erase(name);
3549 void dedicated_server_loop(Server &server, bool &kill)
3551 verbosestream<<"dedicated_server_loop()"<<std::endl;
3553 IntervalLimiter m_profiler_interval;
3555 static thread_local const float steplen =
3556 g_settings->getFloat("dedicated_server_step");
3557 static thread_local const float profiler_print_interval =
3558 g_settings->getFloat("profiler_print_interval");
3561 // This is kind of a hack but can be done like this
3562 // because server.step() is very light
3564 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3565 sleep_ms((int)(steplen*1000.0));
3567 server.step(steplen);
3569 if (server.getShutdownRequested() || kill)
3575 if (profiler_print_interval != 0) {
3576 if(m_profiler_interval.step(steplen, profiler_print_interval))
3578 infostream<<"Profiler:"<<std::endl;
3579 g_profiler->print(infostream);
3580 g_profiler->clear();
3585 infostream << "Dedicated server quitting" << std::endl;
3587 if (g_settings->getBool("server_announce"))
3588 ServerList::sendAnnounce(ServerList::AA_DELETE,
3589 server.m_bind_addr.getPort());
3598 bool Server::joinModChannel(const std::string &channel)
3600 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3601 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3604 bool Server::leaveModChannel(const std::string &channel)
3606 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3609 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3611 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3614 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3618 ModChannel* Server::getModChannel(const std::string &channel)
3620 return m_modchannel_mgr->getModChannel(channel);
3623 void Server::broadcastModChannelMessage(const std::string &channel,
3624 const std::string &message, session_t from_peer)
3626 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3630 if (message.size() > STRING_MAX_LEN) {
3631 warningstream << "ModChannel message too long, dropping before sending "
3632 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3633 << channel << ")" << std::endl;
3638 if (from_peer != PEER_ID_SERVER) {
3639 sender = getPlayerName(from_peer);
3642 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3643 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3644 resp_pkt << channel << sender << message;
3645 for (session_t peer_id : peers) {
3647 if (peer_id == from_peer)
3650 Send(peer_id, &resp_pkt);
3653 if (from_peer != PEER_ID_SERVER) {
3654 m_script->on_modchannel_message(channel, sender, message);