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 "scripting_game.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content_abm.h"
51 #include "content_sao.h"
53 #include "sound.h" // dummySoundManager
54 #include "event_manager.h"
55 #include "serverlist.h"
56 #include "util/string.h"
57 #include "util/mathconstants.h"
59 #include "util/serialize.h"
60 #include "util/thread.h"
61 #include "defaultsettings.h"
62 #include "util/base64.h"
63 #include "util/sha1.h"
66 class ClientNotFoundException : public BaseException
69 ClientNotFoundException(const char *s):
74 class ServerThread : public Thread
78 ServerThread(Server *server):
89 void *ServerThread::run()
91 DSTACK(FUNCTION_NAME);
92 BEGIN_DEBUG_EXCEPTION_HANDLER
94 m_server->AsyncRunStep(true);
96 while (!stopRequested()) {
98 //TimeTaker timer("AsyncRunStep() + Receive()");
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("Lua: " + std::string(e.what()));
115 END_DEBUG_EXCEPTION_HANDLER
120 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
122 if(pos_exists) *pos_exists = false;
127 if(pos_exists) *pos_exists = true;
132 ServerActiveObject *sao = env->getActiveObject(object);
135 if(pos_exists) *pos_exists = true;
136 return sao->getBasePosition(); }
148 const std::string &path_world,
149 const SubgameSpec &gamespec,
150 bool simple_singleplayer_mode,
154 m_path_world(path_world),
155 m_gamespec(gamespec),
156 m_simple_singleplayer_mode(simple_singleplayer_mode),
157 m_async_fatal_error(""),
166 m_enable_rollback_recording(false),
169 m_itemdef(createItemDefManager()),
170 m_nodedef(createNodeDefManager()),
171 m_craftdef(createCraftDefManager()),
172 m_event(new EventManager()),
174 m_time_of_day_send_timer(0),
177 m_shutdown_requested(false),
178 m_shutdown_ask_reconnect(false),
180 m_ignore_map_edit_events(false),
181 m_ignore_map_edit_events_peer_id(0),
185 m_liquid_transform_timer = 0.0;
186 m_liquid_transform_every = 1.0;
187 m_masterserver_timer = 0.0;
188 m_emergethread_trigger_timer = 0.0;
189 m_savemap_timer = 0.0;
192 m_lag = g_settings->getFloat("dedicated_server_step");
195 throw ServerError("Supplied empty world path");
197 if(!gamespec.isValid())
198 throw ServerError("Supplied invalid gamespec");
200 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
201 if(m_simple_singleplayer_mode)
202 infostream<<" in simple singleplayer mode"<<std::endl;
204 infostream<<std::endl;
205 infostream<<"- world: "<<m_path_world<<std::endl;
206 infostream<<"- game: "<<m_gamespec.path<<std::endl;
208 // Create world if it doesn't exist
209 if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
210 throw ServerError("Failed to initialize world");
212 // Create server thread
213 m_thread = new ServerThread(this);
215 // Create emerge manager
216 m_emerge = new EmergeManager(this);
218 // Create ban manager
219 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
220 m_banmanager = new BanManager(ban_path);
222 ModConfiguration modconf(m_path_world);
223 m_mods = modconf.getMods();
224 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
225 // complain about mods with unsatisfied dependencies
226 if(!modconf.isConsistent()) {
227 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
228 it != unsatisfied_mods.end(); ++it) {
230 errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
231 for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
232 dep_it != mod.unsatisfied_depends.end(); ++dep_it)
233 errorstream << " \"" << *dep_it << "\"";
234 errorstream << std::endl;
238 Settings worldmt_settings;
239 std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
240 worldmt_settings.readConfigFile(worldmt.c_str());
241 std::vector<std::string> names = worldmt_settings.getNames();
242 std::set<std::string> load_mod_names;
243 for(std::vector<std::string>::iterator it = names.begin();
244 it != names.end(); ++it) {
245 std::string name = *it;
246 if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
247 load_mod_names.insert(name.substr(9));
249 // complain about mods declared to be loaded, but not found
250 for(std::vector<ModSpec>::iterator it = m_mods.begin();
251 it != m_mods.end(); ++it)
252 load_mod_names.erase((*it).name);
253 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
254 it != unsatisfied_mods.end(); ++it)
255 load_mod_names.erase((*it).name);
256 if(!load_mod_names.empty()) {
257 errorstream << "The following mods could not be found:";
258 for(std::set<std::string>::iterator it = load_mod_names.begin();
259 it != load_mod_names.end(); ++it)
260 errorstream << " \"" << (*it) << "\"";
261 errorstream << std::endl;
265 MutexAutoLock envlock(m_env_mutex);
267 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
268 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
270 // Initialize scripting
271 infostream<<"Server: Initializing Lua"<<std::endl;
273 m_script = new GameScripting(this);
275 std::string script_path = getBuiltinLuaPath() + DIR_DELIM "init.lua";
277 m_script->loadMod(script_path, BUILTIN_MOD_NAME);
280 infostream << "Server: Loading mods: ";
281 for(std::vector<ModSpec>::iterator i = m_mods.begin();
282 i != m_mods.end(); ++i) {
283 const ModSpec &mod = *i;
284 infostream << mod.name << " ";
286 infostream << std::endl;
287 // Load and run "mod" scripts
288 for (std::vector<ModSpec>::iterator it = m_mods.begin();
289 it != m_mods.end(); ++it) {
290 const ModSpec &mod = *it;
291 if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
292 throw ModError("Error loading mod \"" + mod.name +
293 "\": Mod name does not follow naming conventions: "
294 "Only chararacters [a-z0-9_] are allowed.");
296 std::string script_path = mod.path + DIR_DELIM + "init.lua";
297 infostream << " [" << padStringRight(mod.name, 12) << "] [\""
298 << script_path << "\"]" << std::endl;
299 m_script->loadMod(script_path, mod.name);
302 // Read Textures and calculate sha1 sums
305 // Apply item aliases in the node definition manager
306 m_nodedef->updateAliases(m_itemdef);
308 // Apply texture overrides from texturepack/override.txt
309 std::string texture_path = g_settings->get("texture_path");
310 if (texture_path != "" && fs::IsDir(texture_path))
311 m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
313 m_nodedef->setNodeRegistrationStatus(true);
315 // Perform pending node name resolutions
316 m_nodedef->runNodeResolveCallbacks();
318 // unmap node names for connected nodeboxes
319 m_nodedef->mapNodeboxConnections();
321 // init the recipe hashes to speed up crafting
322 m_craftdef->initHashes(this);
324 // Initialize Environment
325 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
327 m_clients.setEnv(m_env);
329 if (!servermap->settings_mgr.makeMapgenParams())
330 FATAL_ERROR("Couldn't create any mapgen type");
332 // Initialize mapgens
333 m_emerge->initMapgens(servermap->getMapgenParams());
335 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
336 if (m_enable_rollback_recording) {
337 // Create rollback manager
338 m_rollback = new RollbackManager(m_path_world, this);
341 // Give environment reference to scripting api
342 m_script->initializeEnvironment(m_env);
344 // Register us to receive map edit events
345 servermap->addEventReceiver(this);
347 // If file exists, load environment metadata
348 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
349 infostream << "Server: Loading environment metadata" << std::endl;
352 m_env->loadDefaultMeta();
355 // Add some test ActiveBlockModifiers to environment
356 add_legacy_abms(m_env, m_nodedef);
358 m_liquid_transform_every = g_settings->getFloat("liquid_update");
359 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
364 infostream<<"Server destructing"<<std::endl;
366 // Send shutdown message
367 SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
370 MutexAutoLock envlock(m_env_mutex);
372 // Execute script shutdown hooks
373 m_script->on_shutdown();
375 infostream << "Server: Saving players" << std::endl;
376 m_env->saveLoadedPlayers();
378 infostream << "Server: Kicking players" << std::endl;
379 std::string kick_msg;
380 bool reconnect = false;
381 if (getShutdownRequested()) {
382 reconnect = m_shutdown_ask_reconnect;
383 kick_msg = m_shutdown_msg;
385 if (kick_msg == "") {
386 kick_msg = g_settings->get("kick_msg_shutdown");
388 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
389 kick_msg, reconnect);
391 infostream << "Server: Saving environment metadata" << std::endl;
399 // stop all emerge threads before deleting players that may have
400 // requested blocks to be emerged
401 m_emerge->stopThreads();
403 // Delete things in the reverse order of creation
413 // Deinitialize scripting
414 infostream<<"Server: Deinitializing scripting"<<std::endl;
417 // Delete detached inventories
418 for (std::map<std::string, Inventory*>::iterator
419 i = m_detached_inventories.begin();
420 i != m_detached_inventories.end(); ++i) {
425 void Server::start(Address bind_addr)
427 DSTACK(FUNCTION_NAME);
429 m_bind_addr = bind_addr;
431 infostream<<"Starting server on "
432 << bind_addr.serializeString() <<"..."<<std::endl;
434 // Stop thread if already running
437 // Initialize connection
438 m_con.SetTimeoutMs(30);
439 m_con.Serve(bind_addr);
444 // ASCII art for the win!
446 <<" .__ __ __ "<<std::endl
447 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
448 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
449 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
450 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
451 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
452 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
453 actionstream<<"Server for gameid=\""<<m_gamespec.id
454 <<"\" listening on "<<bind_addr.serializeString()<<":"
455 <<bind_addr.getPort() << "."<<std::endl;
460 DSTACK(FUNCTION_NAME);
462 infostream<<"Server: Stopping and waiting threads"<<std::endl;
464 // Stop threads (set run=false first so both start stopping)
466 //m_emergethread.setRun(false);
468 //m_emergethread.stop();
470 infostream<<"Server: Threads stopped"<<std::endl;
473 void Server::step(float dtime)
475 DSTACK(FUNCTION_NAME);
480 MutexAutoLock lock(m_step_dtime_mutex);
481 m_step_dtime += dtime;
483 // Throw if fatal error occurred in thread
484 std::string async_err = m_async_fatal_error.get();
485 if (!async_err.empty()) {
486 if (!m_simple_singleplayer_mode) {
487 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
488 g_settings->get("kick_msg_crash"),
489 g_settings->getBool("ask_reconnect_on_crash"));
491 throw ServerError(async_err);
495 void Server::AsyncRunStep(bool initial_step)
497 DSTACK(FUNCTION_NAME);
499 g_profiler->add("Server::AsyncRunStep (num)", 1);
503 MutexAutoLock lock1(m_step_dtime_mutex);
504 dtime = m_step_dtime;
508 // Send blocks to clients
512 if((dtime < 0.001) && (initial_step == false))
515 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
517 //infostream<<"Server steps "<<dtime<<std::endl;
518 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
521 MutexAutoLock lock1(m_step_dtime_mutex);
522 m_step_dtime -= dtime;
529 m_uptime.set(m_uptime.get() + dtime);
535 Update time of day and overall game time
537 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
540 Send to clients at constant intervals
543 m_time_of_day_send_timer -= dtime;
544 if(m_time_of_day_send_timer < 0.0) {
545 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
546 u16 time = m_env->getTimeOfDay();
547 float time_speed = g_settings->getFloat("time_speed");
548 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
552 MutexAutoLock lock(m_env_mutex);
553 // Figure out and report maximum lag to environment
554 float max_lag = m_env->getMaxLagEstimate();
555 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
557 if(dtime > 0.1 && dtime > max_lag * 2.0)
558 infostream<<"Server: Maximum lag peaked to "<<dtime
562 m_env->reportMaxLagEstimate(max_lag);
564 ScopeProfiler sp(g_profiler, "SEnv step");
565 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
569 static const float map_timer_and_unload_dtime = 2.92;
570 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
572 MutexAutoLock lock(m_env_mutex);
573 // Run Map's timers and unload unused data
574 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
575 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
576 g_settings->getFloat("server_unload_unused_data_timeout"),
581 Listen to the admin chat, if available
584 if (!m_admin_chat->command_queue.empty()) {
585 MutexAutoLock lock(m_env_mutex);
586 while (!m_admin_chat->command_queue.empty()) {
587 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
588 handleChatInterfaceEvent(evt);
592 m_admin_chat->outgoing_queue.push_back(
593 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
600 /* Transform liquids */
601 m_liquid_transform_timer += dtime;
602 if(m_liquid_transform_timer >= m_liquid_transform_every)
604 m_liquid_transform_timer -= m_liquid_transform_every;
606 MutexAutoLock lock(m_env_mutex);
608 ScopeProfiler sp(g_profiler, "Server: liquid transform");
610 std::map<v3s16, MapBlock*> modified_blocks;
611 m_env->getMap().transformLiquids(modified_blocks);
616 core::map<v3s16, MapBlock*> lighting_modified_blocks;
617 ServerMap &map = ((ServerMap&)m_env->getMap());
618 map.updateLighting(modified_blocks, lighting_modified_blocks);
620 // Add blocks modified by lighting to modified_blocks
621 for(core::map<v3s16, MapBlock*>::Iterator
622 i = lighting_modified_blocks.getIterator();
623 i.atEnd() == false; i++)
625 MapBlock *block = i.getNode()->getValue();
626 modified_blocks.insert(block->getPos(), block);
630 Set the modified blocks unsent for all the clients
632 if(!modified_blocks.empty())
634 SetBlocksNotSent(modified_blocks);
637 m_clients.step(dtime);
639 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
641 // send masterserver announce
643 float &counter = m_masterserver_timer;
644 if(!isSingleplayer() && (!counter || counter >= 300.0) &&
645 g_settings->getBool("server_announce"))
647 ServerList::sendAnnounce(counter ? "update" : "start",
648 m_bind_addr.getPort(),
649 m_clients.getPlayerNames(),
651 m_env->getGameTime(),
654 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
663 Check added and deleted active objects
666 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
667 MutexAutoLock envlock(m_env_mutex);
670 UNORDERED_MAP<u16, RemoteClient*> clients = m_clients.getClientList();
671 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
673 // Radius inside which objects are active
674 static const s16 radius =
675 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
677 // Radius inside which players are active
678 static const bool is_transfer_limited =
679 g_settings->exists("unlimited_player_transfer_distance") &&
680 !g_settings->getBool("unlimited_player_transfer_distance");
681 static const s16 player_transfer_dist = g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
682 s16 player_radius = player_transfer_dist;
683 if (player_radius == 0 && is_transfer_limited)
684 player_radius = radius;
686 for (UNORDERED_MAP<u16, RemoteClient*>::iterator i = clients.begin();
687 i != clients.end(); ++i) {
688 RemoteClient *client = i->second;
690 // If definitions and textures have not been sent, don't
691 // send objects either
692 if (client->getState() < CS_DefinitionsSent)
695 RemotePlayer *player = m_env->getPlayer(client->peer_id);
696 if (player == NULL) {
697 // This can happen if the client timeouts somehow
698 /*warningstream<<FUNCTION_NAME<<": Client "
700 <<" has no associated player"<<std::endl;*/
704 PlayerSAO *playersao = player->getPlayerSAO();
705 if (playersao == NULL)
708 std::queue<u16> removed_objects;
709 std::queue<u16> added_objects;
710 m_env->getRemovedActiveObjects(playersao, radius, player_radius,
711 client->m_known_objects, removed_objects);
712 m_env->getAddedActiveObjects(playersao, radius, player_radius,
713 client->m_known_objects, added_objects);
715 // Ignore if nothing happened
716 if (removed_objects.empty() && added_objects.empty()) {
720 std::string data_buffer;
724 // Handle removed objects
725 writeU16((u8*)buf, removed_objects.size());
726 data_buffer.append(buf, 2);
727 while (!removed_objects.empty()) {
729 u16 id = removed_objects.front();
730 ServerActiveObject* obj = m_env->getActiveObject(id);
732 // Add to data buffer for sending
733 writeU16((u8*)buf, id);
734 data_buffer.append(buf, 2);
736 // Remove from known objects
737 client->m_known_objects.erase(id);
739 if(obj && obj->m_known_by_count > 0)
740 obj->m_known_by_count--;
741 removed_objects.pop();
744 // Handle added objects
745 writeU16((u8*)buf, added_objects.size());
746 data_buffer.append(buf, 2);
747 while (!added_objects.empty()) {
749 u16 id = added_objects.front();
750 ServerActiveObject* obj = m_env->getActiveObject(id);
753 u8 type = ACTIVEOBJECT_TYPE_INVALID;
755 warningstream<<FUNCTION_NAME
756 <<": NULL object"<<std::endl;
758 type = obj->getSendType();
760 // Add to data buffer for sending
761 writeU16((u8*)buf, id);
762 data_buffer.append(buf, 2);
763 writeU8((u8*)buf, type);
764 data_buffer.append(buf, 1);
767 data_buffer.append(serializeLongString(
768 obj->getClientInitializationData(client->net_proto_version)));
770 data_buffer.append(serializeLongString(""));
772 // Add to known objects
773 client->m_known_objects.insert(id);
776 obj->m_known_by_count++;
781 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
782 verbosestream << "Server: Sent object remove/add: "
783 << removed_objects.size() << " removed, "
784 << added_objects.size() << " added, "
785 << "packet size is " << pktSize << std::endl;
794 MutexAutoLock envlock(m_env_mutex);
795 ScopeProfiler sp(g_profiler, "Server: sending object messages");
798 // Value = data sent by object
799 UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* > buffered_messages;
801 // Get active object messages from environment
803 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
807 std::vector<ActiveObjectMessage>* message_list = NULL;
808 UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator n;
809 n = buffered_messages.find(aom.id);
810 if (n == buffered_messages.end()) {
811 message_list = new std::vector<ActiveObjectMessage>;
812 buffered_messages[aom.id] = message_list;
815 message_list = n->second;
817 message_list->push_back(aom);
821 UNORDERED_MAP<u16, RemoteClient*> clients = m_clients.getClientList();
822 // Route data to every client
823 for (UNORDERED_MAP<u16, RemoteClient*>::iterator i = clients.begin();
824 i != clients.end(); ++i) {
825 RemoteClient *client = i->second;
826 std::string reliable_data;
827 std::string unreliable_data;
828 // Go through all objects in message buffer
829 for (UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator
830 j = buffered_messages.begin();
831 j != buffered_messages.end(); ++j) {
832 // If object is not known by client, skip it
834 if (client->m_known_objects.find(id) == client->m_known_objects.end())
837 // Get message list of object
838 std::vector<ActiveObjectMessage>* list = j->second;
839 // Go through every message
840 for (std::vector<ActiveObjectMessage>::iterator
841 k = list->begin(); k != list->end(); ++k) {
842 // Compose the full new data with header
843 ActiveObjectMessage aom = *k;
844 std::string new_data;
847 writeU16((u8*)&buf[0], aom.id);
848 new_data.append(buf, 2);
850 new_data += serializeString(aom.datastring);
851 // Add data to buffer
853 reliable_data += new_data;
855 unreliable_data += new_data;
859 reliable_data and unreliable_data are now ready.
862 if(reliable_data.size() > 0) {
863 SendActiveObjectMessages(client->peer_id, reliable_data);
866 if(unreliable_data.size() > 0) {
867 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
872 // Clear buffered_messages
873 for (UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator
874 i = buffered_messages.begin();
875 i != buffered_messages.end(); ++i) {
881 Send queued-for-sending map edit events.
884 // We will be accessing the environment
885 MutexAutoLock lock(m_env_mutex);
887 // Don't send too many at a time
890 // Single change sending is disabled if queue size is not small
891 bool disable_single_change_sending = false;
892 if(m_unsent_map_edit_queue.size() >= 4)
893 disable_single_change_sending = true;
895 int event_count = m_unsent_map_edit_queue.size();
897 // We'll log the amount of each
900 while(m_unsent_map_edit_queue.size() != 0)
902 MapEditEvent* event = m_unsent_map_edit_queue.front();
903 m_unsent_map_edit_queue.pop();
905 // Players far away from the change are stored here.
906 // Instead of sending the changes, MapBlocks are set not sent
908 std::vector<u16> far_players;
910 switch (event->type) {
913 prof.add("MEET_ADDNODE", 1);
914 sendAddNode(event->p, event->n, event->already_known_by_peer,
915 &far_players, disable_single_change_sending ? 5 : 30,
916 event->type == MEET_ADDNODE);
918 case MEET_REMOVENODE:
919 prof.add("MEET_REMOVENODE", 1);
920 sendRemoveNode(event->p, event->already_known_by_peer,
921 &far_players, disable_single_change_sending ? 5 : 30);
923 case MEET_BLOCK_NODE_METADATA_CHANGED:
924 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
925 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
926 setBlockNotSent(event->p);
929 infostream << "Server: MEET_OTHER" << std::endl;
930 prof.add("MEET_OTHER", 1);
931 for(std::set<v3s16>::iterator
932 i = event->modified_blocks.begin();
933 i != event->modified_blocks.end(); ++i) {
938 prof.add("unknown", 1);
939 warningstream << "Server: Unknown MapEditEvent "
940 << ((u32)event->type) << std::endl;
945 Set blocks not sent to far players
947 if(!far_players.empty()) {
948 // Convert list format to that wanted by SetBlocksNotSent
949 std::map<v3s16, MapBlock*> modified_blocks2;
950 for(std::set<v3s16>::iterator
951 i = event->modified_blocks.begin();
952 i != event->modified_blocks.end(); ++i) {
953 modified_blocks2[*i] =
954 m_env->getMap().getBlockNoCreateNoEx(*i);
957 // Set blocks not sent
958 for(std::vector<u16>::iterator
959 i = far_players.begin();
960 i != far_players.end(); ++i) {
961 if(RemoteClient *client = getClient(*i))
962 client->SetBlocksNotSent(modified_blocks2);
968 /*// Don't send too many at a time
970 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
974 if(event_count >= 5){
975 infostream<<"Server: MapEditEvents:"<<std::endl;
976 prof.print(infostream);
977 } else if(event_count != 0){
978 verbosestream<<"Server: MapEditEvents:"<<std::endl;
979 prof.print(verbosestream);
985 Trigger emergethread (it somehow gets to a non-triggered but
986 bysy state sometimes)
989 float &counter = m_emergethread_trigger_timer;
991 if (counter >= 2.0) {
994 m_emerge->startThreads();
998 // Save map, players and auth stuff
1000 float &counter = m_savemap_timer;
1002 static const float save_interval =
1003 g_settings->getFloat("server_map_save_interval");
1004 if (counter >= save_interval) {
1006 MutexAutoLock lock(m_env_mutex);
1008 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1011 if (m_banmanager->isModified()) {
1012 m_banmanager->save();
1015 // Save changed parts of map
1016 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1019 m_env->saveLoadedPlayers();
1021 // Save environment metadata
1027 void Server::Receive()
1029 DSTACK(FUNCTION_NAME);
1030 SharedBuffer<u8> data;
1034 m_con.Receive(&pkt);
1035 peer_id = pkt.getPeerId();
1038 catch(con::InvalidIncomingDataException &e) {
1039 infostream<<"Server::Receive(): "
1040 "InvalidIncomingDataException: what()="
1041 <<e.what()<<std::endl;
1043 catch(SerializationError &e) {
1044 infostream<<"Server::Receive(): "
1045 "SerializationError: what()="
1046 <<e.what()<<std::endl;
1048 catch(ClientStateError &e) {
1049 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
1050 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
1051 L"Try reconnecting or updating your client");
1053 catch(con::PeerNotFoundException &e) {
1058 PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
1060 std::string playername = "";
1061 PlayerSAO *playersao = NULL;
1064 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1065 if (client != NULL) {
1066 playername = client->getName();
1067 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1069 } catch (std::exception &e) {
1075 RemotePlayer *player =
1076 static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
1078 // If failed, cancel
1079 if ((playersao == NULL) || (player == NULL)) {
1080 if (player && player->peer_id != 0) {
1081 actionstream << "Server: Failed to emerge player \"" << playername
1082 << "\" (player allocated to an another client)" << std::endl;
1083 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1084 L"name. If your client closed unexpectedly, try again in "
1087 errorstream << "Server: " << playername << ": Failed to emerge player"
1089 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1095 Send complete position information
1097 SendMovePlayer(peer_id);
1100 SendPlayerPrivileges(peer_id);
1102 // Send inventory formspec
1103 SendPlayerInventoryFormspec(peer_id);
1106 SendInventory(playersao);
1109 SendPlayerHPOrDie(playersao);
1112 SendPlayerBreath(peer_id);
1114 // Show death screen if necessary
1115 if (playersao->isDead())
1116 SendDeathscreen(peer_id, false, v3f(0,0,0));
1118 // Note things in chat if not in simple singleplayer mode
1119 if(!m_simple_singleplayer_mode) {
1120 // Send information about server to player in chat
1121 SendChatMessage(peer_id, getStatusString());
1123 Address addr = getPeerAddress(player->peer_id);
1124 std::string ip_str = addr.serializeString();
1125 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1130 const std::vector<std::string> &names = m_clients.getPlayerNames();
1132 actionstream << player->getName() << " joins game. List of players: ";
1134 for (std::vector<std::string>::const_iterator i = names.begin();
1135 i != names.end(); ++i) {
1136 actionstream << *i << " ";
1139 actionstream << player->getName() <<std::endl;
1144 inline void Server::handleCommand(NetworkPacket* pkt)
1146 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1147 (this->*opHandle.handler)(pkt);
1150 void Server::ProcessData(NetworkPacket *pkt)
1152 DSTACK(FUNCTION_NAME);
1153 // Environment is locked first.
1154 MutexAutoLock envlock(m_env_mutex);
1156 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1157 u32 peer_id = pkt->getPeerId();
1160 Address address = getPeerAddress(peer_id);
1161 std::string addr_s = address.serializeString();
1163 if(m_banmanager->isIpBanned(addr_s)) {
1164 std::string ban_name = m_banmanager->getBanName(addr_s);
1165 infostream << "Server: A banned client tried to connect from "
1166 << addr_s << "; banned name was "
1167 << ban_name << std::endl;
1168 // This actually doesn't seem to transfer to the client
1169 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1170 + utf8_to_wide(ban_name));
1174 catch(con::PeerNotFoundException &e) {
1176 * no peer for this packet found
1177 * most common reason is peer timeout, e.g. peer didn't
1178 * respond for some time, your server was overloaded or
1181 infostream << "Server::ProcessData(): Canceling: peer "
1182 << peer_id << " not found" << std::endl;
1187 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1189 // Command must be handled into ToServerCommandHandler
1190 if (command >= TOSERVER_NUM_MSG_TYPES) {
1191 infostream << "Server: Ignoring unknown command "
1192 << command << std::endl;
1196 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1201 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1203 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1204 errorstream << "Server::ProcessData(): Cancelling: Peer"
1205 " serialization format invalid or not initialized."
1206 " Skipping incoming command=" << command << std::endl;
1210 /* Handle commands related to client startup */
1211 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1216 if (m_clients.getClientState(peer_id) < CS_Active) {
1217 if (command == TOSERVER_PLAYERPOS) return;
1219 errorstream << "Got packet command: " << command << " for peer id "
1220 << peer_id << " but client isn't active yet. Dropping packet "
1226 } catch (SendFailedException &e) {
1227 errorstream << "Server::ProcessData(): SendFailedException: "
1228 << "what=" << e.what()
1230 } catch (PacketError &e) {
1231 actionstream << "Server::ProcessData(): PacketError: "
1232 << "what=" << e.what()
1237 void Server::setTimeOfDay(u32 time)
1239 m_env->setTimeOfDay(time);
1240 m_time_of_day_send_timer = 0;
1243 void Server::onMapEditEvent(MapEditEvent *event)
1245 if(m_ignore_map_edit_events)
1247 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1249 MapEditEvent *e = event->clone();
1250 m_unsent_map_edit_queue.push(e);
1253 Inventory* Server::getInventory(const InventoryLocation &loc)
1256 case InventoryLocation::UNDEFINED:
1257 case InventoryLocation::CURRENT_PLAYER:
1259 case InventoryLocation::PLAYER:
1261 RemotePlayer *player = dynamic_cast<RemotePlayer *>(m_env->getPlayer(loc.name.c_str()));
1264 PlayerSAO *playersao = player->getPlayerSAO();
1267 return playersao->getInventory();
1270 case InventoryLocation::NODEMETA:
1272 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1275 return meta->getInventory();
1278 case InventoryLocation::DETACHED:
1280 if(m_detached_inventories.count(loc.name) == 0)
1282 return m_detached_inventories[loc.name];
1286 sanity_check(false); // abort
1291 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1294 case InventoryLocation::UNDEFINED:
1296 case InventoryLocation::PLAYER:
1301 RemotePlayer *player =
1302 dynamic_cast<RemotePlayer *>(m_env->getPlayer(loc.name.c_str()));
1307 PlayerSAO *playersao = player->getPlayerSAO();
1311 SendInventory(playersao);
1314 case InventoryLocation::NODEMETA:
1316 v3s16 blockpos = getNodeBlockPos(loc.p);
1318 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1320 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1322 setBlockNotSent(blockpos);
1325 case InventoryLocation::DETACHED:
1327 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1331 sanity_check(false); // abort
1336 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1338 std::vector<u16> clients = m_clients.getClientIDs();
1340 // Set the modified blocks unsent for all the clients
1341 for (std::vector<u16>::iterator i = clients.begin();
1342 i != clients.end(); ++i) {
1343 if (RemoteClient *client = m_clients.lockedGetClientNoEx(*i))
1344 client->SetBlocksNotSent(block);
1349 void Server::peerAdded(con::Peer *peer)
1351 DSTACK(FUNCTION_NAME);
1352 verbosestream<<"Server::peerAdded(): peer->id="
1353 <<peer->id<<std::endl;
1356 c.type = con::PEER_ADDED;
1357 c.peer_id = peer->id;
1359 m_peer_change_queue.push(c);
1362 void Server::deletingPeer(con::Peer *peer, bool timeout)
1364 DSTACK(FUNCTION_NAME);
1365 verbosestream<<"Server::deletingPeer(): peer->id="
1366 <<peer->id<<", timeout="<<timeout<<std::endl;
1368 m_clients.event(peer->id, CSE_Disconnect);
1370 c.type = con::PEER_REMOVED;
1371 c.peer_id = peer->id;
1372 c.timeout = timeout;
1373 m_peer_change_queue.push(c);
1376 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
1378 *retval = m_con.getPeerStat(peer_id,type);
1379 if (*retval == -1) return false;
1383 bool Server::getClientInfo(
1392 std::string* vers_string
1395 *state = m_clients.getClientState(peer_id);
1397 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1399 if (client == NULL) {
1404 *uptime = client->uptime();
1405 *ser_vers = client->serialization_version;
1406 *prot_vers = client->net_proto_version;
1408 *major = client->getMajor();
1409 *minor = client->getMinor();
1410 *patch = client->getPatch();
1411 *vers_string = client->getPatch();
1418 void Server::handlePeerChanges()
1420 while(m_peer_change_queue.size() > 0)
1422 con::PeerChange c = m_peer_change_queue.front();
1423 m_peer_change_queue.pop();
1425 verbosestream<<"Server: Handling peer change: "
1426 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1431 case con::PEER_ADDED:
1432 m_clients.CreateClient(c.peer_id);
1435 case con::PEER_REMOVED:
1436 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1440 FATAL_ERROR("Invalid peer change event received!");
1446 void Server::printToConsoleOnly(const std::string &text)
1449 m_admin_chat->outgoing_queue.push_back(
1450 new ChatEventChat("", utf8_to_wide(text)));
1452 std::cout << text << std::endl;
1456 void Server::Send(NetworkPacket* pkt)
1458 m_clients.send(pkt->getPeerId(),
1459 clientCommandFactoryTable[pkt->getCommand()].channel,
1461 clientCommandFactoryTable[pkt->getCommand()].reliable);
1464 void Server::SendMovement(u16 peer_id)
1466 DSTACK(FUNCTION_NAME);
1467 std::ostringstream os(std::ios_base::binary);
1469 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1471 pkt << g_settings->getFloat("movement_acceleration_default");
1472 pkt << g_settings->getFloat("movement_acceleration_air");
1473 pkt << g_settings->getFloat("movement_acceleration_fast");
1474 pkt << g_settings->getFloat("movement_speed_walk");
1475 pkt << g_settings->getFloat("movement_speed_crouch");
1476 pkt << g_settings->getFloat("movement_speed_fast");
1477 pkt << g_settings->getFloat("movement_speed_climb");
1478 pkt << g_settings->getFloat("movement_speed_jump");
1479 pkt << g_settings->getFloat("movement_liquid_fluidity");
1480 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1481 pkt << g_settings->getFloat("movement_liquid_sink");
1482 pkt << g_settings->getFloat("movement_gravity");
1487 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1489 if (!g_settings->getBool("enable_damage"))
1492 u16 peer_id = playersao->getPeerID();
1493 bool is_alive = playersao->getHP() > 0;
1496 SendPlayerHP(peer_id);
1501 void Server::SendHP(u16 peer_id, u8 hp)
1503 DSTACK(FUNCTION_NAME);
1505 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1510 void Server::SendBreath(u16 peer_id, u16 breath)
1512 DSTACK(FUNCTION_NAME);
1514 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1515 pkt << (u16) breath;
1519 void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
1520 const std::string &custom_reason, bool reconnect)
1522 assert(reason < SERVER_ACCESSDENIED_MAX);
1524 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1526 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1527 pkt << custom_reason;
1528 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1529 reason == SERVER_ACCESSDENIED_CRASH)
1530 pkt << custom_reason << (u8)reconnect;
1534 void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
1536 DSTACK(FUNCTION_NAME);
1538 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1543 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
1544 v3f camera_point_target)
1546 DSTACK(FUNCTION_NAME);
1548 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1549 pkt << set_camera_point_target << camera_point_target;
1553 void Server::SendItemDef(u16 peer_id,
1554 IItemDefManager *itemdef, u16 protocol_version)
1556 DSTACK(FUNCTION_NAME);
1558 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1562 u32 length of the next item
1563 zlib-compressed serialized ItemDefManager
1565 std::ostringstream tmp_os(std::ios::binary);
1566 itemdef->serialize(tmp_os, protocol_version);
1567 std::ostringstream tmp_os2(std::ios::binary);
1568 compressZlib(tmp_os.str(), tmp_os2);
1569 pkt.putLongString(tmp_os2.str());
1572 verbosestream << "Server: Sending item definitions to id(" << peer_id
1573 << "): size=" << pkt.getSize() << std::endl;
1578 void Server::SendNodeDef(u16 peer_id,
1579 INodeDefManager *nodedef, u16 protocol_version)
1581 DSTACK(FUNCTION_NAME);
1583 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1587 u32 length of the next item
1588 zlib-compressed serialized NodeDefManager
1590 std::ostringstream tmp_os(std::ios::binary);
1591 nodedef->serialize(tmp_os, protocol_version);
1592 std::ostringstream tmp_os2(std::ios::binary);
1593 compressZlib(tmp_os.str(), tmp_os2);
1595 pkt.putLongString(tmp_os2.str());
1598 verbosestream << "Server: Sending node definitions to id(" << peer_id
1599 << "): size=" << pkt.getSize() << std::endl;
1605 Non-static send methods
1608 void Server::SendInventory(PlayerSAO* playerSAO)
1610 DSTACK(FUNCTION_NAME);
1612 UpdateCrafting(playerSAO->getPlayer());
1618 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1620 std::ostringstream os;
1621 playerSAO->getInventory()->serialize(os);
1623 std::string s = os.str();
1625 pkt.putRawString(s.c_str(), s.size());
1629 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
1631 DSTACK(FUNCTION_NAME);
1633 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1636 if (peer_id != PEER_ID_INEXISTENT) {
1640 m_clients.sendToAll(0, &pkt, true);
1644 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
1645 const std::string &formname)
1647 DSTACK(FUNCTION_NAME);
1649 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1651 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1657 // Spawns a particle on peer with peer_id
1658 void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration,
1659 float expirationtime, float size, bool collisiondetection,
1660 bool collision_removal,
1661 bool vertical, const std::string &texture,
1662 u32 material_type_param, AnimationType animation_type,
1663 u16 vertical_frame_num, u16 horizontal_frame_num, u16 first_frame,
1664 float frame_length, bool loop_animation,
1667 DSTACK(FUNCTION_NAME);
1669 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1671 pkt << pos << velocity << acceleration << expirationtime
1672 << size << collisiondetection;
1673 pkt.putLongString(texture);
1675 pkt << collision_removal;
1677 pkt << material_type_param
1678 << (u8)animation_type
1679 << vertical_frame_num
1680 << horizontal_frame_num << first_frame
1681 << frame_length << loop_animation << glow;
1683 if (peer_id != PEER_ID_INEXISTENT) {
1687 m_clients.sendToAll(0, &pkt, true);
1691 // Adds a ParticleSpawner on peer with peer_id
1692 void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos,
1693 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1694 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1695 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1696 u32 material_type_param, AnimationType animation_type, u16 vertical_frame_num, u16 horizontal_frame_num,
1697 u16 min_first_frame, u16 max_first_frame, float frame_length,
1698 bool loop_animation, u8 glow)
1700 DSTACK(FUNCTION_NAME);
1702 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1704 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1705 << minacc << maxacc << minexptime << maxexptime << minsize
1706 << maxsize << collisiondetection;
1708 pkt.putLongString(texture);
1710 pkt << id << vertical;
1711 pkt << collision_removal;
1714 pkt << material_type_param
1715 << (u8)animation_type
1716 << vertical_frame_num << horizontal_frame_num
1717 << min_first_frame << max_first_frame
1718 << frame_length << loop_animation << glow;
1720 if (peer_id != PEER_ID_INEXISTENT) {
1724 m_clients.sendToAll(0, &pkt, true);
1728 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
1730 DSTACK(FUNCTION_NAME);
1732 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
1734 // Ugly error in this packet
1737 if (peer_id != PEER_ID_INEXISTENT) {
1741 m_clients.sendToAll(0, &pkt, true);
1746 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
1748 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1750 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1751 << form->text << form->number << form->item << form->dir
1752 << form->align << form->offset << form->world_pos << form->size;
1757 void Server::SendHUDRemove(u16 peer_id, u32 id)
1759 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1764 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
1766 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1767 pkt << id << (u8) stat;
1771 case HUD_STAT_SCALE:
1772 case HUD_STAT_ALIGN:
1773 case HUD_STAT_OFFSET:
1774 pkt << *(v2f *) value;
1778 pkt << *(std::string *) value;
1780 case HUD_STAT_WORLD_POS:
1781 pkt << *(v3f *) value;
1784 pkt << *(v2s32 *) value;
1786 case HUD_STAT_NUMBER:
1790 pkt << *(u32 *) value;
1797 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
1799 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1801 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1803 pkt << flags << mask;
1808 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
1810 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1811 pkt << param << value;
1815 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
1816 const std::string &type, const std::vector<std::string> ¶ms)
1818 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1819 pkt << bgcolor << type << (u16) params.size();
1821 for(size_t i=0; i<params.size(); i++)
1827 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
1830 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1833 pkt << do_override << (u16) (ratio * 65535);
1838 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
1840 DSTACK(FUNCTION_NAME);
1842 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1843 pkt << time << time_speed;
1845 if (peer_id == PEER_ID_INEXISTENT) {
1846 m_clients.sendToAll(0, &pkt, true);
1853 void Server::SendPlayerHP(u16 peer_id)
1855 DSTACK(FUNCTION_NAME);
1856 PlayerSAO *playersao = getPlayerSAO(peer_id);
1857 // In some rare case if the player is disconnected
1858 // while Lua call l_punch, for example, this can be NULL
1862 SendHP(peer_id, playersao->getHP());
1863 m_script->player_event(playersao,"health_changed");
1865 // Send to other clients
1866 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1867 ActiveObjectMessage aom(playersao->getId(), true, str);
1868 playersao->m_messages_out.push(aom);
1871 void Server::SendPlayerBreath(u16 peer_id)
1873 DSTACK(FUNCTION_NAME);
1874 PlayerSAO *playersao = getPlayerSAO(peer_id);
1877 m_script->player_event(playersao, "breath_changed");
1878 SendBreath(peer_id, playersao->getBreath());
1881 void Server::SendMovePlayer(u16 peer_id)
1883 DSTACK(FUNCTION_NAME);
1884 RemotePlayer *player = m_env->getPlayer(peer_id);
1886 PlayerSAO *sao = player->getPlayerSAO();
1889 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1890 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1893 v3f pos = sao->getBasePosition();
1894 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1895 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1896 << " pitch=" << sao->getPitch()
1897 << " yaw=" << sao->getYaw()
1904 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
1906 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1909 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1910 << animation_frames[3] << animation_speed;
1915 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
1917 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1918 pkt << first << third;
1921 void Server::SendPlayerPrivileges(u16 peer_id)
1923 RemotePlayer *player = m_env->getPlayer(peer_id);
1925 if(player->peer_id == PEER_ID_INEXISTENT)
1928 std::set<std::string> privs;
1929 m_script->getAuth(player->getName(), NULL, &privs);
1931 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1932 pkt << (u16) privs.size();
1934 for(std::set<std::string>::const_iterator i = privs.begin();
1935 i != privs.end(); ++i) {
1942 void Server::SendPlayerInventoryFormspec(u16 peer_id)
1944 RemotePlayer *player = m_env->getPlayer(peer_id);
1946 if(player->peer_id == PEER_ID_INEXISTENT)
1949 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1950 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1954 u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
1956 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1957 pkt.putRawString(datas.c_str(), datas.size());
1959 return pkt.getSize();
1962 void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
1964 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1965 datas.size(), peer_id);
1967 pkt.putRawString(datas.c_str(), datas.size());
1969 m_clients.send(pkt.getPeerId(),
1970 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1975 s32 Server::playSound(const SimpleSoundSpec &spec,
1976 const ServerSoundParams ¶ms)
1978 // Find out initial position of sound
1979 bool pos_exists = false;
1980 v3f pos = params.getPos(m_env, &pos_exists);
1981 // If position is not found while it should be, cancel sound
1982 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
1985 // Filter destination clients
1986 std::vector<u16> dst_clients;
1987 if(params.to_player != "")
1989 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
1991 infostream<<"Server::playSound: Player \""<<params.to_player
1992 <<"\" not found"<<std::endl;
1995 if(player->peer_id == PEER_ID_INEXISTENT){
1996 infostream<<"Server::playSound: Player \""<<params.to_player
1997 <<"\" not connected"<<std::endl;
2000 dst_clients.push_back(player->peer_id);
2003 std::vector<u16> clients = m_clients.getClientIDs();
2005 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2006 RemotePlayer *player = m_env->getPlayer(*i);
2010 PlayerSAO *sao = player->getPlayerSAO();
2015 if(sao->getBasePosition().getDistanceFrom(pos) >
2016 params.max_hear_distance)
2019 dst_clients.push_back(*i);
2023 if(dst_clients.empty())
2027 s32 id = m_next_sound_id++;
2028 // The sound will exist as a reference in m_playing_sounds
2029 m_playing_sounds[id] = ServerPlayingSound();
2030 ServerPlayingSound &psound = m_playing_sounds[id];
2031 psound.params = params;
2033 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2034 pkt << id << spec.name << (float) (spec.gain * params.gain)
2035 << (u8) params.type << pos << params.object << params.loop;
2037 for(std::vector<u16>::iterator i = dst_clients.begin();
2038 i != dst_clients.end(); ++i) {
2039 psound.clients.insert(*i);
2040 m_clients.send(*i, 0, &pkt, true);
2044 void Server::stopSound(s32 handle)
2046 // Get sound reference
2047 UNORDERED_MAP<s32, ServerPlayingSound>::iterator i = m_playing_sounds.find(handle);
2048 if (i == m_playing_sounds.end())
2050 ServerPlayingSound &psound = i->second;
2052 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2055 for (UNORDERED_SET<u16>::iterator i = psound.clients.begin();
2056 i != psound.clients.end(); ++i) {
2058 m_clients.send(*i, 0, &pkt, true);
2060 // Remove sound reference
2061 m_playing_sounds.erase(i);
2064 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2065 std::vector<u16> *far_players, float far_d_nodes)
2067 float maxd = far_d_nodes*BS;
2068 v3f p_f = intToFloat(p, BS);
2070 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2073 std::vector<u16> clients = m_clients.getClientIDs();
2074 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2077 if (RemotePlayer *player = m_env->getPlayer(*i)) {
2078 PlayerSAO *sao = player->getPlayerSAO();
2082 // If player is far away, only set modified blocks not sent
2083 v3f player_pos = sao->getBasePosition();
2084 if (player_pos.getDistanceFrom(p_f) > maxd) {
2085 far_players->push_back(*i);
2092 m_clients.send(*i, 0, &pkt, true);
2096 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2097 std::vector<u16> *far_players, float far_d_nodes,
2098 bool remove_metadata)
2100 float maxd = far_d_nodes*BS;
2101 v3f p_f = intToFloat(p, BS);
2103 std::vector<u16> clients = m_clients.getClientIDs();
2104 for(std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2107 if (RemotePlayer *player = m_env->getPlayer(*i)) {
2108 PlayerSAO *sao = player->getPlayerSAO();
2112 // If player is far away, only set modified blocks not sent
2113 v3f player_pos = sao->getBasePosition();
2114 if(player_pos.getDistanceFrom(p_f) > maxd) {
2115 far_players->push_back(*i);
2121 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2123 RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
2125 pkt << p << n.param0 << n.param1 << n.param2
2126 << (u8) (remove_metadata ? 0 : 1);
2128 if (!remove_metadata) {
2129 if (client->net_proto_version <= 21) {
2130 // Old clients always clear metadata; fix it
2131 // by sending the full block again.
2132 client->SetBlockNotSent(getNodeBlockPos(p));
2139 if (pkt.getSize() > 0)
2140 m_clients.send(*i, 0, &pkt, true);
2144 void Server::setBlockNotSent(v3s16 p)
2146 std::vector<u16> clients = m_clients.getClientIDs();
2148 for(std::vector<u16>::iterator i = clients.begin();
2149 i != clients.end(); ++i) {
2150 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
2151 client->SetBlockNotSent(p);
2156 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
2158 DSTACK(FUNCTION_NAME);
2160 v3s16 p = block->getPos();
2163 Create a packet with the block in the right format
2166 std::ostringstream os(std::ios_base::binary);
2167 block->serialize(os, ver, false);
2168 block->serializeNetworkSpecific(os, net_proto_version);
2169 std::string s = os.str();
2171 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2174 pkt.putRawString(s.c_str(), s.size());
2178 void Server::SendBlocks(float dtime)
2180 DSTACK(FUNCTION_NAME);
2182 MutexAutoLock envlock(m_env_mutex);
2183 //TODO check if one big lock could be faster then multiple small ones
2185 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2187 std::vector<PrioritySortedBlockTransfer> queue;
2189 s32 total_sending = 0;
2192 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
2194 std::vector<u16> clients = m_clients.getClientIDs();
2197 for(std::vector<u16>::iterator i = clients.begin();
2198 i != clients.end(); ++i) {
2199 RemoteClient *client = m_clients.lockedGetClientNoEx(*i, CS_Active);
2204 total_sending += client->SendingCount();
2205 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2211 // Lowest priority number comes first.
2212 // Lowest is most important.
2213 std::sort(queue.begin(), queue.end());
2216 for(u32 i=0; i<queue.size(); i++)
2218 //TODO: Calculate limit dynamically
2219 if(total_sending >= g_settings->getS32
2220 ("max_simultaneous_block_sends_server_total"))
2223 PrioritySortedBlockTransfer q = queue[i];
2225 MapBlock *block = NULL;
2228 block = m_env->getMap().getBlockNoCreate(q.pos);
2230 catch(InvalidPositionException &e)
2235 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id, CS_Active);
2240 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
2242 client->SentBlock(q.pos);
2248 void Server::fillMediaCache()
2250 DSTACK(FUNCTION_NAME);
2252 infostream<<"Server: Calculating media file checksums"<<std::endl;
2254 // Collect all media file paths
2255 std::vector<std::string> paths;
2256 for(std::vector<ModSpec>::iterator i = m_mods.begin();
2257 i != m_mods.end(); ++i) {
2258 const ModSpec &mod = *i;
2259 paths.push_back(mod.path + DIR_DELIM + "textures");
2260 paths.push_back(mod.path + DIR_DELIM + "sounds");
2261 paths.push_back(mod.path + DIR_DELIM + "media");
2262 paths.push_back(mod.path + DIR_DELIM + "models");
2264 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2266 // Collect media file information from paths into cache
2267 for(std::vector<std::string>::iterator i = paths.begin();
2268 i != paths.end(); ++i) {
2269 std::string mediapath = *i;
2270 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2271 for (u32 j = 0; j < dirlist.size(); j++) {
2272 if (dirlist[j].dir) // Ignode dirs
2274 std::string filename = dirlist[j].name;
2275 // If name contains illegal characters, ignore the file
2276 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2277 infostream<<"Server: ignoring illegal file name: \""
2278 << filename << "\"" << std::endl;
2281 // If name is not in a supported format, ignore it
2282 const char *supported_ext[] = {
2283 ".png", ".jpg", ".bmp", ".tga",
2284 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2286 ".x", ".b3d", ".md2", ".obj",
2289 if (removeStringEnd(filename, supported_ext) == ""){
2290 infostream << "Server: ignoring unsupported file extension: \""
2291 << filename << "\"" << std::endl;
2294 // Ok, attempt to load the file and add to cache
2295 std::string filepath = mediapath + DIR_DELIM + 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(u16 peer_id)
2346 DSTACK(FUNCTION_NAME);
2348 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2352 std::ostringstream os(std::ios_base::binary);
2354 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2355 pkt << (u16) m_media.size();
2357 for (UNORDERED_MAP<std::string, MediaInfo>::iterator i = m_media.begin();
2358 i != m_media.end(); ++i) {
2359 pkt << i->first << i->second.sha1_digest;
2362 pkt << g_settings->get("remote_media");
2366 struct SendableMedia
2372 SendableMedia(const std::string &name_="", const std::string &path_="",
2373 const std::string &data_=""):
2380 void Server::sendRequestedMedia(u16 peer_id,
2381 const std::vector<std::string> &tosend)
2383 DSTACK(FUNCTION_NAME);
2385 verbosestream<<"Server::sendRequestedMedia(): "
2386 <<"Sending files to client"<<std::endl;
2390 // Put 5kB in one bunch (this is not accurate)
2391 u32 bytes_per_bunch = 5000;
2393 std::vector< std::vector<SendableMedia> > file_bunches;
2394 file_bunches.push_back(std::vector<SendableMedia>());
2396 u32 file_size_bunch_total = 0;
2398 for(std::vector<std::string>::const_iterator i = tosend.begin();
2399 i != tosend.end(); ++i) {
2400 const std::string &name = *i;
2402 if (m_media.find(name) == m_media.end()) {
2403 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2404 <<"unknown file \""<<(name)<<"\""<<std::endl;
2408 //TODO get path + name
2409 std::string tpath = m_media[name].path;
2412 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2413 if(fis.good() == false){
2414 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2415 <<tpath<<"\" for reading"<<std::endl;
2418 std::ostringstream tmp_os(std::ios_base::binary);
2422 fis.read(buf, 1024);
2423 std::streamsize len = fis.gcount();
2424 tmp_os.write(buf, len);
2425 file_size_bunch_total += len;
2434 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2435 <<name<<"\""<<std::endl;
2438 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2439 <<tname<<"\""<<std::endl;*/
2441 file_bunches[file_bunches.size()-1].push_back(
2442 SendableMedia(name, tpath, tmp_os.str()));
2444 // Start next bunch if got enough data
2445 if(file_size_bunch_total >= bytes_per_bunch) {
2446 file_bunches.push_back(std::vector<SendableMedia>());
2447 file_size_bunch_total = 0;
2452 /* Create and send packets */
2454 u16 num_bunches = file_bunches.size();
2455 for(u16 i = 0; i < num_bunches; i++) {
2458 u16 total number of texture bunches
2459 u16 index of this bunch
2460 u32 number of files in this bunch
2469 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2470 pkt << num_bunches << i << (u32) file_bunches[i].size();
2472 for(std::vector<SendableMedia>::iterator
2473 j = file_bunches[i].begin();
2474 j != file_bunches[i].end(); ++j) {
2476 pkt.putLongString(j->data);
2479 verbosestream << "Server::sendRequestedMedia(): bunch "
2480 << i << "/" << num_bunches
2481 << " files=" << file_bunches[i].size()
2482 << " size=" << pkt.getSize() << std::endl;
2487 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
2489 if(m_detached_inventories.count(name) == 0) {
2490 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2493 Inventory *inv = m_detached_inventories[name];
2494 std::ostringstream os(std::ios_base::binary);
2496 os << serializeString(name);
2500 std::string s = os.str();
2502 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2503 pkt.putRawString(s.c_str(), s.size());
2505 if (peer_id != PEER_ID_INEXISTENT) {
2509 m_clients.sendToAll(0, &pkt, true);
2513 void Server::sendDetachedInventories(u16 peer_id)
2515 DSTACK(FUNCTION_NAME);
2517 for(std::map<std::string, Inventory*>::iterator
2518 i = m_detached_inventories.begin();
2519 i != m_detached_inventories.end(); ++i) {
2520 const std::string &name = i->first;
2521 //Inventory *inv = i->second;
2522 sendDetachedInventory(name, peer_id);
2530 void Server::DiePlayer(u16 peer_id)
2532 DSTACK(FUNCTION_NAME);
2533 PlayerSAO *playersao = getPlayerSAO(peer_id);
2534 // In some rare cases this can be NULL -- if the player is disconnected
2535 // when a Lua function modifies l_punch, for example
2539 infostream << "Server::DiePlayer(): Player "
2540 << playersao->getPlayer()->getName()
2541 << " dies" << std::endl;
2543 playersao->setHP(0);
2545 // Trigger scripted stuff
2546 m_script->on_dieplayer(playersao);
2548 SendPlayerHP(peer_id);
2549 SendDeathscreen(peer_id, false, v3f(0,0,0));
2552 void Server::RespawnPlayer(u16 peer_id)
2554 DSTACK(FUNCTION_NAME);
2556 PlayerSAO *playersao = getPlayerSAO(peer_id);
2559 infostream << "Server::RespawnPlayer(): Player "
2560 << playersao->getPlayer()->getName()
2561 << " respawns" << std::endl;
2563 playersao->setHP(PLAYER_MAX_HP);
2564 playersao->setBreath(PLAYER_MAX_BREATH);
2566 SendPlayerHP(peer_id);
2567 SendPlayerBreath(peer_id);
2569 bool repositioned = m_script->on_respawnplayer(playersao);
2571 v3f pos = findSpawnPos();
2572 // setPos will send the new position to client
2573 playersao->setPos(pos);
2578 void Server::DenySudoAccess(u16 peer_id)
2580 DSTACK(FUNCTION_NAME);
2582 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2587 void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
2588 const std::string &str_reason, bool reconnect)
2590 if (proto_ver >= 25) {
2591 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2593 std::wstring wreason = utf8_to_wide(
2594 reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
2595 accessDeniedStrings[(u8)reason]);
2596 SendAccessDenied_Legacy(peer_id, wreason);
2599 m_clients.event(peer_id, CSE_SetDenied);
2600 m_con.DisconnectPeer(peer_id);
2604 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
2606 DSTACK(FUNCTION_NAME);
2608 SendAccessDenied(peer_id, reason, custom_reason);
2609 m_clients.event(peer_id, CSE_SetDenied);
2610 m_con.DisconnectPeer(peer_id);
2613 // 13/03/15: remove this function when protocol version 25 will become
2614 // the minimum version for MT users, maybe in 1 year
2615 void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
2617 DSTACK(FUNCTION_NAME);
2619 SendAccessDenied_Legacy(peer_id, reason);
2620 m_clients.event(peer_id, CSE_SetDenied);
2621 m_con.DisconnectPeer(peer_id);
2624 void Server::acceptAuth(u16 peer_id, bool forSudoMode)
2626 DSTACK(FUNCTION_NAME);
2629 RemoteClient* client = getClient(peer_id, CS_Invalid);
2631 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2633 // Right now, the auth mechs don't change between login and sudo mode.
2634 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2635 client->allowed_sudo_mechs = sudo_auth_mechs;
2637 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2638 << g_settings->getFloat("dedicated_server_step")
2642 m_clients.event(peer_id, CSE_AuthAccept);
2644 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2646 // We only support SRP right now
2647 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2649 resp_pkt << sudo_auth_mechs;
2651 m_clients.event(peer_id, CSE_SudoSuccess);
2655 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
2657 DSTACK(FUNCTION_NAME);
2658 std::wstring message;
2661 Clear references to playing sounds
2663 for (UNORDERED_MAP<s32, ServerPlayingSound>::iterator
2664 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2665 ServerPlayingSound &psound = i->second;
2666 psound.clients.erase(peer_id);
2667 if (psound.clients.empty())
2668 m_playing_sounds.erase(i++);
2673 RemotePlayer *player = m_env->getPlayer(peer_id);
2675 /* Run scripts and remove from environment */
2676 if (player != NULL) {
2677 PlayerSAO *playersao = player->getPlayerSAO();
2680 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2682 playersao->disconnected();
2689 if(player != NULL && reason != CDR_DENY) {
2690 std::ostringstream os(std::ios_base::binary);
2691 std::vector<u16> clients = m_clients.getClientIDs();
2693 for(std::vector<u16>::iterator i = clients.begin();
2694 i != clients.end(); ++i) {
2696 RemotePlayer *player = m_env->getPlayer(*i);
2700 // Get name of player
2701 os << player->getName() << " ";
2704 std::string name = player->getName();
2705 actionstream << name << " "
2706 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2707 << " List of players: " << os.str() << std::endl;
2709 m_admin_chat->outgoing_queue.push_back(
2710 new ChatEventNick(CET_NICK_REMOVE, name));
2714 MutexAutoLock env_lock(m_env_mutex);
2715 m_clients.DeleteClient(peer_id);
2719 // Send leave chat message to all remaining clients
2720 if(message.length() != 0)
2721 SendChatMessage(PEER_ID_INEXISTENT,message);
2724 void Server::UpdateCrafting(RemotePlayer *player)
2726 DSTACK(FUNCTION_NAME);
2728 // Get a preview for crafting
2730 InventoryLocation loc;
2731 loc.setPlayer(player->getName());
2732 std::vector<ItemStack> output_replacements;
2733 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2734 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2735 (&player->inventory)->getList("craft"), loc);
2737 // Put the new preview in
2738 InventoryList *plist = player->inventory.getList("craftpreview");
2739 sanity_check(plist);
2740 sanity_check(plist->getSize() >= 1);
2741 plist->changeItem(0, preview);
2744 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2746 if (evt->type == CET_NICK_ADD) {
2747 // The terminal informed us of its nick choice
2748 m_admin_nick = ((ChatEventNick *)evt)->nick;
2749 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2750 errorstream << "You haven't set up an account." << std::endl
2751 << "Please log in using the client as '"
2752 << m_admin_nick << "' with a secure password." << std::endl
2753 << "Until then, you can't execute admin tasks via the console," << std::endl
2754 << "and everybody can claim the user account instead of you," << std::endl
2755 << "giving them full control over this server." << std::endl;
2758 assert(evt->type == CET_CHAT);
2759 handleAdminChat((ChatEventChat *)evt);
2763 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2764 const std::wstring &wmessage, bool check_shout_priv, RemotePlayer *player)
2766 // If something goes wrong, this player is to blame
2767 RollbackScopeActor rollback_scope(m_rollback,
2768 std::string("player:") + name);
2772 // Whether to send line to the player that sent the message, or to all players
2773 bool broadcast_line = true;
2776 bool ate = m_script->on_chat_message(name,
2777 wide_to_utf8(wmessage));
2778 // If script ate the message, don't proceed
2783 switch (player->canSendChatMessage()) {
2784 case RPLAYER_CHATRESULT_FLOODING: {
2785 std::wstringstream ws;
2786 ws << L"You cannot send more messages. You are limited to "
2787 << g_settings->getFloat("chat_message_limit_per_10sec")
2788 << L" messages per 10 seconds.";
2791 case RPLAYER_CHATRESULT_KICK:
2792 DenyAccess_Legacy(player->peer_id, L"You have been kicked due to message flooding.");
2794 case RPLAYER_CHATRESULT_OK: break;
2795 default: FATAL_ERROR("Unhandled chat filtering result found.");
2799 if (m_max_chatmessage_length > 0 && wmessage.length() > m_max_chatmessage_length) {
2800 return L"Your message exceed the maximum chat message limit set on the server. "
2801 L"It was refused. Send a shorter message";
2804 // Commands are implemented in Lua, so only catch invalid
2805 // commands that were not "eaten" and send an error back
2806 if (wmessage[0] == L'/') {
2807 std::wstring wcmd = wmessage.substr(1);
2808 broadcast_line = false;
2809 if (wcmd.length() == 0)
2810 line += L"-!- Empty command";
2812 line += L"-!- Invalid command: " + str_split(wcmd, L' ')[0];
2814 if (check_shout_priv && !checkPriv(name, "shout")) {
2815 line += L"-!- You don't have permission to shout.";
2816 broadcast_line = false;
2826 Tell calling method to send the message to sender
2828 if (!broadcast_line) {
2832 Send the message to others
2834 actionstream << "CHAT: " << wide_to_narrow(line) << std::endl;
2836 std::vector<u16> clients = m_clients.getClientIDs();
2838 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2839 for (u16 i = 0; i < clients.size(); i++) {
2840 u16 cid = clients[i];
2841 if (cid != peer_id_to_avoid_sending)
2842 SendChatMessage(cid, line);
2848 void Server::handleAdminChat(const ChatEventChat *evt)
2850 std::string name = evt->nick;
2851 std::wstring wname = utf8_to_wide(name);
2852 std::wstring wmessage = evt->evt_msg;
2854 std::wstring answer = handleChat(name, wname, wmessage);
2856 // If asked to send answer to sender
2857 if (!answer.empty()) {
2858 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2862 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2864 RemoteClient *client = getClientNoEx(peer_id,state_min);
2866 throw ClientNotFoundException("Client not found");
2870 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2872 return m_clients.getClientNoEx(peer_id, state_min);
2875 std::string Server::getPlayerName(u16 peer_id)
2877 RemotePlayer *player = m_env->getPlayer(peer_id);
2879 return "[id="+itos(peer_id)+"]";
2880 return player->getName();
2883 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2885 RemotePlayer *player = m_env->getPlayer(peer_id);
2888 return player->getPlayerSAO();
2891 std::wstring Server::getStatusString()
2893 std::wostringstream os(std::ios_base::binary);
2896 os<<L"version="<<narrow_to_wide(g_version_string);
2898 os<<L", uptime="<<m_uptime.get();
2900 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2901 // Information about clients
2904 std::vector<u16> clients = m_clients.getClientIDs();
2905 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2907 RemotePlayer *player = m_env->getPlayer(*i);
2908 // Get name of player
2909 std::wstring name = L"unknown";
2911 name = narrow_to_wide(player->getName());
2912 // Add name to information string
2920 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
2921 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2922 if(g_settings->get("motd") != "")
2923 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2927 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2929 std::set<std::string> privs;
2930 m_script->getAuth(name, NULL, &privs);
2934 bool Server::checkPriv(const std::string &name, const std::string &priv)
2936 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2937 return (privs.count(priv) != 0);
2940 void Server::reportPrivsModified(const std::string &name)
2943 std::vector<u16> clients = m_clients.getClientIDs();
2944 for(std::vector<u16>::iterator i = clients.begin();
2945 i != clients.end(); ++i) {
2946 RemotePlayer *player = m_env->getPlayer(*i);
2947 reportPrivsModified(player->getName());
2950 RemotePlayer *player = m_env->getPlayer(name.c_str());
2953 SendPlayerPrivileges(player->peer_id);
2954 PlayerSAO *sao = player->getPlayerSAO();
2957 sao->updatePrivileges(
2958 getPlayerEffectivePrivs(name),
2963 void Server::reportInventoryFormspecModified(const std::string &name)
2965 RemotePlayer *player = m_env->getPlayer(name.c_str());
2968 SendPlayerInventoryFormspec(player->peer_id);
2971 void Server::setIpBanned(const std::string &ip, const std::string &name)
2973 m_banmanager->add(ip, name);
2976 void Server::unsetIpBanned(const std::string &ip_or_name)
2978 m_banmanager->remove(ip_or_name);
2981 std::string Server::getBanDescription(const std::string &ip_or_name)
2983 return m_banmanager->getBanDescription(ip_or_name);
2986 void Server::notifyPlayer(const char *name, const std::wstring &msg)
2988 // m_env will be NULL if the server is initializing
2992 if (m_admin_nick == name && !m_admin_nick.empty()) {
2993 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
2996 RemotePlayer *player = m_env->getPlayer(name);
3001 if (player->peer_id == PEER_ID_INEXISTENT)
3004 SendChatMessage(player->peer_id, msg);
3007 bool Server::showFormspec(const char *playername, const std::string &formspec,
3008 const std::string &formname)
3010 // m_env will be NULL if the server is initializing
3014 RemotePlayer *player = m_env->getPlayer(playername);
3018 SendShowFormspecMessage(player->peer_id, formspec, formname);
3022 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3027 u32 id = player->addHud(form);
3029 SendHUDAdd(player->peer_id, id, form);
3034 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3038 HudElement* todel = player->removeHud(id);
3045 SendHUDRemove(player->peer_id, id);
3049 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3054 SendHUDChange(player->peer_id, id, stat, data);
3058 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3063 SendHUDSetFlags(player->peer_id, flags, mask);
3064 player->hud_flags &= ~mask;
3065 player->hud_flags |= flags;
3067 PlayerSAO* playersao = player->getPlayerSAO();
3069 if (playersao == NULL)
3072 m_script->player_event(playersao, "hud_changed");
3076 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3081 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3084 player->setHotbarItemcount(hotbar_itemcount);
3085 std::ostringstream os(std::ios::binary);
3086 writeS32(os, hotbar_itemcount);
3087 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3091 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3096 player->setHotbarImage(name);
3097 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3100 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3104 return player->getHotbarImage();
3107 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3112 player->setHotbarSelectedImage(name);
3113 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3116 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3117 v2s32 animation_frames[4], f32 frame_speed)
3122 player->setLocalAnimations(animation_frames, frame_speed);
3123 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3127 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3132 player->eye_offset_first = first;
3133 player->eye_offset_third = third;
3134 SendEyeOffset(player->peer_id, first, third);
3138 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3139 const std::string &type, const std::vector<std::string> ¶ms)
3144 player->setSky(bgcolor, type, params);
3145 SendSetSky(player->peer_id, bgcolor, type, params);
3149 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3155 player->overrideDayNightRatio(do_override, ratio);
3156 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3160 void Server::notifyPlayers(const std::wstring &msg)
3162 SendChatMessage(PEER_ID_INEXISTENT,msg);
3165 void Server::spawnParticle(const std::string &playername, v3f pos,
3166 v3f velocity, v3f acceleration,
3167 float expirationtime, float size, bool
3168 collisiondetection, bool collision_removal,
3169 bool vertical, const std::string &texture,
3170 u32 material_type_param, AnimationType animation_type,
3171 u16 vertical_frame_num, u16 horizontal_frame_num, u16 first_frame,
3172 float frame_length, bool loop_animation,
3175 // m_env will be NULL if the server is initializing
3179 u16 peer_id = PEER_ID_INEXISTENT;
3180 if (playername != "") {
3181 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3184 peer_id = player->peer_id;
3187 SendSpawnParticle(peer_id, pos, velocity, acceleration,
3188 expirationtime, size, collisiondetection,
3189 collision_removal, vertical, texture,
3190 material_type_param, animation_type,
3191 vertical_frame_num, horizontal_frame_num,
3192 first_frame, frame_length, loop_animation,
3196 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3197 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3198 float minexptime, float maxexptime, float minsize, float maxsize,
3199 bool collisiondetection, bool collision_removal,
3200 ServerActiveObject *attached, bool vertical, const std::string &texture,
3201 const std::string &playername, u32 material_type_param, AnimationType animation_type,
3202 u16 vertical_frame_num, u16 horizontal_frame_num, u16 min_first_frame, u16 max_first_frame,
3203 float frame_length, bool loop_animation, u8 glow)
3205 // m_env will be NULL if the server is initializing
3209 u16 peer_id = PEER_ID_INEXISTENT;
3210 if (playername != "") {
3211 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3214 peer_id = player->peer_id;
3217 u16 attached_id = attached ? attached->getId() : 0;
3220 if (attached_id == 0)
3221 id = m_env->addParticleSpawner(spawntime);
3223 id = m_env->addParticleSpawner(spawntime, attached_id);
3225 SendAddParticleSpawner(peer_id, amount, spawntime,
3226 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3227 minexptime, maxexptime, minsize, maxsize,
3228 collisiondetection, collision_removal, attached_id, vertical,
3229 texture, id, material_type_param, animation_type,
3230 vertical_frame_num, horizontal_frame_num,
3231 min_first_frame, max_first_frame, frame_length, loop_animation,
3237 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3239 // m_env will be NULL if the server is initializing
3241 throw ServerError("Can't delete particle spawners during initialisation!");
3243 u16 peer_id = PEER_ID_INEXISTENT;
3244 if (playername != "") {
3245 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3248 peer_id = player->peer_id;
3251 m_env->deleteParticleSpawner(id);
3252 SendDeleteParticleSpawner(peer_id, id);
3255 Inventory* Server::createDetachedInventory(const std::string &name)
3257 if(m_detached_inventories.count(name) > 0){
3258 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3259 delete m_detached_inventories[name];
3261 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3263 Inventory *inv = new Inventory(m_itemdef);
3265 m_detached_inventories[name] = inv;
3266 //TODO find a better way to do this
3267 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3271 // actions: time-reversed list
3272 // Return value: success/failure
3273 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3274 std::list<std::string> *log)
3276 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3277 ServerMap *map = (ServerMap*)(&m_env->getMap());
3279 // Fail if no actions to handle
3280 if(actions.empty()){
3281 log->push_back("Nothing to do.");
3288 for(std::list<RollbackAction>::const_iterator
3289 i = actions.begin();
3290 i != actions.end(); ++i)
3292 const RollbackAction &action = *i;
3294 bool success = action.applyRevert(map, this, this);
3297 std::ostringstream os;
3298 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3299 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3301 log->push_back(os.str());
3303 std::ostringstream os;
3304 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3305 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3307 log->push_back(os.str());
3311 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3312 <<" failed"<<std::endl;
3314 // Call it done if less than half failed
3315 return num_failed <= num_tried/2;
3318 // IGameDef interface
3320 IItemDefManager *Server::getItemDefManager()
3325 INodeDefManager *Server::getNodeDefManager()
3330 ICraftDefManager *Server::getCraftDefManager()
3334 ITextureSource *Server::getTextureSource()
3338 IShaderSource *Server::getShaderSource()
3342 scene::ISceneManager *Server::getSceneManager()
3347 u16 Server::allocateUnknownNodeId(const std::string &name)
3349 return m_nodedef->allocateDummy(name);
3352 ISoundManager *Server::getSoundManager()
3354 return &dummySoundManager;
3357 MtEventManager *Server::getEventManager()
3362 IWritableItemDefManager *Server::getWritableItemDefManager()
3367 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3372 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3377 const ModSpec *Server::getModSpec(const std::string &modname) const
3379 std::vector<ModSpec>::const_iterator it;
3380 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3381 const ModSpec &mod = *it;
3382 if (mod.name == modname)
3388 void Server::getModNames(std::vector<std::string> &modlist)
3390 std::vector<ModSpec>::iterator it;
3391 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3392 modlist.push_back(it->name);
3395 std::string Server::getBuiltinLuaPath()
3397 return porting::path_share + DIR_DELIM + "builtin";
3400 v3f Server::findSpawnPos()
3402 ServerMap &map = m_env->getServerMap();
3404 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3405 return nodeposf * BS;
3408 bool is_good = false;
3410 // Try to find a good place a few times
3411 for(s32 i = 0; i < 4000 && !is_good; i++) {
3413 // We're going to try to throw the player to this position
3414 v2s16 nodepos2d = v2s16(
3415 -range + (myrand() % (range * 2)),
3416 -range + (myrand() % (range * 2)));
3418 // Get spawn level at point
3419 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3420 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3421 // the mapgen to signify an unsuitable spawn position
3422 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3425 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3428 for (s32 i = 0; i < 10; i++) {
3429 v3s16 blockpos = getNodeBlockPos(nodepos);
3430 map.emergeBlock(blockpos, true);
3431 content_t c = map.getNodeNoEx(nodepos).getContent();
3432 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3434 if (air_count >= 2) {
3435 nodeposf = intToFloat(nodepos, BS);
3436 // Don't spawn the player outside map boundaries
3437 if (objectpos_over_limit(nodeposf))
3450 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3452 bool newplayer = false;
3455 Try to get an existing player
3457 RemotePlayer *player = m_env->getPlayer(name);
3459 // If player is already connected, cancel
3460 if (player != NULL && player->peer_id != 0) {
3461 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3466 If player with the wanted peer_id already exists, cancel.
3468 if (m_env->getPlayer(peer_id) != NULL) {
3469 infostream<<"emergePlayer(): Player with wrong name but same"
3470 " peer_id already exists"<<std::endl;
3474 // Create a new player active object
3475 PlayerSAO *playersao = new PlayerSAO(m_env, peer_id, isSingleplayer());
3476 player = m_env->loadPlayer(name, playersao);
3478 // Create player if it doesn't exist
3481 player = new RemotePlayer(name, this->idef());
3482 // Set player position
3483 infostream<<"Server: Finding spawn place for player \""
3484 <<name<<"\""<<std::endl;
3485 playersao->setBasePosition(findSpawnPos());
3487 // Make sure the player is saved
3488 player->setModified(true);
3490 // Add player to environment
3491 m_env->addPlayer(player);
3493 // If the player exists, ensure that they respawn inside legal bounds
3494 // This fixes an assert crash when the player can't be added
3495 // to the environment
3496 if (objectpos_over_limit(playersao->getBasePosition())) {
3497 actionstream << "Respawn position for player \""
3498 << name << "\" outside limits, resetting" << std::endl;
3499 playersao->setBasePosition(findSpawnPos());
3503 playersao->initialize(player, getPlayerEffectivePrivs(player->getName()));
3505 player->protocol_version = proto_version;
3507 /* Clean up old HUD elements from previous sessions */
3510 /* Add object to environment */
3511 m_env->addActiveObject(playersao);
3515 m_script->on_newplayer(playersao);
3521 void dedicated_server_loop(Server &server, bool &kill)
3523 DSTACK(FUNCTION_NAME);
3525 verbosestream<<"dedicated_server_loop()"<<std::endl;
3527 IntervalLimiter m_profiler_interval;
3529 static const float steplen = g_settings->getFloat("dedicated_server_step");
3530 static const float profiler_print_interval =
3531 g_settings->getFloat("profiler_print_interval");
3534 // This is kind of a hack but can be done like this
3535 // because server.step() is very light
3537 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3538 sleep_ms((int)(steplen*1000.0));
3540 server.step(steplen);
3542 if(server.getShutdownRequested() || kill)
3544 infostream<<"Dedicated server quitting"<<std::endl;
3546 if(g_settings->getBool("server_announce"))
3547 ServerList::sendAnnounce("delete", server.m_bind_addr.getPort());
3555 if (profiler_print_interval != 0) {
3556 if(m_profiler_interval.step(steplen, profiler_print_interval))
3558 infostream<<"Profiler:"<<std::endl;
3559 g_profiler->print(infostream);
3560 g_profiler->clear();