3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "network/connection.h"
25 #include "network/networkprotocol.h"
26 #include "network/serveropcodes.h"
28 #include "environment.h"
30 #include "threading/mutex_auto_lock.h"
31 #include "constants.h"
37 #include "serverobject.h"
38 #include "genericobject.h"
42 #include "scripting_server.h"
47 #include "mapgen/mapgen.h"
48 #include "mapgen/mg_biome.h"
49 #include "content_mapnode.h"
50 #include "content_nodemeta.h"
51 #include "content_sao.h"
53 #include "event_manager.h"
54 #include "modchannels.h"
55 #include "serverlist.h"
56 #include "util/string.h"
58 #include "util/serialize.h"
59 #include "util/thread.h"
60 #include "defaultsettings.h"
61 #include "util/base64.h"
62 #include "util/sha1.h"
64 #include "database/database.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::vector<std::string> paths;
257 fs::GetRecursiveDirs(paths, g_settings->get("texture_path"));
258 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
259 for (const std::string &path : paths)
260 m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
262 m_nodedef->setNodeRegistrationStatus(true);
264 // Perform pending node name resolutions
265 m_nodedef->runNodeResolveCallbacks();
267 // unmap node names for connected nodeboxes
268 m_nodedef->mapNodeboxConnections();
270 // init the recipe hashes to speed up crafting
271 m_craftdef->initHashes(this);
273 // Initialize Environment
274 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
276 m_clients.setEnv(m_env);
278 if (!servermap->settings_mgr.makeMapgenParams())
279 FATAL_ERROR("Couldn't create any mapgen type");
281 // Initialize mapgens
282 m_emerge->initMapgens(servermap->getMapgenParams());
284 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
285 if (m_enable_rollback_recording) {
286 // Create rollback manager
287 m_rollback = new RollbackManager(m_path_world, this);
290 // Give environment reference to scripting api
291 m_script->initializeEnvironment(m_env);
293 // Register us to receive map edit events
294 servermap->addEventReceiver(this);
296 // If file exists, load environment metadata
297 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
298 infostream << "Server: Loading environment metadata" << std::endl;
301 m_env->loadDefaultMeta();
304 m_liquid_transform_every = g_settings->getFloat("liquid_update");
305 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
306 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
307 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
312 infostream << "Server destructing" << std::endl;
314 // Send shutdown message
315 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
316 L"*** Server shutting down"));
319 MutexAutoLock envlock(m_env_mutex);
321 infostream << "Server: Saving players" << std::endl;
322 m_env->saveLoadedPlayers();
324 infostream << "Server: Kicking players" << std::endl;
325 std::string kick_msg;
326 bool reconnect = false;
327 if (getShutdownRequested()) {
328 reconnect = m_shutdown_ask_reconnect;
329 kick_msg = m_shutdown_msg;
331 if (kick_msg.empty()) {
332 kick_msg = g_settings->get("kick_msg_shutdown");
334 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
335 kick_msg, reconnect);
338 // Do this before stopping the server in case mapgen callbacks need to access
339 // server-controlled resources (like ModStorages). Also do them before
340 // shutdown callbacks since they may modify state that is finalized in a
342 m_emerge->stopThreads();
345 MutexAutoLock envlock(m_env_mutex);
347 // Execute script shutdown hooks
348 infostream << "Executing shutdown hooks" << std::endl;
349 m_script->on_shutdown();
351 infostream << "Server: Saving environment metadata" << std::endl;
359 // Delete things in the reverse order of creation
369 // Deinitialize scripting
370 infostream << "Server: Deinitializing scripting" << std::endl;
373 // Delete detached inventories
374 for (auto &detached_inventory : m_detached_inventories) {
375 delete detached_inventory.second;
381 infostream << "Starting server on " << m_bind_addr.serializeString()
382 << "..." << std::endl;
384 // Stop thread if already running
387 // Initialize connection
388 m_con->SetTimeoutMs(30);
389 m_con->Serve(m_bind_addr);
394 // ASCII art for the win!
396 << " .__ __ __ " << std::endl
397 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
398 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
399 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
400 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
401 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
402 actionstream << "World at [" << m_path_world << "]" << std::endl;
403 actionstream << "Server for gameid=\"" << m_gamespec.id
404 << "\" listening on " << m_bind_addr.serializeString() << ":"
405 << m_bind_addr.getPort() << "." << std::endl;
410 infostream<<"Server: Stopping and waiting threads"<<std::endl;
412 // Stop threads (set run=false first so both start stopping)
414 //m_emergethread.setRun(false);
416 //m_emergethread.stop();
418 infostream<<"Server: Threads stopped"<<std::endl;
421 void Server::step(float dtime)
427 MutexAutoLock lock(m_step_dtime_mutex);
428 m_step_dtime += dtime;
430 // Throw if fatal error occurred in thread
431 std::string async_err = m_async_fatal_error.get();
432 if (!async_err.empty()) {
433 if (!m_simple_singleplayer_mode) {
434 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
435 g_settings->get("kick_msg_crash"),
436 g_settings->getBool("ask_reconnect_on_crash"));
438 throw ServerError("AsyncErr: " + async_err);
442 void Server::AsyncRunStep(bool initial_step)
444 g_profiler->add("Server::AsyncRunStep (num)", 1);
448 MutexAutoLock lock1(m_step_dtime_mutex);
449 dtime = m_step_dtime;
453 // Send blocks to clients
457 if((dtime < 0.001) && !initial_step)
460 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
462 //infostream<<"Server steps "<<dtime<<std::endl;
463 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
466 MutexAutoLock lock1(m_step_dtime_mutex);
467 m_step_dtime -= dtime;
474 m_uptime.set(m_uptime.get() + dtime);
480 Update time of day and overall game time
482 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
485 Send to clients at constant intervals
488 m_time_of_day_send_timer -= dtime;
489 if(m_time_of_day_send_timer < 0.0) {
490 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
491 u16 time = m_env->getTimeOfDay();
492 float time_speed = g_settings->getFloat("time_speed");
493 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
497 MutexAutoLock lock(m_env_mutex);
498 // Figure out and report maximum lag to environment
499 float max_lag = m_env->getMaxLagEstimate();
500 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
502 if(dtime > 0.1 && dtime > max_lag * 2.0)
503 infostream<<"Server: Maximum lag peaked to "<<dtime
507 m_env->reportMaxLagEstimate(max_lag);
509 ScopeProfiler sp(g_profiler, "SEnv step");
510 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
514 static const float map_timer_and_unload_dtime = 2.92;
515 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
517 MutexAutoLock lock(m_env_mutex);
518 // Run Map's timers and unload unused data
519 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
520 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
521 g_settings->getFloat("server_unload_unused_data_timeout"),
526 Listen to the admin chat, if available
529 if (!m_admin_chat->command_queue.empty()) {
530 MutexAutoLock lock(m_env_mutex);
531 while (!m_admin_chat->command_queue.empty()) {
532 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
533 handleChatInterfaceEvent(evt);
537 m_admin_chat->outgoing_queue.push_back(
538 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
545 /* Transform liquids */
546 m_liquid_transform_timer += dtime;
547 if(m_liquid_transform_timer >= m_liquid_transform_every)
549 m_liquid_transform_timer -= m_liquid_transform_every;
551 MutexAutoLock lock(m_env_mutex);
553 ScopeProfiler sp(g_profiler, "Server: liquid transform");
555 std::map<v3s16, MapBlock*> modified_blocks;
556 m_env->getMap().transformLiquids(modified_blocks, m_env);
559 Set the modified blocks unsent for all the clients
561 if (!modified_blocks.empty()) {
562 SetBlocksNotSent(modified_blocks);
565 m_clients.step(dtime);
567 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
569 // send masterserver announce
571 float &counter = m_masterserver_timer;
572 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
573 g_settings->getBool("server_announce")) {
574 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
575 ServerList::AA_START,
576 m_bind_addr.getPort(),
577 m_clients.getPlayerNames(),
579 m_env->getGameTime(),
582 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
592 Check added and deleted active objects
595 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
596 MutexAutoLock envlock(m_env_mutex);
599 const RemoteClientMap &clients = m_clients.getClientList();
600 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
602 // Radius inside which objects are active
603 static thread_local const s16 radius =
604 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
606 // Radius inside which players are active
607 static thread_local const bool is_transfer_limited =
608 g_settings->exists("unlimited_player_transfer_distance") &&
609 !g_settings->getBool("unlimited_player_transfer_distance");
610 static thread_local const s16 player_transfer_dist =
611 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
612 s16 player_radius = player_transfer_dist;
613 if (player_radius == 0 && is_transfer_limited)
614 player_radius = radius;
616 for (const auto &client_it : clients) {
617 RemoteClient *client = client_it.second;
619 // If definitions and textures have not been sent, don't
620 // send objects either
621 if (client->getState() < CS_DefinitionsSent)
624 RemotePlayer *player = m_env->getPlayer(client->peer_id);
626 // This can happen if the client timeouts somehow
630 PlayerSAO *playersao = player->getPlayerSAO();
634 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
635 if (my_radius <= 0) my_radius = radius;
636 //infostream << "Server: Active Radius " << my_radius << std::endl;
638 std::queue<u16> removed_objects;
639 std::queue<u16> added_objects;
640 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
641 client->m_known_objects, removed_objects);
642 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
643 client->m_known_objects, added_objects);
645 // Ignore if nothing happened
646 if (removed_objects.empty() && added_objects.empty()) {
650 std::string data_buffer;
654 // Handle removed objects
655 writeU16((u8*)buf, removed_objects.size());
656 data_buffer.append(buf, 2);
657 while (!removed_objects.empty()) {
659 u16 id = removed_objects.front();
660 ServerActiveObject* obj = m_env->getActiveObject(id);
662 // Add to data buffer for sending
663 writeU16((u8*)buf, id);
664 data_buffer.append(buf, 2);
666 // Remove from known objects
667 client->m_known_objects.erase(id);
669 if(obj && obj->m_known_by_count > 0)
670 obj->m_known_by_count--;
671 removed_objects.pop();
674 // Handle added objects
675 writeU16((u8*)buf, added_objects.size());
676 data_buffer.append(buf, 2);
677 while (!added_objects.empty()) {
679 u16 id = added_objects.front();
680 ServerActiveObject* obj = m_env->getActiveObject(id);
683 u8 type = ACTIVEOBJECT_TYPE_INVALID;
685 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
687 type = obj->getSendType();
689 // Add to data buffer for sending
690 writeU16((u8*)buf, id);
691 data_buffer.append(buf, 2);
692 writeU8((u8*)buf, type);
693 data_buffer.append(buf, 1);
696 data_buffer.append(serializeLongString(
697 obj->getClientInitializationData(client->net_proto_version)));
699 data_buffer.append(serializeLongString(""));
701 // Add to known objects
702 client->m_known_objects.insert(id);
705 obj->m_known_by_count++;
710 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
711 verbosestream << "Server: Sent object remove/add: "
712 << removed_objects.size() << " removed, "
713 << added_objects.size() << " added, "
714 << "packet size is " << pktSize << std::endl;
718 m_mod_storage_save_timer -= dtime;
719 if (m_mod_storage_save_timer <= 0.0f) {
720 infostream << "Saving registered mod storages." << std::endl;
721 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
722 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
723 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
724 if (it->second->isModified()) {
725 it->second->save(getModStoragePath());
735 MutexAutoLock envlock(m_env_mutex);
736 ScopeProfiler sp(g_profiler, "Server: sending object messages");
739 // Value = data sent by object
740 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
742 // Get active object messages from environment
744 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
748 std::vector<ActiveObjectMessage>* message_list = nullptr;
749 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
750 n = buffered_messages.find(aom.id);
751 if (n == buffered_messages.end()) {
752 message_list = new std::vector<ActiveObjectMessage>;
753 buffered_messages[aom.id] = message_list;
756 message_list = n->second;
758 message_list->push_back(aom);
762 const RemoteClientMap &clients = m_clients.getClientList();
763 // Route data to every client
764 for (const auto &client_it : clients) {
765 RemoteClient *client = client_it.second;
766 std::string reliable_data;
767 std::string unreliable_data;
768 // Go through all objects in message buffer
769 for (const auto &buffered_message : buffered_messages) {
770 // If object is not known by client, skip it
771 u16 id = buffered_message.first;
772 if (client->m_known_objects.find(id) == client->m_known_objects.end())
775 // Get message list of object
776 std::vector<ActiveObjectMessage>* list = buffered_message.second;
777 // Go through every message
778 for (const ActiveObjectMessage &aom : *list) {
779 // Compose the full new data with header
780 std::string new_data;
783 writeU16((u8*)&buf[0], aom.id);
784 new_data.append(buf, 2);
786 new_data += serializeString(aom.datastring);
787 // Add data to buffer
789 reliable_data += new_data;
791 unreliable_data += new_data;
795 reliable_data and unreliable_data are now ready.
798 if (!reliable_data.empty()) {
799 SendActiveObjectMessages(client->peer_id, reliable_data);
802 if (!unreliable_data.empty()) {
803 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
808 // Clear buffered_messages
809 for (auto &buffered_message : buffered_messages) {
810 delete buffered_message.second;
815 Send queued-for-sending map edit events.
818 // We will be accessing the environment
819 MutexAutoLock lock(m_env_mutex);
821 // Don't send too many at a time
824 // Single change sending is disabled if queue size is not small
825 bool disable_single_change_sending = false;
826 if(m_unsent_map_edit_queue.size() >= 4)
827 disable_single_change_sending = true;
829 int event_count = m_unsent_map_edit_queue.size();
831 // We'll log the amount of each
834 while (!m_unsent_map_edit_queue.empty()) {
835 MapEditEvent* event = m_unsent_map_edit_queue.front();
836 m_unsent_map_edit_queue.pop();
838 // Players far away from the change are stored here.
839 // Instead of sending the changes, MapBlocks are set not sent
841 std::vector<u16> far_players;
843 switch (event->type) {
846 prof.add("MEET_ADDNODE", 1);
847 sendAddNode(event->p, event->n, event->already_known_by_peer,
848 &far_players, disable_single_change_sending ? 5 : 30,
849 event->type == MEET_ADDNODE);
851 case MEET_REMOVENODE:
852 prof.add("MEET_REMOVENODE", 1);
853 sendRemoveNode(event->p, event->already_known_by_peer,
854 &far_players, disable_single_change_sending ? 5 : 30);
856 case MEET_BLOCK_NODE_METADATA_CHANGED:
857 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
858 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
859 m_clients.markBlockposAsNotSent(event->p);
862 infostream << "Server: MEET_OTHER" << std::endl;
863 prof.add("MEET_OTHER", 1);
864 for (const v3s16 &modified_block : event->modified_blocks) {
865 m_clients.markBlockposAsNotSent(modified_block);
869 prof.add("unknown", 1);
870 warningstream << "Server: Unknown MapEditEvent "
871 << ((u32)event->type) << std::endl;
876 Set blocks not sent to far players
878 if (!far_players.empty()) {
879 // Convert list format to that wanted by SetBlocksNotSent
880 std::map<v3s16, MapBlock*> modified_blocks2;
881 for (const v3s16 &modified_block : event->modified_blocks) {
882 modified_blocks2[modified_block] =
883 m_env->getMap().getBlockNoCreateNoEx(modified_block);
886 // Set blocks not sent
887 for (const u16 far_player : far_players) {
888 if (RemoteClient *client = getClient(far_player))
889 client->SetBlocksNotSent(modified_blocks2);
896 if (event_count >= 5) {
897 infostream << "Server: MapEditEvents:" << std::endl;
898 prof.print(infostream);
899 } else if (event_count != 0) {
900 verbosestream << "Server: MapEditEvents:" << std::endl;
901 prof.print(verbosestream);
907 Trigger emergethread (it somehow gets to a non-triggered but
908 bysy state sometimes)
911 float &counter = m_emergethread_trigger_timer;
913 if (counter >= 2.0) {
916 m_emerge->startThreads();
920 // Save map, players and auth stuff
922 float &counter = m_savemap_timer;
924 static thread_local const float save_interval =
925 g_settings->getFloat("server_map_save_interval");
926 if (counter >= save_interval) {
928 MutexAutoLock lock(m_env_mutex);
930 ScopeProfiler sp(g_profiler, "Server: saving stuff");
933 if (m_banmanager->isModified()) {
934 m_banmanager->save();
937 // Save changed parts of map
938 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
941 m_env->saveLoadedPlayers();
943 // Save environment metadata
949 static const float shutdown_msg_times[] =
951 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
954 if (m_shutdown_timer > 0.0f) {
955 // Automated messages
956 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
957 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
958 // If shutdown timer matches an automessage, shot it
959 if (m_shutdown_timer > shutdown_msg_times[i] &&
960 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
961 std::wstringstream ws;
963 ws << L"*** Server shutting down in "
964 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
967 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
968 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
974 m_shutdown_timer -= dtime;
975 if (m_shutdown_timer < 0.0f) {
976 m_shutdown_timer = 0.0f;
977 m_shutdown_requested = true;
982 void Server::Receive()
987 m_con->Receive(&pkt);
988 peer_id = pkt.getPeerId();
990 } catch (const con::InvalidIncomingDataException &e) {
991 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
992 << e.what() << std::endl;
993 } catch (const SerializationError &e) {
994 infostream << "Server::Receive(): SerializationError: what()="
995 << e.what() << std::endl;
996 } catch (const ClientStateError &e) {
997 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
998 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
999 L"Try reconnecting or updating your client");
1000 } catch (const con::PeerNotFoundException &e) {
1005 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1007 std::string playername;
1008 PlayerSAO *playersao = NULL;
1011 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1013 playername = client->getName();
1014 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1016 } catch (std::exception &e) {
1022 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1024 // If failed, cancel
1025 if (!playersao || !player) {
1026 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1027 actionstream << "Server: Failed to emerge player \"" << playername
1028 << "\" (player allocated to an another client)" << std::endl;
1029 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1030 L"name. If your client closed unexpectedly, try again in "
1033 errorstream << "Server: " << playername << ": Failed to emerge player"
1035 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1041 Send complete position information
1043 SendMovePlayer(peer_id);
1046 SendPlayerPrivileges(peer_id);
1048 // Send inventory formspec
1049 SendPlayerInventoryFormspec(peer_id);
1052 SendInventory(playersao);
1054 // Send HP or death screen
1055 if (playersao->isDead())
1056 SendDeathscreen(peer_id, false, v3f(0,0,0));
1058 SendPlayerHPOrDie(playersao);
1061 SendPlayerBreath(playersao);
1063 // Note things in chat if not in simple singleplayer mode
1064 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1065 // Send information about server to player in chat
1066 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1068 Address addr = getPeerAddress(player->getPeerId());
1069 std::string ip_str = addr.serializeString();
1070 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1075 const std::vector<std::string> &names = m_clients.getPlayerNames();
1077 actionstream << player->getName() << " joins game. List of players: ";
1079 for (const std::string &name : names) {
1080 actionstream << name << " ";
1083 actionstream << player->getName() <<std::endl;
1088 inline void Server::handleCommand(NetworkPacket* pkt)
1090 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1091 (this->*opHandle.handler)(pkt);
1094 void Server::ProcessData(NetworkPacket *pkt)
1096 // Environment is locked first.
1097 MutexAutoLock envlock(m_env_mutex);
1099 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1100 u32 peer_id = pkt->getPeerId();
1103 Address address = getPeerAddress(peer_id);
1104 std::string addr_s = address.serializeString();
1106 if(m_banmanager->isIpBanned(addr_s)) {
1107 std::string ban_name = m_banmanager->getBanName(addr_s);
1108 infostream << "Server: A banned client tried to connect from "
1109 << addr_s << "; banned name was "
1110 << ban_name << std::endl;
1111 // This actually doesn't seem to transfer to the client
1112 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1113 + utf8_to_wide(ban_name));
1117 catch(con::PeerNotFoundException &e) {
1119 * no peer for this packet found
1120 * most common reason is peer timeout, e.g. peer didn't
1121 * respond for some time, your server was overloaded or
1124 infostream << "Server::ProcessData(): Canceling: peer "
1125 << peer_id << " not found" << std::endl;
1130 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1132 // Command must be handled into ToServerCommandHandler
1133 if (command >= TOSERVER_NUM_MSG_TYPES) {
1134 infostream << "Server: Ignoring unknown command "
1135 << command << std::endl;
1139 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1144 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1146 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1147 errorstream << "Server::ProcessData(): Cancelling: Peer"
1148 " serialization format invalid or not initialized."
1149 " Skipping incoming command=" << command << std::endl;
1153 /* Handle commands related to client startup */
1154 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1159 if (m_clients.getClientState(peer_id) < CS_Active) {
1160 if (command == TOSERVER_PLAYERPOS) return;
1162 errorstream << "Got packet command: " << command << " for peer id "
1163 << peer_id << " but client isn't active yet. Dropping packet "
1169 } catch (SendFailedException &e) {
1170 errorstream << "Server::ProcessData(): SendFailedException: "
1171 << "what=" << e.what()
1173 } catch (PacketError &e) {
1174 actionstream << "Server::ProcessData(): PacketError: "
1175 << "what=" << e.what()
1180 void Server::setTimeOfDay(u32 time)
1182 m_env->setTimeOfDay(time);
1183 m_time_of_day_send_timer = 0;
1186 void Server::onMapEditEvent(MapEditEvent *event)
1188 if(m_ignore_map_edit_events)
1190 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1192 MapEditEvent *e = event->clone();
1193 m_unsent_map_edit_queue.push(e);
1196 Inventory* Server::getInventory(const InventoryLocation &loc)
1199 case InventoryLocation::UNDEFINED:
1200 case InventoryLocation::CURRENT_PLAYER:
1202 case InventoryLocation::PLAYER:
1204 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1207 PlayerSAO *playersao = player->getPlayerSAO();
1210 return playersao->getInventory();
1213 case InventoryLocation::NODEMETA:
1215 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1218 return meta->getInventory();
1221 case InventoryLocation::DETACHED:
1223 if(m_detached_inventories.count(loc.name) == 0)
1225 return m_detached_inventories[loc.name];
1229 sanity_check(false); // abort
1234 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1237 case InventoryLocation::UNDEFINED:
1239 case InventoryLocation::PLAYER:
1244 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1249 PlayerSAO *playersao = player->getPlayerSAO();
1253 SendInventory(playersao);
1256 case InventoryLocation::NODEMETA:
1258 v3s16 blockpos = getNodeBlockPos(loc.p);
1260 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1262 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1264 m_clients.markBlockposAsNotSent(blockpos);
1267 case InventoryLocation::DETACHED:
1269 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1273 sanity_check(false); // abort
1278 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1280 std::vector<session_t> clients = m_clients.getClientIDs();
1282 // Set the modified blocks unsent for all the clients
1283 for (const session_t client_id : clients) {
1284 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1285 client->SetBlocksNotSent(block);
1290 void Server::peerAdded(con::Peer *peer)
1292 verbosestream<<"Server::peerAdded(): peer->id="
1293 <<peer->id<<std::endl;
1295 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1298 void Server::deletingPeer(con::Peer *peer, bool timeout)
1300 verbosestream<<"Server::deletingPeer(): peer->id="
1301 <<peer->id<<", timeout="<<timeout<<std::endl;
1303 m_clients.event(peer->id, CSE_Disconnect);
1304 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1307 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1309 *retval = m_con->getPeerStat(peer_id,type);
1310 return *retval != -1;
1313 bool Server::getClientInfo(
1322 std::string* vers_string
1325 *state = m_clients.getClientState(peer_id);
1327 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1334 *uptime = client->uptime();
1335 *ser_vers = client->serialization_version;
1336 *prot_vers = client->net_proto_version;
1338 *major = client->getMajor();
1339 *minor = client->getMinor();
1340 *patch = client->getPatch();
1341 *vers_string = client->getPatch();
1348 void Server::handlePeerChanges()
1350 while(!m_peer_change_queue.empty())
1352 con::PeerChange c = m_peer_change_queue.front();
1353 m_peer_change_queue.pop();
1355 verbosestream<<"Server: Handling peer change: "
1356 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1361 case con::PEER_ADDED:
1362 m_clients.CreateClient(c.peer_id);
1365 case con::PEER_REMOVED:
1366 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1370 FATAL_ERROR("Invalid peer change event received!");
1376 void Server::printToConsoleOnly(const std::string &text)
1379 m_admin_chat->outgoing_queue.push_back(
1380 new ChatEventChat("", utf8_to_wide(text)));
1382 std::cout << text << std::endl;
1386 void Server::Send(NetworkPacket *pkt)
1388 Send(pkt->getPeerId(), pkt);
1391 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1393 m_clients.send(peer_id,
1394 clientCommandFactoryTable[pkt->getCommand()].channel,
1396 clientCommandFactoryTable[pkt->getCommand()].reliable);
1399 void Server::SendMovement(session_t peer_id)
1401 std::ostringstream os(std::ios_base::binary);
1403 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1405 pkt << g_settings->getFloat("movement_acceleration_default");
1406 pkt << g_settings->getFloat("movement_acceleration_air");
1407 pkt << g_settings->getFloat("movement_acceleration_fast");
1408 pkt << g_settings->getFloat("movement_speed_walk");
1409 pkt << g_settings->getFloat("movement_speed_crouch");
1410 pkt << g_settings->getFloat("movement_speed_fast");
1411 pkt << g_settings->getFloat("movement_speed_climb");
1412 pkt << g_settings->getFloat("movement_speed_jump");
1413 pkt << g_settings->getFloat("movement_liquid_fluidity");
1414 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1415 pkt << g_settings->getFloat("movement_liquid_sink");
1416 pkt << g_settings->getFloat("movement_gravity");
1421 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1423 if (!g_settings->getBool("enable_damage"))
1426 session_t peer_id = playersao->getPeerID();
1427 bool is_alive = playersao->getHP() > 0;
1430 SendPlayerHP(peer_id);
1435 void Server::SendHP(session_t peer_id, u16 hp)
1437 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1442 void Server::SendBreath(session_t peer_id, u16 breath)
1444 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1445 pkt << (u16) breath;
1449 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1450 const std::string &custom_reason, bool reconnect)
1452 assert(reason < SERVER_ACCESSDENIED_MAX);
1454 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1456 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1457 pkt << custom_reason;
1458 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1459 reason == SERVER_ACCESSDENIED_CRASH)
1460 pkt << custom_reason << (u8)reconnect;
1464 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1466 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1471 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1472 v3f camera_point_target)
1474 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1475 pkt << set_camera_point_target << camera_point_target;
1479 void Server::SendItemDef(session_t peer_id,
1480 IItemDefManager *itemdef, u16 protocol_version)
1482 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1486 u32 length of the next item
1487 zlib-compressed serialized ItemDefManager
1489 std::ostringstream tmp_os(std::ios::binary);
1490 itemdef->serialize(tmp_os, protocol_version);
1491 std::ostringstream tmp_os2(std::ios::binary);
1492 compressZlib(tmp_os.str(), tmp_os2);
1493 pkt.putLongString(tmp_os2.str());
1496 verbosestream << "Server: Sending item definitions to id(" << peer_id
1497 << "): size=" << pkt.getSize() << std::endl;
1502 void Server::SendNodeDef(session_t peer_id,
1503 const NodeDefManager *nodedef, u16 protocol_version)
1505 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1509 u32 length of the next item
1510 zlib-compressed serialized NodeDefManager
1512 std::ostringstream tmp_os(std::ios::binary);
1513 nodedef->serialize(tmp_os, protocol_version);
1514 std::ostringstream tmp_os2(std::ios::binary);
1515 compressZlib(tmp_os.str(), tmp_os2);
1517 pkt.putLongString(tmp_os2.str());
1520 verbosestream << "Server: Sending node definitions to id(" << peer_id
1521 << "): size=" << pkt.getSize() << std::endl;
1527 Non-static send methods
1530 void Server::SendInventory(PlayerSAO* playerSAO)
1532 UpdateCrafting(playerSAO->getPlayer());
1538 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1540 std::ostringstream os;
1541 playerSAO->getInventory()->serialize(os);
1543 std::string s = os.str();
1545 pkt.putRawString(s.c_str(), s.size());
1549 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1551 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1553 u8 type = message.type;
1554 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1556 if (peer_id != PEER_ID_INEXISTENT) {
1557 RemotePlayer *player = m_env->getPlayer(peer_id);
1563 m_clients.sendToAll(&pkt);
1567 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1568 const std::string &formname)
1570 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1571 if (formspec.empty()){
1572 //the client should close the formspec
1573 m_formspec_state_data.erase(peer_id);
1574 pkt.putLongString("");
1576 m_formspec_state_data[peer_id] = formname;
1577 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1584 // Spawns a particle on peer with peer_id
1585 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1586 v3f pos, v3f velocity, v3f acceleration,
1587 float expirationtime, float size, bool collisiondetection,
1588 bool collision_removal,
1589 bool vertical, const std::string &texture,
1590 const struct TileAnimationParams &animation, u8 glow)
1592 static thread_local const float radius =
1593 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1595 if (peer_id == PEER_ID_INEXISTENT) {
1596 std::vector<session_t> clients = m_clients.getClientIDs();
1598 for (const session_t client_id : clients) {
1599 RemotePlayer *player = m_env->getPlayer(client_id);
1603 PlayerSAO *sao = player->getPlayerSAO();
1607 // Do not send to distant clients
1608 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1611 SendSpawnParticle(client_id, player->protocol_version,
1612 pos, velocity, acceleration,
1613 expirationtime, size, collisiondetection,
1614 collision_removal, vertical, texture, animation, glow);
1619 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1621 pkt << pos << velocity << acceleration << expirationtime
1622 << size << collisiondetection;
1623 pkt.putLongString(texture);
1625 pkt << collision_removal;
1626 // This is horrible but required (why are there two ways to serialize pkts?)
1627 std::ostringstream os(std::ios_base::binary);
1628 animation.serialize(os, protocol_version);
1629 pkt.putRawString(os.str());
1635 // Adds a ParticleSpawner on peer with peer_id
1636 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1637 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1638 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1639 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1640 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1641 const struct TileAnimationParams &animation, u8 glow)
1643 if (peer_id == PEER_ID_INEXISTENT) {
1644 // This sucks and should be replaced:
1645 std::vector<session_t> clients = m_clients.getClientIDs();
1646 for (const session_t client_id : clients) {
1647 RemotePlayer *player = m_env->getPlayer(client_id);
1650 SendAddParticleSpawner(client_id, player->protocol_version,
1651 amount, spawntime, minpos, maxpos,
1652 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1653 minsize, maxsize, collisiondetection, collision_removal,
1654 attached_id, vertical, texture, id, animation, glow);
1659 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1661 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1662 << minacc << maxacc << minexptime << maxexptime << minsize
1663 << maxsize << collisiondetection;
1665 pkt.putLongString(texture);
1667 pkt << id << vertical;
1668 pkt << collision_removal;
1670 // This is horrible but required
1671 std::ostringstream os(std::ios_base::binary);
1672 animation.serialize(os, protocol_version);
1673 pkt.putRawString(os.str());
1679 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1681 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1683 // Ugly error in this packet
1686 if (peer_id != PEER_ID_INEXISTENT)
1689 m_clients.sendToAll(&pkt);
1693 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1695 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1697 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1698 << form->text << form->number << form->item << form->dir
1699 << form->align << form->offset << form->world_pos << form->size;
1704 void Server::SendHUDRemove(session_t peer_id, u32 id)
1706 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1711 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1713 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1714 pkt << id << (u8) stat;
1718 case HUD_STAT_SCALE:
1719 case HUD_STAT_ALIGN:
1720 case HUD_STAT_OFFSET:
1721 pkt << *(v2f *) value;
1725 pkt << *(std::string *) value;
1727 case HUD_STAT_WORLD_POS:
1728 pkt << *(v3f *) value;
1731 pkt << *(v2s32 *) value;
1733 case HUD_STAT_NUMBER:
1737 pkt << *(u32 *) value;
1744 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1746 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1748 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1750 pkt << flags << mask;
1755 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1757 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1758 pkt << param << value;
1762 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1763 const std::string &type, const std::vector<std::string> ¶ms,
1766 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1767 pkt << bgcolor << type << (u16) params.size();
1769 for (const std::string ¶m : params)
1777 void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms)
1779 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1780 pkt << params.density << params.color_bright << params.color_ambient
1781 << params.height << params.thickness << params.speed;
1785 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1788 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1791 pkt << do_override << (u16) (ratio * 65535);
1796 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1798 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1799 pkt << time << time_speed;
1801 if (peer_id == PEER_ID_INEXISTENT) {
1802 m_clients.sendToAll(&pkt);
1809 void Server::SendPlayerHP(session_t peer_id)
1811 PlayerSAO *playersao = getPlayerSAO(peer_id);
1812 // In some rare case if the player is disconnected
1813 // while Lua call l_punch, for example, this can be NULL
1817 SendHP(peer_id, playersao->getHP());
1818 m_script->player_event(playersao,"health_changed");
1820 // Send to other clients
1821 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1822 ActiveObjectMessage aom(playersao->getId(), true, str);
1823 playersao->m_messages_out.push(aom);
1826 void Server::SendPlayerBreath(PlayerSAO *sao)
1830 m_script->player_event(sao, "breath_changed");
1831 SendBreath(sao->getPeerID(), sao->getBreath());
1834 void Server::SendMovePlayer(session_t peer_id)
1836 RemotePlayer *player = m_env->getPlayer(peer_id);
1838 PlayerSAO *sao = player->getPlayerSAO();
1841 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1842 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1845 v3f pos = sao->getBasePosition();
1846 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1847 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1848 << " pitch=" << sao->getPitch()
1849 << " yaw=" << sao->getYaw()
1856 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1857 f32 animation_speed)
1859 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1862 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1863 << animation_frames[3] << animation_speed;
1868 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1870 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1871 pkt << first << third;
1875 void Server::SendPlayerPrivileges(session_t peer_id)
1877 RemotePlayer *player = m_env->getPlayer(peer_id);
1879 if(player->getPeerId() == PEER_ID_INEXISTENT)
1882 std::set<std::string> privs;
1883 m_script->getAuth(player->getName(), NULL, &privs);
1885 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1886 pkt << (u16) privs.size();
1888 for (const std::string &priv : privs) {
1895 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1897 RemotePlayer *player = m_env->getPlayer(peer_id);
1899 if(player->getPeerId() == PEER_ID_INEXISTENT)
1902 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1903 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1907 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1909 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1910 pkt.putRawString(datas.c_str(), datas.size());
1912 return pkt.getSize();
1915 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1918 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1919 datas.size(), peer_id);
1921 pkt.putRawString(datas.c_str(), datas.size());
1923 m_clients.send(pkt.getPeerId(),
1924 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1928 void Server::SendCSMFlavourLimits(session_t peer_id)
1930 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1931 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1932 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1936 s32 Server::playSound(const SimpleSoundSpec &spec,
1937 const ServerSoundParams ¶ms)
1939 // Find out initial position of sound
1940 bool pos_exists = false;
1941 v3f pos = params.getPos(m_env, &pos_exists);
1942 // If position is not found while it should be, cancel sound
1943 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1946 // Filter destination clients
1947 std::vector<session_t> dst_clients;
1948 if(!params.to_player.empty()) {
1949 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1951 infostream<<"Server::playSound: Player \""<<params.to_player
1952 <<"\" not found"<<std::endl;
1955 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1956 infostream<<"Server::playSound: Player \""<<params.to_player
1957 <<"\" not connected"<<std::endl;
1960 dst_clients.push_back(player->getPeerId());
1962 std::vector<session_t> clients = m_clients.getClientIDs();
1964 for (const session_t client_id : clients) {
1965 RemotePlayer *player = m_env->getPlayer(client_id);
1969 PlayerSAO *sao = player->getPlayerSAO();
1974 if(sao->getBasePosition().getDistanceFrom(pos) >
1975 params.max_hear_distance)
1978 dst_clients.push_back(client_id);
1982 if(dst_clients.empty())
1986 s32 id = m_next_sound_id++;
1987 // The sound will exist as a reference in m_playing_sounds
1988 m_playing_sounds[id] = ServerPlayingSound();
1989 ServerPlayingSound &psound = m_playing_sounds[id];
1990 psound.params = params;
1993 float gain = params.gain * spec.gain;
1994 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1995 pkt << id << spec.name << gain
1996 << (u8) params.type << pos << params.object
1997 << params.loop << params.fade << params.pitch;
1999 // Backwards compability
2000 bool play_sound = gain > 0;
2002 for (const u16 dst_client : dst_clients) {
2003 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2004 psound.clients.insert(dst_client);
2005 m_clients.send(dst_client, 0, &pkt, true);
2010 void Server::stopSound(s32 handle)
2012 // Get sound reference
2013 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2014 m_playing_sounds.find(handle);
2015 if (i == m_playing_sounds.end())
2017 ServerPlayingSound &psound = i->second;
2019 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2022 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2023 si != psound.clients.end(); ++si) {
2025 m_clients.send(*si, 0, &pkt, true);
2027 // Remove sound reference
2028 m_playing_sounds.erase(i);
2031 void Server::fadeSound(s32 handle, float step, float gain)
2033 // Get sound reference
2034 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2035 m_playing_sounds.find(handle);
2036 if (i == m_playing_sounds.end())
2039 ServerPlayingSound &psound = i->second;
2040 psound.params.gain = gain;
2042 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2043 pkt << handle << step << gain;
2045 // Backwards compability
2046 bool play_sound = gain > 0;
2047 ServerPlayingSound compat_psound = psound;
2048 compat_psound.clients.clear();
2050 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2051 compat_pkt << handle;
2053 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2054 it != psound.clients.end();) {
2055 if (m_clients.getProtocolVersion(*it) >= 32) {
2057 m_clients.send(*it, 0, &pkt, true);
2060 compat_psound.clients.insert(*it);
2062 m_clients.send(*it, 0, &compat_pkt, true);
2063 psound.clients.erase(it++);
2067 // Remove sound reference
2068 if (!play_sound || psound.clients.empty())
2069 m_playing_sounds.erase(i);
2071 if (play_sound && !compat_psound.clients.empty()) {
2072 // Play new sound volume on older clients
2073 playSound(compat_psound.spec, compat_psound.params);
2077 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2078 std::vector<u16> *far_players, float far_d_nodes)
2080 float maxd = far_d_nodes*BS;
2081 v3f p_f = intToFloat(p, BS);
2083 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2086 std::vector<session_t> clients = m_clients.getClientIDs();
2087 for (session_t client_id : clients) {
2090 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2091 PlayerSAO *sao = player->getPlayerSAO();
2095 // If player is far away, only set modified blocks not sent
2096 v3f player_pos = sao->getBasePosition();
2097 if (player_pos.getDistanceFrom(p_f) > maxd) {
2098 far_players->push_back(client_id);
2105 m_clients.send(client_id, 0, &pkt, true);
2109 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2110 std::vector<u16> *far_players, float far_d_nodes,
2111 bool remove_metadata)
2113 float maxd = far_d_nodes*BS;
2114 v3f p_f = intToFloat(p, BS);
2116 std::vector<session_t> clients = m_clients.getClientIDs();
2117 for (const session_t client_id : clients) {
2120 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2121 PlayerSAO *sao = player->getPlayerSAO();
2125 // If player is far away, only set modified blocks not sent
2126 v3f player_pos = sao->getBasePosition();
2127 if(player_pos.getDistanceFrom(p_f) > maxd) {
2128 far_players->push_back(client_id);
2134 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2136 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2138 pkt << p << n.param0 << n.param1 << n.param2
2139 << (u8) (remove_metadata ? 0 : 1);
2144 if (pkt.getSize() > 0)
2145 m_clients.send(client_id, 0, &pkt, true);
2149 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2150 u16 net_proto_version)
2153 Create a packet with the block in the right format
2156 std::ostringstream os(std::ios_base::binary);
2157 block->serialize(os, ver, false);
2158 block->serializeNetworkSpecific(os);
2159 std::string s = os.str();
2161 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2163 pkt << block->getPos();
2164 pkt.putRawString(s.c_str(), s.size());
2168 void Server::SendBlocks(float dtime)
2170 MutexAutoLock envlock(m_env_mutex);
2171 //TODO check if one big lock could be faster then multiple small ones
2173 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2175 std::vector<PrioritySortedBlockTransfer> queue;
2177 u32 total_sending = 0;
2180 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2182 std::vector<session_t> clients = m_clients.getClientIDs();
2185 for (const session_t client_id : clients) {
2186 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2191 total_sending += client->getSendingCount();
2192 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2198 // Lowest priority number comes first.
2199 // Lowest is most important.
2200 std::sort(queue.begin(), queue.end());
2204 // Maximal total count calculation
2205 // The per-client block sends is halved with the maximal online users
2206 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2207 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2209 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2210 if (total_sending >= max_blocks_to_send)
2213 MapBlock *block = nullptr;
2215 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2216 } catch (const InvalidPositionException &e) {
2220 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2225 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2226 client->net_proto_version);
2228 client->SentBlock(block_to_send.pos);
2234 void Server::fillMediaCache()
2236 infostream<<"Server: Calculating media file checksums"<<std::endl;
2238 // Collect all media file paths
2239 std::vector<std::string> paths;
2240 for (const ModSpec &mod : m_mods) {
2241 paths.push_back(mod.path + DIR_DELIM + "textures");
2242 paths.push_back(mod.path + DIR_DELIM + "sounds");
2243 paths.push_back(mod.path + DIR_DELIM + "media");
2244 paths.push_back(mod.path + DIR_DELIM + "models");
2245 paths.push_back(mod.path + DIR_DELIM + "locale");
2247 fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures");
2248 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2250 // Collect media file information from paths into cache
2251 for (const std::string &mediapath : paths) {
2252 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2253 for (const fs::DirListNode &dln : dirlist) {
2254 if (dln.dir) // Ignode dirs
2256 std::string filename = dln.name;
2257 // If name contains illegal characters, ignore the file
2258 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2259 infostream<<"Server: ignoring illegal file name: \""
2260 << filename << "\"" << std::endl;
2263 // If name is not in a supported format, ignore it
2264 const char *supported_ext[] = {
2265 ".png", ".jpg", ".bmp", ".tga",
2266 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2268 ".x", ".b3d", ".md2", ".obj",
2269 // Custom translation file format
2273 if (removeStringEnd(filename, supported_ext).empty()){
2274 infostream << "Server: ignoring unsupported file extension: \""
2275 << filename << "\"" << std::endl;
2278 // Ok, attempt to load the file and add to cache
2279 std::string filepath;
2280 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2283 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2285 errorstream << "Server::fillMediaCache(): Could not open \""
2286 << filename << "\" for reading" << std::endl;
2289 std::ostringstream tmp_os(std::ios_base::binary);
2293 fis.read(buf, 1024);
2294 std::streamsize len = fis.gcount();
2295 tmp_os.write(buf, len);
2304 errorstream<<"Server::fillMediaCache(): Failed to read \""
2305 << filename << "\"" << std::endl;
2308 if(tmp_os.str().length() == 0) {
2309 errorstream << "Server::fillMediaCache(): Empty file \""
2310 << filepath << "\"" << std::endl;
2315 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2317 unsigned char *digest = sha1.getDigest();
2318 std::string sha1_base64 = base64_encode(digest, 20);
2319 std::string sha1_hex = hex_encode((char*)digest, 20);
2323 m_media[filename] = MediaInfo(filepath, sha1_base64);
2324 verbosestream << "Server: " << sha1_hex << " is " << filename
2330 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2332 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2336 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2339 std::string lang_suffix;
2340 lang_suffix.append(".").append(lang_code).append(".tr");
2341 for (const auto &i : m_media) {
2342 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2349 for (const auto &i : m_media) {
2350 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2352 pkt << i.first << i.second.sha1_digest;
2355 pkt << g_settings->get("remote_media");
2359 struct SendableMedia
2365 SendableMedia(const std::string &name_="", const std::string &path_="",
2366 const std::string &data_=""):
2373 void Server::sendRequestedMedia(session_t peer_id,
2374 const std::vector<std::string> &tosend)
2376 verbosestream<<"Server::sendRequestedMedia(): "
2377 <<"Sending files to client"<<std::endl;
2381 // Put 5kB in one bunch (this is not accurate)
2382 u32 bytes_per_bunch = 5000;
2384 std::vector< std::vector<SendableMedia> > file_bunches;
2385 file_bunches.emplace_back();
2387 u32 file_size_bunch_total = 0;
2389 for (const std::string &name : tosend) {
2390 if (m_media.find(name) == m_media.end()) {
2391 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2392 <<"unknown file \""<<(name)<<"\""<<std::endl;
2396 //TODO get path + name
2397 std::string tpath = m_media[name].path;
2400 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2402 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2403 <<tpath<<"\" for reading"<<std::endl;
2406 std::ostringstream tmp_os(std::ios_base::binary);
2410 fis.read(buf, 1024);
2411 std::streamsize len = fis.gcount();
2412 tmp_os.write(buf, len);
2413 file_size_bunch_total += len;
2422 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2423 <<name<<"\""<<std::endl;
2426 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2427 <<tname<<"\""<<std::endl;*/
2429 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2431 // Start next bunch if got enough data
2432 if(file_size_bunch_total >= bytes_per_bunch) {
2433 file_bunches.emplace_back();
2434 file_size_bunch_total = 0;
2439 /* Create and send packets */
2441 u16 num_bunches = file_bunches.size();
2442 for (u16 i = 0; i < num_bunches; i++) {
2445 u16 total number of texture bunches
2446 u16 index of this bunch
2447 u32 number of files in this bunch
2456 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2457 pkt << num_bunches << i << (u32) file_bunches[i].size();
2459 for (const SendableMedia &j : file_bunches[i]) {
2461 pkt.putLongString(j.data);
2464 verbosestream << "Server::sendRequestedMedia(): bunch "
2465 << i << "/" << num_bunches
2466 << " files=" << file_bunches[i].size()
2467 << " size=" << pkt.getSize() << std::endl;
2472 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2474 if(m_detached_inventories.count(name) == 0) {
2475 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2478 Inventory *inv = m_detached_inventories[name];
2479 std::ostringstream os(std::ios_base::binary);
2481 os << serializeString(name);
2485 std::string s = os.str();
2487 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2488 pkt.putRawString(s.c_str(), s.size());
2490 const std::string &check = m_detached_inventories_player[name];
2491 if (peer_id == PEER_ID_INEXISTENT) {
2493 return m_clients.sendToAll(&pkt);
2494 RemotePlayer *p = m_env->getPlayer(check.c_str());
2496 m_clients.send(p->getPeerId(), 0, &pkt, true);
2498 if (check.empty() || getPlayerName(peer_id) == check)
2503 void Server::sendDetachedInventories(session_t peer_id)
2505 for (const auto &detached_inventory : m_detached_inventories) {
2506 const std::string &name = detached_inventory.first;
2507 //Inventory *inv = i->second;
2508 sendDetachedInventory(name, peer_id);
2516 void Server::DiePlayer(session_t peer_id)
2518 PlayerSAO *playersao = getPlayerSAO(peer_id);
2519 // In some rare cases this can be NULL -- if the player is disconnected
2520 // when a Lua function modifies l_punch, for example
2524 infostream << "Server::DiePlayer(): Player "
2525 << playersao->getPlayer()->getName()
2526 << " dies" << std::endl;
2528 playersao->setHP(0);
2530 // Trigger scripted stuff
2531 m_script->on_dieplayer(playersao);
2533 SendPlayerHP(peer_id);
2534 SendDeathscreen(peer_id, false, v3f(0,0,0));
2537 void Server::RespawnPlayer(session_t peer_id)
2539 PlayerSAO *playersao = getPlayerSAO(peer_id);
2542 infostream << "Server::RespawnPlayer(): Player "
2543 << playersao->getPlayer()->getName()
2544 << " respawns" << std::endl;
2546 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2547 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2549 bool repositioned = m_script->on_respawnplayer(playersao);
2550 if (!repositioned) {
2551 // setPos will send the new position to client
2552 playersao->setPos(findSpawnPos());
2555 SendPlayerHP(peer_id);
2559 void Server::DenySudoAccess(session_t peer_id)
2561 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2566 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2567 const std::string &str_reason, bool reconnect)
2569 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2571 m_clients.event(peer_id, CSE_SetDenied);
2572 DisconnectPeer(peer_id);
2576 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2577 const std::string &custom_reason)
2579 SendAccessDenied(peer_id, reason, custom_reason);
2580 m_clients.event(peer_id, CSE_SetDenied);
2581 DisconnectPeer(peer_id);
2584 // 13/03/15: remove this function when protocol version 25 will become
2585 // the minimum version for MT users, maybe in 1 year
2586 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2588 SendAccessDenied_Legacy(peer_id, reason);
2589 m_clients.event(peer_id, CSE_SetDenied);
2590 DisconnectPeer(peer_id);
2593 void Server::DisconnectPeer(session_t peer_id)
2595 m_modchannel_mgr->leaveAllChannels(peer_id);
2596 m_con->DisconnectPeer(peer_id);
2599 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2602 RemoteClient* client = getClient(peer_id, CS_Invalid);
2604 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2606 // Right now, the auth mechs don't change between login and sudo mode.
2607 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2608 client->allowed_sudo_mechs = sudo_auth_mechs;
2610 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2611 << g_settings->getFloat("dedicated_server_step")
2615 m_clients.event(peer_id, CSE_AuthAccept);
2617 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2619 // We only support SRP right now
2620 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2622 resp_pkt << sudo_auth_mechs;
2624 m_clients.event(peer_id, CSE_SudoSuccess);
2628 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2630 std::wstring message;
2633 Clear references to playing sounds
2635 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2636 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2637 ServerPlayingSound &psound = i->second;
2638 psound.clients.erase(peer_id);
2639 if (psound.clients.empty())
2640 m_playing_sounds.erase(i++);
2645 // clear formspec info so the next client can't abuse the current state
2646 m_formspec_state_data.erase(peer_id);
2648 RemotePlayer *player = m_env->getPlayer(peer_id);
2650 /* Run scripts and remove from environment */
2652 PlayerSAO *playersao = player->getPlayerSAO();
2655 // inform connected clients
2656 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2657 // (u16) 1 + std::string represents a vector serialization representation
2658 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2659 m_clients.sendToAll(¬ice);
2661 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2663 playersao->disconnected();
2670 if (player && reason != CDR_DENY) {
2671 std::ostringstream os(std::ios_base::binary);
2672 std::vector<session_t> clients = m_clients.getClientIDs();
2674 for (const session_t client_id : clients) {
2676 RemotePlayer *player = m_env->getPlayer(client_id);
2680 // Get name of player
2681 os << player->getName() << " ";
2684 std::string name = player->getName();
2685 actionstream << name << " "
2686 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2687 << " List of players: " << os.str() << std::endl;
2689 m_admin_chat->outgoing_queue.push_back(
2690 new ChatEventNick(CET_NICK_REMOVE, name));
2694 MutexAutoLock env_lock(m_env_mutex);
2695 m_clients.DeleteClient(peer_id);
2699 // Send leave chat message to all remaining clients
2700 if (!message.empty()) {
2701 SendChatMessage(PEER_ID_INEXISTENT,
2702 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2706 void Server::UpdateCrafting(RemotePlayer *player)
2708 // Get a preview for crafting
2710 InventoryLocation loc;
2711 loc.setPlayer(player->getName());
2712 std::vector<ItemStack> output_replacements;
2713 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2714 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2715 (&player->inventory)->getList("craft"), loc);
2717 // Put the new preview in
2718 InventoryList *plist = player->inventory.getList("craftpreview");
2719 sanity_check(plist);
2720 sanity_check(plist->getSize() >= 1);
2721 plist->changeItem(0, preview);
2724 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2726 if (evt->type == CET_NICK_ADD) {
2727 // The terminal informed us of its nick choice
2728 m_admin_nick = ((ChatEventNick *)evt)->nick;
2729 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2730 errorstream << "You haven't set up an account." << std::endl
2731 << "Please log in using the client as '"
2732 << m_admin_nick << "' with a secure password." << std::endl
2733 << "Until then, you can't execute admin tasks via the console," << std::endl
2734 << "and everybody can claim the user account instead of you," << std::endl
2735 << "giving them full control over this server." << std::endl;
2738 assert(evt->type == CET_CHAT);
2739 handleAdminChat((ChatEventChat *)evt);
2743 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2744 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2746 // If something goes wrong, this player is to blame
2747 RollbackScopeActor rollback_scope(m_rollback,
2748 std::string("player:") + name);
2750 if (g_settings->getBool("strip_color_codes"))
2751 wmessage = unescape_enriched(wmessage);
2754 switch (player->canSendChatMessage()) {
2755 case RPLAYER_CHATRESULT_FLOODING: {
2756 std::wstringstream ws;
2757 ws << L"You cannot send more messages. You are limited to "
2758 << g_settings->getFloat("chat_message_limit_per_10sec")
2759 << L" messages per 10 seconds.";
2762 case RPLAYER_CHATRESULT_KICK:
2763 DenyAccess_Legacy(player->getPeerId(),
2764 L"You have been kicked due to message flooding.");
2766 case RPLAYER_CHATRESULT_OK:
2769 FATAL_ERROR("Unhandled chat filtering result found.");
2773 if (m_max_chatmessage_length > 0
2774 && wmessage.length() > m_max_chatmessage_length) {
2775 return L"Your message exceed the maximum chat message limit set on the server. "
2776 L"It was refused. Send a shorter message";
2779 // Run script hook, exit if script ate the chat message
2780 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2785 // Whether to send line to the player that sent the message, or to all players
2786 bool broadcast_line = true;
2788 if (check_shout_priv && !checkPriv(name, "shout")) {
2789 line += L"-!- You don't have permission to shout.";
2790 broadcast_line = false;
2799 Tell calling method to send the message to sender
2801 if (!broadcast_line)
2805 Send the message to others
2807 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2809 std::vector<session_t> clients = m_clients.getClientIDs();
2812 Send the message back to the inital sender
2813 if they are using protocol version >= 29
2816 session_t peer_id_to_avoid_sending =
2817 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2819 if (player && player->protocol_version >= 29)
2820 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2822 for (u16 cid : clients) {
2823 if (cid != peer_id_to_avoid_sending)
2824 SendChatMessage(cid, ChatMessage(line));
2829 void Server::handleAdminChat(const ChatEventChat *evt)
2831 std::string name = evt->nick;
2832 std::wstring wname = utf8_to_wide(name);
2833 std::wstring wmessage = evt->evt_msg;
2835 std::wstring answer = handleChat(name, wname, wmessage);
2837 // If asked to send answer to sender
2838 if (!answer.empty()) {
2839 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2843 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2845 RemoteClient *client = getClientNoEx(peer_id,state_min);
2847 throw ClientNotFoundException("Client not found");
2851 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2853 return m_clients.getClientNoEx(peer_id, state_min);
2856 std::string Server::getPlayerName(session_t peer_id)
2858 RemotePlayer *player = m_env->getPlayer(peer_id);
2860 return "[id="+itos(peer_id)+"]";
2861 return player->getName();
2864 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2866 RemotePlayer *player = m_env->getPlayer(peer_id);
2869 return player->getPlayerSAO();
2872 std::wstring Server::getStatusString()
2874 std::wostringstream os(std::ios_base::binary);
2877 os<<L"version="<<narrow_to_wide(g_version_string);
2879 os<<L", uptime="<<m_uptime.get();
2881 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2882 // Information about clients
2885 std::vector<session_t> clients = m_clients.getClientIDs();
2886 for (session_t client_id : clients) {
2888 RemotePlayer *player = m_env->getPlayer(client_id);
2889 // Get name of player
2890 std::wstring name = L"unknown";
2892 name = narrow_to_wide(player->getName());
2893 // Add name to information string
2902 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2903 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2905 if (!g_settings->get("motd").empty())
2906 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2910 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2912 std::set<std::string> privs;
2913 m_script->getAuth(name, NULL, &privs);
2917 bool Server::checkPriv(const std::string &name, const std::string &priv)
2919 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2920 return (privs.count(priv) != 0);
2923 void Server::reportPrivsModified(const std::string &name)
2926 std::vector<session_t> clients = m_clients.getClientIDs();
2927 for (const session_t client_id : clients) {
2928 RemotePlayer *player = m_env->getPlayer(client_id);
2929 reportPrivsModified(player->getName());
2932 RemotePlayer *player = m_env->getPlayer(name.c_str());
2935 SendPlayerPrivileges(player->getPeerId());
2936 PlayerSAO *sao = player->getPlayerSAO();
2939 sao->updatePrivileges(
2940 getPlayerEffectivePrivs(name),
2945 void Server::reportInventoryFormspecModified(const std::string &name)
2947 RemotePlayer *player = m_env->getPlayer(name.c_str());
2950 SendPlayerInventoryFormspec(player->getPeerId());
2953 void Server::setIpBanned(const std::string &ip, const std::string &name)
2955 m_banmanager->add(ip, name);
2958 void Server::unsetIpBanned(const std::string &ip_or_name)
2960 m_banmanager->remove(ip_or_name);
2963 std::string Server::getBanDescription(const std::string &ip_or_name)
2965 return m_banmanager->getBanDescription(ip_or_name);
2968 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2970 // m_env will be NULL if the server is initializing
2974 if (m_admin_nick == name && !m_admin_nick.empty()) {
2975 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2978 RemotePlayer *player = m_env->getPlayer(name);
2983 if (player->getPeerId() == PEER_ID_INEXISTENT)
2986 SendChatMessage(player->getPeerId(), ChatMessage(msg));
2989 bool Server::showFormspec(const char *playername, const std::string &formspec,
2990 const std::string &formname)
2992 // m_env will be NULL if the server is initializing
2996 RemotePlayer *player = m_env->getPlayer(playername);
3000 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3004 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3009 u32 id = player->addHud(form);
3011 SendHUDAdd(player->getPeerId(), id, form);
3016 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3020 HudElement* todel = player->removeHud(id);
3027 SendHUDRemove(player->getPeerId(), id);
3031 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3036 SendHUDChange(player->getPeerId(), id, stat, data);
3040 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3045 SendHUDSetFlags(player->getPeerId(), flags, mask);
3046 player->hud_flags &= ~mask;
3047 player->hud_flags |= flags;
3049 PlayerSAO* playersao = player->getPlayerSAO();
3054 m_script->player_event(playersao, "hud_changed");
3058 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3063 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3066 player->setHotbarItemcount(hotbar_itemcount);
3067 std::ostringstream os(std::ios::binary);
3068 writeS32(os, hotbar_itemcount);
3069 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3073 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3078 player->setHotbarImage(name);
3079 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3082 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3087 player->setHotbarSelectedImage(name);
3088 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3091 Address Server::getPeerAddress(session_t peer_id)
3093 return m_con->GetPeerAddress(peer_id);
3096 void Server::setLocalPlayerAnimations(RemotePlayer *player,
3097 v2s32 animation_frames[4], f32 frame_speed)
3099 sanity_check(player);
3100 player->setLocalAnimations(animation_frames, frame_speed);
3101 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3104 void Server::setPlayerEyeOffset(RemotePlayer *player, const v3f &first, const v3f &third)
3106 sanity_check(player);
3107 player->eye_offset_first = first;
3108 player->eye_offset_third = third;
3109 SendEyeOffset(player->getPeerId(), first, third);
3112 void Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3113 const std::string &type, const std::vector<std::string> ¶ms,
3116 sanity_check(player);
3117 player->setSky(bgcolor, type, params, clouds);
3118 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3121 void Server::setClouds(RemotePlayer *player, const CloudParams ¶ms)
3123 sanity_check(player);
3124 player->setCloudParams(params);
3125 SendCloudParams(player->getPeerId(), params);
3128 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3134 player->overrideDayNightRatio(do_override, ratio);
3135 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3139 void Server::notifyPlayers(const std::wstring &msg)
3141 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3144 void Server::spawnParticle(const std::string &playername, v3f pos,
3145 v3f velocity, v3f acceleration,
3146 float expirationtime, float size, bool
3147 collisiondetection, bool collision_removal,
3148 bool vertical, const std::string &texture,
3149 const struct TileAnimationParams &animation, u8 glow)
3151 // m_env will be NULL if the server is initializing
3155 session_t peer_id = PEER_ID_INEXISTENT;
3157 if (!playername.empty()) {
3158 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3161 peer_id = player->getPeerId();
3162 proto_ver = player->protocol_version;
3165 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3166 expirationtime, size, collisiondetection,
3167 collision_removal, vertical, texture, animation, glow);
3170 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3171 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3172 float minexptime, float maxexptime, float minsize, float maxsize,
3173 bool collisiondetection, bool collision_removal,
3174 ServerActiveObject *attached, bool vertical, const std::string &texture,
3175 const std::string &playername, const struct TileAnimationParams &animation,
3178 // m_env will be NULL if the server is initializing
3182 session_t peer_id = PEER_ID_INEXISTENT;
3184 if (!playername.empty()) {
3185 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3188 peer_id = player->getPeerId();
3189 proto_ver = player->protocol_version;
3192 u16 attached_id = attached ? attached->getId() : 0;
3195 if (attached_id == 0)
3196 id = m_env->addParticleSpawner(spawntime);
3198 id = m_env->addParticleSpawner(spawntime, attached_id);
3200 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3201 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3202 minexptime, maxexptime, minsize, maxsize,
3203 collisiondetection, collision_removal, attached_id, vertical,
3204 texture, id, animation, glow);
3209 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3211 // m_env will be NULL if the server is initializing
3213 throw ServerError("Can't delete particle spawners during initialisation!");
3215 session_t peer_id = PEER_ID_INEXISTENT;
3216 if (!playername.empty()) {
3217 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3220 peer_id = player->getPeerId();
3223 m_env->deleteParticleSpawner(id);
3224 SendDeleteParticleSpawner(peer_id, id);
3227 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3229 if(m_detached_inventories.count(name) > 0){
3230 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3231 delete m_detached_inventories[name];
3233 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3235 Inventory *inv = new Inventory(m_itemdef);
3237 m_detached_inventories[name] = inv;
3238 m_detached_inventories_player[name] = player;
3239 //TODO find a better way to do this
3240 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3244 // actions: time-reversed list
3245 // Return value: success/failure
3246 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3247 std::list<std::string> *log)
3249 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3250 ServerMap *map = (ServerMap*)(&m_env->getMap());
3252 // Fail if no actions to handle
3253 if(actions.empty()){
3254 log->push_back("Nothing to do.");
3261 for (const RollbackAction &action : actions) {
3263 bool success = action.applyRevert(map, this, this);
3266 std::ostringstream os;
3267 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3268 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3270 log->push_back(os.str());
3272 std::ostringstream os;
3273 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3274 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3276 log->push_back(os.str());
3280 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3281 <<" failed"<<std::endl;
3283 // Call it done if less than half failed
3284 return num_failed <= num_tried/2;
3287 // IGameDef interface
3289 IItemDefManager *Server::getItemDefManager()
3294 const NodeDefManager *Server::getNodeDefManager()
3299 ICraftDefManager *Server::getCraftDefManager()
3304 u16 Server::allocateUnknownNodeId(const std::string &name)
3306 return m_nodedef->allocateDummy(name);
3309 MtEventManager *Server::getEventManager()
3314 IWritableItemDefManager *Server::getWritableItemDefManager()
3319 NodeDefManager *Server::getWritableNodeDefManager()
3324 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3329 const ModSpec *Server::getModSpec(const std::string &modname) const
3331 std::vector<ModSpec>::const_iterator it;
3332 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3333 const ModSpec &mod = *it;
3334 if (mod.name == modname)
3340 void Server::getModNames(std::vector<std::string> &modlist)
3342 std::vector<ModSpec>::iterator it;
3343 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3344 modlist.push_back(it->name);
3347 std::string Server::getBuiltinLuaPath()
3349 return porting::path_share + DIR_DELIM + "builtin";
3352 std::string Server::getModStoragePath() const
3354 return m_path_world + DIR_DELIM + "mod_storage";
3357 v3f Server::findSpawnPos()
3359 ServerMap &map = m_env->getServerMap();
3361 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3362 return nodeposf * BS;
3365 bool is_good = false;
3366 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3367 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3369 // Try to find a good place a few times
3370 for(s32 i = 0; i < 4000 && !is_good; i++) {
3371 s32 range = MYMIN(1 + i, range_max);
3372 // We're going to try to throw the player to this position
3373 v2s16 nodepos2d = v2s16(
3374 -range + (myrand() % (range * 2)),
3375 -range + (myrand() % (range * 2)));
3377 // Get spawn level at point
3378 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3379 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3380 // the mapgen to signify an unsuitable spawn position
3381 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3384 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3387 for (s32 i = 0; i < 10; i++) {
3388 v3s16 blockpos = getNodeBlockPos(nodepos);
3389 map.emergeBlock(blockpos, true);
3390 content_t c = map.getNodeNoEx(nodepos).getContent();
3391 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3393 if (air_count >= 2) {
3394 nodeposf = intToFloat(nodepos, BS);
3395 // Don't spawn the player outside map boundaries
3396 if (objectpos_over_limit(nodeposf))
3409 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3411 m_shutdown_timer = delay;
3412 m_shutdown_msg = msg;
3413 m_shutdown_ask_reconnect = reconnect;
3415 if (delay == 0.0f) {
3416 // No delay, shutdown immediately
3417 m_shutdown_requested = true;
3418 // only print to the infostream, a chat message saying
3419 // "Server Shutting Down" is sent when the server destructs.
3420 infostream << "*** Immediate Server shutdown requested." << std::endl;
3421 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3422 // Negative delay, cancel shutdown if requested
3423 m_shutdown_timer = 0.0f;
3424 m_shutdown_msg = "";
3425 m_shutdown_ask_reconnect = false;
3426 m_shutdown_requested = false;
3427 std::wstringstream ws;
3429 ws << L"*** Server shutdown canceled.";
3431 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3432 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3433 } else if (delay > 0.0f) {
3434 // Positive delay, tell the clients when the server will shut down
3435 std::wstringstream ws;
3437 ws << L"*** Server shutting down in "
3438 << duration_to_string(myround(m_shutdown_timer)).c_str()
3441 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3442 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3446 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3449 Try to get an existing player
3451 RemotePlayer *player = m_env->getPlayer(name);
3453 // If player is already connected, cancel
3454 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3455 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3460 If player with the wanted peer_id already exists, cancel.
3462 if (m_env->getPlayer(peer_id)) {
3463 infostream<<"emergePlayer(): Player with wrong name but same"
3464 " peer_id already exists"<<std::endl;
3469 player = new RemotePlayer(name, idef());
3472 bool newplayer = false;
3475 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3477 // Complete init with server parts
3478 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3479 player->protocol_version = proto_version;
3483 m_script->on_newplayer(playersao);
3489 bool Server::registerModStorage(ModMetadata *storage)
3491 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3492 errorstream << "Unable to register same mod storage twice. Storage name: "
3493 << storage->getModName() << std::endl;
3497 m_mod_storages[storage->getModName()] = storage;
3501 void Server::unregisterModStorage(const std::string &name)
3503 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3504 if (it != m_mod_storages.end()) {
3505 // Save unconditionaly on unregistration
3506 it->second->save(getModStoragePath());
3507 m_mod_storages.erase(name);
3511 void dedicated_server_loop(Server &server, bool &kill)
3513 verbosestream<<"dedicated_server_loop()"<<std::endl;
3515 IntervalLimiter m_profiler_interval;
3517 static thread_local const float steplen =
3518 g_settings->getFloat("dedicated_server_step");
3519 static thread_local const float profiler_print_interval =
3520 g_settings->getFloat("profiler_print_interval");
3523 // This is kind of a hack but can be done like this
3524 // because server.step() is very light
3526 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3527 sleep_ms((int)(steplen*1000.0));
3529 server.step(steplen);
3531 if (server.getShutdownRequested() || kill)
3537 if (profiler_print_interval != 0) {
3538 if(m_profiler_interval.step(steplen, profiler_print_interval))
3540 infostream<<"Profiler:"<<std::endl;
3541 g_profiler->print(infostream);
3542 g_profiler->clear();
3547 infostream << "Dedicated server quitting" << std::endl;
3549 if (g_settings->getBool("server_announce"))
3550 ServerList::sendAnnounce(ServerList::AA_DELETE,
3551 server.m_bind_addr.getPort());
3560 bool Server::joinModChannel(const std::string &channel)
3562 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3563 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3566 bool Server::leaveModChannel(const std::string &channel)
3568 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3571 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3573 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3576 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3580 ModChannel* Server::getModChannel(const std::string &channel)
3582 return m_modchannel_mgr->getModChannel(channel);
3585 void Server::broadcastModChannelMessage(const std::string &channel,
3586 const std::string &message, session_t from_peer)
3588 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3592 if (message.size() > STRING_MAX_LEN) {
3593 warningstream << "ModChannel message too long, dropping before sending "
3594 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3595 << channel << ")" << std::endl;
3600 if (from_peer != PEER_ID_SERVER) {
3601 sender = getPlayerName(from_peer);
3604 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3605 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3606 resp_pkt << channel << sender << message;
3607 for (session_t peer_id : peers) {
3609 if (peer_id == from_peer)
3612 Send(peer_id, &resp_pkt);
3615 if (from_peer != PEER_ID_SERVER) {
3616 m_script->on_modchannel_message(channel, sender, message);