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 "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,
153 m_path_world(path_world),
154 m_gamespec(gamespec),
155 m_simple_singleplayer_mode(simple_singleplayer_mode),
156 m_async_fatal_error(""),
165 m_enable_rollback_recording(false),
168 m_itemdef(createItemDefManager()),
169 m_nodedef(createNodeDefManager()),
170 m_craftdef(createCraftDefManager()),
171 m_event(new EventManager()),
173 m_time_of_day_send_timer(0),
176 m_shutdown_requested(false),
177 m_shutdown_ask_reconnect(false),
179 m_ignore_map_edit_events(false),
180 m_ignore_map_edit_events_peer_id(0),
184 m_liquid_transform_timer = 0.0;
185 m_liquid_transform_every = 1.0;
186 m_masterserver_timer = 0.0;
187 m_emergethread_trigger_timer = 0.0;
188 m_savemap_timer = 0.0;
191 m_lag = g_settings->getFloat("dedicated_server_step");
194 throw ServerError("Supplied empty world path");
196 if(!gamespec.isValid())
197 throw ServerError("Supplied invalid gamespec");
199 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
200 if(m_simple_singleplayer_mode)
201 infostream<<" in simple singleplayer mode"<<std::endl;
203 infostream<<std::endl;
204 infostream<<"- world: "<<m_path_world<<std::endl;
205 infostream<<"- game: "<<m_gamespec.path<<std::endl;
207 // Create world if it doesn't exist
208 if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
209 throw ServerError("Failed to initialize world");
211 // Create server thread
212 m_thread = new ServerThread(this);
214 // Create emerge manager
215 m_emerge = new EmergeManager(this);
217 // Create ban manager
218 std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
219 m_banmanager = new BanManager(ban_path);
221 ModConfiguration modconf(m_path_world);
222 m_mods = modconf.getMods();
223 std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
224 // complain about mods with unsatisfied dependencies
225 if(!modconf.isConsistent()) {
226 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
227 it != unsatisfied_mods.end(); ++it) {
229 errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
230 for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
231 dep_it != mod.unsatisfied_depends.end(); ++dep_it)
232 errorstream << " \"" << *dep_it << "\"";
233 errorstream << std::endl;
237 Settings worldmt_settings;
238 std::string worldmt = m_path_world + DIR_DELIM + "world.mt";
239 worldmt_settings.readConfigFile(worldmt.c_str());
240 std::vector<std::string> names = worldmt_settings.getNames();
241 std::set<std::string> load_mod_names;
242 for(std::vector<std::string>::iterator it = names.begin();
243 it != names.end(); ++it) {
244 std::string name = *it;
245 if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
246 load_mod_names.insert(name.substr(9));
248 // complain about mods declared to be loaded, but not found
249 for(std::vector<ModSpec>::iterator it = m_mods.begin();
250 it != m_mods.end(); ++it)
251 load_mod_names.erase((*it).name);
252 for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
253 it != unsatisfied_mods.end(); ++it)
254 load_mod_names.erase((*it).name);
255 if(!load_mod_names.empty()) {
256 errorstream << "The following mods could not be found:";
257 for(std::set<std::string>::iterator it = load_mod_names.begin();
258 it != load_mod_names.end(); ++it)
259 errorstream << " \"" << (*it) << "\"";
260 errorstream << std::endl;
264 MutexAutoLock envlock(m_env_mutex);
266 // Create the Map (loads map_meta.txt, overriding configured mapgen params)
267 ServerMap *servermap = new ServerMap(path_world, this, m_emerge);
269 // Initialize scripting
270 infostream<<"Server: Initializing Lua"<<std::endl;
272 m_script = new GameScripting(this);
274 std::string script_path = getBuiltinLuaPath() + DIR_DELIM "init.lua";
276 m_script->loadMod(script_path, BUILTIN_MOD_NAME);
279 infostream << "Server: Loading mods: ";
280 for(std::vector<ModSpec>::iterator i = m_mods.begin();
281 i != m_mods.end(); ++i) {
282 const ModSpec &mod = *i;
283 infostream << mod.name << " ";
285 infostream << std::endl;
286 // Load and run "mod" scripts
287 for (std::vector<ModSpec>::iterator it = m_mods.begin();
288 it != m_mods.end(); ++it) {
289 const ModSpec &mod = *it;
290 if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
291 throw ModError("Error loading mod \"" + mod.name +
292 "\": Mod name does not follow naming conventions: "
293 "Only chararacters [a-z0-9_] are allowed.");
295 std::string script_path = mod.path + DIR_DELIM + "init.lua";
296 infostream << " [" << padStringRight(mod.name, 12) << "] [\""
297 << script_path << "\"]" << std::endl;
298 m_script->loadMod(script_path, mod.name);
301 // Read Textures and calculate sha1 sums
304 // Apply item aliases in the node definition manager
305 m_nodedef->updateAliases(m_itemdef);
307 // Apply texture overrides from texturepack/override.txt
308 std::string texture_path = g_settings->get("texture_path");
309 if (texture_path != "" && fs::IsDir(texture_path))
310 m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
312 m_nodedef->setNodeRegistrationStatus(true);
314 // Perform pending node name resolutions
315 m_nodedef->runNodeResolveCallbacks();
317 // unmap node names for connected nodeboxes
318 m_nodedef->mapNodeboxConnections();
320 // init the recipe hashes to speed up crafting
321 m_craftdef->initHashes(this);
323 // Initialize Environment
324 m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
326 m_clients.setEnv(m_env);
328 if (!servermap->settings_mgr.makeMapgenParams())
329 FATAL_ERROR("Couldn't create any mapgen type");
331 // Initialize mapgens
332 m_emerge->initMapgens(servermap->getMapgenParams());
334 m_enable_rollback_recording = g_settings->getBool("enable_rollback_recording");
335 if (m_enable_rollback_recording) {
336 // Create rollback manager
337 m_rollback = new RollbackManager(m_path_world, this);
340 // Give environment reference to scripting api
341 m_script->initializeEnvironment(m_env);
343 // Register us to receive map edit events
344 servermap->addEventReceiver(this);
346 // If file exists, load environment metadata
347 if (fs::PathExists(m_path_world + DIR_DELIM "env_meta.txt")) {
348 infostream << "Server: Loading environment metadata" << std::endl;
351 m_env->loadDefaultMeta();
354 // Add some test ActiveBlockModifiers to environment
355 add_legacy_abms(m_env, m_nodedef);
357 m_liquid_transform_every = g_settings->getFloat("liquid_update");
358 m_max_chatmessage_length = g_settings->getU16("chat_message_max_size");
363 infostream<<"Server destructing"<<std::endl;
365 // Send shutdown message
366 SendChatMessage(PEER_ID_INEXISTENT, L"*** Server shutting down");
369 MutexAutoLock envlock(m_env_mutex);
371 // Execute script shutdown hooks
372 m_script->on_shutdown();
374 infostream << "Server: Saving players" << std::endl;
375 m_env->saveLoadedPlayers();
377 infostream << "Server: Kicking players" << std::endl;
378 std::string kick_msg;
379 bool reconnect = false;
380 if (getShutdownRequested()) {
381 reconnect = m_shutdown_ask_reconnect;
382 kick_msg = m_shutdown_msg;
384 if (kick_msg == "") {
385 kick_msg = g_settings->get("kick_msg_shutdown");
387 m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
388 kick_msg, reconnect);
390 infostream << "Server: Saving environment metadata" << std::endl;
398 // stop all emerge threads before deleting players that may have
399 // requested blocks to be emerged
400 m_emerge->stopThreads();
402 // Delete things in the reverse order of creation
412 // Deinitialize scripting
413 infostream<<"Server: Deinitializing scripting"<<std::endl;
416 // Delete detached inventories
417 for (std::map<std::string, Inventory*>::iterator
418 i = m_detached_inventories.begin();
419 i != m_detached_inventories.end(); ++i) {
424 void Server::start(Address bind_addr)
426 DSTACK(FUNCTION_NAME);
428 m_bind_addr = bind_addr;
430 infostream<<"Starting server on "
431 << bind_addr.serializeString() <<"..."<<std::endl;
433 // Stop thread if already running
436 // Initialize connection
437 m_con.SetTimeoutMs(30);
438 m_con.Serve(bind_addr);
443 // ASCII art for the win!
445 <<" .__ __ __ "<<std::endl
446 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
447 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
448 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
449 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
450 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
451 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
452 actionstream<<"Server for gameid=\""<<m_gamespec.id
453 <<"\" listening on "<<bind_addr.serializeString()<<":"
454 <<bind_addr.getPort() << "."<<std::endl;
459 DSTACK(FUNCTION_NAME);
461 infostream<<"Server: Stopping and waiting threads"<<std::endl;
463 // Stop threads (set run=false first so both start stopping)
465 //m_emergethread.setRun(false);
467 //m_emergethread.stop();
469 infostream<<"Server: Threads stopped"<<std::endl;
472 void Server::step(float dtime)
474 DSTACK(FUNCTION_NAME);
479 MutexAutoLock lock(m_step_dtime_mutex);
480 m_step_dtime += dtime;
482 // Throw if fatal error occurred in thread
483 std::string async_err = m_async_fatal_error.get();
484 if (!async_err.empty()) {
485 if (!m_simple_singleplayer_mode) {
486 m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
487 g_settings->get("kick_msg_crash"),
488 g_settings->getBool("ask_reconnect_on_crash"));
490 throw ServerError("AsyncErr: " + async_err);
494 void Server::AsyncRunStep(bool initial_step)
496 DSTACK(FUNCTION_NAME);
498 g_profiler->add("Server::AsyncRunStep (num)", 1);
502 MutexAutoLock lock1(m_step_dtime_mutex);
503 dtime = m_step_dtime;
507 // Send blocks to clients
511 if((dtime < 0.001) && (initial_step == false))
514 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
516 //infostream<<"Server steps "<<dtime<<std::endl;
517 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
520 MutexAutoLock lock1(m_step_dtime_mutex);
521 m_step_dtime -= dtime;
528 m_uptime.set(m_uptime.get() + dtime);
534 Update time of day and overall game time
536 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
539 Send to clients at constant intervals
542 m_time_of_day_send_timer -= dtime;
543 if(m_time_of_day_send_timer < 0.0) {
544 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
545 u16 time = m_env->getTimeOfDay();
546 float time_speed = g_settings->getFloat("time_speed");
547 SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
551 MutexAutoLock lock(m_env_mutex);
552 // Figure out and report maximum lag to environment
553 float max_lag = m_env->getMaxLagEstimate();
554 max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
556 if(dtime > 0.1 && dtime > max_lag * 2.0)
557 infostream<<"Server: Maximum lag peaked to "<<dtime
561 m_env->reportMaxLagEstimate(max_lag);
563 ScopeProfiler sp(g_profiler, "SEnv step");
564 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
568 static const float map_timer_and_unload_dtime = 2.92;
569 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
571 MutexAutoLock lock(m_env_mutex);
572 // Run Map's timers and unload unused data
573 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
574 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
575 g_settings->getFloat("server_unload_unused_data_timeout"),
580 Listen to the admin chat, if available
583 if (!m_admin_chat->command_queue.empty()) {
584 MutexAutoLock lock(m_env_mutex);
585 while (!m_admin_chat->command_queue.empty()) {
586 ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx();
587 handleChatInterfaceEvent(evt);
591 m_admin_chat->outgoing_queue.push_back(
592 new ChatEventTimeInfo(m_env->getGameTime(), m_env->getTimeOfDay()));
599 /* Transform liquids */
600 m_liquid_transform_timer += dtime;
601 if(m_liquid_transform_timer >= m_liquid_transform_every)
603 m_liquid_transform_timer -= m_liquid_transform_every;
605 MutexAutoLock lock(m_env_mutex);
607 ScopeProfiler sp(g_profiler, "Server: liquid transform");
609 std::map<v3s16, MapBlock*> modified_blocks;
610 m_env->getMap().transformLiquids(modified_blocks);
615 core::map<v3s16, MapBlock*> lighting_modified_blocks;
616 ServerMap &map = ((ServerMap&)m_env->getMap());
617 map.updateLighting(modified_blocks, lighting_modified_blocks);
619 // Add blocks modified by lighting to modified_blocks
620 for(core::map<v3s16, MapBlock*>::Iterator
621 i = lighting_modified_blocks.getIterator();
622 i.atEnd() == false; i++)
624 MapBlock *block = i.getNode()->getValue();
625 modified_blocks.insert(block->getPos(), block);
629 Set the modified blocks unsent for all the clients
631 if(!modified_blocks.empty())
633 SetBlocksNotSent(modified_blocks);
636 m_clients.step(dtime);
638 m_lag += (m_lag > dtime ? -1 : 1) * dtime/100;
640 // send masterserver announce
642 float &counter = m_masterserver_timer;
643 if(!isSingleplayer() && (!counter || counter >= 300.0) &&
644 g_settings->getBool("server_announce"))
646 ServerList::sendAnnounce(counter ? "update" : "start",
647 m_bind_addr.getPort(),
648 m_clients.getPlayerNames(),
650 m_env->getGameTime(),
653 Mapgen::getMapgenName(m_emerge->mgparams->mgtype),
662 Check added and deleted active objects
665 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
666 MutexAutoLock envlock(m_env_mutex);
669 UNORDERED_MAP<u16, RemoteClient*> clients = m_clients.getClientList();
670 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
672 // Radius inside which objects are active
673 static const s16 radius =
674 g_settings->getS16("active_object_send_range_blocks") * MAP_BLOCKSIZE;
676 // Radius inside which players are active
677 static const bool is_transfer_limited =
678 g_settings->exists("unlimited_player_transfer_distance") &&
679 !g_settings->getBool("unlimited_player_transfer_distance");
680 static const s16 player_transfer_dist = g_settings->getS16("player_transfer_distance") * MAP_BLOCKSIZE;
681 s16 player_radius = player_transfer_dist;
682 if (player_radius == 0 && is_transfer_limited)
683 player_radius = radius;
685 for (UNORDERED_MAP<u16, RemoteClient*>::iterator i = clients.begin();
686 i != clients.end(); ++i) {
687 RemoteClient *client = i->second;
689 // If definitions and textures have not been sent, don't
690 // send objects either
691 if (client->getState() < CS_DefinitionsSent)
694 RemotePlayer *player = m_env->getPlayer(client->peer_id);
695 if (player == NULL) {
696 // This can happen if the client timeouts somehow
697 /*warningstream<<FUNCTION_NAME<<": Client "
699 <<" has no associated player"<<std::endl;*/
703 PlayerSAO *playersao = player->getPlayerSAO();
704 if (playersao == NULL)
707 s16 my_radius = MYMIN(radius, playersao->getWantedRange() * MAP_BLOCKSIZE);
708 if (my_radius <= 0) my_radius = radius;
709 //infostream << "Server: Active Radius " << my_radius << std::endl;
711 std::queue<u16> removed_objects;
712 std::queue<u16> added_objects;
713 m_env->getRemovedActiveObjects(playersao, my_radius, player_radius,
714 client->m_known_objects, removed_objects);
715 m_env->getAddedActiveObjects(playersao, my_radius, player_radius,
716 client->m_known_objects, added_objects);
718 // Ignore if nothing happened
719 if (removed_objects.empty() && added_objects.empty()) {
723 std::string data_buffer;
727 // Handle removed objects
728 writeU16((u8*)buf, removed_objects.size());
729 data_buffer.append(buf, 2);
730 while (!removed_objects.empty()) {
732 u16 id = removed_objects.front();
733 ServerActiveObject* obj = m_env->getActiveObject(id);
735 // Add to data buffer for sending
736 writeU16((u8*)buf, id);
737 data_buffer.append(buf, 2);
739 // Remove from known objects
740 client->m_known_objects.erase(id);
742 if(obj && obj->m_known_by_count > 0)
743 obj->m_known_by_count--;
744 removed_objects.pop();
747 // Handle added objects
748 writeU16((u8*)buf, added_objects.size());
749 data_buffer.append(buf, 2);
750 while (!added_objects.empty()) {
752 u16 id = added_objects.front();
753 ServerActiveObject* obj = m_env->getActiveObject(id);
756 u8 type = ACTIVEOBJECT_TYPE_INVALID;
758 warningstream<<FUNCTION_NAME
759 <<": NULL object"<<std::endl;
761 type = obj->getSendType();
763 // Add to data buffer for sending
764 writeU16((u8*)buf, id);
765 data_buffer.append(buf, 2);
766 writeU8((u8*)buf, type);
767 data_buffer.append(buf, 1);
770 data_buffer.append(serializeLongString(
771 obj->getClientInitializationData(client->net_proto_version)));
773 data_buffer.append(serializeLongString(""));
775 // Add to known objects
776 client->m_known_objects.insert(id);
779 obj->m_known_by_count++;
784 u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
785 verbosestream << "Server: Sent object remove/add: "
786 << removed_objects.size() << " removed, "
787 << added_objects.size() << " added, "
788 << "packet size is " << pktSize << std::endl;
797 MutexAutoLock envlock(m_env_mutex);
798 ScopeProfiler sp(g_profiler, "Server: sending object messages");
801 // Value = data sent by object
802 UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* > buffered_messages;
804 // Get active object messages from environment
806 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
810 std::vector<ActiveObjectMessage>* message_list = NULL;
811 UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator n;
812 n = buffered_messages.find(aom.id);
813 if (n == buffered_messages.end()) {
814 message_list = new std::vector<ActiveObjectMessage>;
815 buffered_messages[aom.id] = message_list;
818 message_list = n->second;
820 message_list->push_back(aom);
824 UNORDERED_MAP<u16, RemoteClient*> clients = m_clients.getClientList();
825 // Route data to every client
826 for (UNORDERED_MAP<u16, RemoteClient*>::iterator i = clients.begin();
827 i != clients.end(); ++i) {
828 RemoteClient *client = i->second;
829 std::string reliable_data;
830 std::string unreliable_data;
831 // Go through all objects in message buffer
832 for (UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator
833 j = buffered_messages.begin();
834 j != buffered_messages.end(); ++j) {
835 // If object is not known by client, skip it
837 if (client->m_known_objects.find(id) == client->m_known_objects.end())
840 // Get message list of object
841 std::vector<ActiveObjectMessage>* list = j->second;
842 // Go through every message
843 for (std::vector<ActiveObjectMessage>::iterator
844 k = list->begin(); k != list->end(); ++k) {
845 // Compose the full new data with header
846 ActiveObjectMessage aom = *k;
847 std::string new_data;
850 writeU16((u8*)&buf[0], aom.id);
851 new_data.append(buf, 2);
853 new_data += serializeString(aom.datastring);
854 // Add data to buffer
856 reliable_data += new_data;
858 unreliable_data += new_data;
862 reliable_data and unreliable_data are now ready.
865 if(reliable_data.size() > 0) {
866 SendActiveObjectMessages(client->peer_id, reliable_data);
869 if(unreliable_data.size() > 0) {
870 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
875 // Clear buffered_messages
876 for (UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator
877 i = buffered_messages.begin();
878 i != buffered_messages.end(); ++i) {
884 Send queued-for-sending map edit events.
887 // We will be accessing the environment
888 MutexAutoLock lock(m_env_mutex);
890 // Don't send too many at a time
893 // Single change sending is disabled if queue size is not small
894 bool disable_single_change_sending = false;
895 if(m_unsent_map_edit_queue.size() >= 4)
896 disable_single_change_sending = true;
898 int event_count = m_unsent_map_edit_queue.size();
900 // We'll log the amount of each
903 while(m_unsent_map_edit_queue.size() != 0)
905 MapEditEvent* event = m_unsent_map_edit_queue.front();
906 m_unsent_map_edit_queue.pop();
908 // Players far away from the change are stored here.
909 // Instead of sending the changes, MapBlocks are set not sent
911 std::vector<u16> far_players;
913 switch (event->type) {
916 prof.add("MEET_ADDNODE", 1);
917 sendAddNode(event->p, event->n, event->already_known_by_peer,
918 &far_players, disable_single_change_sending ? 5 : 30,
919 event->type == MEET_ADDNODE);
921 case MEET_REMOVENODE:
922 prof.add("MEET_REMOVENODE", 1);
923 sendRemoveNode(event->p, event->already_known_by_peer,
924 &far_players, disable_single_change_sending ? 5 : 30);
926 case MEET_BLOCK_NODE_METADATA_CHANGED:
927 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
928 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
929 setBlockNotSent(event->p);
932 infostream << "Server: MEET_OTHER" << std::endl;
933 prof.add("MEET_OTHER", 1);
934 for(std::set<v3s16>::iterator
935 i = event->modified_blocks.begin();
936 i != event->modified_blocks.end(); ++i) {
941 prof.add("unknown", 1);
942 warningstream << "Server: Unknown MapEditEvent "
943 << ((u32)event->type) << std::endl;
948 Set blocks not sent to far players
950 if(!far_players.empty()) {
951 // Convert list format to that wanted by SetBlocksNotSent
952 std::map<v3s16, MapBlock*> modified_blocks2;
953 for(std::set<v3s16>::iterator
954 i = event->modified_blocks.begin();
955 i != event->modified_blocks.end(); ++i) {
956 modified_blocks2[*i] =
957 m_env->getMap().getBlockNoCreateNoEx(*i);
960 // Set blocks not sent
961 for(std::vector<u16>::iterator
962 i = far_players.begin();
963 i != far_players.end(); ++i) {
964 if(RemoteClient *client = getClient(*i))
965 client->SetBlocksNotSent(modified_blocks2);
971 /*// Don't send too many at a time
973 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
977 if(event_count >= 5){
978 infostream<<"Server: MapEditEvents:"<<std::endl;
979 prof.print(infostream);
980 } else if(event_count != 0){
981 verbosestream<<"Server: MapEditEvents:"<<std::endl;
982 prof.print(verbosestream);
988 Trigger emergethread (it somehow gets to a non-triggered but
989 bysy state sometimes)
992 float &counter = m_emergethread_trigger_timer;
994 if (counter >= 2.0) {
997 m_emerge->startThreads();
1001 // Save map, players and auth stuff
1003 float &counter = m_savemap_timer;
1005 static const float save_interval =
1006 g_settings->getFloat("server_map_save_interval");
1007 if (counter >= save_interval) {
1009 MutexAutoLock lock(m_env_mutex);
1011 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1014 if (m_banmanager->isModified()) {
1015 m_banmanager->save();
1018 // Save changed parts of map
1019 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1022 m_env->saveLoadedPlayers();
1024 // Save environment metadata
1030 void Server::Receive()
1032 DSTACK(FUNCTION_NAME);
1033 SharedBuffer<u8> data;
1037 m_con.Receive(&pkt);
1038 peer_id = pkt.getPeerId();
1041 catch(con::InvalidIncomingDataException &e) {
1042 infostream<<"Server::Receive(): "
1043 "InvalidIncomingDataException: what()="
1044 <<e.what()<<std::endl;
1046 catch(SerializationError &e) {
1047 infostream<<"Server::Receive(): "
1048 "SerializationError: what()="
1049 <<e.what()<<std::endl;
1051 catch(ClientStateError &e) {
1052 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
1053 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
1054 L"Try reconnecting or updating your client");
1056 catch(con::PeerNotFoundException &e) {
1061 PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
1063 std::string playername = "";
1064 PlayerSAO *playersao = NULL;
1067 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1068 if (client != NULL) {
1069 playername = client->getName();
1070 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1072 } catch (std::exception &e) {
1078 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1080 // If failed, cancel
1081 if ((playersao == NULL) || (player == NULL)) {
1082 if (player && player->peer_id != 0) {
1083 actionstream << "Server: Failed to emerge player \"" << playername
1084 << "\" (player allocated to an another client)" << std::endl;
1085 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1086 L"name. If your client closed unexpectedly, try again in "
1089 errorstream << "Server: " << playername << ": Failed to emerge player"
1091 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1097 Send complete position information
1099 SendMovePlayer(peer_id);
1102 SendPlayerPrivileges(peer_id);
1104 // Send inventory formspec
1105 SendPlayerInventoryFormspec(peer_id);
1108 SendInventory(playersao);
1111 SendPlayerHPOrDie(playersao);
1114 SendPlayerBreath(playersao);
1116 // Show death screen if necessary
1117 if (playersao->isDead())
1118 SendDeathscreen(peer_id, false, v3f(0,0,0));
1120 // Note things in chat if not in simple singleplayer mode
1121 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1122 // Send information about server to player in chat
1123 SendChatMessage(peer_id, getStatusString());
1125 Address addr = getPeerAddress(player->peer_id);
1126 std::string ip_str = addr.serializeString();
1127 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1132 const std::vector<std::string> &names = m_clients.getPlayerNames();
1134 actionstream << player->getName() << " joins game. List of players: ";
1136 for (std::vector<std::string>::const_iterator i = names.begin();
1137 i != names.end(); ++i) {
1138 actionstream << *i << " ";
1141 actionstream << player->getName() <<std::endl;
1146 inline void Server::handleCommand(NetworkPacket* pkt)
1148 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1149 (this->*opHandle.handler)(pkt);
1152 void Server::ProcessData(NetworkPacket *pkt)
1154 DSTACK(FUNCTION_NAME);
1155 // Environment is locked first.
1156 MutexAutoLock envlock(m_env_mutex);
1158 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1159 u32 peer_id = pkt->getPeerId();
1162 Address address = getPeerAddress(peer_id);
1163 std::string addr_s = address.serializeString();
1165 if(m_banmanager->isIpBanned(addr_s)) {
1166 std::string ban_name = m_banmanager->getBanName(addr_s);
1167 infostream << "Server: A banned client tried to connect from "
1168 << addr_s << "; banned name was "
1169 << ban_name << std::endl;
1170 // This actually doesn't seem to transfer to the client
1171 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1172 + utf8_to_wide(ban_name));
1176 catch(con::PeerNotFoundException &e) {
1178 * no peer for this packet found
1179 * most common reason is peer timeout, e.g. peer didn't
1180 * respond for some time, your server was overloaded or
1183 infostream << "Server::ProcessData(): Canceling: peer "
1184 << peer_id << " not found" << std::endl;
1189 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1191 // Command must be handled into ToServerCommandHandler
1192 if (command >= TOSERVER_NUM_MSG_TYPES) {
1193 infostream << "Server: Ignoring unknown command "
1194 << command << std::endl;
1198 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1203 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1205 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1206 errorstream << "Server::ProcessData(): Cancelling: Peer"
1207 " serialization format invalid or not initialized."
1208 " Skipping incoming command=" << command << std::endl;
1212 /* Handle commands related to client startup */
1213 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1218 if (m_clients.getClientState(peer_id) < CS_Active) {
1219 if (command == TOSERVER_PLAYERPOS) return;
1221 errorstream << "Got packet command: " << command << " for peer id "
1222 << peer_id << " but client isn't active yet. Dropping packet "
1228 } catch (SendFailedException &e) {
1229 errorstream << "Server::ProcessData(): SendFailedException: "
1230 << "what=" << e.what()
1232 } catch (PacketError &e) {
1233 actionstream << "Server::ProcessData(): PacketError: "
1234 << "what=" << e.what()
1239 void Server::setTimeOfDay(u32 time)
1241 m_env->setTimeOfDay(time);
1242 m_time_of_day_send_timer = 0;
1245 void Server::onMapEditEvent(MapEditEvent *event)
1247 if(m_ignore_map_edit_events)
1249 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1251 MapEditEvent *e = event->clone();
1252 m_unsent_map_edit_queue.push(e);
1255 Inventory* Server::getInventory(const InventoryLocation &loc)
1258 case InventoryLocation::UNDEFINED:
1259 case InventoryLocation::CURRENT_PLAYER:
1261 case InventoryLocation::PLAYER:
1263 RemotePlayer *player = dynamic_cast<RemotePlayer *>(m_env->getPlayer(loc.name.c_str()));
1266 PlayerSAO *playersao = player->getPlayerSAO();
1269 return playersao->getInventory();
1272 case InventoryLocation::NODEMETA:
1274 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1277 return meta->getInventory();
1280 case InventoryLocation::DETACHED:
1282 if(m_detached_inventories.count(loc.name) == 0)
1284 return m_detached_inventories[loc.name];
1288 sanity_check(false); // abort
1293 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1296 case InventoryLocation::UNDEFINED:
1298 case InventoryLocation::PLAYER:
1303 RemotePlayer *player =
1304 dynamic_cast<RemotePlayer *>(m_env->getPlayer(loc.name.c_str()));
1309 PlayerSAO *playersao = player->getPlayerSAO();
1313 SendInventory(playersao);
1316 case InventoryLocation::NODEMETA:
1318 v3s16 blockpos = getNodeBlockPos(loc.p);
1320 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1322 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1324 setBlockNotSent(blockpos);
1327 case InventoryLocation::DETACHED:
1329 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1333 sanity_check(false); // abort
1338 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1340 std::vector<u16> clients = m_clients.getClientIDs();
1342 // Set the modified blocks unsent for all the clients
1343 for (std::vector<u16>::iterator i = clients.begin();
1344 i != clients.end(); ++i) {
1345 if (RemoteClient *client = m_clients.lockedGetClientNoEx(*i))
1346 client->SetBlocksNotSent(block);
1351 void Server::peerAdded(con::Peer *peer)
1353 DSTACK(FUNCTION_NAME);
1354 verbosestream<<"Server::peerAdded(): peer->id="
1355 <<peer->id<<std::endl;
1358 c.type = con::PEER_ADDED;
1359 c.peer_id = peer->id;
1361 m_peer_change_queue.push(c);
1364 void Server::deletingPeer(con::Peer *peer, bool timeout)
1366 DSTACK(FUNCTION_NAME);
1367 verbosestream<<"Server::deletingPeer(): peer->id="
1368 <<peer->id<<", timeout="<<timeout<<std::endl;
1370 m_clients.event(peer->id, CSE_Disconnect);
1372 c.type = con::PEER_REMOVED;
1373 c.peer_id = peer->id;
1374 c.timeout = timeout;
1375 m_peer_change_queue.push(c);
1378 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
1380 *retval = m_con.getPeerStat(peer_id,type);
1381 if (*retval == -1) return false;
1385 bool Server::getClientInfo(
1394 std::string* vers_string
1397 *state = m_clients.getClientState(peer_id);
1399 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1401 if (client == NULL) {
1406 *uptime = client->uptime();
1407 *ser_vers = client->serialization_version;
1408 *prot_vers = client->net_proto_version;
1410 *major = client->getMajor();
1411 *minor = client->getMinor();
1412 *patch = client->getPatch();
1413 *vers_string = client->getPatch();
1420 void Server::handlePeerChanges()
1422 while(m_peer_change_queue.size() > 0)
1424 con::PeerChange c = m_peer_change_queue.front();
1425 m_peer_change_queue.pop();
1427 verbosestream<<"Server: Handling peer change: "
1428 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1433 case con::PEER_ADDED:
1434 m_clients.CreateClient(c.peer_id);
1437 case con::PEER_REMOVED:
1438 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1442 FATAL_ERROR("Invalid peer change event received!");
1448 void Server::printToConsoleOnly(const std::string &text)
1451 m_admin_chat->outgoing_queue.push_back(
1452 new ChatEventChat("", utf8_to_wide(text)));
1454 std::cout << text << std::endl;
1458 void Server::Send(NetworkPacket* pkt)
1460 m_clients.send(pkt->getPeerId(),
1461 clientCommandFactoryTable[pkt->getCommand()].channel,
1463 clientCommandFactoryTable[pkt->getCommand()].reliable);
1466 void Server::SendMovement(u16 peer_id)
1468 DSTACK(FUNCTION_NAME);
1469 std::ostringstream os(std::ios_base::binary);
1471 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1473 pkt << g_settings->getFloat("movement_acceleration_default");
1474 pkt << g_settings->getFloat("movement_acceleration_air");
1475 pkt << g_settings->getFloat("movement_acceleration_fast");
1476 pkt << g_settings->getFloat("movement_speed_walk");
1477 pkt << g_settings->getFloat("movement_speed_crouch");
1478 pkt << g_settings->getFloat("movement_speed_fast");
1479 pkt << g_settings->getFloat("movement_speed_climb");
1480 pkt << g_settings->getFloat("movement_speed_jump");
1481 pkt << g_settings->getFloat("movement_liquid_fluidity");
1482 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1483 pkt << g_settings->getFloat("movement_liquid_sink");
1484 pkt << g_settings->getFloat("movement_gravity");
1489 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1491 if (!g_settings->getBool("enable_damage"))
1494 u16 peer_id = playersao->getPeerID();
1495 bool is_alive = playersao->getHP() > 0;
1498 SendPlayerHP(peer_id);
1503 void Server::SendHP(u16 peer_id, u8 hp)
1505 DSTACK(FUNCTION_NAME);
1507 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1512 void Server::SendBreath(u16 peer_id, u16 breath)
1514 DSTACK(FUNCTION_NAME);
1516 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1517 pkt << (u16) breath;
1521 void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
1522 const std::string &custom_reason, bool reconnect)
1524 assert(reason < SERVER_ACCESSDENIED_MAX);
1526 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1528 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1529 pkt << custom_reason;
1530 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1531 reason == SERVER_ACCESSDENIED_CRASH)
1532 pkt << custom_reason << (u8)reconnect;
1536 void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
1538 DSTACK(FUNCTION_NAME);
1540 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1545 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
1546 v3f camera_point_target)
1548 DSTACK(FUNCTION_NAME);
1550 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1551 pkt << set_camera_point_target << camera_point_target;
1555 void Server::SendItemDef(u16 peer_id,
1556 IItemDefManager *itemdef, u16 protocol_version)
1558 DSTACK(FUNCTION_NAME);
1560 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1564 u32 length of the next item
1565 zlib-compressed serialized ItemDefManager
1567 std::ostringstream tmp_os(std::ios::binary);
1568 itemdef->serialize(tmp_os, protocol_version);
1569 std::ostringstream tmp_os2(std::ios::binary);
1570 compressZlib(tmp_os.str(), tmp_os2);
1571 pkt.putLongString(tmp_os2.str());
1574 verbosestream << "Server: Sending item definitions to id(" << peer_id
1575 << "): size=" << pkt.getSize() << std::endl;
1580 void Server::SendNodeDef(u16 peer_id,
1581 INodeDefManager *nodedef, u16 protocol_version)
1583 DSTACK(FUNCTION_NAME);
1585 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1589 u32 length of the next item
1590 zlib-compressed serialized NodeDefManager
1592 std::ostringstream tmp_os(std::ios::binary);
1593 nodedef->serialize(tmp_os, protocol_version);
1594 std::ostringstream tmp_os2(std::ios::binary);
1595 compressZlib(tmp_os.str(), tmp_os2);
1597 pkt.putLongString(tmp_os2.str());
1600 verbosestream << "Server: Sending node definitions to id(" << peer_id
1601 << "): size=" << pkt.getSize() << std::endl;
1607 Non-static send methods
1610 void Server::SendInventory(PlayerSAO* playerSAO)
1612 DSTACK(FUNCTION_NAME);
1614 UpdateCrafting(playerSAO->getPlayer());
1620 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1622 std::ostringstream os;
1623 playerSAO->getInventory()->serialize(os);
1625 std::string s = os.str();
1627 pkt.putRawString(s.c_str(), s.size());
1631 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
1633 DSTACK(FUNCTION_NAME);
1635 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1638 if (peer_id != PEER_ID_INEXISTENT) {
1642 m_clients.sendToAll(0, &pkt, true);
1646 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
1647 const std::string &formname)
1649 DSTACK(FUNCTION_NAME);
1651 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1652 if (formspec == "" ){
1653 //the client should close the formspec
1654 pkt.putLongString("");
1656 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1663 // Spawns a particle on peer with peer_id
1664 void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
1665 v3f pos, v3f velocity, v3f acceleration,
1666 float expirationtime, float size, bool collisiondetection,
1667 bool collision_removal,
1668 bool vertical, const std::string &texture,
1669 const struct TileAnimationParams &animation, u8 glow)
1671 DSTACK(FUNCTION_NAME);
1672 if (peer_id == PEER_ID_INEXISTENT) {
1673 // This sucks and should be replaced by a better solution in a refactor:
1674 std::vector<u16> clients = m_clients.getClientIDs();
1675 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
1676 RemotePlayer *player = m_env->getPlayer(*i);
1679 SendSpawnParticle(*i, player->protocol_version,
1680 pos, velocity, acceleration,
1681 expirationtime, size, collisiondetection,
1682 collision_removal, vertical, texture, animation, glow);
1687 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1689 pkt << pos << velocity << acceleration << expirationtime
1690 << size << collisiondetection;
1691 pkt.putLongString(texture);
1693 pkt << collision_removal;
1694 // This is horrible but required (why are there two ways to serialize pkts?)
1695 std::ostringstream os(std::ios_base::binary);
1696 animation.serialize(os, protocol_version);
1697 pkt.putRawString(os.str());
1703 // Adds a ParticleSpawner on peer with peer_id
1704 void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version,
1705 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1706 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1707 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1708 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1709 const struct TileAnimationParams &animation, u8 glow)
1711 DSTACK(FUNCTION_NAME);
1712 if (peer_id == PEER_ID_INEXISTENT) {
1713 // This sucks and should be replaced:
1714 std::vector<u16> clients = m_clients.getClientIDs();
1715 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
1716 RemotePlayer *player = m_env->getPlayer(*i);
1719 SendAddParticleSpawner(*i, player->protocol_version,
1720 amount, spawntime, minpos, maxpos,
1721 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1722 minsize, maxsize, collisiondetection, collision_removal,
1723 attached_id, vertical, texture, id, animation, glow);
1728 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1730 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1731 << minacc << maxacc << minexptime << maxexptime << minsize
1732 << maxsize << collisiondetection;
1734 pkt.putLongString(texture);
1736 pkt << id << vertical;
1737 pkt << collision_removal;
1739 // This is horrible but required
1740 std::ostringstream os(std::ios_base::binary);
1741 animation.serialize(os, protocol_version);
1742 pkt.putRawString(os.str());
1748 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
1750 DSTACK(FUNCTION_NAME);
1752 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
1754 // Ugly error in this packet
1757 if (peer_id != PEER_ID_INEXISTENT) {
1761 m_clients.sendToAll(0, &pkt, true);
1766 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
1768 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1770 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1771 << form->text << form->number << form->item << form->dir
1772 << form->align << form->offset << form->world_pos << form->size;
1777 void Server::SendHUDRemove(u16 peer_id, u32 id)
1779 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1784 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
1786 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1787 pkt << id << (u8) stat;
1791 case HUD_STAT_SCALE:
1792 case HUD_STAT_ALIGN:
1793 case HUD_STAT_OFFSET:
1794 pkt << *(v2f *) value;
1798 pkt << *(std::string *) value;
1800 case HUD_STAT_WORLD_POS:
1801 pkt << *(v3f *) value;
1804 pkt << *(v2s32 *) value;
1806 case HUD_STAT_NUMBER:
1810 pkt << *(u32 *) value;
1817 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
1819 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1821 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1823 pkt << flags << mask;
1828 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
1830 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1831 pkt << param << value;
1835 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
1836 const std::string &type, const std::vector<std::string> ¶ms)
1838 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1839 pkt << bgcolor << type << (u16) params.size();
1841 for(size_t i=0; i<params.size(); i++)
1847 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
1850 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1853 pkt << do_override << (u16) (ratio * 65535);
1858 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
1860 DSTACK(FUNCTION_NAME);
1862 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1863 pkt << time << time_speed;
1865 if (peer_id == PEER_ID_INEXISTENT) {
1866 m_clients.sendToAll(0, &pkt, true);
1873 void Server::SendPlayerHP(u16 peer_id)
1875 DSTACK(FUNCTION_NAME);
1876 PlayerSAO *playersao = getPlayerSAO(peer_id);
1877 // In some rare case if the player is disconnected
1878 // while Lua call l_punch, for example, this can be NULL
1882 SendHP(peer_id, playersao->getHP());
1883 m_script->player_event(playersao,"health_changed");
1885 // Send to other clients
1886 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1887 ActiveObjectMessage aom(playersao->getId(), true, str);
1888 playersao->m_messages_out.push(aom);
1891 void Server::SendPlayerBreath(PlayerSAO *sao)
1893 DSTACK(FUNCTION_NAME);
1896 m_script->player_event(sao, "breath_changed");
1897 SendBreath(sao->getPeerID(), sao->getBreath());
1900 void Server::SendMovePlayer(u16 peer_id)
1902 DSTACK(FUNCTION_NAME);
1903 RemotePlayer *player = m_env->getPlayer(peer_id);
1905 PlayerSAO *sao = player->getPlayerSAO();
1908 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1909 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1912 v3f pos = sao->getBasePosition();
1913 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1914 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1915 << " pitch=" << sao->getPitch()
1916 << " yaw=" << sao->getYaw()
1923 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
1925 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1928 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1929 << animation_frames[3] << animation_speed;
1934 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
1936 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1937 pkt << first << third;
1940 void Server::SendPlayerPrivileges(u16 peer_id)
1942 RemotePlayer *player = m_env->getPlayer(peer_id);
1944 if(player->peer_id == PEER_ID_INEXISTENT)
1947 std::set<std::string> privs;
1948 m_script->getAuth(player->getName(), NULL, &privs);
1950 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1951 pkt << (u16) privs.size();
1953 for(std::set<std::string>::const_iterator i = privs.begin();
1954 i != privs.end(); ++i) {
1961 void Server::SendPlayerInventoryFormspec(u16 peer_id)
1963 RemotePlayer *player = m_env->getPlayer(peer_id);
1965 if(player->peer_id == PEER_ID_INEXISTENT)
1968 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1969 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1973 u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
1975 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1976 pkt.putRawString(datas.c_str(), datas.size());
1978 return pkt.getSize();
1981 void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
1983 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1984 datas.size(), peer_id);
1986 pkt.putRawString(datas.c_str(), datas.size());
1988 m_clients.send(pkt.getPeerId(),
1989 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
1994 s32 Server::playSound(const SimpleSoundSpec &spec,
1995 const ServerSoundParams ¶ms)
1997 // Find out initial position of sound
1998 bool pos_exists = false;
1999 v3f pos = params.getPos(m_env, &pos_exists);
2000 // If position is not found while it should be, cancel sound
2001 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2004 // Filter destination clients
2005 std::vector<u16> dst_clients;
2006 if(params.to_player != "")
2008 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2010 infostream<<"Server::playSound: Player \""<<params.to_player
2011 <<"\" not found"<<std::endl;
2014 if(player->peer_id == PEER_ID_INEXISTENT){
2015 infostream<<"Server::playSound: Player \""<<params.to_player
2016 <<"\" not connected"<<std::endl;
2019 dst_clients.push_back(player->peer_id);
2022 std::vector<u16> clients = m_clients.getClientIDs();
2024 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2025 RemotePlayer *player = m_env->getPlayer(*i);
2029 PlayerSAO *sao = player->getPlayerSAO();
2034 if(sao->getBasePosition().getDistanceFrom(pos) >
2035 params.max_hear_distance)
2038 dst_clients.push_back(*i);
2042 if(dst_clients.empty())
2046 s32 id = m_next_sound_id++;
2047 // The sound will exist as a reference in m_playing_sounds
2048 m_playing_sounds[id] = ServerPlayingSound();
2049 ServerPlayingSound &psound = m_playing_sounds[id];
2050 psound.params = params;
2052 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2053 pkt << id << spec.name << (float) (spec.gain * params.gain)
2054 << (u8) params.type << pos << params.object << params.loop;
2056 for(std::vector<u16>::iterator i = dst_clients.begin();
2057 i != dst_clients.end(); ++i) {
2058 psound.clients.insert(*i);
2059 m_clients.send(*i, 0, &pkt, true);
2063 void Server::stopSound(s32 handle)
2065 // Get sound reference
2066 UNORDERED_MAP<s32, ServerPlayingSound>::iterator i = m_playing_sounds.find(handle);
2067 if (i == m_playing_sounds.end())
2069 ServerPlayingSound &psound = i->second;
2071 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2074 for (UNORDERED_SET<u16>::iterator i = psound.clients.begin();
2075 i != psound.clients.end(); ++i) {
2077 m_clients.send(*i, 0, &pkt, true);
2079 // Remove sound reference
2080 m_playing_sounds.erase(i);
2083 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2084 std::vector<u16> *far_players, float far_d_nodes)
2086 float maxd = far_d_nodes*BS;
2087 v3f p_f = intToFloat(p, BS);
2089 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2092 std::vector<u16> clients = m_clients.getClientIDs();
2093 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2096 if (RemotePlayer *player = m_env->getPlayer(*i)) {
2097 PlayerSAO *sao = player->getPlayerSAO();
2101 // If player is far away, only set modified blocks not sent
2102 v3f player_pos = sao->getBasePosition();
2103 if (player_pos.getDistanceFrom(p_f) > maxd) {
2104 far_players->push_back(*i);
2111 m_clients.send(*i, 0, &pkt, true);
2115 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2116 std::vector<u16> *far_players, float far_d_nodes,
2117 bool remove_metadata)
2119 float maxd = far_d_nodes*BS;
2120 v3f p_f = intToFloat(p, BS);
2122 std::vector<u16> clients = m_clients.getClientIDs();
2123 for(std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2126 if (RemotePlayer *player = m_env->getPlayer(*i)) {
2127 PlayerSAO *sao = player->getPlayerSAO();
2131 // If player is far away, only set modified blocks not sent
2132 v3f player_pos = sao->getBasePosition();
2133 if(player_pos.getDistanceFrom(p_f) > maxd) {
2134 far_players->push_back(*i);
2140 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2142 RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
2144 pkt << p << n.param0 << n.param1 << n.param2
2145 << (u8) (remove_metadata ? 0 : 1);
2147 if (!remove_metadata) {
2148 if (client->net_proto_version <= 21) {
2149 // Old clients always clear metadata; fix it
2150 // by sending the full block again.
2151 client->SetBlockNotSent(getNodeBlockPos(p));
2158 if (pkt.getSize() > 0)
2159 m_clients.send(*i, 0, &pkt, true);
2163 void Server::setBlockNotSent(v3s16 p)
2165 std::vector<u16> clients = m_clients.getClientIDs();
2167 for(std::vector<u16>::iterator i = clients.begin();
2168 i != clients.end(); ++i) {
2169 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
2170 client->SetBlockNotSent(p);
2175 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
2177 DSTACK(FUNCTION_NAME);
2179 v3s16 p = block->getPos();
2182 Create a packet with the block in the right format
2185 std::ostringstream os(std::ios_base::binary);
2186 block->serialize(os, ver, false);
2187 block->serializeNetworkSpecific(os, net_proto_version);
2188 std::string s = os.str();
2190 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2193 pkt.putRawString(s.c_str(), s.size());
2197 void Server::SendBlocks(float dtime)
2199 DSTACK(FUNCTION_NAME);
2201 MutexAutoLock envlock(m_env_mutex);
2202 //TODO check if one big lock could be faster then multiple small ones
2204 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2206 std::vector<PrioritySortedBlockTransfer> queue;
2208 s32 total_sending = 0;
2211 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
2213 std::vector<u16> clients = m_clients.getClientIDs();
2216 for(std::vector<u16>::iterator i = clients.begin();
2217 i != clients.end(); ++i) {
2218 RemoteClient *client = m_clients.lockedGetClientNoEx(*i, CS_Active);
2223 total_sending += client->SendingCount();
2224 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2230 // Lowest priority number comes first.
2231 // Lowest is most important.
2232 std::sort(queue.begin(), queue.end());
2235 for(u32 i=0; i<queue.size(); i++)
2237 //TODO: Calculate limit dynamically
2238 if(total_sending >= g_settings->getS32
2239 ("max_simultaneous_block_sends_server_total"))
2242 PrioritySortedBlockTransfer q = queue[i];
2244 MapBlock *block = NULL;
2247 block = m_env->getMap().getBlockNoCreate(q.pos);
2249 catch(InvalidPositionException &e)
2254 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id, CS_Active);
2259 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
2261 client->SentBlock(q.pos);
2267 void Server::fillMediaCache()
2269 DSTACK(FUNCTION_NAME);
2271 infostream<<"Server: Calculating media file checksums"<<std::endl;
2273 // Collect all media file paths
2274 std::vector<std::string> paths;
2275 for(std::vector<ModSpec>::iterator i = m_mods.begin();
2276 i != m_mods.end(); ++i) {
2277 const ModSpec &mod = *i;
2278 paths.push_back(mod.path + DIR_DELIM + "textures");
2279 paths.push_back(mod.path + DIR_DELIM + "sounds");
2280 paths.push_back(mod.path + DIR_DELIM + "media");
2281 paths.push_back(mod.path + DIR_DELIM + "models");
2283 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2285 // Collect media file information from paths into cache
2286 for(std::vector<std::string>::iterator i = paths.begin();
2287 i != paths.end(); ++i) {
2288 std::string mediapath = *i;
2289 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2290 for (u32 j = 0; j < dirlist.size(); j++) {
2291 if (dirlist[j].dir) // Ignode dirs
2293 std::string filename = dirlist[j].name;
2294 // If name contains illegal characters, ignore the file
2295 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2296 infostream<<"Server: ignoring illegal file name: \""
2297 << filename << "\"" << std::endl;
2300 // If name is not in a supported format, ignore it
2301 const char *supported_ext[] = {
2302 ".png", ".jpg", ".bmp", ".tga",
2303 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2305 ".x", ".b3d", ".md2", ".obj",
2308 if (removeStringEnd(filename, supported_ext) == ""){
2309 infostream << "Server: ignoring unsupported file extension: \""
2310 << filename << "\"" << std::endl;
2313 // Ok, attempt to load the file and add to cache
2314 std::string filepath = mediapath + DIR_DELIM + filename;
2316 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2318 errorstream << "Server::fillMediaCache(): Could not open \""
2319 << filename << "\" for reading" << std::endl;
2322 std::ostringstream tmp_os(std::ios_base::binary);
2326 fis.read(buf, 1024);
2327 std::streamsize len = fis.gcount();
2328 tmp_os.write(buf, len);
2337 errorstream<<"Server::fillMediaCache(): Failed to read \""
2338 << filename << "\"" << std::endl;
2341 if(tmp_os.str().length() == 0) {
2342 errorstream << "Server::fillMediaCache(): Empty file \""
2343 << filepath << "\"" << std::endl;
2348 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2350 unsigned char *digest = sha1.getDigest();
2351 std::string sha1_base64 = base64_encode(digest, 20);
2352 std::string sha1_hex = hex_encode((char*)digest, 20);
2356 m_media[filename] = MediaInfo(filepath, sha1_base64);
2357 verbosestream << "Server: " << sha1_hex << " is " << filename
2363 void Server::sendMediaAnnouncement(u16 peer_id)
2365 DSTACK(FUNCTION_NAME);
2367 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2371 std::ostringstream os(std::ios_base::binary);
2373 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2374 pkt << (u16) m_media.size();
2376 for (UNORDERED_MAP<std::string, MediaInfo>::iterator i = m_media.begin();
2377 i != m_media.end(); ++i) {
2378 pkt << i->first << i->second.sha1_digest;
2381 pkt << g_settings->get("remote_media");
2385 struct SendableMedia
2391 SendableMedia(const std::string &name_="", const std::string &path_="",
2392 const std::string &data_=""):
2399 void Server::sendRequestedMedia(u16 peer_id,
2400 const std::vector<std::string> &tosend)
2402 DSTACK(FUNCTION_NAME);
2404 verbosestream<<"Server::sendRequestedMedia(): "
2405 <<"Sending files to client"<<std::endl;
2409 // Put 5kB in one bunch (this is not accurate)
2410 u32 bytes_per_bunch = 5000;
2412 std::vector< std::vector<SendableMedia> > file_bunches;
2413 file_bunches.push_back(std::vector<SendableMedia>());
2415 u32 file_size_bunch_total = 0;
2417 for(std::vector<std::string>::const_iterator i = tosend.begin();
2418 i != tosend.end(); ++i) {
2419 const std::string &name = *i;
2421 if (m_media.find(name) == m_media.end()) {
2422 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2423 <<"unknown file \""<<(name)<<"\""<<std::endl;
2427 //TODO get path + name
2428 std::string tpath = m_media[name].path;
2431 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2432 if(fis.good() == false){
2433 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2434 <<tpath<<"\" for reading"<<std::endl;
2437 std::ostringstream tmp_os(std::ios_base::binary);
2441 fis.read(buf, 1024);
2442 std::streamsize len = fis.gcount();
2443 tmp_os.write(buf, len);
2444 file_size_bunch_total += len;
2453 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2454 <<name<<"\""<<std::endl;
2457 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2458 <<tname<<"\""<<std::endl;*/
2460 file_bunches[file_bunches.size()-1].push_back(
2461 SendableMedia(name, tpath, tmp_os.str()));
2463 // Start next bunch if got enough data
2464 if(file_size_bunch_total >= bytes_per_bunch) {
2465 file_bunches.push_back(std::vector<SendableMedia>());
2466 file_size_bunch_total = 0;
2471 /* Create and send packets */
2473 u16 num_bunches = file_bunches.size();
2474 for(u16 i = 0; i < num_bunches; i++) {
2477 u16 total number of texture bunches
2478 u16 index of this bunch
2479 u32 number of files in this bunch
2488 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2489 pkt << num_bunches << i << (u32) file_bunches[i].size();
2491 for(std::vector<SendableMedia>::iterator
2492 j = file_bunches[i].begin();
2493 j != file_bunches[i].end(); ++j) {
2495 pkt.putLongString(j->data);
2498 verbosestream << "Server::sendRequestedMedia(): bunch "
2499 << i << "/" << num_bunches
2500 << " files=" << file_bunches[i].size()
2501 << " size=" << pkt.getSize() << std::endl;
2506 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
2508 if(m_detached_inventories.count(name) == 0) {
2509 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2512 Inventory *inv = m_detached_inventories[name];
2513 std::ostringstream os(std::ios_base::binary);
2515 os << serializeString(name);
2519 std::string s = os.str();
2521 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2522 pkt.putRawString(s.c_str(), s.size());
2524 const std::string &check = m_detached_inventories_player[name];
2525 if (peer_id == PEER_ID_INEXISTENT) {
2527 return m_clients.sendToAll(0, &pkt, true);
2528 RemotePlayer *p = m_env->getPlayer(check.c_str());
2530 m_clients.send(p->peer_id, 0, &pkt, true);
2532 if (check == "" || getPlayerName(peer_id) == check)
2537 void Server::sendDetachedInventories(u16 peer_id)
2539 DSTACK(FUNCTION_NAME);
2541 for(std::map<std::string, Inventory*>::iterator
2542 i = m_detached_inventories.begin();
2543 i != m_detached_inventories.end(); ++i) {
2544 const std::string &name = i->first;
2545 //Inventory *inv = i->second;
2546 sendDetachedInventory(name, peer_id);
2554 void Server::DiePlayer(u16 peer_id)
2556 DSTACK(FUNCTION_NAME);
2557 PlayerSAO *playersao = getPlayerSAO(peer_id);
2558 // In some rare cases this can be NULL -- if the player is disconnected
2559 // when a Lua function modifies l_punch, for example
2563 infostream << "Server::DiePlayer(): Player "
2564 << playersao->getPlayer()->getName()
2565 << " dies" << std::endl;
2567 playersao->setHP(0);
2569 // Trigger scripted stuff
2570 m_script->on_dieplayer(playersao);
2572 SendPlayerHP(peer_id);
2573 SendDeathscreen(peer_id, false, v3f(0,0,0));
2576 void Server::RespawnPlayer(u16 peer_id)
2578 DSTACK(FUNCTION_NAME);
2580 PlayerSAO *playersao = getPlayerSAO(peer_id);
2583 infostream << "Server::RespawnPlayer(): Player "
2584 << playersao->getPlayer()->getName()
2585 << " respawns" << std::endl;
2587 playersao->setHP(PLAYER_MAX_HP);
2588 playersao->setBreath(PLAYER_MAX_BREATH);
2590 bool repositioned = m_script->on_respawnplayer(playersao);
2591 if (!repositioned) {
2592 v3f pos = findSpawnPos();
2593 // setPos will send the new position to client
2594 playersao->setPos(pos);
2597 SendPlayerHP(peer_id);
2601 void Server::DenySudoAccess(u16 peer_id)
2603 DSTACK(FUNCTION_NAME);
2605 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2610 void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
2611 const std::string &str_reason, bool reconnect)
2613 if (proto_ver >= 25) {
2614 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2616 std::wstring wreason = utf8_to_wide(
2617 reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
2618 accessDeniedStrings[(u8)reason]);
2619 SendAccessDenied_Legacy(peer_id, wreason);
2622 m_clients.event(peer_id, CSE_SetDenied);
2623 m_con.DisconnectPeer(peer_id);
2627 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
2629 DSTACK(FUNCTION_NAME);
2631 SendAccessDenied(peer_id, reason, custom_reason);
2632 m_clients.event(peer_id, CSE_SetDenied);
2633 m_con.DisconnectPeer(peer_id);
2636 // 13/03/15: remove this function when protocol version 25 will become
2637 // the minimum version for MT users, maybe in 1 year
2638 void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
2640 DSTACK(FUNCTION_NAME);
2642 SendAccessDenied_Legacy(peer_id, reason);
2643 m_clients.event(peer_id, CSE_SetDenied);
2644 m_con.DisconnectPeer(peer_id);
2647 void Server::acceptAuth(u16 peer_id, bool forSudoMode)
2649 DSTACK(FUNCTION_NAME);
2652 RemoteClient* client = getClient(peer_id, CS_Invalid);
2654 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2656 // Right now, the auth mechs don't change between login and sudo mode.
2657 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2658 client->allowed_sudo_mechs = sudo_auth_mechs;
2660 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2661 << g_settings->getFloat("dedicated_server_step")
2665 m_clients.event(peer_id, CSE_AuthAccept);
2667 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2669 // We only support SRP right now
2670 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2672 resp_pkt << sudo_auth_mechs;
2674 m_clients.event(peer_id, CSE_SudoSuccess);
2678 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
2680 DSTACK(FUNCTION_NAME);
2681 std::wstring message;
2684 Clear references to playing sounds
2686 for (UNORDERED_MAP<s32, ServerPlayingSound>::iterator
2687 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2688 ServerPlayingSound &psound = i->second;
2689 psound.clients.erase(peer_id);
2690 if (psound.clients.empty())
2691 m_playing_sounds.erase(i++);
2696 RemotePlayer *player = m_env->getPlayer(peer_id);
2698 /* Run scripts and remove from environment */
2699 if (player != NULL) {
2700 PlayerSAO *playersao = player->getPlayerSAO();
2703 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2705 playersao->disconnected();
2712 if(player != NULL && reason != CDR_DENY) {
2713 std::ostringstream os(std::ios_base::binary);
2714 std::vector<u16> clients = m_clients.getClientIDs();
2716 for(std::vector<u16>::iterator i = clients.begin();
2717 i != clients.end(); ++i) {
2719 RemotePlayer *player = m_env->getPlayer(*i);
2723 // Get name of player
2724 os << player->getName() << " ";
2727 std::string name = player->getName();
2728 actionstream << name << " "
2729 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2730 << " List of players: " << os.str() << std::endl;
2732 m_admin_chat->outgoing_queue.push_back(
2733 new ChatEventNick(CET_NICK_REMOVE, name));
2737 MutexAutoLock env_lock(m_env_mutex);
2738 m_clients.DeleteClient(peer_id);
2742 // Send leave chat message to all remaining clients
2743 if(message.length() != 0)
2744 SendChatMessage(PEER_ID_INEXISTENT,message);
2747 void Server::UpdateCrafting(RemotePlayer *player)
2749 DSTACK(FUNCTION_NAME);
2751 // Get a preview for crafting
2753 InventoryLocation loc;
2754 loc.setPlayer(player->getName());
2755 std::vector<ItemStack> output_replacements;
2756 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2757 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2758 (&player->inventory)->getList("craft"), loc);
2760 // Put the new preview in
2761 InventoryList *plist = player->inventory.getList("craftpreview");
2762 sanity_check(plist);
2763 sanity_check(plist->getSize() >= 1);
2764 plist->changeItem(0, preview);
2767 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2769 if (evt->type == CET_NICK_ADD) {
2770 // The terminal informed us of its nick choice
2771 m_admin_nick = ((ChatEventNick *)evt)->nick;
2772 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2773 errorstream << "You haven't set up an account." << std::endl
2774 << "Please log in using the client as '"
2775 << m_admin_nick << "' with a secure password." << std::endl
2776 << "Until then, you can't execute admin tasks via the console," << std::endl
2777 << "and everybody can claim the user account instead of you," << std::endl
2778 << "giving them full control over this server." << std::endl;
2781 assert(evt->type == CET_CHAT);
2782 handleAdminChat((ChatEventChat *)evt);
2786 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2787 const std::wstring &wmessage, bool check_shout_priv, RemotePlayer *player)
2789 // If something goes wrong, this player is to blame
2790 RollbackScopeActor rollback_scope(m_rollback,
2791 std::string("player:") + name);
2795 // Whether to send line to the player that sent the message, or to all players
2796 bool broadcast_line = true;
2799 bool ate = m_script->on_chat_message(name,
2800 wide_to_utf8(wmessage));
2801 // If script ate the message, don't proceed
2806 switch (player->canSendChatMessage()) {
2807 case RPLAYER_CHATRESULT_FLOODING: {
2808 std::wstringstream ws;
2809 ws << L"You cannot send more messages. You are limited to "
2810 << g_settings->getFloat("chat_message_limit_per_10sec")
2811 << L" messages per 10 seconds.";
2814 case RPLAYER_CHATRESULT_KICK:
2815 DenyAccess_Legacy(player->peer_id, L"You have been kicked due to message flooding.");
2817 case RPLAYER_CHATRESULT_OK: break;
2818 default: FATAL_ERROR("Unhandled chat filtering result found.");
2822 if (m_max_chatmessage_length > 0 && wmessage.length() > m_max_chatmessage_length) {
2823 return L"Your message exceed the maximum chat message limit set on the server. "
2824 L"It was refused. Send a shorter message";
2827 // Commands are implemented in Lua, so only catch invalid
2828 // commands that were not "eaten" and send an error back
2829 if (wmessage[0] == L'/') {
2830 std::wstring wcmd = wmessage.substr(1);
2831 broadcast_line = false;
2832 if (wcmd.length() == 0)
2833 line += L"-!- Empty command";
2835 line += L"-!- Invalid command: " + str_split(wcmd, L' ')[0];
2837 if (check_shout_priv && !checkPriv(name, "shout")) {
2838 line += L"-!- You don't have permission to shout.";
2839 broadcast_line = false;
2849 Tell calling method to send the message to sender
2851 if (!broadcast_line) {
2855 Send the message to others
2857 actionstream << "CHAT: " << wide_to_narrow(line) << std::endl;
2859 std::vector<u16> clients = m_clients.getClientIDs();
2862 Send the message back to the inital sender
2863 if they are using protocol version >= 29
2866 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2867 if (player->protocol_version >= 29)
2868 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2870 for (u16 i = 0; i < clients.size(); i++) {
2871 u16 cid = clients[i];
2872 if (cid != peer_id_to_avoid_sending)
2873 SendChatMessage(cid, line);
2879 void Server::handleAdminChat(const ChatEventChat *evt)
2881 std::string name = evt->nick;
2882 std::wstring wname = utf8_to_wide(name);
2883 std::wstring wmessage = evt->evt_msg;
2885 std::wstring answer = handleChat(name, wname, wmessage);
2887 // If asked to send answer to sender
2888 if (!answer.empty()) {
2889 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2893 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2895 RemoteClient *client = getClientNoEx(peer_id,state_min);
2897 throw ClientNotFoundException("Client not found");
2901 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2903 return m_clients.getClientNoEx(peer_id, state_min);
2906 std::string Server::getPlayerName(u16 peer_id)
2908 RemotePlayer *player = m_env->getPlayer(peer_id);
2910 return "[id="+itos(peer_id)+"]";
2911 return player->getName();
2914 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2916 RemotePlayer *player = m_env->getPlayer(peer_id);
2919 return player->getPlayerSAO();
2922 std::wstring Server::getStatusString()
2924 std::wostringstream os(std::ios_base::binary);
2927 os<<L"version="<<narrow_to_wide(g_version_string);
2929 os<<L", uptime="<<m_uptime.get();
2931 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2932 // Information about clients
2935 std::vector<u16> clients = m_clients.getClientIDs();
2936 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2938 RemotePlayer *player = m_env->getPlayer(*i);
2939 // Get name of player
2940 std::wstring name = L"unknown";
2942 name = narrow_to_wide(player->getName());
2943 // Add name to information string
2951 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
2952 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2953 if(g_settings->get("motd") != "")
2954 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2958 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2960 std::set<std::string> privs;
2961 m_script->getAuth(name, NULL, &privs);
2965 bool Server::checkPriv(const std::string &name, const std::string &priv)
2967 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2968 return (privs.count(priv) != 0);
2971 void Server::reportPrivsModified(const std::string &name)
2974 std::vector<u16> clients = m_clients.getClientIDs();
2975 for(std::vector<u16>::iterator i = clients.begin();
2976 i != clients.end(); ++i) {
2977 RemotePlayer *player = m_env->getPlayer(*i);
2978 reportPrivsModified(player->getName());
2981 RemotePlayer *player = m_env->getPlayer(name.c_str());
2984 SendPlayerPrivileges(player->peer_id);
2985 PlayerSAO *sao = player->getPlayerSAO();
2988 sao->updatePrivileges(
2989 getPlayerEffectivePrivs(name),
2994 void Server::reportInventoryFormspecModified(const std::string &name)
2996 RemotePlayer *player = m_env->getPlayer(name.c_str());
2999 SendPlayerInventoryFormspec(player->peer_id);
3002 void Server::setIpBanned(const std::string &ip, const std::string &name)
3004 m_banmanager->add(ip, name);
3007 void Server::unsetIpBanned(const std::string &ip_or_name)
3009 m_banmanager->remove(ip_or_name);
3012 std::string Server::getBanDescription(const std::string &ip_or_name)
3014 return m_banmanager->getBanDescription(ip_or_name);
3017 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3019 // m_env will be NULL if the server is initializing
3023 if (m_admin_nick == name && !m_admin_nick.empty()) {
3024 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3027 RemotePlayer *player = m_env->getPlayer(name);
3032 if (player->peer_id == PEER_ID_INEXISTENT)
3035 SendChatMessage(player->peer_id, msg);
3038 bool Server::showFormspec(const char *playername, const std::string &formspec,
3039 const std::string &formname)
3041 // m_env will be NULL if the server is initializing
3045 RemotePlayer *player = m_env->getPlayer(playername);
3049 SendShowFormspecMessage(player->peer_id, formspec, formname);
3053 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3058 u32 id = player->addHud(form);
3060 SendHUDAdd(player->peer_id, id, form);
3065 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3069 HudElement* todel = player->removeHud(id);
3076 SendHUDRemove(player->peer_id, id);
3080 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3085 SendHUDChange(player->peer_id, id, stat, data);
3089 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3094 SendHUDSetFlags(player->peer_id, flags, mask);
3095 player->hud_flags &= ~mask;
3096 player->hud_flags |= flags;
3098 PlayerSAO* playersao = player->getPlayerSAO();
3100 if (playersao == NULL)
3103 m_script->player_event(playersao, "hud_changed");
3107 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3112 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3115 player->setHotbarItemcount(hotbar_itemcount);
3116 std::ostringstream os(std::ios::binary);
3117 writeS32(os, hotbar_itemcount);
3118 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3122 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3127 player->setHotbarImage(name);
3128 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3131 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3135 return player->getHotbarImage();
3138 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3143 player->setHotbarSelectedImage(name);
3144 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3147 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3148 v2s32 animation_frames[4], f32 frame_speed)
3153 player->setLocalAnimations(animation_frames, frame_speed);
3154 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3158 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3163 player->eye_offset_first = first;
3164 player->eye_offset_third = third;
3165 SendEyeOffset(player->peer_id, first, third);
3169 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3170 const std::string &type, const std::vector<std::string> ¶ms)
3175 player->setSky(bgcolor, type, params);
3176 SendSetSky(player->peer_id, bgcolor, type, params);
3180 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3186 player->overrideDayNightRatio(do_override, ratio);
3187 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3191 void Server::notifyPlayers(const std::wstring &msg)
3193 SendChatMessage(PEER_ID_INEXISTENT,msg);
3196 void Server::spawnParticle(const std::string &playername, v3f pos,
3197 v3f velocity, v3f acceleration,
3198 float expirationtime, float size, bool
3199 collisiondetection, bool collision_removal,
3200 bool vertical, const std::string &texture,
3201 const struct TileAnimationParams &animation, u8 glow)
3203 // m_env will be NULL if the server is initializing
3207 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3208 if (playername != "") {
3209 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3212 peer_id = player->peer_id;
3213 proto_ver = player->protocol_version;
3216 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3217 expirationtime, size, collisiondetection,
3218 collision_removal, vertical, texture, animation, glow);
3221 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3222 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3223 float minexptime, float maxexptime, float minsize, float maxsize,
3224 bool collisiondetection, bool collision_removal,
3225 ServerActiveObject *attached, bool vertical, const std::string &texture,
3226 const std::string &playername, const struct TileAnimationParams &animation,
3229 // m_env will be NULL if the server is initializing
3233 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3234 if (playername != "") {
3235 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3238 peer_id = player->peer_id;
3239 proto_ver = player->protocol_version;
3242 u16 attached_id = attached ? attached->getId() : 0;
3245 if (attached_id == 0)
3246 id = m_env->addParticleSpawner(spawntime);
3248 id = m_env->addParticleSpawner(spawntime, attached_id);
3250 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3251 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3252 minexptime, maxexptime, minsize, maxsize,
3253 collisiondetection, collision_removal, attached_id, vertical,
3254 texture, id, animation, glow);
3259 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3261 // m_env will be NULL if the server is initializing
3263 throw ServerError("Can't delete particle spawners during initialisation!");
3265 u16 peer_id = PEER_ID_INEXISTENT;
3266 if (playername != "") {
3267 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3270 peer_id = player->peer_id;
3273 m_env->deleteParticleSpawner(id);
3274 SendDeleteParticleSpawner(peer_id, id);
3277 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3279 if(m_detached_inventories.count(name) > 0){
3280 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3281 delete m_detached_inventories[name];
3283 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3285 Inventory *inv = new Inventory(m_itemdef);
3287 m_detached_inventories[name] = inv;
3288 m_detached_inventories_player[name] = player;
3289 //TODO find a better way to do this
3290 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3294 // actions: time-reversed list
3295 // Return value: success/failure
3296 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3297 std::list<std::string> *log)
3299 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3300 ServerMap *map = (ServerMap*)(&m_env->getMap());
3302 // Fail if no actions to handle
3303 if(actions.empty()){
3304 log->push_back("Nothing to do.");
3311 for(std::list<RollbackAction>::const_iterator
3312 i = actions.begin();
3313 i != actions.end(); ++i)
3315 const RollbackAction &action = *i;
3317 bool success = action.applyRevert(map, this, this);
3320 std::ostringstream os;
3321 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3322 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3324 log->push_back(os.str());
3326 std::ostringstream os;
3327 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3328 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3330 log->push_back(os.str());
3334 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3335 <<" failed"<<std::endl;
3337 // Call it done if less than half failed
3338 return num_failed <= num_tried/2;
3341 // IGameDef interface
3343 IItemDefManager *Server::getItemDefManager()
3348 INodeDefManager *Server::getNodeDefManager()
3353 ICraftDefManager *Server::getCraftDefManager()
3358 u16 Server::allocateUnknownNodeId(const std::string &name)
3360 return m_nodedef->allocateDummy(name);
3363 MtEventManager *Server::getEventManager()
3368 IWritableItemDefManager *Server::getWritableItemDefManager()
3373 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3378 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3383 const ModSpec *Server::getModSpec(const std::string &modname) const
3385 std::vector<ModSpec>::const_iterator it;
3386 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3387 const ModSpec &mod = *it;
3388 if (mod.name == modname)
3394 void Server::getModNames(std::vector<std::string> &modlist)
3396 std::vector<ModSpec>::iterator it;
3397 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3398 modlist.push_back(it->name);
3401 std::string Server::getBuiltinLuaPath()
3403 return porting::path_share + DIR_DELIM + "builtin";
3406 v3f Server::findSpawnPos()
3408 ServerMap &map = m_env->getServerMap();
3410 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3411 return nodeposf * BS;
3414 bool is_good = false;
3416 // Try to find a good place a few times
3417 for(s32 i = 0; i < 4000 && !is_good; i++) {
3419 // We're going to try to throw the player to this position
3420 v2s16 nodepos2d = v2s16(
3421 -range + (myrand() % (range * 2)),
3422 -range + (myrand() % (range * 2)));
3424 // Get spawn level at point
3425 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3426 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3427 // the mapgen to signify an unsuitable spawn position
3428 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3431 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3434 for (s32 i = 0; i < 10; i++) {
3435 v3s16 blockpos = getNodeBlockPos(nodepos);
3436 map.emergeBlock(blockpos, true);
3437 content_t c = map.getNodeNoEx(nodepos).getContent();
3438 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3440 if (air_count >= 2) {
3441 nodeposf = intToFloat(nodepos, BS);
3442 // Don't spawn the player outside map boundaries
3443 if (objectpos_over_limit(nodeposf))
3456 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3458 bool newplayer = false;
3461 Try to get an existing player
3463 RemotePlayer *player = m_env->getPlayer(name);
3465 // If player is already connected, cancel
3466 if (player != NULL && player->peer_id != 0) {
3467 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3472 If player with the wanted peer_id already exists, cancel.
3474 if (m_env->getPlayer(peer_id) != NULL) {
3475 infostream<<"emergePlayer(): Player with wrong name but same"
3476 " peer_id already exists"<<std::endl;
3480 // Create a new player active object
3481 PlayerSAO *playersao = new PlayerSAO(m_env, peer_id, isSingleplayer());
3482 player = m_env->loadPlayer(name, playersao);
3484 // Create player if it doesn't exist
3487 player = new RemotePlayer(name, this->idef());
3488 // Set player position
3489 infostream<<"Server: Finding spawn place for player \""
3490 <<name<<"\""<<std::endl;
3491 playersao->setBasePosition(findSpawnPos());
3493 // Make sure the player is saved
3494 player->setModified(true);
3496 // Add player to environment
3497 m_env->addPlayer(player);
3499 // If the player exists, ensure that they respawn inside legal bounds
3500 // This fixes an assert crash when the player can't be added
3501 // to the environment
3502 if (objectpos_over_limit(playersao->getBasePosition())) {
3503 actionstream << "Respawn position for player \""
3504 << name << "\" outside limits, resetting" << std::endl;
3505 playersao->setBasePosition(findSpawnPos());
3509 playersao->initialize(player, getPlayerEffectivePrivs(player->getName()));
3511 player->protocol_version = proto_version;
3513 /* Clean up old HUD elements from previous sessions */
3516 /* Add object to environment */
3517 m_env->addActiveObject(playersao);
3521 m_script->on_newplayer(playersao);
3527 void dedicated_server_loop(Server &server, bool &kill)
3529 DSTACK(FUNCTION_NAME);
3531 verbosestream<<"dedicated_server_loop()"<<std::endl;
3533 IntervalLimiter m_profiler_interval;
3535 static const float steplen = g_settings->getFloat("dedicated_server_step");
3536 static const float profiler_print_interval =
3537 g_settings->getFloat("profiler_print_interval");
3540 // This is kind of a hack but can be done like this
3541 // because server.step() is very light
3543 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3544 sleep_ms((int)(steplen*1000.0));
3546 server.step(steplen);
3548 if(server.getShutdownRequested() || kill)
3550 infostream<<"Dedicated server quitting"<<std::endl;
3552 if(g_settings->getBool("server_announce"))
3553 ServerList::sendAnnounce("delete", server.m_bind_addr.getPort());
3561 if (profiler_print_interval != 0) {
3562 if(m_profiler_interval.step(steplen, profiler_print_interval))
3564 infostream<<"Profiler:"<<std::endl;
3565 g_profiler->print(infostream);
3566 g_profiler->clear();