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/networkprotocol.h"
25 #include "network/serveropcodes.h"
27 #include "environment.h"
29 #include "threading/mutex_auto_lock.h"
30 #include "constants.h"
36 #include "serverobject.h"
37 #include "genericobject.h"
41 #include "serverscripting.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content_abm.h"
51 #include "content_sao.h"
53 #include "event_manager.h"
54 #include "serverlist.h"
55 #include "util/string.h"
57 #include "util/serialize.h"
58 #include "util/thread.h"
59 #include "defaultsettings.h"
60 #include "util/base64.h"
61 #include "util/sha1.h"
64 class ClientNotFoundException : public BaseException
67 ClientNotFoundException(const char *s):
72 class ServerThread : public Thread
76 ServerThread(Server *server):
87 void *ServerThread::run()
89 DSTACK(FUNCTION_NAME);
90 BEGIN_DEBUG_EXCEPTION_HANDLER
92 m_server->AsyncRunStep(true);
94 while (!stopRequested()) {
96 //TimeTaker timer("AsyncRunStep() + Receive()");
98 m_server->AsyncRunStep();
102 } catch (con::NoIncomingDataException &e) {
103 } catch (con::PeerNotFoundException &e) {
104 infostream<<"Server: PeerNotFoundException"<<std::endl;
105 } catch (ClientNotFoundException &e) {
106 } catch (con::ConnectionBindFailed &e) {
107 m_server->setAsyncFatalError(e.what());
108 } catch (LuaError &e) {
109 m_server->setAsyncFatalError(
110 "ServerThread::run Lua: " + std::string(e.what()));
114 END_DEBUG_EXCEPTION_HANDLER
119 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
121 if(pos_exists) *pos_exists = false;
126 if(pos_exists) *pos_exists = true;
131 ServerActiveObject *sao = env->getActiveObject(object);
134 if(pos_exists) *pos_exists = true;
135 return sao->getBasePosition(); }
147 const std::string &path_world,
148 const SubgameSpec &gamespec,
149 bool simple_singleplayer_mode,
154 m_path_world(path_world),
155 m_gamespec(gamespec),
156 m_simple_singleplayer_mode(simple_singleplayer_mode),
157 m_dedicated(dedicated),
158 m_async_fatal_error(""),
167 m_enable_rollback_recording(false),
170 m_itemdef(createItemDefManager()),
171 m_nodedef(createNodeDefManager()),
172 m_craftdef(createCraftDefManager()),
173 m_event(new EventManager()),
175 m_time_of_day_send_timer(0),
178 m_shutdown_requested(false),
179 m_shutdown_ask_reconnect(false),
181 m_ignore_map_edit_events(false),
182 m_ignore_map_edit_events_peer_id(0),
184 m_mod_storage_save_timer(10.0f)
186 m_liquid_transform_timer = 0.0;
187 m_liquid_transform_every = 1.0;
188 m_masterserver_timer = 0.0;
189 m_emergethread_trigger_timer = 0.0;
190 m_savemap_timer = 0.0;
193 m_lag = g_settings->getFloat("dedicated_server_step");
196 throw ServerError("Supplied empty world path");
198 if(!gamespec.isValid())
199 throw ServerError("Supplied invalid gamespec");
201 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
202 if(m_simple_singleplayer_mode)
203 infostream<<" in simple singleplayer mode"<<std::endl;
205 infostream<<std::endl;
206 infostream<<"- world: "<<m_path_world<<std::endl;
207 infostream<<"- game: "<<m_gamespec.path<<std::endl;
209 // Create world if it doesn't exist
210 if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
211 throw ServerError("Failed to initialize world");
213 // Create server thread
214 m_thread = new ServerThread(this);
216 // Create emerge manager
217 m_emerge = new EmergeManager(this);
219 // Create ban manager
220 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
221 m_banmanager = new BanManager(ban_path);
223 ServerModConfiguration modconf(m_path_world);
224 m_mods = modconf.getMods();
225 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
226 // complain about mods with unsatisfied dependencies
227 if (!modconf.isConsistent()) {
228 modconf.printUnsatisfiedModsError();
231 Settings worldmt_settings;
232 std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
233 worldmt_settings.readConfigFile(worldmt.c_str());
234 std::vector<std::string> names = worldmt_settings.getNames();
235 std::set<std::string> load_mod_names;
236 for(std::vector<std::string>::iterator it = names.begin();
237 it != names.end(); ++it) {
238 std::string name = *it;
239 if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
240 load_mod_names.insert(name.substr(9));
242 // complain about mods declared to be loaded, but not found
243 for(std::vector<ModSpec>::iterator it = m_mods.begin();
244 it != m_mods.end(); ++it)
245 load_mod_names.erase((*it).name);
246 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
247 it != unsatisfied_mods.end(); ++it)
248 load_mod_names.erase((*it).name);
249 if(!load_mod_names.empty()) {
250 errorstream << "The following mods could not be found:";
251 for(std::set<std::string>::iterator it = load_mod_names.begin();
252 it != load_mod_names.end(); ++it)
253 errorstream << " \"" << (*it) << "\"";
254 errorstream << std::endl;
258 MutexAutoLock envlock(m_env_mutex);
260 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
261 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
263 // Initialize scripting
264 infostream<<"Server: Initializing Lua"<<std::endl;
266 m_script = new ServerScripting(this);
268 m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
271 infostream << "Server: Loading mods: ";
272 for (std::vector<ModSpec>::const_iterator i = m_mods.begin();
273 i != m_mods.end(); ++i) {
274 infostream << (*i).name << " ";
276 infostream << std::endl;
277 // Load and run "mod" scripts
278 for (std::vector<ModSpec>::const_iterator it = m_mods.begin();
279 it != m_mods.end(); ++it) {
280 const ModSpec &mod = *it;
281 if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
282 throw ModError("Error loading mod \"" + mod.name +
283 "\": Mod name does not follow naming conventions: "
284 "Only chararacters [a-z0-9_] are allowed.");
286 std::string script_path = mod.path + DIR_DELIM + "init.lua";
287 infostream << " [" << padStringRight(mod.name, 12) << "] [\""
288 << script_path << "\"]" << std::endl;
289 m_script->loadMod(script_path, mod.name);
292 // Read Textures and calculate sha1 sums
295 // Apply item aliases in the node definition manager
296 m_nodedef->updateAliases(m_itemdef);
298 // Apply texture overrides from texturepack/override.txt
299 std::string texture_path = g_settings->get("texture_path");
300 if (texture_path != "" && fs::IsDir(texture_path))
301 m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
303 m_nodedef->setNodeRegistrationStatus(true);
305 // Perform pending node name resolutions
306 m_nodedef->runNodeResolveCallbacks();
308 // unmap node names for connected nodeboxes
309 m_nodedef->mapNodeboxConnections();
311 // init the recipe hashes to speed up crafting
312 m_craftdef->initHashes(this);
314 // Initialize Environment
315 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
317 m_clients.setEnv(m_env);
319 if (!servermap->settings_mgr.makeMapgenParams())
320 FATAL_ERROR("Couldn't create any mapgen type");
322 // Initialize mapgens
323 m_emerge->initMapgens(servermap->getMapgenParams());
325 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
326 if (m_enable_rollback_recording) {
327 // Create rollback manager
328 m_rollback = new RollbackManager(m_path_world, this);
331 // Give environment reference to scripting api
332 m_script->initializeEnvironment(m_env);
334 // Register us to receive map edit events
335 servermap->addEventReceiver(this);
337 // If file exists, load environment metadata
338 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
339 infostream << "Server: Loading environment metadata" << std::endl;
342 m_env->loadDefaultMeta();
345 // Add some test ActiveBlockModifiers to environment
346 add_legacy_abms(m_env, m_nodedef);
348 m_liquid_transform_every = g_settings->getFloat("liquid_update");
349 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
354 infostream<<"Server destructing"<<std::endl;
356 // Send shutdown message
357 SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
360 MutexAutoLock envlock(m_env_mutex);
362 // Execute script shutdown hooks
363 m_script->on_shutdown();
365 infostream << "Server: Saving players" << std::endl;
366 m_env->saveLoadedPlayers();
368 infostream << "Server: Kicking players" << std::endl;
369 std::string kick_msg;
370 bool reconnect = false;
371 if (getShutdownRequested()) {
372 reconnect = m_shutdown_ask_reconnect;
373 kick_msg = m_shutdown_msg;
375 if (kick_msg == "") {
376 kick_msg = g_settings->get("kick_msg_shutdown");
378 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
379 kick_msg, reconnect);
381 infostream << "Server: Saving environment metadata" << std::endl;
389 // stop all emerge threads before deleting players that may have
390 // requested blocks to be emerged
391 m_emerge->stopThreads();
393 // Delete things in the reverse order of creation
403 // Deinitialize scripting
404 infostream<<"Server: Deinitializing scripting"<<std::endl;
407 // Delete detached inventories
408 for (std::map<std::string, Inventory*>::iterator
409 i = m_detached_inventories.begin();
410 i != m_detached_inventories.end(); ++i) {
415 void Server::start(Address bind_addr)
417 DSTACK(FUNCTION_NAME);
419 m_bind_addr = bind_addr;
421 infostream<<"Starting server on "
422 << bind_addr.serializeString() <<"..."<<std::endl;
424 // Stop thread if already running
427 // Initialize connection
428 m_con.SetTimeoutMs(30);
429 m_con.Serve(bind_addr);
434 // ASCII art for the win!
436 <<" .__ __ __ "<<std::endl
437 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
438 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
439 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
440 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
441 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
442 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
443 actionstream<<"Server for gameid=\""<<m_gamespec.id
444 <<"\" listening on "<<bind_addr.serializeString()<<":"
445 <<bind_addr.getPort() << "."<<std::endl;
450 DSTACK(FUNCTION_NAME);
452 infostream<<"Server: Stopping and waiting threads"<<std::endl;
454 // Stop threads (set run=false first so both start stopping)
456 //m_emergethread.setRun(false);
458 //m_emergethread.stop();
460 infostream<<"Server: Threads stopped"<<std::endl;
463 void Server::step(float dtime)
465 DSTACK(FUNCTION_NAME);
470 MutexAutoLock lock(m_step_dtime_mutex);
471 m_step_dtime += dtime;
473 // Throw if fatal error occurred in thread
474 std::string async_err = m_async_fatal_error.get();
475 if (!async_err.empty()) {
476 if (!m_simple_singleplayer_mode) {
477 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
478 g_settings->get("kick_msg_crash"),
479 g_settings->getBool("ask_reconnect_on_crash"));
481 throw ServerError("AsyncErr: " + async_err);
485 void Server::AsyncRunStep(bool initial_step)
487 DSTACK(FUNCTION_NAME);
489 g_profiler->add("Server::AsyncRunStep (num)", 1);
493 MutexAutoLock lock1(m_step_dtime_mutex);
494 dtime = m_step_dtime;
498 // Send blocks to clients
502 if((dtime < 0.001) && (initial_step == false))
505 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
507 //infostream<<"Server steps "<<dtime<<std::endl;
508 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
511 MutexAutoLock lock1(m_step_dtime_mutex);
512 m_step_dtime -= dtime;
519 m_uptime.set(m_uptime.get() + dtime);
525 Update time of day and overall game time
527 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
530 Send to clients at constant intervals
533 m_time_of_day_send_timer -= dtime;
534 if(m_time_of_day_send_timer < 0.0) {
535 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
536 u16 time = m_env->getTimeOfDay();
537 float time_speed = g_settings->getFloat("time_speed");
538 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
542 MutexAutoLock lock(m_env_mutex);
543 // Figure out and report maximum lag to environment
544 float max_lag = m_env->getMaxLagEstimate();
545 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
547 if(dtime > 0.1 && dtime > max_lag * 2.0)
548 infostream<<"Server: Maximum lag peaked to "<<dtime
552 m_env->reportMaxLagEstimate(max_lag);
554 ScopeProfiler sp(g_profiler, "SEnv step");
555 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
559 static const float map_timer_and_unload_dtime = 2.92;
560 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
562 MutexAutoLock lock(m_env_mutex);
563 // Run Map's timers and unload unused data
564 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
565 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
566 g_settings->getFloat("server_unload_unused_data_timeout"),
571 Listen to the admin chat, if available
574 if (!m_admin_chat->command_queue.empty()) {
575 MutexAutoLock lock(m_env_mutex);
576 while (!m_admin_chat->command_queue.empty()) {
577 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
578 handleChatInterfaceEvent(evt);
582 m_admin_chat->outgoing_queue.push_back(
583 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
590 /* Transform liquids */
591 m_liquid_transform_timer += dtime;
592 if(m_liquid_transform_timer >= m_liquid_transform_every)
594 m_liquid_transform_timer -= m_liquid_transform_every;
596 MutexAutoLock lock(m_env_mutex);
598 ScopeProfiler sp(g_profiler, "Server: liquid transform");
600 std::map<v3s16, MapBlock*> modified_blocks;
601 m_env->getMap().transformLiquids(modified_blocks);
606 core::map<v3s16, MapBlock*> lighting_modified_blocks;
607 ServerMap &map = ((ServerMap&)m_env->getMap());
608 map.updateLighting(modified_blocks, lighting_modified_blocks);
610 // Add blocks modified by lighting to modified_blocks
611 for(core::map<v3s16, MapBlock*>::Iterator
612 i = lighting_modified_blocks.getIterator();
613 i.atEnd() == false; i++)
615 MapBlock *block = i.getNode()->getValue();
616 modified_blocks.insert(block->getPos(), block);
620 Set the modified blocks unsent for all the clients
622 if(!modified_blocks.empty())
624 SetBlocksNotSent(modified_blocks);
627 m_clients.step(dtime);
629 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
631 // send masterserver announce
633 float &counter = m_masterserver_timer;
634 if (!isSingleplayer() && (!counter || counter >= 300.0) &&
635 g_settings->getBool("server_announce")) {
636 ServerList::sendAnnounce(counter ? ServerList::AA_UPDATE :
637 ServerList::AA_START,
638 m_bind_addr.getPort(),
639 m_clients.getPlayerNames(),
641 m_env->getGameTime(),
644 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
654 Check added and deleted active objects
657 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
658 MutexAutoLock envlock(m_env_mutex);
661 UNORDERED_MAP<u16, RemoteClient*> clients = m_clients.getClientList();
662 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
664 // Radius inside which objects are active
665 static const s16 radius =
666 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
668 // Radius inside which players are active
669 static const bool is_transfer_limited =
670 g_settings->exists("unlimited_player_transfer_distance") &&
671 !g_settings->getBool("unlimited_player_transfer_distance");
672 static const s16 player_transfer_dist = g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
673 s16 player_radius = player_transfer_dist;
674 if (player_radius == 0 && is_transfer_limited)
675 player_radius = radius;
677 for (UNORDERED_MAP<u16, RemoteClient*>::iterator i = clients.begin();
678 i != clients.end(); ++i) {
679 RemoteClient *client = i->second;
681 // If definitions and textures have not been sent, don't
682 // send objects either
683 if (client->getState() < CS_DefinitionsSent)
686 RemotePlayer *player = m_env->getPlayer(client->peer_id);
687 if (player == NULL) {
688 // This can happen if the client timeouts somehow
689 /*warningstream<<FUNCTION_NAME<<": Client "
691 <<" has no associated player"<<std::endl;*/
695 PlayerSAO *playersao = player->getPlayerSAO();
696 if (playersao == NULL)
699 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
700 if (my_radius <= 0) my_radius = radius;
701 //infostream << "Server: Active Radius " << my_radius << std::endl;
703 std::queue<u16> removed_objects;
704 std::queue<u16> added_objects;
705 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
706 client->m_known_objects, removed_objects);
707 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
708 client->m_known_objects, added_objects);
710 // Ignore if nothing happened
711 if (removed_objects.empty() && added_objects.empty()) {
715 std::string data_buffer;
719 // Handle removed objects
720 writeU16((u8*)buf, removed_objects.size());
721 data_buffer.append(buf, 2);
722 while (!removed_objects.empty()) {
724 u16 id = removed_objects.front();
725 ServerActiveObject* obj = m_env->getActiveObject(id);
727 // Add to data buffer for sending
728 writeU16((u8*)buf, id);
729 data_buffer.append(buf, 2);
731 // Remove from known objects
732 client->m_known_objects.erase(id);
734 if(obj && obj->m_known_by_count > 0)
735 obj->m_known_by_count--;
736 removed_objects.pop();
739 // Handle added objects
740 writeU16((u8*)buf, added_objects.size());
741 data_buffer.append(buf, 2);
742 while (!added_objects.empty()) {
744 u16 id = added_objects.front();
745 ServerActiveObject* obj = m_env->getActiveObject(id);
748 u8 type = ACTIVEOBJECT_TYPE_INVALID;
750 warningstream<<FUNCTION_NAME
751 <<": NULL object"<<std::endl;
753 type = obj->getSendType();
755 // Add to data buffer for sending
756 writeU16((u8*)buf, id);
757 data_buffer.append(buf, 2);
758 writeU8((u8*)buf, type);
759 data_buffer.append(buf, 1);
762 data_buffer.append(serializeLongString(
763 obj->getClientInitializationData(client->net_proto_version)));
765 data_buffer.append(serializeLongString(""));
767 // Add to known objects
768 client->m_known_objects.insert(id);
771 obj->m_known_by_count++;
776 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
777 verbosestream << "Server: Sent object remove/add: "
778 << removed_objects.size() << " removed, "
779 << added_objects.size() << " added, "
780 << "packet size is " << pktSize << std::endl;
784 m_mod_storage_save_timer -= dtime;
785 if (m_mod_storage_save_timer <= 0.0f) {
786 infostream << "Saving registered mod storages." << std::endl;
787 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
788 for (UNORDERED_MAP<std::string, ModMetadata *>::const_iterator
789 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
790 if (it->second->isModified()) {
791 it->second->save(getModStoragePath());
801 MutexAutoLock envlock(m_env_mutex);
802 ScopeProfiler sp(g_profiler, "Server: sending object messages");
805 // Value = data sent by object
806 UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* > buffered_messages;
808 // Get active object messages from environment
810 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
814 std::vector<ActiveObjectMessage>* message_list = NULL;
815 UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator n;
816 n = buffered_messages.find(aom.id);
817 if (n == buffered_messages.end()) {
818 message_list = new std::vector<ActiveObjectMessage>;
819 buffered_messages[aom.id] = message_list;
822 message_list = n->second;
824 message_list->push_back(aom);
828 UNORDERED_MAP<u16, RemoteClient*> clients = m_clients.getClientList();
829 // Route data to every client
830 for (UNORDERED_MAP<u16, RemoteClient*>::iterator i = clients.begin();
831 i != clients.end(); ++i) {
832 RemoteClient *client = i->second;
833 std::string reliable_data;
834 std::string unreliable_data;
835 // Go through all objects in message buffer
836 for (UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator
837 j = buffered_messages.begin();
838 j != buffered_messages.end(); ++j) {
839 // If object is not known by client, skip it
841 if (client->m_known_objects.find(id) == client->m_known_objects.end())
844 // Get message list of object
845 std::vector<ActiveObjectMessage>* list = j->second;
846 // Go through every message
847 for (std::vector<ActiveObjectMessage>::iterator
848 k = list->begin(); k != list->end(); ++k) {
849 // Compose the full new data with header
850 ActiveObjectMessage aom = *k;
851 std::string new_data;
854 writeU16((u8*)&buf[0], aom.id);
855 new_data.append(buf, 2);
857 new_data += serializeString(aom.datastring);
858 // Add data to buffer
860 reliable_data += new_data;
862 unreliable_data += new_data;
866 reliable_data and unreliable_data are now ready.
869 if(reliable_data.size() > 0) {
870 SendActiveObjectMessages(client->peer_id, reliable_data);
873 if(unreliable_data.size() > 0) {
874 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
879 // Clear buffered_messages
880 for (UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator
881 i = buffered_messages.begin();
882 i != buffered_messages.end(); ++i) {
888 Send queued-for-sending map edit events.
891 // We will be accessing the environment
892 MutexAutoLock lock(m_env_mutex);
894 // Don't send too many at a time
897 // Single change sending is disabled if queue size is not small
898 bool disable_single_change_sending = false;
899 if(m_unsent_map_edit_queue.size() >= 4)
900 disable_single_change_sending = true;
902 int event_count = m_unsent_map_edit_queue.size();
904 // We'll log the amount of each
907 while(m_unsent_map_edit_queue.size() != 0)
909 MapEditEvent* event = m_unsent_map_edit_queue.front();
910 m_unsent_map_edit_queue.pop();
912 // Players far away from the change are stored here.
913 // Instead of sending the changes, MapBlocks are set not sent
915 std::vector<u16> far_players;
917 switch (event->type) {
920 prof.add("MEET_ADDNODE", 1);
921 sendAddNode(event->p, event->n, event->already_known_by_peer,
922 &far_players, disable_single_change_sending ? 5 : 30,
923 event->type == MEET_ADDNODE);
925 case MEET_REMOVENODE:
926 prof.add("MEET_REMOVENODE", 1);
927 sendRemoveNode(event->p, event->already_known_by_peer,
928 &far_players, disable_single_change_sending ? 5 : 30);
930 case MEET_BLOCK_NODE_METADATA_CHANGED:
931 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
932 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
933 setBlockNotSent(event->p);
936 infostream << "Server: MEET_OTHER" << std::endl;
937 prof.add("MEET_OTHER", 1);
938 for(std::set<v3s16>::iterator
939 i = event->modified_blocks.begin();
940 i != event->modified_blocks.end(); ++i) {
945 prof.add("unknown", 1);
946 warningstream << "Server: Unknown MapEditEvent "
947 << ((u32)event->type) << std::endl;
952 Set blocks not sent to far players
954 if(!far_players.empty()) {
955 // Convert list format to that wanted by SetBlocksNotSent
956 std::map<v3s16, MapBlock*> modified_blocks2;
957 for(std::set<v3s16>::iterator
958 i = event->modified_blocks.begin();
959 i != event->modified_blocks.end(); ++i) {
960 modified_blocks2[*i] =
961 m_env->getMap().getBlockNoCreateNoEx(*i);
964 // Set blocks not sent
965 for(std::vector<u16>::iterator
966 i = far_players.begin();
967 i != far_players.end(); ++i) {
968 if(RemoteClient *client = getClient(*i))
969 client->SetBlocksNotSent(modified_blocks2);
975 /*// Don't send too many at a time
977 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
981 if(event_count >= 5){
982 infostream<<"Server: MapEditEvents:"<<std::endl;
983 prof.print(infostream);
984 } else if(event_count != 0){
985 verbosestream<<"Server: MapEditEvents:"<<std::endl;
986 prof.print(verbosestream);
992 Trigger emergethread (it somehow gets to a non-triggered but
993 bysy state sometimes)
996 float &counter = m_emergethread_trigger_timer;
998 if (counter >= 2.0) {
1001 m_emerge->startThreads();
1005 // Save map, players and auth stuff
1007 float &counter = m_savemap_timer;
1009 static const float save_interval =
1010 g_settings->getFloat("server_map_save_interval");
1011 if (counter >= save_interval) {
1013 MutexAutoLock lock(m_env_mutex);
1015 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1018 if (m_banmanager->isModified()) {
1019 m_banmanager->save();
1022 // Save changed parts of map
1023 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1026 m_env->saveLoadedPlayers();
1028 // Save environment metadata
1034 void Server::Receive()
1036 DSTACK(FUNCTION_NAME);
1037 SharedBuffer<u8> data;
1041 m_con.Receive(&pkt);
1042 peer_id = pkt.getPeerId();
1045 catch(con::InvalidIncomingDataException &e) {
1046 infostream<<"Server::Receive(): "
1047 "InvalidIncomingDataException: what()="
1048 <<e.what()<<std::endl;
1050 catch(SerializationError &e) {
1051 infostream<<"Server::Receive(): "
1052 "SerializationError: what()="
1053 <<e.what()<<std::endl;
1055 catch(ClientStateError &e) {
1056 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
1057 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
1058 L"Try reconnecting or updating your client");
1060 catch(con::PeerNotFoundException &e) {
1065 PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
1067 std::string playername = "";
1068 PlayerSAO *playersao = NULL;
1071 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1072 if (client != NULL) {
1073 playername = client->getName();
1074 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1076 } catch (std::exception &e) {
1082 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1084 // If failed, cancel
1085 if ((playersao == NULL) || (player == NULL)) {
1086 if (player && player->peer_id != 0) {
1087 actionstream << "Server: Failed to emerge player \"" << playername
1088 << "\" (player allocated to an another client)" << std::endl;
1089 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1090 L"name. If your client closed unexpectedly, try again in "
1093 errorstream << "Server: " << playername << ": Failed to emerge player"
1095 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1101 Send complete position information
1103 SendMovePlayer(peer_id);
1106 SendPlayerPrivileges(peer_id);
1108 // Send inventory formspec
1109 SendPlayerInventoryFormspec(peer_id);
1112 SendInventory(playersao);
1114 // Send HP or death screen
1115 if (playersao->isDead())
1116 SendDeathscreen(peer_id, false, v3f(0,0,0));
1118 SendPlayerHPOrDie(playersao);
1121 SendPlayerBreath(playersao);
1123 // Note things in chat if not in simple singleplayer mode
1124 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1125 // Send information about server to player in chat
1126 SendChatMessage(peer_id, getStatusString());
1128 Address addr = getPeerAddress(player->peer_id);
1129 std::string ip_str = addr.serializeString();
1130 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1135 const std::vector<std::string> &names = m_clients.getPlayerNames();
1137 actionstream << player->getName() << " joins game. List of players: ";
1139 for (std::vector<std::string>::const_iterator i = names.begin();
1140 i != names.end(); ++i) {
1141 actionstream << *i << " ";
1144 actionstream << player->getName() <<std::endl;
1149 inline void Server::handleCommand(NetworkPacket* pkt)
1151 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1152 (this->*opHandle.handler)(pkt);
1155 void Server::ProcessData(NetworkPacket *pkt)
1157 DSTACK(FUNCTION_NAME);
1158 // Environment is locked first.
1159 MutexAutoLock envlock(m_env_mutex);
1161 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1162 u32 peer_id = pkt->getPeerId();
1165 Address address = getPeerAddress(peer_id);
1166 std::string addr_s = address.serializeString();
1168 if(m_banmanager->isIpBanned(addr_s)) {
1169 std::string ban_name = m_banmanager->getBanName(addr_s);
1170 infostream << "Server: A banned client tried to connect from "
1171 << addr_s << "; banned name was "
1172 << ban_name << std::endl;
1173 // This actually doesn't seem to transfer to the client
1174 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1175 + utf8_to_wide(ban_name));
1179 catch(con::PeerNotFoundException &e) {
1181 * no peer for this packet found
1182 * most common reason is peer timeout, e.g. peer didn't
1183 * respond for some time, your server was overloaded or
1186 infostream << "Server::ProcessData(): Canceling: peer "
1187 << peer_id << " not found" << std::endl;
1192 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1194 // Command must be handled into ToServerCommandHandler
1195 if (command >= TOSERVER_NUM_MSG_TYPES) {
1196 infostream << "Server: Ignoring unknown command "
1197 << command << std::endl;
1201 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1206 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1208 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1209 errorstream << "Server::ProcessData(): Cancelling: Peer"
1210 " serialization format invalid or not initialized."
1211 " Skipping incoming command=" << command << std::endl;
1215 /* Handle commands related to client startup */
1216 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1221 if (m_clients.getClientState(peer_id) < CS_Active) {
1222 if (command == TOSERVER_PLAYERPOS) return;
1224 errorstream << "Got packet command: " << command << " for peer id "
1225 << peer_id << " but client isn't active yet. Dropping packet "
1231 } catch (SendFailedException &e) {
1232 errorstream << "Server::ProcessData(): SendFailedException: "
1233 << "what=" << e.what()
1235 } catch (PacketError &e) {
1236 actionstream << "Server::ProcessData(): PacketError: "
1237 << "what=" << e.what()
1242 void Server::setTimeOfDay(u32 time)
1244 m_env->setTimeOfDay(time);
1245 m_time_of_day_send_timer = 0;
1248 void Server::onMapEditEvent(MapEditEvent *event)
1250 if(m_ignore_map_edit_events)
1252 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1254 MapEditEvent *e = event->clone();
1255 m_unsent_map_edit_queue.push(e);
1258 Inventory* Server::getInventory(const InventoryLocation &loc)
1261 case InventoryLocation::UNDEFINED:
1262 case InventoryLocation::CURRENT_PLAYER:
1264 case InventoryLocation::PLAYER:
1266 RemotePlayer *player = dynamic_cast<RemotePlayer *>(m_env->getPlayer(loc.name.c_str()));
1269 PlayerSAO *playersao = player->getPlayerSAO();
1272 return playersao->getInventory();
1275 case InventoryLocation::NODEMETA:
1277 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1280 return meta->getInventory();
1283 case InventoryLocation::DETACHED:
1285 if(m_detached_inventories.count(loc.name) == 0)
1287 return m_detached_inventories[loc.name];
1291 sanity_check(false); // abort
1296 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1299 case InventoryLocation::UNDEFINED:
1301 case InventoryLocation::PLAYER:
1306 RemotePlayer *player =
1307 dynamic_cast<RemotePlayer *>(m_env->getPlayer(loc.name.c_str()));
1312 PlayerSAO *playersao = player->getPlayerSAO();
1316 SendInventory(playersao);
1319 case InventoryLocation::NODEMETA:
1321 v3s16 blockpos = getNodeBlockPos(loc.p);
1323 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1325 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1327 setBlockNotSent(blockpos);
1330 case InventoryLocation::DETACHED:
1332 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1336 sanity_check(false); // abort
1341 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1343 std::vector<u16> clients = m_clients.getClientIDs();
1345 // Set the modified blocks unsent for all the clients
1346 for (std::vector<u16>::iterator i = clients.begin();
1347 i != clients.end(); ++i) {
1348 if (RemoteClient *client = m_clients.lockedGetClientNoEx(*i))
1349 client->SetBlocksNotSent(block);
1354 void Server::peerAdded(con::Peer *peer)
1356 DSTACK(FUNCTION_NAME);
1357 verbosestream<<"Server::peerAdded(): peer->id="
1358 <<peer->id<<std::endl;
1361 c.type = con::PEER_ADDED;
1362 c.peer_id = peer->id;
1364 m_peer_change_queue.push(c);
1367 void Server::deletingPeer(con::Peer *peer, bool timeout)
1369 DSTACK(FUNCTION_NAME);
1370 verbosestream<<"Server::deletingPeer(): peer->id="
1371 <<peer->id<<", timeout="<<timeout<<std::endl;
1373 m_clients.event(peer->id, CSE_Disconnect);
1375 c.type = con::PEER_REMOVED;
1376 c.peer_id = peer->id;
1377 c.timeout = timeout;
1378 m_peer_change_queue.push(c);
1381 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
1383 *retval = m_con.getPeerStat(peer_id,type);
1384 if (*retval == -1) return false;
1388 bool Server::getClientInfo(
1397 std::string* vers_string
1400 *state = m_clients.getClientState(peer_id);
1402 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1404 if (client == NULL) {
1409 *uptime = client->uptime();
1410 *ser_vers = client->serialization_version;
1411 *prot_vers = client->net_proto_version;
1413 *major = client->getMajor();
1414 *minor = client->getMinor();
1415 *patch = client->getPatch();
1416 *vers_string = client->getPatch();
1423 void Server::handlePeerChanges()
1425 while(m_peer_change_queue.size() > 0)
1427 con::PeerChange c = m_peer_change_queue.front();
1428 m_peer_change_queue.pop();
1430 verbosestream<<"Server: Handling peer change: "
1431 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1436 case con::PEER_ADDED:
1437 m_clients.CreateClient(c.peer_id);
1440 case con::PEER_REMOVED:
1441 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1445 FATAL_ERROR("Invalid peer change event received!");
1451 void Server::printToConsoleOnly(const std::string &text)
1454 m_admin_chat->outgoing_queue.push_back(
1455 new ChatEventChat("", utf8_to_wide(text)));
1457 std::cout << text << std::endl;
1461 void Server::Send(NetworkPacket* pkt)
1463 m_clients.send(pkt->getPeerId(),
1464 clientCommandFactoryTable[pkt->getCommand()].channel,
1466 clientCommandFactoryTable[pkt->getCommand()].reliable);
1469 void Server::SendMovement(u16 peer_id)
1471 DSTACK(FUNCTION_NAME);
1472 std::ostringstream os(std::ios_base::binary);
1474 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1476 pkt << g_settings->getFloat("movement_acceleration_default");
1477 pkt << g_settings->getFloat("movement_acceleration_air");
1478 pkt << g_settings->getFloat("movement_acceleration_fast");
1479 pkt << g_settings->getFloat("movement_speed_walk");
1480 pkt << g_settings->getFloat("movement_speed_crouch");
1481 pkt << g_settings->getFloat("movement_speed_fast");
1482 pkt << g_settings->getFloat("movement_speed_climb");
1483 pkt << g_settings->getFloat("movement_speed_jump");
1484 pkt << g_settings->getFloat("movement_liquid_fluidity");
1485 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1486 pkt << g_settings->getFloat("movement_liquid_sink");
1487 pkt << g_settings->getFloat("movement_gravity");
1492 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1494 if (!g_settings->getBool("enable_damage"))
1497 u16 peer_id = playersao->getPeerID();
1498 bool is_alive = playersao->getHP() > 0;
1501 SendPlayerHP(peer_id);
1506 void Server::SendHP(u16 peer_id, u8 hp)
1508 DSTACK(FUNCTION_NAME);
1510 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1515 void Server::SendBreath(u16 peer_id, u16 breath)
1517 DSTACK(FUNCTION_NAME);
1519 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1520 pkt << (u16) breath;
1524 void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
1525 const std::string &custom_reason, bool reconnect)
1527 assert(reason < SERVER_ACCESSDENIED_MAX);
1529 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1531 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1532 pkt << custom_reason;
1533 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1534 reason == SERVER_ACCESSDENIED_CRASH)
1535 pkt << custom_reason << (u8)reconnect;
1539 void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
1541 DSTACK(FUNCTION_NAME);
1543 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1548 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
1549 v3f camera_point_target)
1551 DSTACK(FUNCTION_NAME);
1553 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1554 pkt << set_camera_point_target << camera_point_target;
1558 void Server::SendItemDef(u16 peer_id,
1559 IItemDefManager *itemdef, u16 protocol_version)
1561 DSTACK(FUNCTION_NAME);
1563 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1567 u32 length of the next item
1568 zlib-compressed serialized ItemDefManager
1570 std::ostringstream tmp_os(std::ios::binary);
1571 itemdef->serialize(tmp_os, protocol_version);
1572 std::ostringstream tmp_os2(std::ios::binary);
1573 compressZlib(tmp_os.str(), tmp_os2);
1574 pkt.putLongString(tmp_os2.str());
1577 verbosestream << "Server: Sending item definitions to id(" << peer_id
1578 << "): size=" << pkt.getSize() << std::endl;
1583 void Server::SendNodeDef(u16 peer_id,
1584 INodeDefManager *nodedef, u16 protocol_version)
1586 DSTACK(FUNCTION_NAME);
1588 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1592 u32 length of the next item
1593 zlib-compressed serialized NodeDefManager
1595 std::ostringstream tmp_os(std::ios::binary);
1596 nodedef->serialize(tmp_os, protocol_version);
1597 std::ostringstream tmp_os2(std::ios::binary);
1598 compressZlib(tmp_os.str(), tmp_os2);
1600 pkt.putLongString(tmp_os2.str());
1603 verbosestream << "Server: Sending node definitions to id(" << peer_id
1604 << "): size=" << pkt.getSize() << std::endl;
1610 Non-static send methods
1613 void Server::SendInventory(PlayerSAO* playerSAO)
1615 DSTACK(FUNCTION_NAME);
1617 UpdateCrafting(playerSAO->getPlayer());
1623 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1625 std::ostringstream os;
1626 playerSAO->getInventory()->serialize(os);
1628 std::string s = os.str();
1630 pkt.putRawString(s.c_str(), s.size());
1634 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
1636 DSTACK(FUNCTION_NAME);
1638 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1641 if (peer_id != PEER_ID_INEXISTENT) {
1645 m_clients.sendToAll(0, &pkt, true);
1649 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
1650 const std::string &formname)
1652 DSTACK(FUNCTION_NAME);
1654 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1655 if (formspec == "" ){
1656 //the client should close the formspec
1657 pkt.putLongString("");
1659 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1666 // Spawns a particle on peer with peer_id
1667 void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
1668 v3f pos, v3f velocity, v3f acceleration,
1669 float expirationtime, float size, bool collisiondetection,
1670 bool collision_removal,
1671 bool vertical, const std::string &texture,
1672 const struct TileAnimationParams &animation, u8 glow)
1674 DSTACK(FUNCTION_NAME);
1675 if (peer_id == PEER_ID_INEXISTENT) {
1676 // This sucks and should be replaced by a better solution in a refactor:
1677 std::vector<u16> clients = m_clients.getClientIDs();
1678 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
1679 RemotePlayer *player = m_env->getPlayer(*i);
1682 SendSpawnParticle(*i, player->protocol_version,
1683 pos, velocity, acceleration,
1684 expirationtime, size, collisiondetection,
1685 collision_removal, vertical, texture, animation, glow);
1690 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1692 pkt << pos << velocity << acceleration << expirationtime
1693 << size << collisiondetection;
1694 pkt.putLongString(texture);
1696 pkt << collision_removal;
1697 // This is horrible but required (why are there two ways to serialize pkts?)
1698 std::ostringstream os(std::ios_base::binary);
1699 animation.serialize(os, protocol_version);
1700 pkt.putRawString(os.str());
1706 // Adds a ParticleSpawner on peer with peer_id
1707 void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version,
1708 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1709 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1710 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1711 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1712 const struct TileAnimationParams &animation, u8 glow)
1714 DSTACK(FUNCTION_NAME);
1715 if (peer_id == PEER_ID_INEXISTENT) {
1716 // This sucks and should be replaced:
1717 std::vector<u16> clients = m_clients.getClientIDs();
1718 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
1719 RemotePlayer *player = m_env->getPlayer(*i);
1722 SendAddParticleSpawner(*i, player->protocol_version,
1723 amount, spawntime, minpos, maxpos,
1724 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1725 minsize, maxsize, collisiondetection, collision_removal,
1726 attached_id, vertical, texture, id, animation, glow);
1731 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1733 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1734 << minacc << maxacc << minexptime << maxexptime << minsize
1735 << maxsize << collisiondetection;
1737 pkt.putLongString(texture);
1739 pkt << id << vertical;
1740 pkt << collision_removal;
1742 // This is horrible but required
1743 std::ostringstream os(std::ios_base::binary);
1744 animation.serialize(os, protocol_version);
1745 pkt.putRawString(os.str());
1751 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
1753 DSTACK(FUNCTION_NAME);
1755 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
1757 // Ugly error in this packet
1760 if (peer_id != PEER_ID_INEXISTENT) {
1764 m_clients.sendToAll(0, &pkt, true);
1769 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
1771 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1773 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1774 << form->text << form->number << form->item << form->dir
1775 << form->align << form->offset << form->world_pos << form->size;
1780 void Server::SendHUDRemove(u16 peer_id, u32 id)
1782 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1787 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
1789 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1790 pkt << id << (u8) stat;
1794 case HUD_STAT_SCALE:
1795 case HUD_STAT_ALIGN:
1796 case HUD_STAT_OFFSET:
1797 pkt << *(v2f *) value;
1801 pkt << *(std::string *) value;
1803 case HUD_STAT_WORLD_POS:
1804 pkt << *(v3f *) value;
1807 pkt << *(v2s32 *) value;
1809 case HUD_STAT_NUMBER:
1813 pkt << *(u32 *) value;
1820 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
1822 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1824 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1826 pkt << flags << mask;
1831 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
1833 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1834 pkt << param << value;
1838 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
1839 const std::string &type, const std::vector<std::string> ¶ms)
1841 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1842 pkt << bgcolor << type << (u16) params.size();
1844 for(size_t i=0; i<params.size(); i++)
1850 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
1853 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1856 pkt << do_override << (u16) (ratio * 65535);
1861 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
1863 DSTACK(FUNCTION_NAME);
1865 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1866 pkt << time << time_speed;
1868 if (peer_id == PEER_ID_INEXISTENT) {
1869 m_clients.sendToAll(0, &pkt, true);
1876 void Server::SendPlayerHP(u16 peer_id)
1878 DSTACK(FUNCTION_NAME);
1879 PlayerSAO *playersao = getPlayerSAO(peer_id);
1880 // In some rare case if the player is disconnected
1881 // while Lua call l_punch, for example, this can be NULL
1885 SendHP(peer_id, playersao->getHP());
1886 m_script->player_event(playersao,"health_changed");
1888 // Send to other clients
1889 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1890 ActiveObjectMessage aom(playersao->getId(), true, str);
1891 playersao->m_messages_out.push(aom);
1894 void Server::SendPlayerBreath(PlayerSAO *sao)
1896 DSTACK(FUNCTION_NAME);
1899 m_script->player_event(sao, "breath_changed");
1900 SendBreath(sao->getPeerID(), sao->getBreath());
1903 void Server::SendMovePlayer(u16 peer_id)
1905 DSTACK(FUNCTION_NAME);
1906 RemotePlayer *player = m_env->getPlayer(peer_id);
1908 PlayerSAO *sao = player->getPlayerSAO();
1911 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1912 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1915 v3f pos = sao->getBasePosition();
1916 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1917 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1918 << " pitch=" << sao->getPitch()
1919 << " yaw=" << sao->getYaw()
1926 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
1928 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1931 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1932 << animation_frames[3] << animation_speed;
1937 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
1939 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1940 pkt << first << third;
1943 void Server::SendPlayerPrivileges(u16 peer_id)
1945 RemotePlayer *player = m_env->getPlayer(peer_id);
1947 if(player->peer_id == PEER_ID_INEXISTENT)
1950 std::set<std::string> privs;
1951 m_script->getAuth(player->getName(), NULL, &privs);
1953 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1954 pkt << (u16) privs.size();
1956 for(std::set<std::string>::const_iterator i = privs.begin();
1957 i != privs.end(); ++i) {
1964 void Server::SendPlayerInventoryFormspec(u16 peer_id)
1966 RemotePlayer *player = m_env->getPlayer(peer_id);
1968 if(player->peer_id == PEER_ID_INEXISTENT)
1971 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1972 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1976 u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
1978 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1979 pkt.putRawString(datas.c_str(), datas.size());
1981 return pkt.getSize();
1984 void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
1986 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1987 datas.size(), peer_id);
1989 pkt.putRawString(datas.c_str(), datas.size());
1991 m_clients.send(pkt.getPeerId(),
1992 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1997 s32 Server::playSound(const SimpleSoundSpec &spec,
1998 const ServerSoundParams ¶ms)
2000 // Find out initial position of sound
2001 bool pos_exists = false;
2002 v3f pos = params.getPos(m_env, &pos_exists);
2003 // If position is not found while it should be, cancel sound
2004 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2007 // Filter destination clients
2008 std::vector<u16> dst_clients;
2009 if(params.to_player != "")
2011 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2013 infostream<<"Server::playSound: Player \""<<params.to_player
2014 <<"\" not found"<<std::endl;
2017 if(player->peer_id == PEER_ID_INEXISTENT){
2018 infostream<<"Server::playSound: Player \""<<params.to_player
2019 <<"\" not connected"<<std::endl;
2022 dst_clients.push_back(player->peer_id);
2025 std::vector<u16> clients = m_clients.getClientIDs();
2027 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2028 RemotePlayer *player = m_env->getPlayer(*i);
2032 PlayerSAO *sao = player->getPlayerSAO();
2037 if(sao->getBasePosition().getDistanceFrom(pos) >
2038 params.max_hear_distance)
2041 dst_clients.push_back(*i);
2045 if(dst_clients.empty())
2049 s32 id = m_next_sound_id++;
2050 // The sound will exist as a reference in m_playing_sounds
2051 m_playing_sounds[id] = ServerPlayingSound();
2052 ServerPlayingSound &psound = m_playing_sounds[id];
2053 psound.params = params;
2055 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2056 pkt << id << spec.name << (float) (spec.gain * params.gain)
2057 << (u8) params.type << pos << params.object << params.loop;
2059 for(std::vector<u16>::iterator i = dst_clients.begin();
2060 i != dst_clients.end(); ++i) {
2061 psound.clients.insert(*i);
2062 m_clients.send(*i, 0, &pkt, true);
2066 void Server::stopSound(s32 handle)
2068 // Get sound reference
2069 UNORDERED_MAP<s32, ServerPlayingSound>::iterator i = m_playing_sounds.find(handle);
2070 if (i == m_playing_sounds.end())
2072 ServerPlayingSound &psound = i->second;
2074 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2077 for (UNORDERED_SET<u16>::iterator i = psound.clients.begin();
2078 i != psound.clients.end(); ++i) {
2080 m_clients.send(*i, 0, &pkt, true);
2082 // Remove sound reference
2083 m_playing_sounds.erase(i);
2086 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2087 std::vector<u16> *far_players, float far_d_nodes)
2089 float maxd = far_d_nodes*BS;
2090 v3f p_f = intToFloat(p, BS);
2092 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2095 std::vector<u16> clients = m_clients.getClientIDs();
2096 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2099 if (RemotePlayer *player = m_env->getPlayer(*i)) {
2100 PlayerSAO *sao = player->getPlayerSAO();
2104 // If player is far away, only set modified blocks not sent
2105 v3f player_pos = sao->getBasePosition();
2106 if (player_pos.getDistanceFrom(p_f) > maxd) {
2107 far_players->push_back(*i);
2114 m_clients.send(*i, 0, &pkt, true);
2118 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2119 std::vector<u16> *far_players, float far_d_nodes,
2120 bool remove_metadata)
2122 float maxd = far_d_nodes*BS;
2123 v3f p_f = intToFloat(p, BS);
2125 std::vector<u16> clients = m_clients.getClientIDs();
2126 for(std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2129 if (RemotePlayer *player = m_env->getPlayer(*i)) {
2130 PlayerSAO *sao = player->getPlayerSAO();
2134 // If player is far away, only set modified blocks not sent
2135 v3f player_pos = sao->getBasePosition();
2136 if(player_pos.getDistanceFrom(p_f) > maxd) {
2137 far_players->push_back(*i);
2143 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2145 RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
2147 pkt << p << n.param0 << n.param1 << n.param2
2148 << (u8) (remove_metadata ? 0 : 1);
2153 if (pkt.getSize() > 0)
2154 m_clients.send(*i, 0, &pkt, true);
2158 void Server::setBlockNotSent(v3s16 p)
2160 std::vector<u16> clients = m_clients.getClientIDs();
2162 for(std::vector<u16>::iterator i = clients.begin();
2163 i != clients.end(); ++i) {
2164 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
2165 client->SetBlockNotSent(p);
2170 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
2172 DSTACK(FUNCTION_NAME);
2174 v3s16 p = block->getPos();
2177 Create a packet with the block in the right format
2180 std::ostringstream os(std::ios_base::binary);
2181 block->serialize(os, ver, false);
2182 block->serializeNetworkSpecific(os);
2183 std::string s = os.str();
2185 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2188 pkt.putRawString(s.c_str(), s.size());
2192 void Server::SendBlocks(float dtime)
2194 DSTACK(FUNCTION_NAME);
2196 MutexAutoLock envlock(m_env_mutex);
2197 //TODO check if one big lock could be faster then multiple small ones
2199 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2201 std::vector<PrioritySortedBlockTransfer> queue;
2203 s32 total_sending = 0;
2206 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
2208 std::vector<u16> clients = m_clients.getClientIDs();
2211 for(std::vector<u16>::iterator i = clients.begin();
2212 i != clients.end(); ++i) {
2213 RemoteClient *client = m_clients.lockedGetClientNoEx(*i, CS_Active);
2218 total_sending += client->SendingCount();
2219 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2225 // Lowest priority number comes first.
2226 // Lowest is most important.
2227 std::sort(queue.begin(), queue.end());
2230 for(u32 i=0; i<queue.size(); i++)
2232 //TODO: Calculate limit dynamically
2233 if(total_sending >= g_settings->getS32
2234 ("max_simultaneous_block_sends_server_total"))
2237 PrioritySortedBlockTransfer q = queue[i];
2239 MapBlock *block = NULL;
2242 block = m_env->getMap().getBlockNoCreate(q.pos);
2244 catch(InvalidPositionException &e)
2249 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id, CS_Active);
2254 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
2256 client->SentBlock(q.pos);
2262 void Server::fillMediaCache()
2264 DSTACK(FUNCTION_NAME);
2266 infostream<<"Server: Calculating media file checksums"<<std::endl;
2268 // Collect all media file paths
2269 std::vector<std::string> paths;
2270 for(std::vector<ModSpec>::iterator i = m_mods.begin();
2271 i != m_mods.end(); ++i) {
2272 const ModSpec &mod = *i;
2273 paths.push_back(mod.path + DIR_DELIM + "textures");
2274 paths.push_back(mod.path + DIR_DELIM + "sounds");
2275 paths.push_back(mod.path + DIR_DELIM + "media");
2276 paths.push_back(mod.path + DIR_DELIM + "models");
2278 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2280 // Collect media file information from paths into cache
2281 for(std::vector<std::string>::iterator i = paths.begin();
2282 i != paths.end(); ++i) {
2283 std::string mediapath = *i;
2284 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2285 for (u32 j = 0; j < dirlist.size(); j++) {
2286 if (dirlist[j].dir) // Ignode dirs
2288 std::string filename = dirlist[j].name;
2289 // If name contains illegal characters, ignore the file
2290 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2291 infostream<<"Server: ignoring illegal file name: \""
2292 << filename << "\"" << std::endl;
2295 // If name is not in a supported format, ignore it
2296 const char *supported_ext[] = {
2297 ".png", ".jpg", ".bmp", ".tga",
2298 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2300 ".x", ".b3d", ".md2", ".obj",
2303 if (removeStringEnd(filename, supported_ext) == ""){
2304 infostream << "Server: ignoring unsupported file extension: \""
2305 << filename << "\"" << std::endl;
2308 // Ok, attempt to load the file and add to cache
2309 std::string filepath = mediapath + DIR_DELIM + filename;
2311 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2313 errorstream << "Server::fillMediaCache(): Could not open \""
2314 << filename << "\" for reading" << std::endl;
2317 std::ostringstream tmp_os(std::ios_base::binary);
2321 fis.read(buf, 1024);
2322 std::streamsize len = fis.gcount();
2323 tmp_os.write(buf, len);
2332 errorstream<<"Server::fillMediaCache(): Failed to read \""
2333 << filename << "\"" << std::endl;
2336 if(tmp_os.str().length() == 0) {
2337 errorstream << "Server::fillMediaCache(): Empty file \""
2338 << filepath << "\"" << std::endl;
2343 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2345 unsigned char *digest = sha1.getDigest();
2346 std::string sha1_base64 = base64_encode(digest, 20);
2347 std::string sha1_hex = hex_encode((char*)digest, 20);
2351 m_media[filename] = MediaInfo(filepath, sha1_base64);
2352 verbosestream << "Server: " << sha1_hex << " is " << filename
2358 void Server::sendMediaAnnouncement(u16 peer_id)
2360 DSTACK(FUNCTION_NAME);
2362 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2366 std::ostringstream os(std::ios_base::binary);
2368 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2369 pkt << (u16) m_media.size();
2371 for (UNORDERED_MAP<std::string, MediaInfo>::iterator i = m_media.begin();
2372 i != m_media.end(); ++i) {
2373 pkt << i->first << i->second.sha1_digest;
2376 pkt << g_settings->get("remote_media");
2380 struct SendableMedia
2386 SendableMedia(const std::string &name_="", const std::string &path_="",
2387 const std::string &data_=""):
2394 void Server::sendRequestedMedia(u16 peer_id,
2395 const std::vector<std::string> &tosend)
2397 DSTACK(FUNCTION_NAME);
2399 verbosestream<<"Server::sendRequestedMedia(): "
2400 <<"Sending files to client"<<std::endl;
2404 // Put 5kB in one bunch (this is not accurate)
2405 u32 bytes_per_bunch = 5000;
2407 std::vector< std::vector<SendableMedia> > file_bunches;
2408 file_bunches.push_back(std::vector<SendableMedia>());
2410 u32 file_size_bunch_total = 0;
2412 for(std::vector<std::string>::const_iterator i = tosend.begin();
2413 i != tosend.end(); ++i) {
2414 const std::string &name = *i;
2416 if (m_media.find(name) == m_media.end()) {
2417 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2418 <<"unknown file \""<<(name)<<"\""<<std::endl;
2422 //TODO get path + name
2423 std::string tpath = m_media[name].path;
2426 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2427 if(fis.good() == false){
2428 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2429 <<tpath<<"\" for reading"<<std::endl;
2432 std::ostringstream tmp_os(std::ios_base::binary);
2436 fis.read(buf, 1024);
2437 std::streamsize len = fis.gcount();
2438 tmp_os.write(buf, len);
2439 file_size_bunch_total += len;
2448 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2449 <<name<<"\""<<std::endl;
2452 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2453 <<tname<<"\""<<std::endl;*/
2455 file_bunches[file_bunches.size()-1].push_back(
2456 SendableMedia(name, tpath, tmp_os.str()));
2458 // Start next bunch if got enough data
2459 if(file_size_bunch_total >= bytes_per_bunch) {
2460 file_bunches.push_back(std::vector<SendableMedia>());
2461 file_size_bunch_total = 0;
2466 /* Create and send packets */
2468 u16 num_bunches = file_bunches.size();
2469 for(u16 i = 0; i < num_bunches; i++) {
2472 u16 total number of texture bunches
2473 u16 index of this bunch
2474 u32 number of files in this bunch
2483 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2484 pkt << num_bunches << i << (u32) file_bunches[i].size();
2486 for(std::vector<SendableMedia>::iterator
2487 j = file_bunches[i].begin();
2488 j != file_bunches[i].end(); ++j) {
2490 pkt.putLongString(j->data);
2493 verbosestream << "Server::sendRequestedMedia(): bunch "
2494 << i << "/" << num_bunches
2495 << " files=" << file_bunches[i].size()
2496 << " size=" << pkt.getSize() << std::endl;
2501 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
2503 if(m_detached_inventories.count(name) == 0) {
2504 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2507 Inventory *inv = m_detached_inventories[name];
2508 std::ostringstream os(std::ios_base::binary);
2510 os << serializeString(name);
2514 std::string s = os.str();
2516 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2517 pkt.putRawString(s.c_str(), s.size());
2519 const std::string &check = m_detached_inventories_player[name];
2520 if (peer_id == PEER_ID_INEXISTENT) {
2522 return m_clients.sendToAll(0, &pkt, true);
2523 RemotePlayer *p = m_env->getPlayer(check.c_str());
2525 m_clients.send(p->peer_id, 0, &pkt, true);
2527 if (check == "" || getPlayerName(peer_id) == check)
2532 void Server::sendDetachedInventories(u16 peer_id)
2534 DSTACK(FUNCTION_NAME);
2536 for(std::map<std::string, Inventory*>::iterator
2537 i = m_detached_inventories.begin();
2538 i != m_detached_inventories.end(); ++i) {
2539 const std::string &name = i->first;
2540 //Inventory *inv = i->second;
2541 sendDetachedInventory(name, peer_id);
2549 void Server::DiePlayer(u16 peer_id)
2551 DSTACK(FUNCTION_NAME);
2552 PlayerSAO *playersao = getPlayerSAO(peer_id);
2553 // In some rare cases this can be NULL -- if the player is disconnected
2554 // when a Lua function modifies l_punch, for example
2558 infostream << "Server::DiePlayer(): Player "
2559 << playersao->getPlayer()->getName()
2560 << " dies" << std::endl;
2562 playersao->setHP(0);
2564 // Trigger scripted stuff
2565 m_script->on_dieplayer(playersao);
2567 SendPlayerHP(peer_id);
2568 SendDeathscreen(peer_id, false, v3f(0,0,0));
2571 void Server::RespawnPlayer(u16 peer_id)
2573 DSTACK(FUNCTION_NAME);
2575 PlayerSAO *playersao = getPlayerSAO(peer_id);
2578 infostream << "Server::RespawnPlayer(): Player "
2579 << playersao->getPlayer()->getName()
2580 << " respawns" << std::endl;
2582 playersao->setHP(PLAYER_MAX_HP);
2583 playersao->setBreath(PLAYER_MAX_BREATH);
2585 bool repositioned = m_script->on_respawnplayer(playersao);
2586 if (!repositioned) {
2587 v3f pos = findSpawnPos();
2588 // setPos will send the new position to client
2589 playersao->setPos(pos);
2592 SendPlayerHP(peer_id);
2596 void Server::DenySudoAccess(u16 peer_id)
2598 DSTACK(FUNCTION_NAME);
2600 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2605 void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
2606 const std::string &str_reason, bool reconnect)
2608 if (proto_ver >= 25) {
2609 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2611 std::wstring wreason = utf8_to_wide(
2612 reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
2613 accessDeniedStrings[(u8)reason]);
2614 SendAccessDenied_Legacy(peer_id, wreason);
2617 m_clients.event(peer_id, CSE_SetDenied);
2618 m_con.DisconnectPeer(peer_id);
2622 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
2624 DSTACK(FUNCTION_NAME);
2626 SendAccessDenied(peer_id, reason, custom_reason);
2627 m_clients.event(peer_id, CSE_SetDenied);
2628 m_con.DisconnectPeer(peer_id);
2631 // 13/03/15: remove this function when protocol version 25 will become
2632 // the minimum version for MT users, maybe in 1 year
2633 void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
2635 DSTACK(FUNCTION_NAME);
2637 SendAccessDenied_Legacy(peer_id, reason);
2638 m_clients.event(peer_id, CSE_SetDenied);
2639 m_con.DisconnectPeer(peer_id);
2642 void Server::acceptAuth(u16 peer_id, bool forSudoMode)
2644 DSTACK(FUNCTION_NAME);
2647 RemoteClient* client = getClient(peer_id, CS_Invalid);
2649 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2651 // Right now, the auth mechs don't change between login and sudo mode.
2652 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2653 client->allowed_sudo_mechs = sudo_auth_mechs;
2655 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2656 << g_settings->getFloat("dedicated_server_step")
2660 m_clients.event(peer_id, CSE_AuthAccept);
2662 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2664 // We only support SRP right now
2665 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2667 resp_pkt << sudo_auth_mechs;
2669 m_clients.event(peer_id, CSE_SudoSuccess);
2673 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
2675 DSTACK(FUNCTION_NAME);
2676 std::wstring message;
2679 Clear references to playing sounds
2681 for (UNORDERED_MAP<s32, ServerPlayingSound>::iterator
2682 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2683 ServerPlayingSound &psound = i->second;
2684 psound.clients.erase(peer_id);
2685 if (psound.clients.empty())
2686 m_playing_sounds.erase(i++);
2691 RemotePlayer *player = m_env->getPlayer(peer_id);
2693 /* Run scripts and remove from environment */
2694 if (player != NULL) {
2695 PlayerSAO *playersao = player->getPlayerSAO();
2698 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2700 playersao->disconnected();
2707 if(player != NULL && reason != CDR_DENY) {
2708 std::ostringstream os(std::ios_base::binary);
2709 std::vector<u16> clients = m_clients.getClientIDs();
2711 for(std::vector<u16>::iterator i = clients.begin();
2712 i != clients.end(); ++i) {
2714 RemotePlayer *player = m_env->getPlayer(*i);
2718 // Get name of player
2719 os << player->getName() << " ";
2722 std::string name = player->getName();
2723 actionstream << name << " "
2724 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2725 << " List of players: " << os.str() << std::endl;
2727 m_admin_chat->outgoing_queue.push_back(
2728 new ChatEventNick(CET_NICK_REMOVE, name));
2732 MutexAutoLock env_lock(m_env_mutex);
2733 m_clients.DeleteClient(peer_id);
2737 // Send leave chat message to all remaining clients
2738 if(message.length() != 0)
2739 SendChatMessage(PEER_ID_INEXISTENT,message);
2742 void Server::UpdateCrafting(RemotePlayer *player)
2744 DSTACK(FUNCTION_NAME);
2746 // Get a preview for crafting
2748 InventoryLocation loc;
2749 loc.setPlayer(player->getName());
2750 std::vector<ItemStack> output_replacements;
2751 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2752 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2753 (&player->inventory)->getList("craft"), loc);
2755 // Put the new preview in
2756 InventoryList *plist = player->inventory.getList("craftpreview");
2757 sanity_check(plist);
2758 sanity_check(plist->getSize() >= 1);
2759 plist->changeItem(0, preview);
2762 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2764 if (evt->type == CET_NICK_ADD) {
2765 // The terminal informed us of its nick choice
2766 m_admin_nick = ((ChatEventNick *)evt)->nick;
2767 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2768 errorstream << "You haven't set up an account." << std::endl
2769 << "Please log in using the client as '"
2770 << m_admin_nick << "' with a secure password." << std::endl
2771 << "Until then, you can't execute admin tasks via the console," << std::endl
2772 << "and everybody can claim the user account instead of you," << std::endl
2773 << "giving them full control over this server." << std::endl;
2776 assert(evt->type == CET_CHAT);
2777 handleAdminChat((ChatEventChat *)evt);
2781 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2782 const std::wstring &wmessage, bool check_shout_priv, RemotePlayer *player)
2784 // If something goes wrong, this player is to blame
2785 RollbackScopeActor rollback_scope(m_rollback,
2786 std::string("player:") + name);
2789 switch (player->canSendChatMessage()) {
2790 case RPLAYER_CHATRESULT_FLOODING: {
2791 std::wstringstream ws;
2792 ws << L"You cannot send more messages. You are limited to "
2793 << g_settings->getFloat("chat_message_limit_per_10sec")
2794 << L" messages per 10 seconds.";
2797 case RPLAYER_CHATRESULT_KICK:
2798 DenyAccess_Legacy(player->peer_id,
2799 L"You have been kicked due to message flooding.");
2801 case RPLAYER_CHATRESULT_OK:
2804 FATAL_ERROR("Unhandled chat filtering result found.");
2808 if (m_max_chatmessage_length > 0
2809 && wmessage.length() > m_max_chatmessage_length) {
2810 return L"Your message exceed the maximum chat message limit set on the server. "
2811 L"It was refused. Send a shorter message";
2814 // Run script hook, exit if script ate the chat message
2815 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2820 // Whether to send line to the player that sent the message, or to all players
2821 bool broadcast_line = true;
2823 if (check_shout_priv && !checkPriv(name, "shout")) {
2824 line += L"-!- You don't have permission to shout.";
2825 broadcast_line = false;
2834 Tell calling method to send the message to sender
2836 if (!broadcast_line) {
2840 Send the message to others
2842 actionstream << "CHAT: " << wide_to_narrow(line) << std::endl;
2844 std::vector<u16> clients = m_clients.getClientIDs();
2847 Send the message back to the inital sender
2848 if they are using protocol version >= 29
2851 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2852 if (player && player->protocol_version >= 29)
2853 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2855 for (u16 i = 0; i < clients.size(); i++) {
2856 u16 cid = clients[i];
2857 if (cid != peer_id_to_avoid_sending)
2858 SendChatMessage(cid, line);
2864 void Server::handleAdminChat(const ChatEventChat *evt)
2866 std::string name = evt->nick;
2867 std::wstring wname = utf8_to_wide(name);
2868 std::wstring wmessage = evt->evt_msg;
2870 std::wstring answer = handleChat(name, wname, wmessage);
2872 // If asked to send answer to sender
2873 if (!answer.empty()) {
2874 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2878 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2880 RemoteClient *client = getClientNoEx(peer_id,state_min);
2882 throw ClientNotFoundException("Client not found");
2886 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2888 return m_clients.getClientNoEx(peer_id, state_min);
2891 std::string Server::getPlayerName(u16 peer_id)
2893 RemotePlayer *player = m_env->getPlayer(peer_id);
2895 return "[id="+itos(peer_id)+"]";
2896 return player->getName();
2899 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2901 RemotePlayer *player = m_env->getPlayer(peer_id);
2904 return player->getPlayerSAO();
2907 std::wstring Server::getStatusString()
2909 std::wostringstream os(std::ios_base::binary);
2912 os<<L"version="<<narrow_to_wide(g_version_string);
2914 os<<L", uptime="<<m_uptime.get();
2916 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2917 // Information about clients
2920 std::vector<u16> clients = m_clients.getClientIDs();
2921 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2923 RemotePlayer *player = m_env->getPlayer(*i);
2924 // Get name of player
2925 std::wstring name = L"unknown";
2927 name = narrow_to_wide(player->getName());
2928 // Add name to information string
2936 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
2937 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2938 if(g_settings->get("motd") != "")
2939 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2943 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2945 std::set<std::string> privs;
2946 m_script->getAuth(name, NULL, &privs);
2950 bool Server::checkPriv(const std::string &name, const std::string &priv)
2952 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2953 return (privs.count(priv) != 0);
2956 void Server::reportPrivsModified(const std::string &name)
2959 std::vector<u16> clients = m_clients.getClientIDs();
2960 for(std::vector<u16>::iterator i = clients.begin();
2961 i != clients.end(); ++i) {
2962 RemotePlayer *player = m_env->getPlayer(*i);
2963 reportPrivsModified(player->getName());
2966 RemotePlayer *player = m_env->getPlayer(name.c_str());
2969 SendPlayerPrivileges(player->peer_id);
2970 PlayerSAO *sao = player->getPlayerSAO();
2973 sao->updatePrivileges(
2974 getPlayerEffectivePrivs(name),
2979 void Server::reportInventoryFormspecModified(const std::string &name)
2981 RemotePlayer *player = m_env->getPlayer(name.c_str());
2984 SendPlayerInventoryFormspec(player->peer_id);
2987 void Server::setIpBanned(const std::string &ip, const std::string &name)
2989 m_banmanager->add(ip, name);
2992 void Server::unsetIpBanned(const std::string &ip_or_name)
2994 m_banmanager->remove(ip_or_name);
2997 std::string Server::getBanDescription(const std::string &ip_or_name)
2999 return m_banmanager->getBanDescription(ip_or_name);
3002 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3004 // m_env will be NULL if the server is initializing
3008 if (m_admin_nick == name && !m_admin_nick.empty()) {
3009 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3012 RemotePlayer *player = m_env->getPlayer(name);
3017 if (player->peer_id == PEER_ID_INEXISTENT)
3020 SendChatMessage(player->peer_id, msg);
3023 bool Server::showFormspec(const char *playername, const std::string &formspec,
3024 const std::string &formname)
3026 // m_env will be NULL if the server is initializing
3030 RemotePlayer *player = m_env->getPlayer(playername);
3034 SendShowFormspecMessage(player->peer_id, formspec, formname);
3038 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3043 u32 id = player->addHud(form);
3045 SendHUDAdd(player->peer_id, id, form);
3050 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3054 HudElement* todel = player->removeHud(id);
3061 SendHUDRemove(player->peer_id, id);
3065 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3070 SendHUDChange(player->peer_id, id, stat, data);
3074 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3079 SendHUDSetFlags(player->peer_id, flags, mask);
3080 player->hud_flags &= ~mask;
3081 player->hud_flags |= flags;
3083 PlayerSAO* playersao = player->getPlayerSAO();
3085 if (playersao == NULL)
3088 m_script->player_event(playersao, "hud_changed");
3092 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3097 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3100 player->setHotbarItemcount(hotbar_itemcount);
3101 std::ostringstream os(std::ios::binary);
3102 writeS32(os, hotbar_itemcount);
3103 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3107 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3112 player->setHotbarImage(name);
3113 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3116 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3120 return player->getHotbarImage();
3123 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3128 player->setHotbarSelectedImage(name);
3129 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3132 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3133 v2s32 animation_frames[4], f32 frame_speed)
3138 player->setLocalAnimations(animation_frames, frame_speed);
3139 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3143 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3148 player->eye_offset_first = first;
3149 player->eye_offset_third = third;
3150 SendEyeOffset(player->peer_id, first, third);
3154 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3155 const std::string &type, const std::vector<std::string> ¶ms)
3160 player->setSky(bgcolor, type, params);
3161 SendSetSky(player->peer_id, bgcolor, type, params);
3165 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3171 player->overrideDayNightRatio(do_override, ratio);
3172 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3176 void Server::notifyPlayers(const std::wstring &msg)
3178 SendChatMessage(PEER_ID_INEXISTENT,msg);
3181 void Server::spawnParticle(const std::string &playername, v3f pos,
3182 v3f velocity, v3f acceleration,
3183 float expirationtime, float size, bool
3184 collisiondetection, bool collision_removal,
3185 bool vertical, const std::string &texture,
3186 const struct TileAnimationParams &animation, u8 glow)
3188 // m_env will be NULL if the server is initializing
3192 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3193 if (playername != "") {
3194 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3197 peer_id = player->peer_id;
3198 proto_ver = player->protocol_version;
3201 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3202 expirationtime, size, collisiondetection,
3203 collision_removal, vertical, texture, animation, glow);
3206 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3207 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3208 float minexptime, float maxexptime, float minsize, float maxsize,
3209 bool collisiondetection, bool collision_removal,
3210 ServerActiveObject *attached, bool vertical, const std::string &texture,
3211 const std::string &playername, const struct TileAnimationParams &animation,
3214 // m_env will be NULL if the server is initializing
3218 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3219 if (playername != "") {
3220 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3223 peer_id = player->peer_id;
3224 proto_ver = player->protocol_version;
3227 u16 attached_id = attached ? attached->getId() : 0;
3230 if (attached_id == 0)
3231 id = m_env->addParticleSpawner(spawntime);
3233 id = m_env->addParticleSpawner(spawntime, attached_id);
3235 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3236 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3237 minexptime, maxexptime, minsize, maxsize,
3238 collisiondetection, collision_removal, attached_id, vertical,
3239 texture, id, animation, glow);
3244 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3246 // m_env will be NULL if the server is initializing
3248 throw ServerError("Can't delete particle spawners during initialisation!");
3250 u16 peer_id = PEER_ID_INEXISTENT;
3251 if (playername != "") {
3252 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3255 peer_id = player->peer_id;
3258 m_env->deleteParticleSpawner(id);
3259 SendDeleteParticleSpawner(peer_id, id);
3262 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3264 if(m_detached_inventories.count(name) > 0){
3265 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3266 delete m_detached_inventories[name];
3268 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3270 Inventory *inv = new Inventory(m_itemdef);
3272 m_detached_inventories[name] = inv;
3273 m_detached_inventories_player[name] = player;
3274 //TODO find a better way to do this
3275 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3279 // actions: time-reversed list
3280 // Return value: success/failure
3281 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3282 std::list<std::string> *log)
3284 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3285 ServerMap *map = (ServerMap*)(&m_env->getMap());
3287 // Fail if no actions to handle
3288 if(actions.empty()){
3289 log->push_back("Nothing to do.");
3296 for(std::list<RollbackAction>::const_iterator
3297 i = actions.begin();
3298 i != actions.end(); ++i)
3300 const RollbackAction &action = *i;
3302 bool success = action.applyRevert(map, this, this);
3305 std::ostringstream os;
3306 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3307 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3309 log->push_back(os.str());
3311 std::ostringstream os;
3312 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3313 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3315 log->push_back(os.str());
3319 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3320 <<" failed"<<std::endl;
3322 // Call it done if less than half failed
3323 return num_failed <= num_tried/2;
3326 // IGameDef interface
3328 IItemDefManager *Server::getItemDefManager()
3333 INodeDefManager *Server::getNodeDefManager()
3338 ICraftDefManager *Server::getCraftDefManager()
3343 u16 Server::allocateUnknownNodeId(const std::string &name)
3345 return m_nodedef->allocateDummy(name);
3348 MtEventManager *Server::getEventManager()
3353 IWritableItemDefManager *Server::getWritableItemDefManager()
3358 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3363 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3368 const ModSpec *Server::getModSpec(const std::string &modname) const
3370 std::vector<ModSpec>::const_iterator it;
3371 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3372 const ModSpec &mod = *it;
3373 if (mod.name == modname)
3379 void Server::getModNames(std::vector<std::string> &modlist)
3381 std::vector<ModSpec>::iterator it;
3382 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3383 modlist.push_back(it->name);
3386 std::string Server::getBuiltinLuaPath()
3388 return porting::path_share + DIR_DELIM + "builtin";
3391 std::string Server::getModStoragePath() const
3393 return m_path_world + DIR_DELIM + "mod_storage";
3396 v3f Server::findSpawnPos()
3398 ServerMap &map = m_env->getServerMap();
3400 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3401 return nodeposf * BS;
3404 bool is_good = false;
3406 // Try to find a good place a few times
3407 for(s32 i = 0; i < 4000 && !is_good; i++) {
3409 // We're going to try to throw the player to this position
3410 v2s16 nodepos2d = v2s16(
3411 -range + (myrand() % (range * 2)),
3412 -range + (myrand() % (range * 2)));
3414 // Get spawn level at point
3415 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3416 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3417 // the mapgen to signify an unsuitable spawn position
3418 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3421 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3424 for (s32 i = 0; i < 10; i++) {
3425 v3s16 blockpos = getNodeBlockPos(nodepos);
3426 map.emergeBlock(blockpos, true);
3427 content_t c = map.getNodeNoEx(nodepos).getContent();
3428 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3430 if (air_count >= 2) {
3431 nodeposf = intToFloat(nodepos, BS);
3432 // Don't spawn the player outside map boundaries
3433 if (objectpos_over_limit(nodeposf))
3446 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3448 bool newplayer = false;
3451 Try to get an existing player
3453 RemotePlayer *player = m_env->getPlayer(name);
3455 // If player is already connected, cancel
3456 if (player != NULL && player->peer_id != 0) {
3457 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3462 If player with the wanted peer_id already exists, cancel.
3464 if (m_env->getPlayer(peer_id) != NULL) {
3465 infostream<<"emergePlayer(): Player with wrong name but same"
3466 " peer_id already exists"<<std::endl;
3470 // Create a new player active object
3471 PlayerSAO *playersao = new PlayerSAO(m_env, peer_id, isSingleplayer());
3472 player = m_env->loadPlayer(name, playersao);
3474 // Create player if it doesn't exist
3477 player = new RemotePlayer(name, this->idef());
3478 // Set player position
3479 infostream<<"Server: Finding spawn place for player \""
3480 <<name<<"\""<<std::endl;
3481 playersao->setBasePosition(findSpawnPos());
3483 // Make sure the player is saved
3484 player->setModified(true);
3486 // Add player to environment
3487 m_env->addPlayer(player);
3489 // If the player exists, ensure that they respawn inside legal bounds
3490 // This fixes an assert crash when the player can't be added
3491 // to the environment
3492 if (objectpos_over_limit(playersao->getBasePosition())) {
3493 actionstream << "Respawn position for player \""
3494 << name << "\" outside limits, resetting" << std::endl;
3495 playersao->setBasePosition(findSpawnPos());
3499 playersao->initialize(player, getPlayerEffectivePrivs(player->getName()));
3501 player->protocol_version = proto_version;
3503 /* Clean up old HUD elements from previous sessions */
3506 /* Add object to environment */
3507 m_env->addActiveObject(playersao);
3511 m_script->on_newplayer(playersao);
3517 bool Server::registerModStorage(ModMetadata *storage)
3519 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3520 errorstream << "Unable to register same mod storage twice. Storage name: "
3521 << storage->getModName() << std::endl;
3525 m_mod_storages[storage->getModName()] = storage;
3529 void Server::unregisterModStorage(const std::string &name)
3531 UNORDERED_MAP<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3532 if (it != m_mod_storages.end()) {
3533 // Save unconditionaly on unregistration
3534 it->second->save(getModStoragePath());
3535 m_mod_storages.erase(name);
3539 void dedicated_server_loop(Server &server, bool &kill)
3541 DSTACK(FUNCTION_NAME);
3543 verbosestream<<"dedicated_server_loop()"<<std::endl;
3545 IntervalLimiter m_profiler_interval;
3547 static const float steplen = g_settings->getFloat("dedicated_server_step");
3548 static const float profiler_print_interval =
3549 g_settings->getFloat("profiler_print_interval");
3552 // This is kind of a hack but can be done like this
3553 // because server.step() is very light
3555 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3556 sleep_ms((int)(steplen*1000.0));
3558 server.step(steplen);
3560 if (server.getShutdownRequested() || kill)
3566 if (profiler_print_interval != 0) {
3567 if(m_profiler_interval.step(steplen, profiler_print_interval))
3569 infostream<<"Profiler:"<<std::endl;
3570 g_profiler->print(infostream);
3571 g_profiler->clear();
3576 infostream << "Dedicated server quitting" << std::endl;
3578 if (g_settings->getBool("server_announce"))
3579 ServerList::sendAnnounce(ServerList::AA_DELETE,
3580 server.m_bind_addr.getPort());