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 for (const auto &path : fs::GetRecursiveDirs(g_settings->get("texture_path")))
257 m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt");
259 m_nodedef->setNodeRegistrationStatus(true);
261 // Perform pending node name resolutions
262 m_nodedef->runNodeResolveCallbacks();
264 // unmap node names for connected nodeboxes
265 m_nodedef->mapNodeboxConnections();
267 // init the recipe hashes to speed up crafting
268 m_craftdef->initHashes(this);
270 // Initialize Environment
271 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
273 m_clients.setEnv(m_env);
275 if (!servermap->settings_mgr.makeMapgenParams())
276 FATAL_ERROR("Couldn't create any mapgen type");
278 // Initialize mapgens
279 m_emerge->initMapgens(servermap->getMapgenParams());
281 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
282 if (m_enable_rollback_recording) {
283 // Create rollback manager
284 m_rollback = new RollbackManager(m_path_world, this);
287 // Give environment reference to scripting api
288 m_script->initializeEnvironment(m_env);
290 // Register us to receive map edit events
291 servermap->addEventReceiver(this);
293 // If file exists, load environment metadata
294 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
295 infostream << "Server: Loading environment metadata" << std::endl;
298 m_env->loadDefaultMeta();
301 m_liquid_transform_every = g_settings->getFloat("liquid_update");
302 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
303 m_csm_flavour_limits = g_settings->getU64("csm_flavour_limits");
304 m_csm_noderange_limit = g_settings->getU32("csm_flavour_noderange_limit");
309 infostream << "Server destructing" << std::endl;
311 // Send shutdown message
312 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE,
313 L"*** Server shutting down"));
316 MutexAutoLock envlock(m_env_mutex);
318 infostream << "Server: Saving players" << std::endl;
319 m_env->saveLoadedPlayers();
321 infostream << "Server: Kicking players" << std::endl;
322 std::string kick_msg;
323 bool reconnect = false;
324 if (getShutdownRequested()) {
325 reconnect = m_shutdown_ask_reconnect;
326 kick_msg = m_shutdown_msg;
328 if (kick_msg.empty()) {
329 kick_msg = g_settings->get("kick_msg_shutdown");
331 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
332 kick_msg, reconnect);
335 // Do this before stopping the server in case mapgen callbacks need to access
336 // server-controlled resources (like ModStorages). Also do them before
337 // shutdown callbacks since they may modify state that is finalized in a
339 m_emerge->stopThreads();
342 MutexAutoLock envlock(m_env_mutex);
344 // Execute script shutdown hooks
345 infostream << "Executing shutdown hooks" << std::endl;
346 m_script->on_shutdown();
348 infostream << "Server: Saving environment metadata" << std::endl;
356 // Delete things in the reverse order of creation
366 // Deinitialize scripting
367 infostream << "Server: Deinitializing scripting" << std::endl;
370 // Delete detached inventories
371 for (auto &detached_inventory : m_detached_inventories) {
372 delete detached_inventory.second;
378 infostream << "Starting server on " << m_bind_addr.serializeString()
379 << "..." << std::endl;
381 // Stop thread if already running
384 // Initialize connection
385 m_con->SetTimeoutMs(30);
386 m_con->Serve(m_bind_addr);
391 // ASCII art for the win!
393 << " .__ __ __ " << std::endl
394 << " _____ |__| ____ _____/ |_ ____ _______/ |_ " << std::endl
395 << " / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\" << std::endl
396 << "| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | " << std::endl
397 << "|__|_| /__|___| /\\___ >__| \\___ >____ > |__| " << std::endl
398 << " \\/ \\/ \\/ \\/ \\/ " << std::endl;
399 actionstream << "World at [" << m_path_world << "]" << std::endl;
400 actionstream << "Server for gameid=\"" << m_gamespec.id
401 << "\" listening on " << m_bind_addr.serializeString() << ":"
402 << m_bind_addr.getPort() << "." << std::endl;
407 infostream<<"Server: Stopping and waiting threads"<<std::endl;
409 // Stop threads (set run=false first so both start stopping)
411 //m_emergethread.setRun(false);
413 //m_emergethread.stop();
415 infostream<<"Server: Threads stopped"<<std::endl;
418 void Server::step(float dtime)
424 MutexAutoLock lock(m_step_dtime_mutex);
425 m_step_dtime += dtime;
427 // Throw if fatal error occurred in thread
428 std::string async_err = m_async_fatal_error.get();
429 if (!async_err.empty()) {
430 if (!m_simple_singleplayer_mode) {
431 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
432 g_settings->get("kick_msg_crash"),
433 g_settings->getBool("ask_reconnect_on_crash"));
435 throw ServerError("AsyncErr: " + async_err);
439 void Server::AsyncRunStep(bool initial_step)
441 g_profiler->add("Server::AsyncRunStep (num)", 1);
445 MutexAutoLock lock1(m_step_dtime_mutex);
446 dtime = m_step_dtime;
450 // Send blocks to clients
454 if((dtime < 0.001) && !initial_step)
457 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
459 //infostream<<"Server steps "<<dtime<<std::endl;
460 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
463 MutexAutoLock lock1(m_step_dtime_mutex);
464 m_step_dtime -= dtime;
471 m_uptime.set(m_uptime.get() + dtime);
477 Update time of day and overall game time
479 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
482 Send to clients at constant intervals
485 m_time_of_day_send_timer -= dtime;
486 if(m_time_of_day_send_timer < 0.0) {
487 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
488 u16 time = m_env->getTimeOfDay();
489 float time_speed = g_settings->getFloat("time_speed");
490 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
494 MutexAutoLock lock(m_env_mutex);
495 // Figure out and report maximum lag to environment
496 float max_lag = m_env->getMaxLagEstimate();
497 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
499 if(dtime > 0.1 && dtime > max_lag * 2.0)
500 infostream<<"Server: Maximum lag peaked to "<<dtime
504 m_env->reportMaxLagEstimate(max_lag);
506 ScopeProfiler sp(g_profiler, "SEnv step");
507 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
511 static const float map_timer_and_unload_dtime = 2.92;
512 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
514 MutexAutoLock lock(m_env_mutex);
515 // Run Map's timers and unload unused data
516 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
517 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
518 g_settings->getFloat("server_unload_unused_data_timeout"),
523 Listen to the admin chat, if available
526 if (!m_admin_chat->command_queue.empty()) {
527 MutexAutoLock lock(m_env_mutex);
528 while (!m_admin_chat->command_queue.empty()) {
529 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
530 handleChatInterfaceEvent(evt);
534 m_admin_chat->outgoing_queue.push_back(
535 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
542 /* Transform liquids */
543 m_liquid_transform_timer += dtime;
544 if(m_liquid_transform_timer >= m_liquid_transform_every)
546 m_liquid_transform_timer -= m_liquid_transform_every;
548 MutexAutoLock lock(m_env_mutex);
550 ScopeProfiler sp(g_profiler, "Server: liquid transform");
552 std::map<v3s16, MapBlock*> modified_blocks;
553 m_env->getMap().transformLiquids(modified_blocks, m_env);
556 Set the modified blocks unsent for all the clients
558 if(!modified_blocks.empty())
560 SetBlocksNotSent(modified_blocks);
563 m_clients.step(dtime);
565 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
567 // send masterserver announce
569 float &counter = m_masterserver_timer;
570 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
571 g_settings->getBool("server_announce")) {
572 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
573 ServerList::AA_START,
574 m_bind_addr.getPort(),
575 m_clients.getPlayerNames(),
577 m_env->getGameTime(),
580 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
590 Check added and deleted active objects
593 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
594 MutexAutoLock envlock(m_env_mutex);
597 const RemoteClientMap &clients = m_clients.getClientList();
598 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
600 // Radius inside which objects are active
601 static thread_local const s16 radius =
602 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
604 // Radius inside which players are active
605 static thread_local const bool is_transfer_limited =
606 g_settings->exists("unlimited_player_transfer_distance") &&
607 !g_settings->getBool("unlimited_player_transfer_distance");
608 static thread_local const s16 player_transfer_dist =
609 g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
610 s16 player_radius = player_transfer_dist;
611 if (player_radius == 0 && is_transfer_limited)
612 player_radius = radius;
614 for (const auto &client_it : clients) {
615 RemoteClient *client = client_it.second;
617 // If definitions and textures have not been sent, don't
618 // send objects either
619 if (client->getState() < CS_DefinitionsSent)
622 RemotePlayer *player = m_env->getPlayer(client->peer_id);
624 // This can happen if the client timeouts somehow
628 PlayerSAO *playersao = player->getPlayerSAO();
632 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
633 if (my_radius <= 0) my_radius = radius;
634 //infostream << "Server: Active Radius " << my_radius << std::endl;
636 std::queue<u16> removed_objects;
637 std::queue<u16> added_objects;
638 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
639 client->m_known_objects, removed_objects);
640 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
641 client->m_known_objects, added_objects);
643 // Ignore if nothing happened
644 if (removed_objects.empty() && added_objects.empty()) {
648 std::string data_buffer;
652 // Handle removed objects
653 writeU16((u8*)buf, removed_objects.size());
654 data_buffer.append(buf, 2);
655 while (!removed_objects.empty()) {
657 u16 id = removed_objects.front();
658 ServerActiveObject* obj = m_env->getActiveObject(id);
660 // Add to data buffer for sending
661 writeU16((u8*)buf, id);
662 data_buffer.append(buf, 2);
664 // Remove from known objects
665 client->m_known_objects.erase(id);
667 if(obj && obj->m_known_by_count > 0)
668 obj->m_known_by_count--;
669 removed_objects.pop();
672 // Handle added objects
673 writeU16((u8*)buf, added_objects.size());
674 data_buffer.append(buf, 2);
675 while (!added_objects.empty()) {
677 u16 id = added_objects.front();
678 ServerActiveObject* obj = m_env->getActiveObject(id);
681 u8 type = ACTIVEOBJECT_TYPE_INVALID;
683 warningstream << FUNCTION_NAME << ": NULL object" << std::endl;
685 type = obj->getSendType();
687 // Add to data buffer for sending
688 writeU16((u8*)buf, id);
689 data_buffer.append(buf, 2);
690 writeU8((u8*)buf, type);
691 data_buffer.append(buf, 1);
694 data_buffer.append(serializeLongString(
695 obj->getClientInitializationData(client->net_proto_version)));
697 data_buffer.append(serializeLongString(""));
699 // Add to known objects
700 client->m_known_objects.insert(id);
703 obj->m_known_by_count++;
708 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
709 verbosestream << "Server: Sent object remove/add: "
710 << removed_objects.size() << " removed, "
711 << added_objects.size() << " added, "
712 << "packet size is " << pktSize << std::endl;
716 m_mod_storage_save_timer -= dtime;
717 if (m_mod_storage_save_timer <= 0.0f) {
718 infostream << "Saving registered mod storages." << std::endl;
719 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
720 for (std::unordered_map<std::string, ModMetadata *>::const_iterator
721 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
722 if (it->second->isModified()) {
723 it->second->save(getModStoragePath());
733 MutexAutoLock envlock(m_env_mutex);
734 ScopeProfiler sp(g_profiler, "Server: sending object messages");
737 // Value = data sent by object
738 std::unordered_map<u16, std::vector<ActiveObjectMessage>*> buffered_messages;
740 // Get active object messages from environment
742 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
746 std::vector<ActiveObjectMessage>* message_list = nullptr;
747 std::unordered_map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
748 n = buffered_messages.find(aom.id);
749 if (n == buffered_messages.end()) {
750 message_list = new std::vector<ActiveObjectMessage>;
751 buffered_messages[aom.id] = message_list;
754 message_list = n->second;
756 message_list->push_back(aom);
760 const RemoteClientMap &clients = m_clients.getClientList();
761 // Route data to every client
762 for (const auto &client_it : clients) {
763 RemoteClient *client = client_it.second;
764 std::string reliable_data;
765 std::string unreliable_data;
766 // Go through all objects in message buffer
767 for (const auto &buffered_message : buffered_messages) {
768 // If object is not known by client, skip it
769 u16 id = buffered_message.first;
770 if (client->m_known_objects.find(id) == client->m_known_objects.end())
773 // Get message list of object
774 std::vector<ActiveObjectMessage>* list = buffered_message.second;
775 // Go through every message
776 for (const ActiveObjectMessage &aom : *list) {
777 // Compose the full new data with header
778 std::string new_data;
781 writeU16((u8*)&buf[0], aom.id);
782 new_data.append(buf, 2);
784 new_data += serializeString(aom.datastring);
785 // Add data to buffer
787 reliable_data += new_data;
789 unreliable_data += new_data;
793 reliable_data and unreliable_data are now ready.
796 if (!reliable_data.empty()) {
797 SendActiveObjectMessages(client->peer_id, reliable_data);
800 if (!unreliable_data.empty()) {
801 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
806 // Clear buffered_messages
807 for (auto &buffered_message : buffered_messages) {
808 delete buffered_message.second;
813 Send queued-for-sending map edit events.
816 // We will be accessing the environment
817 MutexAutoLock lock(m_env_mutex);
819 // Don't send too many at a time
822 // Single change sending is disabled if queue size is not small
823 bool disable_single_change_sending = false;
824 if(m_unsent_map_edit_queue.size() >= 4)
825 disable_single_change_sending = true;
827 int event_count = m_unsent_map_edit_queue.size();
829 // We'll log the amount of each
832 while (!m_unsent_map_edit_queue.empty()) {
833 MapEditEvent* event = m_unsent_map_edit_queue.front();
834 m_unsent_map_edit_queue.pop();
836 // Players far away from the change are stored here.
837 // Instead of sending the changes, MapBlocks are set not sent
839 std::vector<u16> far_players;
841 switch (event->type) {
844 prof.add("MEET_ADDNODE", 1);
845 sendAddNode(event->p, event->n, event->already_known_by_peer,
846 &far_players, disable_single_change_sending ? 5 : 30,
847 event->type == MEET_ADDNODE);
849 case MEET_REMOVENODE:
850 prof.add("MEET_REMOVENODE", 1);
851 sendRemoveNode(event->p, event->already_known_by_peer,
852 &far_players, disable_single_change_sending ? 5 : 30);
854 case MEET_BLOCK_NODE_METADATA_CHANGED:
855 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
856 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
857 setBlockNotSent(event->p);
860 infostream << "Server: MEET_OTHER" << std::endl;
861 prof.add("MEET_OTHER", 1);
862 for (const v3s16 &modified_block : event->modified_blocks) {
863 setBlockNotSent(modified_block);
867 prof.add("unknown", 1);
868 warningstream << "Server: Unknown MapEditEvent "
869 << ((u32)event->type) << std::endl;
874 Set blocks not sent to far players
876 if (!far_players.empty()) {
877 // Convert list format to that wanted by SetBlocksNotSent
878 std::map<v3s16, MapBlock*> modified_blocks2;
879 for (const v3s16 &modified_block : event->modified_blocks) {
880 modified_blocks2[modified_block] =
881 m_env->getMap().getBlockNoCreateNoEx(modified_block);
884 // Set blocks not sent
885 for (const u16 far_player : far_players) {
886 if (RemoteClient *client = getClient(far_player))
887 client->SetBlocksNotSent(modified_blocks2);
894 if (event_count >= 5) {
895 infostream << "Server: MapEditEvents:" << std::endl;
896 prof.print(infostream);
897 } else if (event_count != 0) {
898 verbosestream << "Server: MapEditEvents:" << std::endl;
899 prof.print(verbosestream);
905 Trigger emergethread (it somehow gets to a non-triggered but
906 bysy state sometimes)
909 float &counter = m_emergethread_trigger_timer;
911 if (counter >= 2.0) {
914 m_emerge->startThreads();
918 // Save map, players and auth stuff
920 float &counter = m_savemap_timer;
922 static thread_local const float save_interval =
923 g_settings->getFloat("server_map_save_interval");
924 if (counter >= save_interval) {
926 MutexAutoLock lock(m_env_mutex);
928 ScopeProfiler sp(g_profiler, "Server: saving stuff");
931 if (m_banmanager->isModified()) {
932 m_banmanager->save();
935 // Save changed parts of map
936 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
939 m_env->saveLoadedPlayers();
941 // Save environment metadata
947 static const float shutdown_msg_times[] =
949 1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 45, 60, 120, 180, 300, 600, 1200, 1800, 3600
952 if (m_shutdown_timer > 0.0f) {
953 // Automated messages
954 if (m_shutdown_timer < shutdown_msg_times[ARRLEN(shutdown_msg_times) - 1]) {
955 for (u16 i = 0; i < ARRLEN(shutdown_msg_times) - 1; i++) {
956 // If shutdown timer matches an automessage, shot it
957 if (m_shutdown_timer > shutdown_msg_times[i] &&
958 m_shutdown_timer - dtime < shutdown_msg_times[i]) {
959 std::wstringstream ws;
961 ws << L"*** Server shutting down in "
962 << duration_to_string(myround(m_shutdown_timer - dtime)).c_str()
965 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
966 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
972 m_shutdown_timer -= dtime;
973 if (m_shutdown_timer < 0.0f) {
974 m_shutdown_timer = 0.0f;
975 m_shutdown_requested = true;
980 void Server::Receive()
985 m_con->Receive(&pkt);
986 peer_id = pkt.getPeerId();
988 } catch (const con::InvalidIncomingDataException &e) {
989 infostream << "Server::Receive(): InvalidIncomingDataException: what()="
990 << e.what() << std::endl;
991 } catch (const SerializationError &e) {
992 infostream << "Server::Receive(): SerializationError: what()="
993 << e.what() << std::endl;
994 } catch (const ClientStateError &e) {
995 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
996 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
997 L"Try reconnecting or updating your client");
998 } catch (const con::PeerNotFoundException &e) {
1003 PlayerSAO* Server::StageTwoClientInit(session_t peer_id)
1005 std::string playername;
1006 PlayerSAO *playersao = NULL;
1009 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1011 playername = client->getName();
1012 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1014 } catch (std::exception &e) {
1020 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1022 // If failed, cancel
1023 if (!playersao || !player) {
1024 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
1025 actionstream << "Server: Failed to emerge player \"" << playername
1026 << "\" (player allocated to an another client)" << std::endl;
1027 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1028 L"name. If your client closed unexpectedly, try again in "
1031 errorstream << "Server: " << playername << ": Failed to emerge player"
1033 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1039 Send complete position information
1041 SendMovePlayer(peer_id);
1044 SendPlayerPrivileges(peer_id);
1046 // Send inventory formspec
1047 SendPlayerInventoryFormspec(peer_id);
1050 SendInventory(playersao);
1052 // Send HP or death screen
1053 if (playersao->isDead())
1054 SendDeathscreen(peer_id, false, v3f(0,0,0));
1056 SendPlayerHPOrDie(playersao);
1059 SendPlayerBreath(playersao);
1061 // Note things in chat if not in simple singleplayer mode
1062 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1063 // Send information about server to player in chat
1064 SendChatMessage(peer_id, ChatMessage(CHATMESSAGE_TYPE_SYSTEM, getStatusString()));
1066 Address addr = getPeerAddress(player->getPeerId());
1067 std::string ip_str = addr.serializeString();
1068 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1073 const std::vector<std::string> &names = m_clients.getPlayerNames();
1075 actionstream << player->getName() << " joins game. List of players: ";
1077 for (const std::string &name : names) {
1078 actionstream << name << " ";
1081 actionstream << player->getName() <<std::endl;
1086 inline void Server::handleCommand(NetworkPacket* pkt)
1088 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1089 (this->*opHandle.handler)(pkt);
1092 void Server::ProcessData(NetworkPacket *pkt)
1094 // Environment is locked first.
1095 MutexAutoLock envlock(m_env_mutex);
1097 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1098 u32 peer_id = pkt->getPeerId();
1101 Address address = getPeerAddress(peer_id);
1102 std::string addr_s = address.serializeString();
1104 if(m_banmanager->isIpBanned(addr_s)) {
1105 std::string ban_name = m_banmanager->getBanName(addr_s);
1106 infostream << "Server: A banned client tried to connect from "
1107 << addr_s << "; banned name was "
1108 << ban_name << std::endl;
1109 // This actually doesn't seem to transfer to the client
1110 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1111 + utf8_to_wide(ban_name));
1115 catch(con::PeerNotFoundException &e) {
1117 * no peer for this packet found
1118 * most common reason is peer timeout, e.g. peer didn't
1119 * respond for some time, your server was overloaded or
1122 infostream << "Server::ProcessData(): Canceling: peer "
1123 << peer_id << " not found" << std::endl;
1128 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1130 // Command must be handled into ToServerCommandHandler
1131 if (command >= TOSERVER_NUM_MSG_TYPES) {
1132 infostream << "Server: Ignoring unknown command "
1133 << command << std::endl;
1137 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1142 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1144 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1145 errorstream << "Server::ProcessData(): Cancelling: Peer"
1146 " serialization format invalid or not initialized."
1147 " Skipping incoming command=" << command << std::endl;
1151 /* Handle commands related to client startup */
1152 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1157 if (m_clients.getClientState(peer_id) < CS_Active) {
1158 if (command == TOSERVER_PLAYERPOS) return;
1160 errorstream << "Got packet command: " << command << " for peer id "
1161 << peer_id << " but client isn't active yet. Dropping packet "
1167 } catch (SendFailedException &e) {
1168 errorstream << "Server::ProcessData(): SendFailedException: "
1169 << "what=" << e.what()
1171 } catch (PacketError &e) {
1172 actionstream << "Server::ProcessData(): PacketError: "
1173 << "what=" << e.what()
1178 void Server::setTimeOfDay(u32 time)
1180 m_env->setTimeOfDay(time);
1181 m_time_of_day_send_timer = 0;
1184 void Server::onMapEditEvent(MapEditEvent *event)
1186 if(m_ignore_map_edit_events)
1188 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1190 MapEditEvent *e = event->clone();
1191 m_unsent_map_edit_queue.push(e);
1194 Inventory* Server::getInventory(const InventoryLocation &loc)
1197 case InventoryLocation::UNDEFINED:
1198 case InventoryLocation::CURRENT_PLAYER:
1200 case InventoryLocation::PLAYER:
1202 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1205 PlayerSAO *playersao = player->getPlayerSAO();
1208 return playersao->getInventory();
1211 case InventoryLocation::NODEMETA:
1213 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1216 return meta->getInventory();
1219 case InventoryLocation::DETACHED:
1221 if(m_detached_inventories.count(loc.name) == 0)
1223 return m_detached_inventories[loc.name];
1227 sanity_check(false); // abort
1232 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1235 case InventoryLocation::UNDEFINED:
1237 case InventoryLocation::PLAYER:
1242 RemotePlayer *player = m_env->getPlayer(loc.name.c_str());
1247 PlayerSAO *playersao = player->getPlayerSAO();
1251 SendInventory(playersao);
1254 case InventoryLocation::NODEMETA:
1256 v3s16 blockpos = getNodeBlockPos(loc.p);
1258 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1260 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1262 setBlockNotSent(blockpos);
1265 case InventoryLocation::DETACHED:
1267 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1271 sanity_check(false); // abort
1276 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1278 std::vector<session_t> clients = m_clients.getClientIDs();
1280 // Set the modified blocks unsent for all the clients
1281 for (const session_t client_id : clients) {
1282 if (RemoteClient *client = m_clients.lockedGetClientNoEx(client_id))
1283 client->SetBlocksNotSent(block);
1288 void Server::peerAdded(con::Peer *peer)
1290 verbosestream<<"Server::peerAdded(): peer->id="
1291 <<peer->id<<std::endl;
1293 m_peer_change_queue.push(con::PeerChange(con::PEER_ADDED, peer->id, false));
1296 void Server::deletingPeer(con::Peer *peer, bool timeout)
1298 verbosestream<<"Server::deletingPeer(): peer->id="
1299 <<peer->id<<", timeout="<<timeout<<std::endl;
1301 m_clients.event(peer->id, CSE_Disconnect);
1302 m_peer_change_queue.push(con::PeerChange(con::PEER_REMOVED, peer->id, timeout));
1305 bool Server::getClientConInfo(session_t peer_id, con::rtt_stat_type type, float* retval)
1307 *retval = m_con->getPeerStat(peer_id,type);
1308 return *retval != -1;
1311 bool Server::getClientInfo(
1320 std::string* vers_string
1323 *state = m_clients.getClientState(peer_id);
1325 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1332 *uptime = client->uptime();
1333 *ser_vers = client->serialization_version;
1334 *prot_vers = client->net_proto_version;
1336 *major = client->getMajor();
1337 *minor = client->getMinor();
1338 *patch = client->getPatch();
1339 *vers_string = client->getPatch();
1346 void Server::handlePeerChanges()
1348 while(!m_peer_change_queue.empty())
1350 con::PeerChange c = m_peer_change_queue.front();
1351 m_peer_change_queue.pop();
1353 verbosestream<<"Server: Handling peer change: "
1354 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1359 case con::PEER_ADDED:
1360 m_clients.CreateClient(c.peer_id);
1363 case con::PEER_REMOVED:
1364 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1368 FATAL_ERROR("Invalid peer change event received!");
1374 void Server::printToConsoleOnly(const std::string &text)
1377 m_admin_chat->outgoing_queue.push_back(
1378 new ChatEventChat("", utf8_to_wide(text)));
1380 std::cout << text << std::endl;
1384 void Server::Send(NetworkPacket *pkt)
1386 Send(pkt->getPeerId(), pkt);
1389 void Server::Send(session_t peer_id, NetworkPacket *pkt)
1391 m_clients.send(peer_id,
1392 clientCommandFactoryTable[pkt->getCommand()].channel,
1394 clientCommandFactoryTable[pkt->getCommand()].reliable);
1397 void Server::SendMovement(session_t peer_id)
1399 std::ostringstream os(std::ios_base::binary);
1401 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1403 pkt << g_settings->getFloat("movement_acceleration_default");
1404 pkt << g_settings->getFloat("movement_acceleration_air");
1405 pkt << g_settings->getFloat("movement_acceleration_fast");
1406 pkt << g_settings->getFloat("movement_speed_walk");
1407 pkt << g_settings->getFloat("movement_speed_crouch");
1408 pkt << g_settings->getFloat("movement_speed_fast");
1409 pkt << g_settings->getFloat("movement_speed_climb");
1410 pkt << g_settings->getFloat("movement_speed_jump");
1411 pkt << g_settings->getFloat("movement_liquid_fluidity");
1412 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1413 pkt << g_settings->getFloat("movement_liquid_sink");
1414 pkt << g_settings->getFloat("movement_gravity");
1419 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1421 if (!g_settings->getBool("enable_damage"))
1424 session_t peer_id = playersao->getPeerID();
1425 bool is_alive = playersao->getHP() > 0;
1428 SendPlayerHP(peer_id);
1433 void Server::SendHP(session_t peer_id, u16 hp)
1435 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1440 void Server::SendBreath(session_t peer_id, u16 breath)
1442 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1443 pkt << (u16) breath;
1447 void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason,
1448 const std::string &custom_reason, bool reconnect)
1450 assert(reason < SERVER_ACCESSDENIED_MAX);
1452 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1454 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1455 pkt << custom_reason;
1456 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1457 reason == SERVER_ACCESSDENIED_CRASH)
1458 pkt << custom_reason << (u8)reconnect;
1462 void Server::SendAccessDenied_Legacy(session_t peer_id,const std::wstring &reason)
1464 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1469 void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target,
1470 v3f camera_point_target)
1472 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1473 pkt << set_camera_point_target << camera_point_target;
1477 void Server::SendItemDef(session_t peer_id,
1478 IItemDefManager *itemdef, u16 protocol_version)
1480 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1484 u32 length of the next item
1485 zlib-compressed serialized ItemDefManager
1487 std::ostringstream tmp_os(std::ios::binary);
1488 itemdef->serialize(tmp_os, protocol_version);
1489 std::ostringstream tmp_os2(std::ios::binary);
1490 compressZlib(tmp_os.str(), tmp_os2);
1491 pkt.putLongString(tmp_os2.str());
1494 verbosestream << "Server: Sending item definitions to id(" << peer_id
1495 << "): size=" << pkt.getSize() << std::endl;
1500 void Server::SendNodeDef(session_t peer_id,
1501 INodeDefManager *nodedef, u16 protocol_version)
1503 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1507 u32 length of the next item
1508 zlib-compressed serialized NodeDefManager
1510 std::ostringstream tmp_os(std::ios::binary);
1511 nodedef->serialize(tmp_os, protocol_version);
1512 std::ostringstream tmp_os2(std::ios::binary);
1513 compressZlib(tmp_os.str(), tmp_os2);
1515 pkt.putLongString(tmp_os2.str());
1518 verbosestream << "Server: Sending node definitions to id(" << peer_id
1519 << "): size=" << pkt.getSize() << std::endl;
1525 Non-static send methods
1528 void Server::SendInventory(PlayerSAO* playerSAO)
1530 UpdateCrafting(playerSAO->getPlayer());
1536 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1538 std::ostringstream os;
1539 playerSAO->getInventory()->serialize(os);
1541 std::string s = os.str();
1543 pkt.putRawString(s.c_str(), s.size());
1547 void Server::SendChatMessage(session_t peer_id, const ChatMessage &message)
1549 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1551 u8 type = message.type;
1552 pkt << version << type << std::wstring(L"") << message.message << message.timestamp;
1554 if (peer_id != PEER_ID_INEXISTENT) {
1555 RemotePlayer *player = m_env->getPlayer(peer_id);
1561 m_clients.sendToAll(&pkt);
1565 void Server::SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
1566 const std::string &formname)
1568 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1569 if (formspec.empty()){
1570 //the client should close the formspec
1571 pkt.putLongString("");
1573 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1580 // Spawns a particle on peer with peer_id
1581 void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version,
1582 v3f pos, v3f velocity, v3f acceleration,
1583 float expirationtime, float size, bool collisiondetection,
1584 bool collision_removal,
1585 bool vertical, const std::string &texture,
1586 const struct TileAnimationParams &animation, u8 glow)
1588 static thread_local const float radius =
1589 g_settings->getS16("max_block_send_distance") * MAP_BLOCKSIZE * BS;
1591 if (peer_id == PEER_ID_INEXISTENT) {
1592 std::vector<session_t> clients = m_clients.getClientIDs();
1594 for (const session_t client_id : clients) {
1595 RemotePlayer *player = m_env->getPlayer(client_id);
1599 PlayerSAO *sao = player->getPlayerSAO();
1603 // Do not send to distant clients
1604 if (sao->getBasePosition().getDistanceFrom(pos * BS) > radius)
1607 SendSpawnParticle(client_id, player->protocol_version,
1608 pos, velocity, acceleration,
1609 expirationtime, size, collisiondetection,
1610 collision_removal, vertical, texture, animation, glow);
1615 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1617 pkt << pos << velocity << acceleration << expirationtime
1618 << size << collisiondetection;
1619 pkt.putLongString(texture);
1621 pkt << collision_removal;
1622 // This is horrible but required (why are there two ways to serialize pkts?)
1623 std::ostringstream os(std::ios_base::binary);
1624 animation.serialize(os, protocol_version);
1625 pkt.putRawString(os.str());
1631 // Adds a ParticleSpawner on peer with peer_id
1632 void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version,
1633 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1634 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1635 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1636 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1637 const struct TileAnimationParams &animation, u8 glow)
1639 if (peer_id == PEER_ID_INEXISTENT) {
1640 // This sucks and should be replaced:
1641 std::vector<session_t> clients = m_clients.getClientIDs();
1642 for (const session_t client_id : clients) {
1643 RemotePlayer *player = m_env->getPlayer(client_id);
1646 SendAddParticleSpawner(client_id, player->protocol_version,
1647 amount, spawntime, minpos, maxpos,
1648 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1649 minsize, maxsize, collisiondetection, collision_removal,
1650 attached_id, vertical, texture, id, animation, glow);
1655 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1657 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1658 << minacc << maxacc << minexptime << maxexptime << minsize
1659 << maxsize << collisiondetection;
1661 pkt.putLongString(texture);
1663 pkt << id << vertical;
1664 pkt << collision_removal;
1666 // This is horrible but required
1667 std::ostringstream os(std::ios_base::binary);
1668 animation.serialize(os, protocol_version);
1669 pkt.putRawString(os.str());
1675 void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id)
1677 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER, 4, peer_id);
1679 // Ugly error in this packet
1682 if (peer_id != PEER_ID_INEXISTENT)
1685 m_clients.sendToAll(&pkt);
1689 void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form)
1691 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1693 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1694 << form->text << form->number << form->item << form->dir
1695 << form->align << form->offset << form->world_pos << form->size;
1700 void Server::SendHUDRemove(session_t peer_id, u32 id)
1702 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1707 void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value)
1709 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1710 pkt << id << (u8) stat;
1714 case HUD_STAT_SCALE:
1715 case HUD_STAT_ALIGN:
1716 case HUD_STAT_OFFSET:
1717 pkt << *(v2f *) value;
1721 pkt << *(std::string *) value;
1723 case HUD_STAT_WORLD_POS:
1724 pkt << *(v3f *) value;
1727 pkt << *(v2s32 *) value;
1729 case HUD_STAT_NUMBER:
1733 pkt << *(u32 *) value;
1740 void Server::SendHUDSetFlags(session_t peer_id, u32 flags, u32 mask)
1742 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1744 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1746 pkt << flags << mask;
1751 void Server::SendHUDSetParam(session_t peer_id, u16 param, const std::string &value)
1753 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1754 pkt << param << value;
1758 void Server::SendSetSky(session_t peer_id, const video::SColor &bgcolor,
1759 const std::string &type, const std::vector<std::string> ¶ms,
1762 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1763 pkt << bgcolor << type << (u16) params.size();
1765 for (const std::string ¶m : params)
1773 void Server::SendCloudParams(session_t peer_id, float density,
1774 const video::SColor &color_bright,
1775 const video::SColor &color_ambient,
1780 NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id);
1781 pkt << density << color_bright << color_ambient
1782 << height << thickness << speed;
1787 void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override,
1790 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1793 pkt << do_override << (u16) (ratio * 65535);
1798 void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed)
1800 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1801 pkt << time << time_speed;
1803 if (peer_id == PEER_ID_INEXISTENT) {
1804 m_clients.sendToAll(&pkt);
1811 void Server::SendPlayerHP(session_t peer_id)
1813 PlayerSAO *playersao = getPlayerSAO(peer_id);
1814 // In some rare case if the player is disconnected
1815 // while Lua call l_punch, for example, this can be NULL
1819 SendHP(peer_id, playersao->getHP());
1820 m_script->player_event(playersao,"health_changed");
1822 // Send to other clients
1823 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1824 ActiveObjectMessage aom(playersao->getId(), true, str);
1825 playersao->m_messages_out.push(aom);
1828 void Server::SendPlayerBreath(PlayerSAO *sao)
1832 m_script->player_event(sao, "breath_changed");
1833 SendBreath(sao->getPeerID(), sao->getBreath());
1836 void Server::SendMovePlayer(session_t peer_id)
1838 RemotePlayer *player = m_env->getPlayer(peer_id);
1840 PlayerSAO *sao = player->getPlayerSAO();
1843 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1844 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1847 v3f pos = sao->getBasePosition();
1848 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1849 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1850 << " pitch=" << sao->getPitch()
1851 << " yaw=" << sao->getYaw()
1858 void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames[4],
1859 f32 animation_speed)
1861 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1864 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1865 << animation_frames[3] << animation_speed;
1870 void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third)
1872 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1873 pkt << first << third;
1877 void Server::SendPlayerPrivileges(session_t peer_id)
1879 RemotePlayer *player = m_env->getPlayer(peer_id);
1881 if(player->getPeerId() == PEER_ID_INEXISTENT)
1884 std::set<std::string> privs;
1885 m_script->getAuth(player->getName(), NULL, &privs);
1887 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1888 pkt << (u16) privs.size();
1890 for (const std::string &priv : privs) {
1897 void Server::SendPlayerInventoryFormspec(session_t peer_id)
1899 RemotePlayer *player = m_env->getPlayer(peer_id);
1901 if(player->getPeerId() == PEER_ID_INEXISTENT)
1904 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1905 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1909 u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
1911 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1912 pkt.putRawString(datas.c_str(), datas.size());
1914 return pkt.getSize();
1917 void Server::SendActiveObjectMessages(session_t peer_id, const std::string &datas,
1920 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1921 datas.size(), peer_id);
1923 pkt.putRawString(datas.c_str(), datas.size());
1925 m_clients.send(pkt.getPeerId(),
1926 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1930 void Server::SendCSMFlavourLimits(session_t peer_id)
1932 NetworkPacket pkt(TOCLIENT_CSM_FLAVOUR_LIMITS,
1933 sizeof(m_csm_flavour_limits) + sizeof(m_csm_noderange_limit), peer_id);
1934 pkt << m_csm_flavour_limits << m_csm_noderange_limit;
1938 s32 Server::playSound(const SimpleSoundSpec &spec,
1939 const ServerSoundParams ¶ms)
1941 // Find out initial position of sound
1942 bool pos_exists = false;
1943 v3f pos = params.getPos(m_env, &pos_exists);
1944 // If position is not found while it should be, cancel sound
1945 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1948 // Filter destination clients
1949 std::vector<session_t> dst_clients;
1950 if(!params.to_player.empty()) {
1951 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1953 infostream<<"Server::playSound: Player \""<<params.to_player
1954 <<"\" not found"<<std::endl;
1957 if (player->getPeerId() == PEER_ID_INEXISTENT) {
1958 infostream<<"Server::playSound: Player \""<<params.to_player
1959 <<"\" not connected"<<std::endl;
1962 dst_clients.push_back(player->getPeerId());
1964 std::vector<session_t> clients = m_clients.getClientIDs();
1966 for (const session_t client_id : clients) {
1967 RemotePlayer *player = m_env->getPlayer(client_id);
1971 PlayerSAO *sao = player->getPlayerSAO();
1976 if(sao->getBasePosition().getDistanceFrom(pos) >
1977 params.max_hear_distance)
1980 dst_clients.push_back(client_id);
1984 if(dst_clients.empty())
1988 s32 id = m_next_sound_id++;
1989 // The sound will exist as a reference in m_playing_sounds
1990 m_playing_sounds[id] = ServerPlayingSound();
1991 ServerPlayingSound &psound = m_playing_sounds[id];
1992 psound.params = params;
1995 float gain = params.gain * spec.gain;
1996 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
1997 pkt << id << spec.name << gain
1998 << (u8) params.type << pos << params.object
1999 << params.loop << params.fade << params.pitch;
2001 // Backwards compability
2002 bool play_sound = gain > 0;
2004 for (const u16 dst_client : dst_clients) {
2005 if (play_sound || m_clients.getProtocolVersion(dst_client) >= 32) {
2006 psound.clients.insert(dst_client);
2007 m_clients.send(dst_client, 0, &pkt, true);
2012 void Server::stopSound(s32 handle)
2014 // Get sound reference
2015 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2016 m_playing_sounds.find(handle);
2017 if (i == m_playing_sounds.end())
2019 ServerPlayingSound &psound = i->second;
2021 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2024 for (std::unordered_set<session_t>::const_iterator si = psound.clients.begin();
2025 si != psound.clients.end(); ++si) {
2027 m_clients.send(*si, 0, &pkt, true);
2029 // Remove sound reference
2030 m_playing_sounds.erase(i);
2033 void Server::fadeSound(s32 handle, float step, float gain)
2035 // Get sound reference
2036 std::unordered_map<s32, ServerPlayingSound>::iterator i =
2037 m_playing_sounds.find(handle);
2038 if (i == m_playing_sounds.end())
2041 ServerPlayingSound &psound = i->second;
2042 psound.params.gain = gain;
2044 NetworkPacket pkt(TOCLIENT_FADE_SOUND, 4);
2045 pkt << handle << step << gain;
2047 // Backwards compability
2048 bool play_sound = gain > 0;
2049 ServerPlayingSound compat_psound = psound;
2050 compat_psound.clients.clear();
2052 NetworkPacket compat_pkt(TOCLIENT_STOP_SOUND, 4);
2053 compat_pkt << handle;
2055 for (std::unordered_set<u16>::iterator it = psound.clients.begin();
2056 it != psound.clients.end();) {
2057 if (m_clients.getProtocolVersion(*it) >= 32) {
2059 m_clients.send(*it, 0, &pkt, true);
2062 compat_psound.clients.insert(*it);
2064 m_clients.send(*it, 0, &compat_pkt, true);
2065 psound.clients.erase(it++);
2069 // Remove sound reference
2070 if (!play_sound || psound.clients.empty())
2071 m_playing_sounds.erase(i);
2073 if (play_sound && !compat_psound.clients.empty()) {
2074 // Play new sound volume on older clients
2075 playSound(compat_psound.spec, compat_psound.params);
2079 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2080 std::vector<u16> *far_players, float far_d_nodes)
2082 float maxd = far_d_nodes*BS;
2083 v3f p_f = intToFloat(p, BS);
2085 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2088 std::vector<session_t> clients = m_clients.getClientIDs();
2089 for (session_t client_id : clients) {
2092 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2093 PlayerSAO *sao = player->getPlayerSAO();
2097 // If player is far away, only set modified blocks not sent
2098 v3f player_pos = sao->getBasePosition();
2099 if (player_pos.getDistanceFrom(p_f) > maxd) {
2100 far_players->push_back(client_id);
2107 m_clients.send(client_id, 0, &pkt, true);
2111 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2112 std::vector<u16> *far_players, float far_d_nodes,
2113 bool remove_metadata)
2115 float maxd = far_d_nodes*BS;
2116 v3f p_f = intToFloat(p, BS);
2118 std::vector<session_t> clients = m_clients.getClientIDs();
2119 for (const session_t client_id : clients) {
2122 if (RemotePlayer *player = m_env->getPlayer(client_id)) {
2123 PlayerSAO *sao = player->getPlayerSAO();
2127 // If player is far away, only set modified blocks not sent
2128 v3f player_pos = sao->getBasePosition();
2129 if(player_pos.getDistanceFrom(p_f) > maxd) {
2130 far_players->push_back(client_id);
2136 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2138 RemoteClient* client = m_clients.lockedGetClientNoEx(client_id);
2140 pkt << p << n.param0 << n.param1 << n.param2
2141 << (u8) (remove_metadata ? 0 : 1);
2146 if (pkt.getSize() > 0)
2147 m_clients.send(client_id, 0, &pkt, true);
2151 void Server::setBlockNotSent(v3s16 p)
2153 std::vector<session_t> clients = m_clients.getClientIDs();
2155 for (const session_t i : clients) {
2156 RemoteClient *client = m_clients.lockedGetClientNoEx(i);
2157 client->SetBlockNotSent(p);
2162 void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver,
2163 u16 net_proto_version)
2165 v3s16 p = block->getPos();
2168 Create a packet with the block in the right format
2171 std::ostringstream os(std::ios_base::binary);
2172 block->serialize(os, ver, false);
2173 block->serializeNetworkSpecific(os);
2174 std::string s = os.str();
2176 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2179 pkt.putRawString(s.c_str(), s.size());
2183 void Server::SendBlocks(float dtime)
2185 MutexAutoLock envlock(m_env_mutex);
2186 //TODO check if one big lock could be faster then multiple small ones
2188 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2190 std::vector<PrioritySortedBlockTransfer> queue;
2192 u32 total_sending = 0;
2195 ScopeProfiler sp2(g_profiler, "Server: selecting blocks for sending");
2197 std::vector<session_t> clients = m_clients.getClientIDs();
2200 for (const session_t client_id : clients) {
2201 RemoteClient *client = m_clients.lockedGetClientNoEx(client_id, CS_Active);
2206 total_sending += client->getSendingCount();
2207 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2213 // Lowest priority number comes first.
2214 // Lowest is most important.
2215 std::sort(queue.begin(), queue.end());
2219 // Maximal total count calculation
2220 // The per-client block sends is halved with the maximal online users
2221 u32 max_blocks_to_send = (m_env->getPlayerCount() + g_settings->getU32("max_users")) *
2222 g_settings->getU32("max_simultaneous_block_sends_per_client") / 4 + 1;
2224 for (const PrioritySortedBlockTransfer &block_to_send : queue) {
2225 if (total_sending >= max_blocks_to_send)
2228 MapBlock *block = nullptr;
2230 block = m_env->getMap().getBlockNoCreate(block_to_send.pos);
2231 } catch (const InvalidPositionException &e) {
2235 RemoteClient *client = m_clients.lockedGetClientNoEx(block_to_send.peer_id,
2240 SendBlockNoLock(block_to_send.peer_id, block, client->serialization_version,
2241 client->net_proto_version);
2243 client->SentBlock(block_to_send.pos);
2249 void Server::fillMediaCache()
2251 infostream<<"Server: Calculating media file checksums"<<std::endl;
2253 // Collect all media file paths
2254 std::vector<std::string> paths;
2255 for (const ModSpec &mod : m_mods) {
2256 paths.push_back(mod.path + DIR_DELIM + "textures");
2257 paths.push_back(mod.path + DIR_DELIM + "sounds");
2258 paths.push_back(mod.path + DIR_DELIM + "media");
2259 paths.push_back(mod.path + DIR_DELIM + "models");
2260 paths.push_back(mod.path + DIR_DELIM + "locale");
2262 fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM +
2263 "textures" + DIR_DELIM + "server");
2264 // Collect media file information from paths into cache
2265 for (const std::string &mediapath : paths) {
2266 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2267 for (const fs::DirListNode &dln : dirlist) {
2268 if (dln.dir) // Ignode dirs
2270 std::string filename = dln.name;
2271 // If name contains illegal characters, ignore the file
2272 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2273 infostream<<"Server: ignoring illegal file name: \""
2274 << filename << "\"" << std::endl;
2277 // If name is not in a supported format, ignore it
2278 const char *supported_ext[] = {
2279 ".png", ".jpg", ".bmp", ".tga",
2280 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2282 ".x", ".b3d", ".md2", ".obj",
2283 // Custom translation file format
2287 if (removeStringEnd(filename, supported_ext).empty()){
2288 infostream << "Server: ignoring unsupported file extension: \""
2289 << filename << "\"" << std::endl;
2292 // Ok, attempt to load the file and add to cache
2293 std::string filepath;
2294 filepath.append(mediapath).append(DIR_DELIM).append(filename);
2297 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2299 errorstream << "Server::fillMediaCache(): Could not open \""
2300 << filename << "\" for reading" << std::endl;
2303 std::ostringstream tmp_os(std::ios_base::binary);
2307 fis.read(buf, 1024);
2308 std::streamsize len = fis.gcount();
2309 tmp_os.write(buf, len);
2318 errorstream<<"Server::fillMediaCache(): Failed to read \""
2319 << filename << "\"" << std::endl;
2322 if(tmp_os.str().length() == 0) {
2323 errorstream << "Server::fillMediaCache(): Empty file \""
2324 << filepath << "\"" << std::endl;
2329 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2331 unsigned char *digest = sha1.getDigest();
2332 std::string sha1_base64 = base64_encode(digest, 20);
2333 std::string sha1_hex = hex_encode((char*)digest, 20);
2337 m_media[filename] = MediaInfo(filepath, sha1_base64);
2338 verbosestream << "Server: " << sha1_hex << " is " << filename
2344 void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
2346 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2350 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2353 std::string lang_suffix;
2354 lang_suffix.append(".").append(lang_code).append(".tr");
2355 for (const auto &i : m_media) {
2356 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2363 for (const auto &i : m_media) {
2364 if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
2366 pkt << i.first << i.second.sha1_digest;
2369 pkt << g_settings->get("remote_media");
2373 struct SendableMedia
2379 SendableMedia(const std::string &name_="", const std::string &path_="",
2380 const std::string &data_=""):
2387 void Server::sendRequestedMedia(session_t peer_id,
2388 const std::vector<std::string> &tosend)
2390 verbosestream<<"Server::sendRequestedMedia(): "
2391 <<"Sending files to client"<<std::endl;
2395 // Put 5kB in one bunch (this is not accurate)
2396 u32 bytes_per_bunch = 5000;
2398 std::vector< std::vector<SendableMedia> > file_bunches;
2399 file_bunches.emplace_back();
2401 u32 file_size_bunch_total = 0;
2403 for (const std::string &name : tosend) {
2404 if (m_media.find(name) == m_media.end()) {
2405 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2406 <<"unknown file \""<<(name)<<"\""<<std::endl;
2410 //TODO get path + name
2411 std::string tpath = m_media[name].path;
2414 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2416 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2417 <<tpath<<"\" for reading"<<std::endl;
2420 std::ostringstream tmp_os(std::ios_base::binary);
2424 fis.read(buf, 1024);
2425 std::streamsize len = fis.gcount();
2426 tmp_os.write(buf, len);
2427 file_size_bunch_total += len;
2436 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2437 <<name<<"\""<<std::endl;
2440 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2441 <<tname<<"\""<<std::endl;*/
2443 file_bunches[file_bunches.size()-1].emplace_back(name, tpath, tmp_os.str());
2445 // Start next bunch if got enough data
2446 if(file_size_bunch_total >= bytes_per_bunch) {
2447 file_bunches.emplace_back();
2448 file_size_bunch_total = 0;
2453 /* Create and send packets */
2455 u16 num_bunches = file_bunches.size();
2456 for (u16 i = 0; i < num_bunches; i++) {
2459 u16 total number of texture bunches
2460 u16 index of this bunch
2461 u32 number of files in this bunch
2470 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2471 pkt << num_bunches << i << (u32) file_bunches[i].size();
2473 for (const SendableMedia &j : file_bunches[i]) {
2475 pkt.putLongString(j.data);
2478 verbosestream << "Server::sendRequestedMedia(): bunch "
2479 << i << "/" << num_bunches
2480 << " files=" << file_bunches[i].size()
2481 << " size=" << pkt.getSize() << std::endl;
2486 void Server::sendDetachedInventory(const std::string &name, session_t peer_id)
2488 if(m_detached_inventories.count(name) == 0) {
2489 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2492 Inventory *inv = m_detached_inventories[name];
2493 std::ostringstream os(std::ios_base::binary);
2495 os << serializeString(name);
2499 std::string s = os.str();
2501 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2502 pkt.putRawString(s.c_str(), s.size());
2504 const std::string &check = m_detached_inventories_player[name];
2505 if (peer_id == PEER_ID_INEXISTENT) {
2507 return m_clients.sendToAll(&pkt);
2508 RemotePlayer *p = m_env->getPlayer(check.c_str());
2510 m_clients.send(p->getPeerId(), 0, &pkt, true);
2512 if (check.empty() || getPlayerName(peer_id) == check)
2517 void Server::sendDetachedInventories(session_t peer_id)
2519 for (const auto &detached_inventory : m_detached_inventories) {
2520 const std::string &name = detached_inventory.first;
2521 //Inventory *inv = i->second;
2522 sendDetachedInventory(name, peer_id);
2530 void Server::DiePlayer(session_t peer_id)
2532 PlayerSAO *playersao = getPlayerSAO(peer_id);
2533 // In some rare cases this can be NULL -- if the player is disconnected
2534 // when a Lua function modifies l_punch, for example
2538 infostream << "Server::DiePlayer(): Player "
2539 << playersao->getPlayer()->getName()
2540 << " dies" << std::endl;
2542 playersao->setHP(0);
2544 // Trigger scripted stuff
2545 m_script->on_dieplayer(playersao);
2547 SendPlayerHP(peer_id);
2548 SendDeathscreen(peer_id, false, v3f(0,0,0));
2551 void Server::RespawnPlayer(session_t peer_id)
2553 PlayerSAO *playersao = getPlayerSAO(peer_id);
2556 infostream << "Server::RespawnPlayer(): Player "
2557 << playersao->getPlayer()->getName()
2558 << " respawns" << std::endl;
2560 playersao->setHP(playersao->accessObjectProperties()->hp_max);
2561 playersao->setBreath(playersao->accessObjectProperties()->breath_max);
2563 bool repositioned = m_script->on_respawnplayer(playersao);
2564 if (!repositioned) {
2565 // setPos will send the new position to client
2566 playersao->setPos(findSpawnPos());
2569 SendPlayerHP(peer_id);
2573 void Server::DenySudoAccess(session_t peer_id)
2575 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2580 void Server::DenyAccessVerCompliant(session_t peer_id, u16 proto_ver, AccessDeniedCode reason,
2581 const std::string &str_reason, bool reconnect)
2583 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2585 m_clients.event(peer_id, CSE_SetDenied);
2586 DisconnectPeer(peer_id);
2590 void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason,
2591 const std::string &custom_reason)
2593 SendAccessDenied(peer_id, reason, custom_reason);
2594 m_clients.event(peer_id, CSE_SetDenied);
2595 DisconnectPeer(peer_id);
2598 // 13/03/15: remove this function when protocol version 25 will become
2599 // the minimum version for MT users, maybe in 1 year
2600 void Server::DenyAccess_Legacy(session_t peer_id, const std::wstring &reason)
2602 SendAccessDenied_Legacy(peer_id, reason);
2603 m_clients.event(peer_id, CSE_SetDenied);
2604 DisconnectPeer(peer_id);
2607 void Server::DisconnectPeer(session_t peer_id)
2609 m_modchannel_mgr->leaveAllChannels(peer_id);
2610 m_con->DisconnectPeer(peer_id);
2613 void Server::acceptAuth(session_t peer_id, bool forSudoMode)
2616 RemoteClient* client = getClient(peer_id, CS_Invalid);
2618 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2620 // Right now, the auth mechs don't change between login and sudo mode.
2621 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2622 client->allowed_sudo_mechs = sudo_auth_mechs;
2624 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2625 << g_settings->getFloat("dedicated_server_step")
2629 m_clients.event(peer_id, CSE_AuthAccept);
2631 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2633 // We only support SRP right now
2634 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2636 resp_pkt << sudo_auth_mechs;
2638 m_clients.event(peer_id, CSE_SudoSuccess);
2642 void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
2644 std::wstring message;
2647 Clear references to playing sounds
2649 for (std::unordered_map<s32, ServerPlayingSound>::iterator
2650 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2651 ServerPlayingSound &psound = i->second;
2652 psound.clients.erase(peer_id);
2653 if (psound.clients.empty())
2654 m_playing_sounds.erase(i++);
2659 RemotePlayer *player = m_env->getPlayer(peer_id);
2661 /* Run scripts and remove from environment */
2663 PlayerSAO *playersao = player->getPlayerSAO();
2666 // inform connected clients
2667 NetworkPacket notice(TOCLIENT_UPDATE_PLAYER_LIST, 0, PEER_ID_INEXISTENT);
2668 // (u16) 1 + std::string represents a vector serialization representation
2669 notice << (u8) PLAYER_LIST_REMOVE << (u16) 1 << std::string(playersao->getPlayer()->getName());
2670 m_clients.sendToAll(¬ice);
2672 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2674 playersao->disconnected();
2681 if (player && reason != CDR_DENY) {
2682 std::ostringstream os(std::ios_base::binary);
2683 std::vector<session_t> clients = m_clients.getClientIDs();
2685 for (const session_t client_id : clients) {
2687 RemotePlayer *player = m_env->getPlayer(client_id);
2691 // Get name of player
2692 os << player->getName() << " ";
2695 std::string name = player->getName();
2696 actionstream << name << " "
2697 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2698 << " List of players: " << os.str() << std::endl;
2700 m_admin_chat->outgoing_queue.push_back(
2701 new ChatEventNick(CET_NICK_REMOVE, name));
2705 MutexAutoLock env_lock(m_env_mutex);
2706 m_clients.DeleteClient(peer_id);
2710 // Send leave chat message to all remaining clients
2711 if (!message.empty()) {
2712 SendChatMessage(PEER_ID_INEXISTENT,
2713 ChatMessage(CHATMESSAGE_TYPE_ANNOUNCE, message));
2717 void Server::UpdateCrafting(RemotePlayer *player)
2719 // Get a preview for crafting
2721 InventoryLocation loc;
2722 loc.setPlayer(player->getName());
2723 std::vector<ItemStack> output_replacements;
2724 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2725 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2726 (&player->inventory)->getList("craft"), loc);
2728 // Put the new preview in
2729 InventoryList *plist = player->inventory.getList("craftpreview");
2730 sanity_check(plist);
2731 sanity_check(plist->getSize() >= 1);
2732 plist->changeItem(0, preview);
2735 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2737 if (evt->type == CET_NICK_ADD) {
2738 // The terminal informed us of its nick choice
2739 m_admin_nick = ((ChatEventNick *)evt)->nick;
2740 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2741 errorstream << "You haven't set up an account." << std::endl
2742 << "Please log in using the client as '"
2743 << m_admin_nick << "' with a secure password." << std::endl
2744 << "Until then, you can't execute admin tasks via the console," << std::endl
2745 << "and everybody can claim the user account instead of you," << std::endl
2746 << "giving them full control over this server." << std::endl;
2749 assert(evt->type == CET_CHAT);
2750 handleAdminChat((ChatEventChat *)evt);
2754 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2755 std::wstring wmessage, bool check_shout_priv, RemotePlayer *player)
2757 // If something goes wrong, this player is to blame
2758 RollbackScopeActor rollback_scope(m_rollback,
2759 std::string("player:") + name);
2761 if (g_settings->getBool("strip_color_codes"))
2762 wmessage = unescape_enriched(wmessage);
2765 switch (player->canSendChatMessage()) {
2766 case RPLAYER_CHATRESULT_FLOODING: {
2767 std::wstringstream ws;
2768 ws << L"You cannot send more messages. You are limited to "
2769 << g_settings->getFloat("chat_message_limit_per_10sec")
2770 << L" messages per 10 seconds.";
2773 case RPLAYER_CHATRESULT_KICK:
2774 DenyAccess_Legacy(player->getPeerId(),
2775 L"You have been kicked due to message flooding.");
2777 case RPLAYER_CHATRESULT_OK:
2780 FATAL_ERROR("Unhandled chat filtering result found.");
2784 if (m_max_chatmessage_length > 0
2785 && wmessage.length() > m_max_chatmessage_length) {
2786 return L"Your message exceed the maximum chat message limit set on the server. "
2787 L"It was refused. Send a shorter message";
2790 // Run script hook, exit if script ate the chat message
2791 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2796 // Whether to send line to the player that sent the message, or to all players
2797 bool broadcast_line = true;
2799 if (check_shout_priv && !checkPriv(name, "shout")) {
2800 line += L"-!- You don't have permission to shout.";
2801 broadcast_line = false;
2810 Tell calling method to send the message to sender
2812 if (!broadcast_line)
2816 Send the message to others
2818 actionstream << "CHAT: " << wide_to_narrow(unescape_enriched(line)) << std::endl;
2820 std::vector<session_t> clients = m_clients.getClientIDs();
2823 Send the message back to the inital sender
2824 if they are using protocol version >= 29
2827 session_t peer_id_to_avoid_sending =
2828 (player ? player->getPeerId() : PEER_ID_INEXISTENT);
2830 if (player && player->protocol_version >= 29)
2831 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2833 for (u16 cid : clients) {
2834 if (cid != peer_id_to_avoid_sending)
2835 SendChatMessage(cid, ChatMessage(line));
2840 void Server::handleAdminChat(const ChatEventChat *evt)
2842 std::string name = evt->nick;
2843 std::wstring wname = utf8_to_wide(name);
2844 std::wstring wmessage = evt->evt_msg;
2846 std::wstring answer = handleChat(name, wname, wmessage);
2848 // If asked to send answer to sender
2849 if (!answer.empty()) {
2850 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2854 RemoteClient *Server::getClient(session_t peer_id, ClientState state_min)
2856 RemoteClient *client = getClientNoEx(peer_id,state_min);
2858 throw ClientNotFoundException("Client not found");
2862 RemoteClient *Server::getClientNoEx(session_t peer_id, ClientState state_min)
2864 return m_clients.getClientNoEx(peer_id, state_min);
2867 std::string Server::getPlayerName(session_t peer_id)
2869 RemotePlayer *player = m_env->getPlayer(peer_id);
2871 return "[id="+itos(peer_id)+"]";
2872 return player->getName();
2875 PlayerSAO *Server::getPlayerSAO(session_t peer_id)
2877 RemotePlayer *player = m_env->getPlayer(peer_id);
2880 return player->getPlayerSAO();
2883 std::wstring Server::getStatusString()
2885 std::wostringstream os(std::ios_base::binary);
2888 os<<L"version="<<narrow_to_wide(g_version_string);
2890 os<<L", uptime="<<m_uptime.get();
2892 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2893 // Information about clients
2896 std::vector<session_t> clients = m_clients.getClientIDs();
2897 for (session_t client_id : clients) {
2899 RemotePlayer *player = m_env->getPlayer(client_id);
2900 // Get name of player
2901 std::wstring name = L"unknown";
2903 name = narrow_to_wide(player->getName());
2904 // Add name to information string
2913 if (!((ServerMap*)(&m_env->getMap()))->isSavingEnabled())
2914 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2916 if (!g_settings->get("motd").empty())
2917 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2921 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2923 std::set<std::string> privs;
2924 m_script->getAuth(name, NULL, &privs);
2928 bool Server::checkPriv(const std::string &name, const std::string &priv)
2930 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2931 return (privs.count(priv) != 0);
2934 void Server::reportPrivsModified(const std::string &name)
2937 std::vector<session_t> clients = m_clients.getClientIDs();
2938 for (const session_t client_id : clients) {
2939 RemotePlayer *player = m_env->getPlayer(client_id);
2940 reportPrivsModified(player->getName());
2943 RemotePlayer *player = m_env->getPlayer(name.c_str());
2946 SendPlayerPrivileges(player->getPeerId());
2947 PlayerSAO *sao = player->getPlayerSAO();
2950 sao->updatePrivileges(
2951 getPlayerEffectivePrivs(name),
2956 void Server::reportInventoryFormspecModified(const std::string &name)
2958 RemotePlayer *player = m_env->getPlayer(name.c_str());
2961 SendPlayerInventoryFormspec(player->getPeerId());
2964 void Server::setIpBanned(const std::string &ip, const std::string &name)
2966 m_banmanager->add(ip, name);
2969 void Server::unsetIpBanned(const std::string &ip_or_name)
2971 m_banmanager->remove(ip_or_name);
2974 std::string Server::getBanDescription(const std::string &ip_or_name)
2976 return m_banmanager->getBanDescription(ip_or_name);
2979 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2981 // m_env will be NULL if the server is initializing
2985 if (m_admin_nick == name && !m_admin_nick.empty()) {
2986 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2989 RemotePlayer *player = m_env->getPlayer(name);
2994 if (player->getPeerId() == PEER_ID_INEXISTENT)
2997 SendChatMessage(player->getPeerId(), ChatMessage(msg));
3000 bool Server::showFormspec(const char *playername, const std::string &formspec,
3001 const std::string &formname)
3003 // m_env will be NULL if the server is initializing
3007 RemotePlayer *player = m_env->getPlayer(playername);
3011 SendShowFormspecMessage(player->getPeerId(), formspec, formname);
3015 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3020 u32 id = player->addHud(form);
3022 SendHUDAdd(player->getPeerId(), id, form);
3027 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3031 HudElement* todel = player->removeHud(id);
3038 SendHUDRemove(player->getPeerId(), id);
3042 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3047 SendHUDChange(player->getPeerId(), id, stat, data);
3051 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3056 SendHUDSetFlags(player->getPeerId(), flags, mask);
3057 player->hud_flags &= ~mask;
3058 player->hud_flags |= flags;
3060 PlayerSAO* playersao = player->getPlayerSAO();
3065 m_script->player_event(playersao, "hud_changed");
3069 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3074 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3077 player->setHotbarItemcount(hotbar_itemcount);
3078 std::ostringstream os(std::ios::binary);
3079 writeS32(os, hotbar_itemcount);
3080 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3084 s32 Server::hudGetHotbarItemcount(RemotePlayer *player) const
3086 return player->getHotbarItemcount();
3089 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3094 player->setHotbarImage(name);
3095 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_IMAGE, name);
3098 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3102 return player->getHotbarImage();
3105 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3110 player->setHotbarSelectedImage(name);
3111 SendHUDSetParam(player->getPeerId(), HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3114 const std::string& Server::hudGetHotbarSelectedImage(RemotePlayer *player) const
3116 return player->getHotbarSelectedImage();
3119 Address Server::getPeerAddress(session_t peer_id)
3121 return m_con->GetPeerAddress(peer_id);
3124 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3125 v2s32 animation_frames[4], f32 frame_speed)
3130 player->setLocalAnimations(animation_frames, frame_speed);
3131 SendLocalPlayerAnimations(player->getPeerId(), animation_frames, frame_speed);
3135 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3140 player->eye_offset_first = first;
3141 player->eye_offset_third = third;
3142 SendEyeOffset(player->getPeerId(), first, third);
3146 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3147 const std::string &type, const std::vector<std::string> ¶ms,
3153 player->setSky(bgcolor, type, params, clouds);
3154 SendSetSky(player->getPeerId(), bgcolor, type, params, clouds);
3158 bool Server::setClouds(RemotePlayer *player, float density,
3159 const video::SColor &color_bright,
3160 const video::SColor &color_ambient,
3168 SendCloudParams(player->getPeerId(), density,
3169 color_bright, color_ambient, height,
3174 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3180 player->overrideDayNightRatio(do_override, ratio);
3181 SendOverrideDayNightRatio(player->getPeerId(), do_override, ratio);
3185 void Server::notifyPlayers(const std::wstring &msg)
3187 SendChatMessage(PEER_ID_INEXISTENT, ChatMessage(msg));
3190 void Server::spawnParticle(const std::string &playername, v3f pos,
3191 v3f velocity, v3f acceleration,
3192 float expirationtime, float size, bool
3193 collisiondetection, bool collision_removal,
3194 bool vertical, const std::string &texture,
3195 const struct TileAnimationParams &animation, u8 glow)
3197 // m_env will be NULL if the server is initializing
3201 session_t peer_id = PEER_ID_INEXISTENT;
3203 if (!playername.empty()) {
3204 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3207 peer_id = player->getPeerId();
3208 proto_ver = player->protocol_version;
3211 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3212 expirationtime, size, collisiondetection,
3213 collision_removal, vertical, texture, animation, glow);
3216 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3217 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3218 float minexptime, float maxexptime, float minsize, float maxsize,
3219 bool collisiondetection, bool collision_removal,
3220 ServerActiveObject *attached, bool vertical, const std::string &texture,
3221 const std::string &playername, const struct TileAnimationParams &animation,
3224 // m_env will be NULL if the server is initializing
3228 session_t peer_id = PEER_ID_INEXISTENT;
3230 if (!playername.empty()) {
3231 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3234 peer_id = player->getPeerId();
3235 proto_ver = player->protocol_version;
3238 u16 attached_id = attached ? attached->getId() : 0;
3241 if (attached_id == 0)
3242 id = m_env->addParticleSpawner(spawntime);
3244 id = m_env->addParticleSpawner(spawntime, attached_id);
3246 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3247 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3248 minexptime, maxexptime, minsize, maxsize,
3249 collisiondetection, collision_removal, attached_id, vertical,
3250 texture, id, animation, glow);
3255 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3257 // m_env will be NULL if the server is initializing
3259 throw ServerError("Can't delete particle spawners during initialisation!");
3261 session_t peer_id = PEER_ID_INEXISTENT;
3262 if (!playername.empty()) {
3263 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3266 peer_id = player->getPeerId();
3269 m_env->deleteParticleSpawner(id);
3270 SendDeleteParticleSpawner(peer_id, id);
3273 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3275 if(m_detached_inventories.count(name) > 0){
3276 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3277 delete m_detached_inventories[name];
3279 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3281 Inventory *inv = new Inventory(m_itemdef);
3283 m_detached_inventories[name] = inv;
3284 m_detached_inventories_player[name] = player;
3285 //TODO find a better way to do this
3286 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3290 // actions: time-reversed list
3291 // Return value: success/failure
3292 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3293 std::list<std::string> *log)
3295 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3296 ServerMap *map = (ServerMap*)(&m_env->getMap());
3298 // Fail if no actions to handle
3299 if(actions.empty()){
3300 log->push_back("Nothing to do.");
3307 for (const RollbackAction &action : actions) {
3309 bool success = action.applyRevert(map, this, this);
3312 std::ostringstream os;
3313 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3314 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3316 log->push_back(os.str());
3318 std::ostringstream os;
3319 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3320 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3322 log->push_back(os.str());
3326 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3327 <<" failed"<<std::endl;
3329 // Call it done if less than half failed
3330 return num_failed <= num_tried/2;
3333 // IGameDef interface
3335 IItemDefManager *Server::getItemDefManager()
3340 INodeDefManager *Server::getNodeDefManager()
3345 ICraftDefManager *Server::getCraftDefManager()
3350 u16 Server::allocateUnknownNodeId(const std::string &name)
3352 return m_nodedef->allocateDummy(name);
3355 MtEventManager *Server::getEventManager()
3360 IWritableItemDefManager *Server::getWritableItemDefManager()
3365 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3370 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3375 const ModSpec *Server::getModSpec(const std::string &modname) const
3377 std::vector<ModSpec>::const_iterator it;
3378 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3379 const ModSpec &mod = *it;
3380 if (mod.name == modname)
3386 void Server::getModNames(std::vector<std::string> &modlist)
3388 std::vector<ModSpec>::iterator it;
3389 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3390 modlist.push_back(it->name);
3393 std::string Server::getBuiltinLuaPath()
3395 return porting::path_share + DIR_DELIM + "builtin";
3398 std::string Server::getModStoragePath() const
3400 return m_path_world + DIR_DELIM + "mod_storage";
3403 v3f Server::findSpawnPos()
3405 ServerMap &map = m_env->getServerMap();
3407 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3408 return nodeposf * BS;
3411 bool is_good = false;
3412 // Limit spawn range to mapgen edges (determined by 'mapgen_limit')
3413 s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
3415 // Try to find a good place a few times
3416 for(s32 i = 0; i < 4000 && !is_good; i++) {
3417 s32 range = MYMIN(1 + i, range_max);
3418 // We're going to try to throw the player to this position
3419 v2s16 nodepos2d = v2s16(
3420 -range + (myrand() % (range * 2)),
3421 -range + (myrand() % (range * 2)));
3423 // Get spawn level at point
3424 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3425 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3426 // the mapgen to signify an unsuitable spawn position
3427 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3430 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3433 for (s32 i = 0; i < 10; i++) {
3434 v3s16 blockpos = getNodeBlockPos(nodepos);
3435 map.emergeBlock(blockpos, true);
3436 content_t c = map.getNodeNoEx(nodepos).getContent();
3437 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3439 if (air_count >= 2) {
3440 nodeposf = intToFloat(nodepos, BS);
3441 // Don't spawn the player outside map boundaries
3442 if (objectpos_over_limit(nodeposf))
3455 void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)
3457 m_shutdown_timer = delay;
3458 m_shutdown_msg = msg;
3459 m_shutdown_ask_reconnect = reconnect;
3461 if (delay == 0.0f) {
3462 // No delay, shutdown immediately
3463 m_shutdown_requested = true;
3464 // only print to the infostream, a chat message saying
3465 // "Server Shutting Down" is sent when the server destructs.
3466 infostream << "*** Immediate Server shutdown requested." << std::endl;
3467 } else if (delay < 0.0f && m_shutdown_timer > 0.0f) {
3468 // Negative delay, cancel shutdown if requested
3469 m_shutdown_timer = 0.0f;
3470 m_shutdown_msg = "";
3471 m_shutdown_ask_reconnect = false;
3472 m_shutdown_requested = false;
3473 std::wstringstream ws;
3475 ws << L"*** Server shutdown canceled.";
3477 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3478 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3479 } else if (delay > 0.0f) {
3480 // Positive delay, tell the clients when the server will shut down
3481 std::wstringstream ws;
3483 ws << L"*** Server shutting down in "
3484 << duration_to_string(myround(m_shutdown_timer)).c_str()
3487 infostream << wide_to_utf8(ws.str()).c_str() << std::endl;
3488 SendChatMessage(PEER_ID_INEXISTENT, ws.str());
3492 PlayerSAO* Server::emergePlayer(const char *name, session_t peer_id, u16 proto_version)
3495 Try to get an existing player
3497 RemotePlayer *player = m_env->getPlayer(name);
3499 // If player is already connected, cancel
3500 if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
3501 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3506 If player with the wanted peer_id already exists, cancel.
3508 if (m_env->getPlayer(peer_id)) {
3509 infostream<<"emergePlayer(): Player with wrong name but same"
3510 " peer_id already exists"<<std::endl;
3515 player = new RemotePlayer(name, idef());
3518 bool newplayer = false;
3521 PlayerSAO *playersao = m_env->loadPlayer(player, &newplayer, peer_id, isSingleplayer());
3523 // Complete init with server parts
3524 playersao->finalize(player, getPlayerEffectivePrivs(player->getName()));
3525 player->protocol_version = proto_version;
3529 m_script->on_newplayer(playersao);
3535 bool Server::registerModStorage(ModMetadata *storage)
3537 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3538 errorstream << "Unable to register same mod storage twice. Storage name: "
3539 << storage->getModName() << std::endl;
3543 m_mod_storages[storage->getModName()] = storage;
3547 void Server::unregisterModStorage(const std::string &name)
3549 std::unordered_map<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3550 if (it != m_mod_storages.end()) {
3551 // Save unconditionaly on unregistration
3552 it->second->save(getModStoragePath());
3553 m_mod_storages.erase(name);
3557 void dedicated_server_loop(Server &server, bool &kill)
3559 verbosestream<<"dedicated_server_loop()"<<std::endl;
3561 IntervalLimiter m_profiler_interval;
3563 static thread_local const float steplen =
3564 g_settings->getFloat("dedicated_server_step");
3565 static thread_local const float profiler_print_interval =
3566 g_settings->getFloat("profiler_print_interval");
3569 // This is kind of a hack but can be done like this
3570 // because server.step() is very light
3572 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3573 sleep_ms((int)(steplen*1000.0));
3575 server.step(steplen);
3577 if (server.getShutdownRequested() || kill)
3583 if (profiler_print_interval != 0) {
3584 if(m_profiler_interval.step(steplen, profiler_print_interval))
3586 infostream<<"Profiler:"<<std::endl;
3587 g_profiler->print(infostream);
3588 g_profiler->clear();
3593 infostream << "Dedicated server quitting" << std::endl;
3595 if (g_settings->getBool("server_announce"))
3596 ServerList::sendAnnounce(ServerList::AA_DELETE,
3597 server.m_bind_addr.getPort());
3606 bool Server::joinModChannel(const std::string &channel)
3608 return m_modchannel_mgr->joinChannel(channel, PEER_ID_SERVER) &&
3609 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
3612 bool Server::leaveModChannel(const std::string &channel)
3614 return m_modchannel_mgr->leaveChannel(channel, PEER_ID_SERVER);
3617 bool Server::sendModChannelMessage(const std::string &channel, const std::string &message)
3619 if (!m_modchannel_mgr->canWriteOnChannel(channel))
3622 broadcastModChannelMessage(channel, message, PEER_ID_SERVER);
3626 ModChannel* Server::getModChannel(const std::string &channel)
3628 return m_modchannel_mgr->getModChannel(channel);
3631 void Server::broadcastModChannelMessage(const std::string &channel,
3632 const std::string &message, session_t from_peer)
3634 const std::vector<u16> &peers = m_modchannel_mgr->getChannelPeers(channel);
3638 if (message.size() > STRING_MAX_LEN) {
3639 warningstream << "ModChannel message too long, dropping before sending "
3640 << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
3641 << channel << ")" << std::endl;
3646 if (from_peer != PEER_ID_SERVER) {
3647 sender = getPlayerName(from_peer);
3650 NetworkPacket resp_pkt(TOCLIENT_MODCHANNEL_MSG,
3651 2 + channel.size() + 2 + sender.size() + 2 + message.size());
3652 resp_pkt << channel << sender << message;
3653 for (session_t peer_id : peers) {
3655 if (peer_id == from_peer)
3658 Send(peer_id, &resp_pkt);
3661 if (from_peer != PEER_ID_SERVER) {
3662 m_script->on_modchannel_message(channel, sender, message);