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->getPeerId() != PEER_ID_INEXISTENT) {
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->getPeerId());
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->getPeerId() == 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->getPeerId() == 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->getPeerId() == PEER_ID_INEXISTENT) {
1952 infostream<<"Server::playSound: Player \""<<params.to_player
1953 <<"\" not connected"<<std::endl;
1956 dst_clients.push_back(player->getPeerId());
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->getPeerId(), 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->getPeerId(),
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 =
2822 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2824 if (player && player->protocol_version >= 29)
2825 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2827 for (u16 cid : clients) {
2828 if (cid != peer_id_to_avoid_sending)
2829 SendChatMessage(cid, ChatMessage(line));
2834 void Server::handleAdminChat(const ChatEventChat *evt)
2836 std::string name = evt->nick;
2837 std::wstring wname = utf8_to_wide(name);
2838 std::wstring wmessage = evt->evt_msg;
2840 std::wstring answer = handleChat(name, wname, wmessage);
2842 // If asked to send answer to sender
2843 if (!answer.empty()) {
2844 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2848 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2850 RemoteClient *client = getClientNoEx(peer_id,state_min);
2852 throw ClientNotFoundException("Client not found");
2856 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2858 return m_clients.getClientNoEx(peer_id, state_min);
2861 std::string Server::getPlayerName(session_t peer_id)
2863 RemotePlayer *player = m_env->getPlayer(peer_id);
2865 return "[id="+itos(peer_id)+"]";
2866 return player->getName();
2869 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2871 RemotePlayer *player = m_env->getPlayer(peer_id);
2874 return player->getPlayerSAO();
2877 std::wstring Server::getStatusString()
2879 std::wostringstream os(std::ios_base::binary);
2882 os<<L"version="<<narrow_to_wide(g_version_string);
2884 os<<L", uptime="<<m_uptime.get();
2886 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2887 // Information about clients
2890 std::vector<session_t> clients = m_clients.getClientIDs();
2891 for (session_t client_id : clients) {
2893 RemotePlayer *player = m_env->getPlayer(client_id);
2894 // Get name of player
2895 std::wstring name = L"unknown";
2897 name = narrow_to_wide(player->getName());
2898 // Add name to information string
2907 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2908 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2910 if (!g_settings->get("motd").empty())
2911 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2915 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2917 std::set<std::string> privs;
2918 m_script->getAuth(name, NULL, &privs);
2922 bool Server::checkPriv(const std::string &name, const std::string &priv)
2924 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2925 return (privs.count(priv) != 0);
2928 void Server::reportPrivsModified(const std::string &name)
2931 std::vector<session_t> clients = m_clients.getClientIDs();
2932 for (const session_t client_id : clients) {
2933 RemotePlayer *player = m_env->getPlayer(client_id);
2934 reportPrivsModified(player->getName());
2937 RemotePlayer *player = m_env->getPlayer(name.c_str());
2940 SendPlayerPrivileges(player->getPeerId());
2941 PlayerSAO *sao = player->getPlayerSAO();
2944 sao->updatePrivileges(
2945 getPlayerEffectivePrivs(name),
2950 void Server::reportInventoryFormspecModified(const std::string &name)
2952 RemotePlayer *player = m_env->getPlayer(name.c_str());
2955 SendPlayerInventoryFormspec(player->getPeerId());
2958 void Server::setIpBanned(const std::string &ip, const std::string &name)
2960 m_banmanager->add(ip, name);
2963 void Server::unsetIpBanned(const std::string &ip_or_name)
2965 m_banmanager->remove(ip_or_name);
2968 std::string Server::getBanDescription(const std::string &ip_or_name)
2970 return m_banmanager->getBanDescription(ip_or_name);
2973 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2975 // m_env will be NULL if the server is initializing
2979 if (m_admin_nick == name && !m_admin_nick.empty()) {
2980 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2983 RemotePlayer *player = m_env->getPlayer(name);
2988 if (player->getPeerId() == PEER_ID_INEXISTENT)
2991 SendChatMessage(player->getPeerId(), ChatMessage(msg));
2994 bool Server::showFormspec(const char *playername, const std::string &formspec,
2995 const std::string &formname)
2997 // m_env will be NULL if the server is initializing
3001 RemotePlayer *player = m_env->getPlayer(playername);
3005 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3009 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3014 u32 id = player->addHud(form);
3016 SendHUDAdd(player->getPeerId(), id, form);
3021 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3025 HudElement* todel = player->removeHud(id);
3032 SendHUDRemove(player->getPeerId(), id);
3036 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3041 SendHUDChange(player->getPeerId(), id, stat, data);
3045 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3050 SendHUDSetFlags(player->getPeerId(), flags, mask);
3051 player->hud_flags &= ~mask;
3052 player->hud_flags |= flags;
3054 PlayerSAO* playersao = player->getPlayerSAO();
3059 m_script->player_event(playersao, "hud_changed");
3063 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3068 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3071 player->setHotbarItemcount(hotbar_itemcount);
3072 std::ostringstream os(std::ios::binary);
3073 writeS32(os, hotbar_itemcount);
3074 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3078 s32 Server::hudGetHotbarItemcount(RemotePlayer *player) const
3080 return player->getHotbarItemcount();
3083 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3088 player->setHotbarImage(name);
3089 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3092 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3096 return player->getHotbarImage();
3099 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3104 player->setHotbarSelectedImage(name);
3105 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3108 const std::string& Server::hudGetHotbarSelectedImage(RemotePlayer *player) const
3110 return player->getHotbarSelectedImage();
3113 Address Server::getPeerAddress(session_t peer_id)
3115 return m_con->GetPeerAddress(peer_id);
3118 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3119 v2s32 animation_frames[4], f32 frame_speed)
3124 player->setLocalAnimations(animation_frames, frame_speed);
3125 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3129 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3134 player->eye_offset_first = first;
3135 player->eye_offset_third = third;
3136 SendEyeOffset(player->getPeerId(), first, third);
3140 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3141 const std::string &type, const std::vector<std::string> ¶ms,
3147 player->setSky(bgcolor, type, params, clouds);
3148 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3152 bool Server::setClouds(RemotePlayer *player, float density,
3153 const video::SColor &color_bright,
3154 const video::SColor &color_ambient,
3162 SendCloudParams(player->getPeerId(), density,
3163 color_bright, color_ambient, height,
3168 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3174 player->overrideDayNightRatio(do_override, ratio);
3175 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3179 void Server::notifyPlayers(const std::wstring &msg)
3181 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3184 void Server::spawnParticle(const std::string &playername, v3f pos,
3185 v3f velocity, v3f acceleration,
3186 float expirationtime, float size, bool
3187 collisiondetection, bool collision_removal,
3188 bool vertical, const std::string &texture,
3189 const struct TileAnimationParams &animation, u8 glow)
3191 // m_env will be NULL if the server is initializing
3195 session_t peer_id = PEER_ID_INEXISTENT;
3197 if (!playername.empty()) {
3198 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3201 peer_id = player->getPeerId();
3202 proto_ver = player->protocol_version;
3205 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3206 expirationtime, size, collisiondetection,
3207 collision_removal, vertical, texture, animation, glow);
3210 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3211 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3212 float minexptime, float maxexptime, float minsize, float maxsize,
3213 bool collisiondetection, bool collision_removal,
3214 ServerActiveObject *attached, bool vertical, const std::string &texture,
3215 const std::string &playername, const struct TileAnimationParams &animation,
3218 // m_env will be NULL if the server is initializing
3222 session_t peer_id = PEER_ID_INEXISTENT;
3224 if (!playername.empty()) {
3225 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3228 peer_id = player->getPeerId();
3229 proto_ver = player->protocol_version;
3232 u16 attached_id = attached ? attached->getId() : 0;
3235 if (attached_id == 0)
3236 id = m_env->addParticleSpawner(spawntime);
3238 id = m_env->addParticleSpawner(spawntime, attached_id);
3240 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3241 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3242 minexptime, maxexptime, minsize, maxsize,
3243 collisiondetection, collision_removal, attached_id, vertical,
3244 texture, id, animation, glow);
3249 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3251 // m_env will be NULL if the server is initializing
3253 throw ServerError("Can't delete particle spawners during initialisation!");
3255 session_t peer_id = PEER_ID_INEXISTENT;
3256 if (!playername.empty()) {
3257 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3260 peer_id = player->getPeerId();
3263 m_env->deleteParticleSpawner(id);
3264 SendDeleteParticleSpawner(peer_id, id);
3267 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3269 if(m_detached_inventories.count(name) > 0){
3270 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3271 delete m_detached_inventories[name];
3273 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3275 Inventory *inv = new Inventory(m_itemdef);
3277 m_detached_inventories[name] = inv;
3278 m_detached_inventories_player[name] = player;
3279 //TODO find a better way to do this
3280 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3284 // actions: time-reversed list
3285 // Return value: success/failure
3286 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3287 std::list<std::string> *log)
3289 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3290 ServerMap *map = (ServerMap*)(&m_env->getMap());
3292 // Fail if no actions to handle
3293 if(actions.empty()){
3294 log->push_back("Nothing to do.");
3301 for (const RollbackAction &action : actions) {
3303 bool success = action.applyRevert(map, this, this);
3306 std::ostringstream os;
3307 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3308 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3310 log->push_back(os.str());
3312 std::ostringstream os;
3313 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3314 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3316 log->push_back(os.str());
3320 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3321 <<" failed"<<std::endl;
3323 // Call it done if less than half failed
3324 return num_failed <= num_tried/2;
3327 // IGameDef interface
3329 IItemDefManager *Server::getItemDefManager()
3334 INodeDefManager *Server::getNodeDefManager()
3339 ICraftDefManager *Server::getCraftDefManager()
3344 u16 Server::allocateUnknownNodeId(const std::string &name)
3346 return m_nodedef->allocateDummy(name);
3349 MtEventManager *Server::getEventManager()
3354 IWritableItemDefManager *Server::getWritableItemDefManager()
3359 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3364 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3369 const ModSpec *Server::getModSpec(const std::string &modname) const
3371 std::vector<ModSpec>::const_iterator it;
3372 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3373 const ModSpec &mod = *it;
3374 if (mod.name == modname)
3380 void Server::getModNames(std::vector<std::string> &modlist)
3382 std::vector<ModSpec>::iterator it;
3383 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3384 modlist.push_back(it->name);
3387 std::string Server::getBuiltinLuaPath()
3389 return porting::path_share + DIR_DELIM + "builtin";
3392 std::string Server::getModStoragePath() const
3394 return m_path_world + DIR_DELIM + "mod_storage";
3397 v3f Server::findSpawnPos()
3399 ServerMap &map = m_env->getServerMap();
3401 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3402 return nodeposf * BS;
3405 bool is_good = false;
3406 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3407 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3409 // Try to find a good place a few times
3410 for(s32 i = 0; i < 4000 && !is_good; i++) {
3411 s32 range = MYMIN(1 + i, range_max);
3412 // We're going to try to throw the player to this position
3413 v2s16 nodepos2d = v2s16(
3414 -range + (myrand() % (range * 2)),
3415 -range + (myrand() % (range * 2)));
3417 // Get spawn level at point
3418 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3419 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3420 // the mapgen to signify an unsuitable spawn position
3421 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3424 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3427 for (s32 i = 0; i < 10; i++) {
3428 v3s16 blockpos = getNodeBlockPos(nodepos);
3429 map.emergeBlock(blockpos, true);
3430 content_t c = map.getNodeNoEx(nodepos).getContent();
3431 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3433 if (air_count >= 2) {
3434 nodeposf = intToFloat(nodepos, BS);
3435 // Don't spawn the player outside map boundaries
3436 if (objectpos_over_limit(nodeposf))
3449 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3451 m_shutdown_timer = delay;
3452 m_shutdown_msg = msg;
3453 m_shutdown_ask_reconnect = reconnect;
3455 if (delay == 0.0f) {
3456 // No delay, shutdown immediately
3457 m_shutdown_requested = true;
3458 // only print to the infostream, a chat message saying
3459 // "Server Shutting Down" is sent when the server destructs.
3460 infostream << "*** Immediate Server shutdown requested." << std::endl;
3461 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3462 // Negative delay, cancel shutdown if requested
3463 m_shutdown_timer = 0.0f;
3464 m_shutdown_msg = "";
3465 m_shutdown_ask_reconnect = false;
3466 m_shutdown_requested = false;
3467 std::wstringstream ws;
3469 ws << L"*** Server shutdown canceled.";
3471 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3472 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3473 } else if (delay > 0.0f) {
3474 // Positive delay, tell the clients when the server will shut down
3475 std::wstringstream ws;
3477 ws << L"*** Server shutting down in "
3478 << duration_to_string(myround(m_shutdown_timer)).c_str()
3481 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3482 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3486 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3489 Try to get an existing player
3491 RemotePlayer *player = m_env->getPlayer(name);
3493 // If player is already connected, cancel
3494 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3495 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3500 If player with the wanted peer_id already exists, cancel.
3502 if (m_env->getPlayer(peer_id)) {
3503 infostream<<"emergePlayer(): Player with wrong name but same"
3504 " peer_id already exists"<<std::endl;
3509 player = new RemotePlayer(name, idef());
3512 bool newplayer = false;
3515 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3517 // Complete init with server parts
3518 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3519 player->protocol_version = proto_version;
3523 m_script->on_newplayer(playersao);
3529 bool Server::registerModStorage(ModMetadata *storage)
3531 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3532 errorstream << "Unable to register same mod storage twice. Storage name: "
3533 << storage->getModName() << std::endl;
3537 m_mod_storages[storage->getModName()] = storage;
3541 void Server::unregisterModStorage(const std::string &name)
3543 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3544 if (it != m_mod_storages.end()) {
3545 // Save unconditionaly on unregistration
3546 it->second->save(getModStoragePath());
3547 m_mod_storages.erase(name);
3551 void dedicated_server_loop(Server &server, bool &kill)
3553 verbosestream<<"dedicated_server_loop()"<<std::endl;
3555 IntervalLimiter m_profiler_interval;
3557 static thread_local const float steplen =
3558 g_settings->getFloat("dedicated_server_step");
3559 static thread_local const float profiler_print_interval =
3560 g_settings->getFloat("profiler_print_interval");
3563 // This is kind of a hack but can be done like this
3564 // because server.step() is very light
3566 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3567 sleep_ms((int)(steplen*1000.0));
3569 server.step(steplen);
3571 if (server.getShutdownRequested() || kill)
3577 if (profiler_print_interval != 0) {
3578 if(m_profiler_interval.step(steplen, profiler_print_interval))
3580 infostream<<"Profiler:"<<std::endl;
3581 g_profiler->print(infostream);
3582 g_profiler->clear();
3587 infostream << "Dedicated server quitting" << std::endl;
3589 if (g_settings->getBool("server_announce"))
3590 ServerList::sendAnnounce(ServerList::AA_DELETE,
3591 server.m_bind_addr.getPort());
3600 bool Server::joinModChannel(const std::string &channel)
3602 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3603 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3606 bool Server::leaveModChannel(const std::string &channel)
3608 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3611 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3613 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3616 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3620 ModChannel* Server::getModChannel(const std::string &channel)
3622 return m_modchannel_mgr->getModChannel(channel);
3625 void Server::broadcastModChannelMessage(const std::string &channel,
3626 const std::string &message, session_t from_peer)
3628 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3632 if (message.size() > STRING_MAX_LEN) {
3633 warningstream << "ModChannel message too long, dropping before sending "
3634 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3635 << channel << ")" << std::endl;
3640 if (from_peer != PEER_ID_SERVER) {
3641 sender = getPlayerName(from_peer);
3644 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3645 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3646 resp_pkt << channel << sender << message;
3647 for (session_t peer_id : peers) {
3649 if (peer_id == from_peer)
3652 Send(peer_id, &resp_pkt);
3655 if (from_peer != PEER_ID_SERVER) {
3656 m_script->on_modchannel_message(channel, sender, message);