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);
2794 switch (player->canSendChatMessage()) {
2795 case RPLAYER_CHATRESULT_FLOODING: {
2796 std::wstringstream ws;
2797 ws << L"You cannot send more messages. You are limited to "
2798 << g_settings->getFloat("chat_message_limit_per_10sec")
2799 << L" messages per 10 seconds.";
2802 case RPLAYER_CHATRESULT_KICK:
2803 DenyAccess_Legacy(player->peer_id,
2804 L"You have been kicked due to message flooding.");
2806 case RPLAYER_CHATRESULT_OK:
2809 FATAL_ERROR("Unhandled chat filtering result found.");
2813 if (m_max_chatmessage_length > 0
2814 && wmessage.length() > m_max_chatmessage_length) {
2815 return L"Your message exceed the maximum chat message limit set on the server. "
2816 L"It was refused. Send a shorter message";
2819 // Run script hook, exit if script ate the chat message
2820 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2825 // Whether to send line to the player that sent the message, or to all players
2826 bool broadcast_line = true;
2828 // Commands are implemented in Lua, so only catch invalid
2829 // commands that were not "eaten" and send an error back
2830 if (wmessage[0] == L'/') {
2831 std::wstring wcmd = wmessage.substr(1);
2832 broadcast_line = false;
2833 if (wcmd.length() == 0)
2834 line += L"-!- Empty command";
2836 line += L"-!- Invalid command: " + str_split(wcmd, L' ')[0];
2838 if (check_shout_priv && !checkPriv(name, "shout")) {
2839 line += L"-!- You don't have permission to shout.";
2840 broadcast_line = false;
2850 Tell calling method to send the message to sender
2852 if (!broadcast_line) {
2856 Send the message to others
2858 actionstream << "CHAT: " << wide_to_narrow(line) << std::endl;
2860 std::vector<u16> clients = m_clients.getClientIDs();
2863 Send the message back to the inital sender
2864 if they are using protocol version >= 29
2867 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2868 if (player && player->protocol_version >= 29)
2869 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2871 for (u16 i = 0; i < clients.size(); i++) {
2872 u16 cid = clients[i];
2873 if (cid != peer_id_to_avoid_sending)
2874 SendChatMessage(cid, line);
2880 void Server::handleAdminChat(const ChatEventChat *evt)
2882 std::string name = evt->nick;
2883 std::wstring wname = utf8_to_wide(name);
2884 std::wstring wmessage = evt->evt_msg;
2886 std::wstring answer = handleChat(name, wname, wmessage);
2888 // If asked to send answer to sender
2889 if (!answer.empty()) {
2890 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2894 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2896 RemoteClient *client = getClientNoEx(peer_id,state_min);
2898 throw ClientNotFoundException("Client not found");
2902 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2904 return m_clients.getClientNoEx(peer_id, state_min);
2907 std::string Server::getPlayerName(u16 peer_id)
2909 RemotePlayer *player = m_env->getPlayer(peer_id);
2911 return "[id="+itos(peer_id)+"]";
2912 return player->getName();
2915 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2917 RemotePlayer *player = m_env->getPlayer(peer_id);
2920 return player->getPlayerSAO();
2923 std::wstring Server::getStatusString()
2925 std::wostringstream os(std::ios_base::binary);
2928 os<<L"version="<<narrow_to_wide(g_version_string);
2930 os<<L", uptime="<<m_uptime.get();
2932 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2933 // Information about clients
2936 std::vector<u16> clients = m_clients.getClientIDs();
2937 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2939 RemotePlayer *player = m_env->getPlayer(*i);
2940 // Get name of player
2941 std::wstring name = L"unknown";
2943 name = narrow_to_wide(player->getName());
2944 // Add name to information string
2952 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
2953 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2954 if(g_settings->get("motd") != "")
2955 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2959 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2961 std::set<std::string> privs;
2962 m_script->getAuth(name, NULL, &privs);
2966 bool Server::checkPriv(const std::string &name, const std::string &priv)
2968 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2969 return (privs.count(priv) != 0);
2972 void Server::reportPrivsModified(const std::string &name)
2975 std::vector<u16> clients = m_clients.getClientIDs();
2976 for(std::vector<u16>::iterator i = clients.begin();
2977 i != clients.end(); ++i) {
2978 RemotePlayer *player = m_env->getPlayer(*i);
2979 reportPrivsModified(player->getName());
2982 RemotePlayer *player = m_env->getPlayer(name.c_str());
2985 SendPlayerPrivileges(player->peer_id);
2986 PlayerSAO *sao = player->getPlayerSAO();
2989 sao->updatePrivileges(
2990 getPlayerEffectivePrivs(name),
2995 void Server::reportInventoryFormspecModified(const std::string &name)
2997 RemotePlayer *player = m_env->getPlayer(name.c_str());
3000 SendPlayerInventoryFormspec(player->peer_id);
3003 void Server::setIpBanned(const std::string &ip, const std::string &name)
3005 m_banmanager->add(ip, name);
3008 void Server::unsetIpBanned(const std::string &ip_or_name)
3010 m_banmanager->remove(ip_or_name);
3013 std::string Server::getBanDescription(const std::string &ip_or_name)
3015 return m_banmanager->getBanDescription(ip_or_name);
3018 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3020 // m_env will be NULL if the server is initializing
3024 if (m_admin_nick == name && !m_admin_nick.empty()) {
3025 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3028 RemotePlayer *player = m_env->getPlayer(name);
3033 if (player->peer_id == PEER_ID_INEXISTENT)
3036 SendChatMessage(player->peer_id, msg);
3039 bool Server::showFormspec(const char *playername, const std::string &formspec,
3040 const std::string &formname)
3042 // m_env will be NULL if the server is initializing
3046 RemotePlayer *player = m_env->getPlayer(playername);
3050 SendShowFormspecMessage(player->peer_id, formspec, formname);
3054 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3059 u32 id = player->addHud(form);
3061 SendHUDAdd(player->peer_id, id, form);
3066 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3070 HudElement* todel = player->removeHud(id);
3077 SendHUDRemove(player->peer_id, id);
3081 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3086 SendHUDChange(player->peer_id, id, stat, data);
3090 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3095 SendHUDSetFlags(player->peer_id, flags, mask);
3096 player->hud_flags &= ~mask;
3097 player->hud_flags |= flags;
3099 PlayerSAO* playersao = player->getPlayerSAO();
3101 if (playersao == NULL)
3104 m_script->player_event(playersao, "hud_changed");
3108 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3113 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3116 player->setHotbarItemcount(hotbar_itemcount);
3117 std::ostringstream os(std::ios::binary);
3118 writeS32(os, hotbar_itemcount);
3119 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3123 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3128 player->setHotbarImage(name);
3129 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3132 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3136 return player->getHotbarImage();
3139 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3144 player->setHotbarSelectedImage(name);
3145 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3148 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3149 v2s32 animation_frames[4], f32 frame_speed)
3154 player->setLocalAnimations(animation_frames, frame_speed);
3155 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3159 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3164 player->eye_offset_first = first;
3165 player->eye_offset_third = third;
3166 SendEyeOffset(player->peer_id, first, third);
3170 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3171 const std::string &type, const std::vector<std::string> ¶ms)
3176 player->setSky(bgcolor, type, params);
3177 SendSetSky(player->peer_id, bgcolor, type, params);
3181 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3187 player->overrideDayNightRatio(do_override, ratio);
3188 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3192 void Server::notifyPlayers(const std::wstring &msg)
3194 SendChatMessage(PEER_ID_INEXISTENT,msg);
3197 void Server::spawnParticle(const std::string &playername, v3f pos,
3198 v3f velocity, v3f acceleration,
3199 float expirationtime, float size, bool
3200 collisiondetection, bool collision_removal,
3201 bool vertical, const std::string &texture,
3202 const struct TileAnimationParams &animation, u8 glow)
3204 // m_env will be NULL if the server is initializing
3208 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3209 if (playername != "") {
3210 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3213 peer_id = player->peer_id;
3214 proto_ver = player->protocol_version;
3217 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3218 expirationtime, size, collisiondetection,
3219 collision_removal, vertical, texture, animation, glow);
3222 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3223 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3224 float minexptime, float maxexptime, float minsize, float maxsize,
3225 bool collisiondetection, bool collision_removal,
3226 ServerActiveObject *attached, bool vertical, const std::string &texture,
3227 const std::string &playername, const struct TileAnimationParams &animation,
3230 // m_env will be NULL if the server is initializing
3234 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3235 if (playername != "") {
3236 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3239 peer_id = player->peer_id;
3240 proto_ver = player->protocol_version;
3243 u16 attached_id = attached ? attached->getId() : 0;
3246 if (attached_id == 0)
3247 id = m_env->addParticleSpawner(spawntime);
3249 id = m_env->addParticleSpawner(spawntime, attached_id);
3251 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3252 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3253 minexptime, maxexptime, minsize, maxsize,
3254 collisiondetection, collision_removal, attached_id, vertical,
3255 texture, id, animation, glow);
3260 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3262 // m_env will be NULL if the server is initializing
3264 throw ServerError("Can't delete particle spawners during initialisation!");
3266 u16 peer_id = PEER_ID_INEXISTENT;
3267 if (playername != "") {
3268 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3271 peer_id = player->peer_id;
3274 m_env->deleteParticleSpawner(id);
3275 SendDeleteParticleSpawner(peer_id, id);
3278 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3280 if(m_detached_inventories.count(name) > 0){
3281 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3282 delete m_detached_inventories[name];
3284 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3286 Inventory *inv = new Inventory(m_itemdef);
3288 m_detached_inventories[name] = inv;
3289 m_detached_inventories_player[name] = player;
3290 //TODO find a better way to do this
3291 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3295 // actions: time-reversed list
3296 // Return value: success/failure
3297 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3298 std::list<std::string> *log)
3300 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3301 ServerMap *map = (ServerMap*)(&m_env->getMap());
3303 // Fail if no actions to handle
3304 if(actions.empty()){
3305 log->push_back("Nothing to do.");
3312 for(std::list<RollbackAction>::const_iterator
3313 i = actions.begin();
3314 i != actions.end(); ++i)
3316 const RollbackAction &action = *i;
3318 bool success = action.applyRevert(map, this, this);
3321 std::ostringstream os;
3322 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3323 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3325 log->push_back(os.str());
3327 std::ostringstream os;
3328 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3329 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3331 log->push_back(os.str());
3335 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3336 <<" failed"<<std::endl;
3338 // Call it done if less than half failed
3339 return num_failed <= num_tried/2;
3342 // IGameDef interface
3344 IItemDefManager *Server::getItemDefManager()
3349 INodeDefManager *Server::getNodeDefManager()
3354 ICraftDefManager *Server::getCraftDefManager()
3359 u16 Server::allocateUnknownNodeId(const std::string &name)
3361 return m_nodedef->allocateDummy(name);
3364 MtEventManager *Server::getEventManager()
3369 IWritableItemDefManager *Server::getWritableItemDefManager()
3374 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3379 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3384 const ModSpec *Server::getModSpec(const std::string &modname) const
3386 std::vector<ModSpec>::const_iterator it;
3387 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3388 const ModSpec &mod = *it;
3389 if (mod.name == modname)
3395 void Server::getModNames(std::vector<std::string> &modlist)
3397 std::vector<ModSpec>::iterator it;
3398 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3399 modlist.push_back(it->name);
3402 std::string Server::getBuiltinLuaPath()
3404 return porting::path_share + DIR_DELIM + "builtin";
3407 v3f Server::findSpawnPos()
3409 ServerMap &map = m_env->getServerMap();
3411 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3412 return nodeposf * BS;
3415 bool is_good = false;
3417 // Try to find a good place a few times
3418 for(s32 i = 0; i < 4000 && !is_good; i++) {
3420 // We're going to try to throw the player to this position
3421 v2s16 nodepos2d = v2s16(
3422 -range + (myrand() % (range * 2)),
3423 -range + (myrand() % (range * 2)));
3425 // Get spawn level at point
3426 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3427 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3428 // the mapgen to signify an unsuitable spawn position
3429 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3432 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3435 for (s32 i = 0; i < 10; i++) {
3436 v3s16 blockpos = getNodeBlockPos(nodepos);
3437 map.emergeBlock(blockpos, true);
3438 content_t c = map.getNodeNoEx(nodepos).getContent();
3439 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3441 if (air_count >= 2) {
3442 nodeposf = intToFloat(nodepos, BS);
3443 // Don't spawn the player outside map boundaries
3444 if (objectpos_over_limit(nodeposf))
3457 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3459 bool newplayer = false;
3462 Try to get an existing player
3464 RemotePlayer *player = m_env->getPlayer(name);
3466 // If player is already connected, cancel
3467 if (player != NULL && player->peer_id != 0) {
3468 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3473 If player with the wanted peer_id already exists, cancel.
3475 if (m_env->getPlayer(peer_id) != NULL) {
3476 infostream<<"emergePlayer(): Player with wrong name but same"
3477 " peer_id already exists"<<std::endl;
3481 // Create a new player active object
3482 PlayerSAO *playersao = new PlayerSAO(m_env, peer_id, isSingleplayer());
3483 player = m_env->loadPlayer(name, playersao);
3485 // Create player if it doesn't exist
3488 player = new RemotePlayer(name, this->idef());
3489 // Set player position
3490 infostream<<"Server: Finding spawn place for player \""
3491 <<name<<"\""<<std::endl;
3492 playersao->setBasePosition(findSpawnPos());
3494 // Make sure the player is saved
3495 player->setModified(true);
3497 // Add player to environment
3498 m_env->addPlayer(player);
3500 // If the player exists, ensure that they respawn inside legal bounds
3501 // This fixes an assert crash when the player can't be added
3502 // to the environment
3503 if (objectpos_over_limit(playersao->getBasePosition())) {
3504 actionstream << "Respawn position for player \""
3505 << name << "\" outside limits, resetting" << std::endl;
3506 playersao->setBasePosition(findSpawnPos());
3510 playersao->initialize(player, getPlayerEffectivePrivs(player->getName()));
3512 player->protocol_version = proto_version;
3514 /* Clean up old HUD elements from previous sessions */
3517 /* Add object to environment */
3518 m_env->addActiveObject(playersao);
3522 m_script->on_newplayer(playersao);
3528 void dedicated_server_loop(Server &server, bool &kill)
3530 DSTACK(FUNCTION_NAME);
3532 verbosestream<<"dedicated_server_loop()"<<std::endl;
3534 IntervalLimiter m_profiler_interval;
3536 static const float steplen = g_settings->getFloat("dedicated_server_step");
3537 static const float profiler_print_interval =
3538 g_settings->getFloat("profiler_print_interval");
3541 // This is kind of a hack but can be done like this
3542 // because server.step() is very light
3544 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3545 sleep_ms((int)(steplen*1000.0));
3547 server.step(steplen);
3549 if(server.getShutdownRequested() || kill)
3551 infostream<<"Dedicated server quitting"<<std::endl;
3553 if(g_settings->getBool("server_announce"))
3554 ServerList::sendAnnounce("delete", server.m_bind_addr.getPort());
3562 if (profiler_print_interval != 0) {
3563 if(m_profiler_interval.step(steplen, profiler_print_interval))
3565 infostream<<"Profiler:"<<std::endl;
3566 g_profiler->print(infostream);
3567 g_profiler->clear();