3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "network/networkprotocol.h"
25 #include "network/serveropcodes.h"
27 #include "environment.h"
29 #include "threading/mutex_auto_lock.h"
30 #include "constants.h"
36 #include "serverobject.h"
37 #include "genericobject.h"
41 #include "serverscripting.h"
48 #include "content_mapnode.h"
49 #include "content_nodemeta.h"
50 #include "content_abm.h"
51 #include "content_sao.h"
53 #include "event_manager.h"
54 #include "serverlist.h"
55 #include "util/string.h"
57 #include "util/serialize.h"
58 #include "util/thread.h"
59 #include "defaultsettings.h"
60 #include "util/base64.h"
61 #include "util/sha1.h"
64 class ClientNotFoundException : public BaseException
67 ClientNotFoundException(const char *s):
72 class ServerThread : public Thread
76 ServerThread(Server *server):
87 void *ServerThread::run()
89 DSTACK(FUNCTION_NAME);
90 BEGIN_DEBUG_EXCEPTION_HANDLER
92 m_server->AsyncRunStep(true);
94 while (!stopRequested()) {
96 //TimeTaker timer("AsyncRunStep() + Receive()");
98 m_server->AsyncRunStep();
102 } catch (con::NoIncomingDataException &e) {
103 } catch (con::PeerNotFoundException &e) {
104 infostream<<"Server: PeerNotFoundException"<<std::endl;
105 } catch (ClientNotFoundException &e) {
106 } catch (con::ConnectionBindFailed &e) {
107 m_server->setAsyncFatalError(e.what());
108 } catch (LuaError &e) {
109 m_server->setAsyncFatalError(
110 "ServerThread::run Lua: " + std::string(e.what()));
114 END_DEBUG_EXCEPTION_HANDLER
119 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
121 if(pos_exists) *pos_exists = false;
126 if(pos_exists) *pos_exists = true;
131 ServerActiveObject *sao = env->getActiveObject(object);
134 if(pos_exists) *pos_exists = true;
135 return sao->getBasePosition(); }
147 const std::string &path_world,
148 const SubgameSpec &gamespec,
149 bool simple_singleplayer_mode,
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),
182 m_mod_storage_save_timer(10.0f)
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 ServerScripting(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;
792 m_mod_storage_save_timer -= dtime;
793 if (m_mod_storage_save_timer <= 0.0f) {
794 infostream << "Saving registered mod storages." << std::endl;
795 m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
796 for (UNORDERED_MAP<std::string, ModMetadata *>::const_iterator
797 it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
798 if (it->second->isModified()) {
799 it->second->save(getModStoragePath());
809 MutexAutoLock envlock(m_env_mutex);
810 ScopeProfiler sp(g_profiler, "Server: sending object messages");
813 // Value = data sent by object
814 UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* > buffered_messages;
816 // Get active object messages from environment
818 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
822 std::vector<ActiveObjectMessage>* message_list = NULL;
823 UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator n;
824 n = buffered_messages.find(aom.id);
825 if (n == buffered_messages.end()) {
826 message_list = new std::vector<ActiveObjectMessage>;
827 buffered_messages[aom.id] = message_list;
830 message_list = n->second;
832 message_list->push_back(aom);
836 UNORDERED_MAP<u16, RemoteClient*> clients = m_clients.getClientList();
837 // Route data to every client
838 for (UNORDERED_MAP<u16, RemoteClient*>::iterator i = clients.begin();
839 i != clients.end(); ++i) {
840 RemoteClient *client = i->second;
841 std::string reliable_data;
842 std::string unreliable_data;
843 // Go through all objects in message buffer
844 for (UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator
845 j = buffered_messages.begin();
846 j != buffered_messages.end(); ++j) {
847 // If object is not known by client, skip it
849 if (client->m_known_objects.find(id) == client->m_known_objects.end())
852 // Get message list of object
853 std::vector<ActiveObjectMessage>* list = j->second;
854 // Go through every message
855 for (std::vector<ActiveObjectMessage>::iterator
856 k = list->begin(); k != list->end(); ++k) {
857 // Compose the full new data with header
858 ActiveObjectMessage aom = *k;
859 std::string new_data;
862 writeU16((u8*)&buf[0], aom.id);
863 new_data.append(buf, 2);
865 new_data += serializeString(aom.datastring);
866 // Add data to buffer
868 reliable_data += new_data;
870 unreliable_data += new_data;
874 reliable_data and unreliable_data are now ready.
877 if(reliable_data.size() > 0) {
878 SendActiveObjectMessages(client->peer_id, reliable_data);
881 if(unreliable_data.size() > 0) {
882 SendActiveObjectMessages(client->peer_id, unreliable_data, false);
887 // Clear buffered_messages
888 for (UNORDERED_MAP<u16, std::vector<ActiveObjectMessage>* >::iterator
889 i = buffered_messages.begin();
890 i != buffered_messages.end(); ++i) {
896 Send queued-for-sending map edit events.
899 // We will be accessing the environment
900 MutexAutoLock lock(m_env_mutex);
902 // Don't send too many at a time
905 // Single change sending is disabled if queue size is not small
906 bool disable_single_change_sending = false;
907 if(m_unsent_map_edit_queue.size() >= 4)
908 disable_single_change_sending = true;
910 int event_count = m_unsent_map_edit_queue.size();
912 // We'll log the amount of each
915 while(m_unsent_map_edit_queue.size() != 0)
917 MapEditEvent* event = m_unsent_map_edit_queue.front();
918 m_unsent_map_edit_queue.pop();
920 // Players far away from the change are stored here.
921 // Instead of sending the changes, MapBlocks are set not sent
923 std::vector<u16> far_players;
925 switch (event->type) {
928 prof.add("MEET_ADDNODE", 1);
929 sendAddNode(event->p, event->n, event->already_known_by_peer,
930 &far_players, disable_single_change_sending ? 5 : 30,
931 event->type == MEET_ADDNODE);
933 case MEET_REMOVENODE:
934 prof.add("MEET_REMOVENODE", 1);
935 sendRemoveNode(event->p, event->already_known_by_peer,
936 &far_players, disable_single_change_sending ? 5 : 30);
938 case MEET_BLOCK_NODE_METADATA_CHANGED:
939 infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
940 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
941 setBlockNotSent(event->p);
944 infostream << "Server: MEET_OTHER" << std::endl;
945 prof.add("MEET_OTHER", 1);
946 for(std::set<v3s16>::iterator
947 i = event->modified_blocks.begin();
948 i != event->modified_blocks.end(); ++i) {
953 prof.add("unknown", 1);
954 warningstream << "Server: Unknown MapEditEvent "
955 << ((u32)event->type) << std::endl;
960 Set blocks not sent to far players
962 if(!far_players.empty()) {
963 // Convert list format to that wanted by SetBlocksNotSent
964 std::map<v3s16, MapBlock*> modified_blocks2;
965 for(std::set<v3s16>::iterator
966 i = event->modified_blocks.begin();
967 i != event->modified_blocks.end(); ++i) {
968 modified_blocks2[*i] =
969 m_env->getMap().getBlockNoCreateNoEx(*i);
972 // Set blocks not sent
973 for(std::vector<u16>::iterator
974 i = far_players.begin();
975 i != far_players.end(); ++i) {
976 if(RemoteClient *client = getClient(*i))
977 client->SetBlocksNotSent(modified_blocks2);
983 /*// Don't send too many at a time
985 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
989 if(event_count >= 5){
990 infostream<<"Server: MapEditEvents:"<<std::endl;
991 prof.print(infostream);
992 } else if(event_count != 0){
993 verbosestream<<"Server: MapEditEvents:"<<std::endl;
994 prof.print(verbosestream);
1000 Trigger emergethread (it somehow gets to a non-triggered but
1001 bysy state sometimes)
1004 float &counter = m_emergethread_trigger_timer;
1006 if (counter >= 2.0) {
1009 m_emerge->startThreads();
1013 // Save map, players and auth stuff
1015 float &counter = m_savemap_timer;
1017 static const float save_interval =
1018 g_settings->getFloat("server_map_save_interval");
1019 if (counter >= save_interval) {
1021 MutexAutoLock lock(m_env_mutex);
1023 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1026 if (m_banmanager->isModified()) {
1027 m_banmanager->save();
1030 // Save changed parts of map
1031 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1034 m_env->saveLoadedPlayers();
1036 // Save environment metadata
1042 void Server::Receive()
1044 DSTACK(FUNCTION_NAME);
1045 SharedBuffer<u8> data;
1049 m_con.Receive(&pkt);
1050 peer_id = pkt.getPeerId();
1053 catch(con::InvalidIncomingDataException &e) {
1054 infostream<<"Server::Receive(): "
1055 "InvalidIncomingDataException: what()="
1056 <<e.what()<<std::endl;
1058 catch(SerializationError &e) {
1059 infostream<<"Server::Receive(): "
1060 "SerializationError: what()="
1061 <<e.what()<<std::endl;
1063 catch(ClientStateError &e) {
1064 errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
1065 DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
1066 L"Try reconnecting or updating your client");
1068 catch(con::PeerNotFoundException &e) {
1073 PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
1075 std::string playername = "";
1076 PlayerSAO *playersao = NULL;
1079 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
1080 if (client != NULL) {
1081 playername = client->getName();
1082 playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
1084 } catch (std::exception &e) {
1090 RemotePlayer *player = m_env->getPlayer(playername.c_str());
1092 // If failed, cancel
1093 if ((playersao == NULL) || (player == NULL)) {
1094 if (player && player->peer_id != 0) {
1095 actionstream << "Server: Failed to emerge player \"" << playername
1096 << "\" (player allocated to an another client)" << std::endl;
1097 DenyAccess_Legacy(peer_id, L"Another client is connected with this "
1098 L"name. If your client closed unexpectedly, try again in "
1101 errorstream << "Server: " << playername << ": Failed to emerge player"
1103 DenyAccess_Legacy(peer_id, L"Could not allocate player.");
1109 Send complete position information
1111 SendMovePlayer(peer_id);
1114 SendPlayerPrivileges(peer_id);
1116 // Send inventory formspec
1117 SendPlayerInventoryFormspec(peer_id);
1120 SendInventory(playersao);
1123 SendPlayerHPOrDie(playersao);
1126 SendPlayerBreath(playersao);
1128 // Show death screen if necessary
1129 if (playersao->isDead())
1130 SendDeathscreen(peer_id, false, v3f(0,0,0));
1132 // Note things in chat if not in simple singleplayer mode
1133 if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) {
1134 // Send information about server to player in chat
1135 SendChatMessage(peer_id, getStatusString());
1137 Address addr = getPeerAddress(player->peer_id);
1138 std::string ip_str = addr.serializeString();
1139 actionstream<<player->getName() <<" [" << ip_str << "] joins game. " << std::endl;
1144 const std::vector<std::string> &names = m_clients.getPlayerNames();
1146 actionstream << player->getName() << " joins game. List of players: ";
1148 for (std::vector<std::string>::const_iterator i = names.begin();
1149 i != names.end(); ++i) {
1150 actionstream << *i << " ";
1153 actionstream << player->getName() <<std::endl;
1158 inline void Server::handleCommand(NetworkPacket* pkt)
1160 const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
1161 (this->*opHandle.handler)(pkt);
1164 void Server::ProcessData(NetworkPacket *pkt)
1166 DSTACK(FUNCTION_NAME);
1167 // Environment is locked first.
1168 MutexAutoLock envlock(m_env_mutex);
1170 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1171 u32 peer_id = pkt->getPeerId();
1174 Address address = getPeerAddress(peer_id);
1175 std::string addr_s = address.serializeString();
1177 if(m_banmanager->isIpBanned(addr_s)) {
1178 std::string ban_name = m_banmanager->getBanName(addr_s);
1179 infostream << "Server: A banned client tried to connect from "
1180 << addr_s << "; banned name was "
1181 << ban_name << std::endl;
1182 // This actually doesn't seem to transfer to the client
1183 DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
1184 + utf8_to_wide(ban_name));
1188 catch(con::PeerNotFoundException &e) {
1190 * no peer for this packet found
1191 * most common reason is peer timeout, e.g. peer didn't
1192 * respond for some time, your server was overloaded or
1195 infostream << "Server::ProcessData(): Canceling: peer "
1196 << peer_id << " not found" << std::endl;
1201 ToServerCommand command = (ToServerCommand) pkt->getCommand();
1203 // Command must be handled into ToServerCommandHandler
1204 if (command >= TOSERVER_NUM_MSG_TYPES) {
1205 infostream << "Server: Ignoring unknown command "
1206 << command << std::endl;
1210 if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
1215 u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
1217 if(peer_ser_ver == SER_FMT_VER_INVALID) {
1218 errorstream << "Server::ProcessData(): Cancelling: Peer"
1219 " serialization format invalid or not initialized."
1220 " Skipping incoming command=" << command << std::endl;
1224 /* Handle commands related to client startup */
1225 if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
1230 if (m_clients.getClientState(peer_id) < CS_Active) {
1231 if (command == TOSERVER_PLAYERPOS) return;
1233 errorstream << "Got packet command: " << command << " for peer id "
1234 << peer_id << " but client isn't active yet. Dropping packet "
1240 } catch (SendFailedException &e) {
1241 errorstream << "Server::ProcessData(): SendFailedException: "
1242 << "what=" << e.what()
1244 } catch (PacketError &e) {
1245 actionstream << "Server::ProcessData(): PacketError: "
1246 << "what=" << e.what()
1251 void Server::setTimeOfDay(u32 time)
1253 m_env->setTimeOfDay(time);
1254 m_time_of_day_send_timer = 0;
1257 void Server::onMapEditEvent(MapEditEvent *event)
1259 if(m_ignore_map_edit_events)
1261 if(m_ignore_map_edit_events_area.contains(event->getArea()))
1263 MapEditEvent *e = event->clone();
1264 m_unsent_map_edit_queue.push(e);
1267 Inventory* Server::getInventory(const InventoryLocation &loc)
1270 case InventoryLocation::UNDEFINED:
1271 case InventoryLocation::CURRENT_PLAYER:
1273 case InventoryLocation::PLAYER:
1275 RemotePlayer *player = dynamic_cast<RemotePlayer *>(m_env->getPlayer(loc.name.c_str()));
1278 PlayerSAO *playersao = player->getPlayerSAO();
1281 return playersao->getInventory();
1284 case InventoryLocation::NODEMETA:
1286 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
1289 return meta->getInventory();
1292 case InventoryLocation::DETACHED:
1294 if(m_detached_inventories.count(loc.name) == 0)
1296 return m_detached_inventories[loc.name];
1300 sanity_check(false); // abort
1305 void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
1308 case InventoryLocation::UNDEFINED:
1310 case InventoryLocation::PLAYER:
1315 RemotePlayer *player =
1316 dynamic_cast<RemotePlayer *>(m_env->getPlayer(loc.name.c_str()));
1321 PlayerSAO *playersao = player->getPlayerSAO();
1325 SendInventory(playersao);
1328 case InventoryLocation::NODEMETA:
1330 v3s16 blockpos = getNodeBlockPos(loc.p);
1332 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
1334 block->raiseModified(MOD_STATE_WRITE_NEEDED);
1336 setBlockNotSent(blockpos);
1339 case InventoryLocation::DETACHED:
1341 sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
1345 sanity_check(false); // abort
1350 void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
1352 std::vector<u16> clients = m_clients.getClientIDs();
1354 // Set the modified blocks unsent for all the clients
1355 for (std::vector<u16>::iterator i = clients.begin();
1356 i != clients.end(); ++i) {
1357 if (RemoteClient *client = m_clients.lockedGetClientNoEx(*i))
1358 client->SetBlocksNotSent(block);
1363 void Server::peerAdded(con::Peer *peer)
1365 DSTACK(FUNCTION_NAME);
1366 verbosestream<<"Server::peerAdded(): peer->id="
1367 <<peer->id<<std::endl;
1370 c.type = con::PEER_ADDED;
1371 c.peer_id = peer->id;
1373 m_peer_change_queue.push(c);
1376 void Server::deletingPeer(con::Peer *peer, bool timeout)
1378 DSTACK(FUNCTION_NAME);
1379 verbosestream<<"Server::deletingPeer(): peer->id="
1380 <<peer->id<<", timeout="<<timeout<<std::endl;
1382 m_clients.event(peer->id, CSE_Disconnect);
1384 c.type = con::PEER_REMOVED;
1385 c.peer_id = peer->id;
1386 c.timeout = timeout;
1387 m_peer_change_queue.push(c);
1390 bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
1392 *retval = m_con.getPeerStat(peer_id,type);
1393 if (*retval == -1) return false;
1397 bool Server::getClientInfo(
1406 std::string* vers_string
1409 *state = m_clients.getClientState(peer_id);
1411 RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_Invalid);
1413 if (client == NULL) {
1418 *uptime = client->uptime();
1419 *ser_vers = client->serialization_version;
1420 *prot_vers = client->net_proto_version;
1422 *major = client->getMajor();
1423 *minor = client->getMinor();
1424 *patch = client->getPatch();
1425 *vers_string = client->getPatch();
1432 void Server::handlePeerChanges()
1434 while(m_peer_change_queue.size() > 0)
1436 con::PeerChange c = m_peer_change_queue.front();
1437 m_peer_change_queue.pop();
1439 verbosestream<<"Server: Handling peer change: "
1440 <<"id="<<c.peer_id<<", timeout="<<c.timeout
1445 case con::PEER_ADDED:
1446 m_clients.CreateClient(c.peer_id);
1449 case con::PEER_REMOVED:
1450 DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE);
1454 FATAL_ERROR("Invalid peer change event received!");
1460 void Server::printToConsoleOnly(const std::string &text)
1463 m_admin_chat->outgoing_queue.push_back(
1464 new ChatEventChat("", utf8_to_wide(text)));
1466 std::cout << text << std::endl;
1470 void Server::Send(NetworkPacket* pkt)
1472 m_clients.send(pkt->getPeerId(),
1473 clientCommandFactoryTable[pkt->getCommand()].channel,
1475 clientCommandFactoryTable[pkt->getCommand()].reliable);
1478 void Server::SendMovement(u16 peer_id)
1480 DSTACK(FUNCTION_NAME);
1481 std::ostringstream os(std::ios_base::binary);
1483 NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
1485 pkt << g_settings->getFloat("movement_acceleration_default");
1486 pkt << g_settings->getFloat("movement_acceleration_air");
1487 pkt << g_settings->getFloat("movement_acceleration_fast");
1488 pkt << g_settings->getFloat("movement_speed_walk");
1489 pkt << g_settings->getFloat("movement_speed_crouch");
1490 pkt << g_settings->getFloat("movement_speed_fast");
1491 pkt << g_settings->getFloat("movement_speed_climb");
1492 pkt << g_settings->getFloat("movement_speed_jump");
1493 pkt << g_settings->getFloat("movement_liquid_fluidity");
1494 pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
1495 pkt << g_settings->getFloat("movement_liquid_sink");
1496 pkt << g_settings->getFloat("movement_gravity");
1501 void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
1503 if (!g_settings->getBool("enable_damage"))
1506 u16 peer_id = playersao->getPeerID();
1507 bool is_alive = playersao->getHP() > 0;
1510 SendPlayerHP(peer_id);
1515 void Server::SendHP(u16 peer_id, u8 hp)
1517 DSTACK(FUNCTION_NAME);
1519 NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
1524 void Server::SendBreath(u16 peer_id, u16 breath)
1526 DSTACK(FUNCTION_NAME);
1528 NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
1529 pkt << (u16) breath;
1533 void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
1534 const std::string &custom_reason, bool reconnect)
1536 assert(reason < SERVER_ACCESSDENIED_MAX);
1538 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
1540 if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
1541 pkt << custom_reason;
1542 else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
1543 reason == SERVER_ACCESSDENIED_CRASH)
1544 pkt << custom_reason << (u8)reconnect;
1548 void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
1550 DSTACK(FUNCTION_NAME);
1552 NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
1557 void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
1558 v3f camera_point_target)
1560 DSTACK(FUNCTION_NAME);
1562 NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
1563 pkt << set_camera_point_target << camera_point_target;
1567 void Server::SendItemDef(u16 peer_id,
1568 IItemDefManager *itemdef, u16 protocol_version)
1570 DSTACK(FUNCTION_NAME);
1572 NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
1576 u32 length of the next item
1577 zlib-compressed serialized ItemDefManager
1579 std::ostringstream tmp_os(std::ios::binary);
1580 itemdef->serialize(tmp_os, protocol_version);
1581 std::ostringstream tmp_os2(std::ios::binary);
1582 compressZlib(tmp_os.str(), tmp_os2);
1583 pkt.putLongString(tmp_os2.str());
1586 verbosestream << "Server: Sending item definitions to id(" << peer_id
1587 << "): size=" << pkt.getSize() << std::endl;
1592 void Server::SendNodeDef(u16 peer_id,
1593 INodeDefManager *nodedef, u16 protocol_version)
1595 DSTACK(FUNCTION_NAME);
1597 NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
1601 u32 length of the next item
1602 zlib-compressed serialized NodeDefManager
1604 std::ostringstream tmp_os(std::ios::binary);
1605 nodedef->serialize(tmp_os, protocol_version);
1606 std::ostringstream tmp_os2(std::ios::binary);
1607 compressZlib(tmp_os.str(), tmp_os2);
1609 pkt.putLongString(tmp_os2.str());
1612 verbosestream << "Server: Sending node definitions to id(" << peer_id
1613 << "): size=" << pkt.getSize() << std::endl;
1619 Non-static send methods
1622 void Server::SendInventory(PlayerSAO* playerSAO)
1624 DSTACK(FUNCTION_NAME);
1626 UpdateCrafting(playerSAO->getPlayer());
1632 NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
1634 std::ostringstream os;
1635 playerSAO->getInventory()->serialize(os);
1637 std::string s = os.str();
1639 pkt.putRawString(s.c_str(), s.size());
1643 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
1645 DSTACK(FUNCTION_NAME);
1647 NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
1650 if (peer_id != PEER_ID_INEXISTENT) {
1654 m_clients.sendToAll(0, &pkt, true);
1658 void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
1659 const std::string &formname)
1661 DSTACK(FUNCTION_NAME);
1663 NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
1664 if (formspec == "" ){
1665 //the client should close the formspec
1666 pkt.putLongString("");
1668 pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
1675 // Spawns a particle on peer with peer_id
1676 void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version,
1677 v3f pos, v3f velocity, v3f acceleration,
1678 float expirationtime, float size, bool collisiondetection,
1679 bool collision_removal,
1680 bool vertical, const std::string &texture,
1681 const struct TileAnimationParams &animation, u8 glow)
1683 DSTACK(FUNCTION_NAME);
1684 if (peer_id == PEER_ID_INEXISTENT) {
1685 // This sucks and should be replaced by a better solution in a refactor:
1686 std::vector<u16> clients = m_clients.getClientIDs();
1687 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
1688 RemotePlayer *player = m_env->getPlayer(*i);
1691 SendSpawnParticle(*i, player->protocol_version,
1692 pos, velocity, acceleration,
1693 expirationtime, size, collisiondetection,
1694 collision_removal, vertical, texture, animation, glow);
1699 NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
1701 pkt << pos << velocity << acceleration << expirationtime
1702 << size << collisiondetection;
1703 pkt.putLongString(texture);
1705 pkt << collision_removal;
1706 // This is horrible but required (why are there two ways to serialize pkts?)
1707 std::ostringstream os(std::ios_base::binary);
1708 animation.serialize(os, protocol_version);
1709 pkt.putRawString(os.str());
1715 // Adds a ParticleSpawner on peer with peer_id
1716 void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version,
1717 u16 amount, float spawntime, v3f minpos, v3f maxpos,
1718 v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime,
1719 float minsize, float maxsize, bool collisiondetection, bool collision_removal,
1720 u16 attached_id, bool vertical, const std::string &texture, u32 id,
1721 const struct TileAnimationParams &animation, u8 glow)
1723 DSTACK(FUNCTION_NAME);
1724 if (peer_id == PEER_ID_INEXISTENT) {
1725 // This sucks and should be replaced:
1726 std::vector<u16> clients = m_clients.getClientIDs();
1727 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
1728 RemotePlayer *player = m_env->getPlayer(*i);
1731 SendAddParticleSpawner(*i, player->protocol_version,
1732 amount, spawntime, minpos, maxpos,
1733 minvel, maxvel, minacc, maxacc, minexptime, maxexptime,
1734 minsize, maxsize, collisiondetection, collision_removal,
1735 attached_id, vertical, texture, id, animation, glow);
1740 NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
1742 pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
1743 << minacc << maxacc << minexptime << maxexptime << minsize
1744 << maxsize << collisiondetection;
1746 pkt.putLongString(texture);
1748 pkt << id << vertical;
1749 pkt << collision_removal;
1751 // This is horrible but required
1752 std::ostringstream os(std::ios_base::binary);
1753 animation.serialize(os, protocol_version);
1754 pkt.putRawString(os.str());
1760 void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
1762 DSTACK(FUNCTION_NAME);
1764 NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
1766 // Ugly error in this packet
1769 if (peer_id != PEER_ID_INEXISTENT) {
1773 m_clients.sendToAll(0, &pkt, true);
1778 void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
1780 NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
1782 pkt << id << (u8) form->type << form->pos << form->name << form->scale
1783 << form->text << form->number << form->item << form->dir
1784 << form->align << form->offset << form->world_pos << form->size;
1789 void Server::SendHUDRemove(u16 peer_id, u32 id)
1791 NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
1796 void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
1798 NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
1799 pkt << id << (u8) stat;
1803 case HUD_STAT_SCALE:
1804 case HUD_STAT_ALIGN:
1805 case HUD_STAT_OFFSET:
1806 pkt << *(v2f *) value;
1810 pkt << *(std::string *) value;
1812 case HUD_STAT_WORLD_POS:
1813 pkt << *(v3f *) value;
1816 pkt << *(v2s32 *) value;
1818 case HUD_STAT_NUMBER:
1822 pkt << *(u32 *) value;
1829 void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
1831 NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
1833 flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
1835 pkt << flags << mask;
1840 void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
1842 NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
1843 pkt << param << value;
1847 void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
1848 const std::string &type, const std::vector<std::string> ¶ms)
1850 NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
1851 pkt << bgcolor << type << (u16) params.size();
1853 for(size_t i=0; i<params.size(); i++)
1859 void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
1862 NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
1865 pkt << do_override << (u16) (ratio * 65535);
1870 void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
1872 DSTACK(FUNCTION_NAME);
1874 NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
1875 pkt << time << time_speed;
1877 if (peer_id == PEER_ID_INEXISTENT) {
1878 m_clients.sendToAll(0, &pkt, true);
1885 void Server::SendPlayerHP(u16 peer_id)
1887 DSTACK(FUNCTION_NAME);
1888 PlayerSAO *playersao = getPlayerSAO(peer_id);
1889 // In some rare case if the player is disconnected
1890 // while Lua call l_punch, for example, this can be NULL
1894 SendHP(peer_id, playersao->getHP());
1895 m_script->player_event(playersao,"health_changed");
1897 // Send to other clients
1898 std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
1899 ActiveObjectMessage aom(playersao->getId(), true, str);
1900 playersao->m_messages_out.push(aom);
1903 void Server::SendPlayerBreath(PlayerSAO *sao)
1905 DSTACK(FUNCTION_NAME);
1908 m_script->player_event(sao, "breath_changed");
1909 SendBreath(sao->getPeerID(), sao->getBreath());
1912 void Server::SendMovePlayer(u16 peer_id)
1914 DSTACK(FUNCTION_NAME);
1915 RemotePlayer *player = m_env->getPlayer(peer_id);
1917 PlayerSAO *sao = player->getPlayerSAO();
1920 NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
1921 pkt << sao->getBasePosition() << sao->getPitch() << sao->getYaw();
1924 v3f pos = sao->getBasePosition();
1925 verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
1926 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
1927 << " pitch=" << sao->getPitch()
1928 << " yaw=" << sao->getYaw()
1935 void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
1937 NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
1940 pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
1941 << animation_frames[3] << animation_speed;
1946 void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
1948 NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
1949 pkt << first << third;
1952 void Server::SendPlayerPrivileges(u16 peer_id)
1954 RemotePlayer *player = m_env->getPlayer(peer_id);
1956 if(player->peer_id == PEER_ID_INEXISTENT)
1959 std::set<std::string> privs;
1960 m_script->getAuth(player->getName(), NULL, &privs);
1962 NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
1963 pkt << (u16) privs.size();
1965 for(std::set<std::string>::const_iterator i = privs.begin();
1966 i != privs.end(); ++i) {
1973 void Server::SendPlayerInventoryFormspec(u16 peer_id)
1975 RemotePlayer *player = m_env->getPlayer(peer_id);
1977 if(player->peer_id == PEER_ID_INEXISTENT)
1980 NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
1981 pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
1985 u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
1987 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
1988 pkt.putRawString(datas.c_str(), datas.size());
1990 return pkt.getSize();
1993 void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
1995 NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
1996 datas.size(), peer_id);
1998 pkt.putRawString(datas.c_str(), datas.size());
2000 m_clients.send(pkt.getPeerId(),
2001 reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
2006 s32 Server::playSound(const SimpleSoundSpec &spec,
2007 const ServerSoundParams ¶ms)
2009 // Find out initial position of sound
2010 bool pos_exists = false;
2011 v3f pos = params.getPos(m_env, &pos_exists);
2012 // If position is not found while it should be, cancel sound
2013 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
2016 // Filter destination clients
2017 std::vector<u16> dst_clients;
2018 if(params.to_player != "")
2020 RemotePlayer *player = m_env->getPlayer(params.to_player.c_str());
2022 infostream<<"Server::playSound: Player \""<<params.to_player
2023 <<"\" not found"<<std::endl;
2026 if(player->peer_id == PEER_ID_INEXISTENT){
2027 infostream<<"Server::playSound: Player \""<<params.to_player
2028 <<"\" not connected"<<std::endl;
2031 dst_clients.push_back(player->peer_id);
2034 std::vector<u16> clients = m_clients.getClientIDs();
2036 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2037 RemotePlayer *player = m_env->getPlayer(*i);
2041 PlayerSAO *sao = player->getPlayerSAO();
2046 if(sao->getBasePosition().getDistanceFrom(pos) >
2047 params.max_hear_distance)
2050 dst_clients.push_back(*i);
2054 if(dst_clients.empty())
2058 s32 id = m_next_sound_id++;
2059 // The sound will exist as a reference in m_playing_sounds
2060 m_playing_sounds[id] = ServerPlayingSound();
2061 ServerPlayingSound &psound = m_playing_sounds[id];
2062 psound.params = params;
2064 NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
2065 pkt << id << spec.name << (float) (spec.gain * params.gain)
2066 << (u8) params.type << pos << params.object << params.loop;
2068 for(std::vector<u16>::iterator i = dst_clients.begin();
2069 i != dst_clients.end(); ++i) {
2070 psound.clients.insert(*i);
2071 m_clients.send(*i, 0, &pkt, true);
2075 void Server::stopSound(s32 handle)
2077 // Get sound reference
2078 UNORDERED_MAP<s32, ServerPlayingSound>::iterator i = m_playing_sounds.find(handle);
2079 if (i == m_playing_sounds.end())
2081 ServerPlayingSound &psound = i->second;
2083 NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
2086 for (UNORDERED_SET<u16>::iterator i = psound.clients.begin();
2087 i != psound.clients.end(); ++i) {
2089 m_clients.send(*i, 0, &pkt, true);
2091 // Remove sound reference
2092 m_playing_sounds.erase(i);
2095 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
2096 std::vector<u16> *far_players, float far_d_nodes)
2098 float maxd = far_d_nodes*BS;
2099 v3f p_f = intToFloat(p, BS);
2101 NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
2104 std::vector<u16> clients = m_clients.getClientIDs();
2105 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2108 if (RemotePlayer *player = m_env->getPlayer(*i)) {
2109 PlayerSAO *sao = player->getPlayerSAO();
2113 // If player is far away, only set modified blocks not sent
2114 v3f player_pos = sao->getBasePosition();
2115 if (player_pos.getDistanceFrom(p_f) > maxd) {
2116 far_players->push_back(*i);
2123 m_clients.send(*i, 0, &pkt, true);
2127 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
2128 std::vector<u16> *far_players, float far_d_nodes,
2129 bool remove_metadata)
2131 float maxd = far_d_nodes*BS;
2132 v3f p_f = intToFloat(p, BS);
2134 std::vector<u16> clients = m_clients.getClientIDs();
2135 for(std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2138 if (RemotePlayer *player = m_env->getPlayer(*i)) {
2139 PlayerSAO *sao = player->getPlayerSAO();
2143 // If player is far away, only set modified blocks not sent
2144 v3f player_pos = sao->getBasePosition();
2145 if(player_pos.getDistanceFrom(p_f) > maxd) {
2146 far_players->push_back(*i);
2152 NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
2154 RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
2156 pkt << p << n.param0 << n.param1 << n.param2
2157 << (u8) (remove_metadata ? 0 : 1);
2159 if (!remove_metadata) {
2160 if (client->net_proto_version <= 21) {
2161 // Old clients always clear metadata; fix it
2162 // by sending the full block again.
2163 client->SetBlockNotSent(getNodeBlockPos(p));
2170 if (pkt.getSize() > 0)
2171 m_clients.send(*i, 0, &pkt, true);
2175 void Server::setBlockNotSent(v3s16 p)
2177 std::vector<u16> clients = m_clients.getClientIDs();
2179 for(std::vector<u16>::iterator i = clients.begin();
2180 i != clients.end(); ++i) {
2181 RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
2182 client->SetBlockNotSent(p);
2187 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version)
2189 DSTACK(FUNCTION_NAME);
2191 v3s16 p = block->getPos();
2194 Create a packet with the block in the right format
2197 std::ostringstream os(std::ios_base::binary);
2198 block->serialize(os, ver, false);
2199 block->serializeNetworkSpecific(os, net_proto_version);
2200 std::string s = os.str();
2202 NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
2205 pkt.putRawString(s.c_str(), s.size());
2209 void Server::SendBlocks(float dtime)
2211 DSTACK(FUNCTION_NAME);
2213 MutexAutoLock envlock(m_env_mutex);
2214 //TODO check if one big lock could be faster then multiple small ones
2216 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
2218 std::vector<PrioritySortedBlockTransfer> queue;
2220 s32 total_sending = 0;
2223 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
2225 std::vector<u16> clients = m_clients.getClientIDs();
2228 for(std::vector<u16>::iterator i = clients.begin();
2229 i != clients.end(); ++i) {
2230 RemoteClient *client = m_clients.lockedGetClientNoEx(*i, CS_Active);
2235 total_sending += client->SendingCount();
2236 client->GetNextBlocks(m_env,m_emerge, dtime, queue);
2242 // Lowest priority number comes first.
2243 // Lowest is most important.
2244 std::sort(queue.begin(), queue.end());
2247 for(u32 i=0; i<queue.size(); i++)
2249 //TODO: Calculate limit dynamically
2250 if(total_sending >= g_settings->getS32
2251 ("max_simultaneous_block_sends_server_total"))
2254 PrioritySortedBlockTransfer q = queue[i];
2256 MapBlock *block = NULL;
2259 block = m_env->getMap().getBlockNoCreate(q.pos);
2261 catch(InvalidPositionException &e)
2266 RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id, CS_Active);
2271 SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version);
2273 client->SentBlock(q.pos);
2279 void Server::fillMediaCache()
2281 DSTACK(FUNCTION_NAME);
2283 infostream<<"Server: Calculating media file checksums"<<std::endl;
2285 // Collect all media file paths
2286 std::vector<std::string> paths;
2287 for(std::vector<ModSpec>::iterator i = m_mods.begin();
2288 i != m_mods.end(); ++i) {
2289 const ModSpec &mod = *i;
2290 paths.push_back(mod.path + DIR_DELIM + "textures");
2291 paths.push_back(mod.path + DIR_DELIM + "sounds");
2292 paths.push_back(mod.path + DIR_DELIM + "media");
2293 paths.push_back(mod.path + DIR_DELIM + "models");
2295 paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
2297 // Collect media file information from paths into cache
2298 for(std::vector<std::string>::iterator i = paths.begin();
2299 i != paths.end(); ++i) {
2300 std::string mediapath = *i;
2301 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
2302 for (u32 j = 0; j < dirlist.size(); j++) {
2303 if (dirlist[j].dir) // Ignode dirs
2305 std::string filename = dirlist[j].name;
2306 // If name contains illegal characters, ignore the file
2307 if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
2308 infostream<<"Server: ignoring illegal file name: \""
2309 << filename << "\"" << std::endl;
2312 // If name is not in a supported format, ignore it
2313 const char *supported_ext[] = {
2314 ".png", ".jpg", ".bmp", ".tga",
2315 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
2317 ".x", ".b3d", ".md2", ".obj",
2320 if (removeStringEnd(filename, supported_ext) == ""){
2321 infostream << "Server: ignoring unsupported file extension: \""
2322 << filename << "\"" << std::endl;
2325 // Ok, attempt to load the file and add to cache
2326 std::string filepath = mediapath + DIR_DELIM + filename;
2328 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
2330 errorstream << "Server::fillMediaCache(): Could not open \""
2331 << filename << "\" for reading" << std::endl;
2334 std::ostringstream tmp_os(std::ios_base::binary);
2338 fis.read(buf, 1024);
2339 std::streamsize len = fis.gcount();
2340 tmp_os.write(buf, len);
2349 errorstream<<"Server::fillMediaCache(): Failed to read \""
2350 << filename << "\"" << std::endl;
2353 if(tmp_os.str().length() == 0) {
2354 errorstream << "Server::fillMediaCache(): Empty file \""
2355 << filepath << "\"" << std::endl;
2360 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
2362 unsigned char *digest = sha1.getDigest();
2363 std::string sha1_base64 = base64_encode(digest, 20);
2364 std::string sha1_hex = hex_encode((char*)digest, 20);
2368 m_media[filename] = MediaInfo(filepath, sha1_base64);
2369 verbosestream << "Server: " << sha1_hex << " is " << filename
2375 void Server::sendMediaAnnouncement(u16 peer_id)
2377 DSTACK(FUNCTION_NAME);
2379 verbosestream << "Server: Announcing files to id(" << peer_id << ")"
2383 std::ostringstream os(std::ios_base::binary);
2385 NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
2386 pkt << (u16) m_media.size();
2388 for (UNORDERED_MAP<std::string, MediaInfo>::iterator i = m_media.begin();
2389 i != m_media.end(); ++i) {
2390 pkt << i->first << i->second.sha1_digest;
2393 pkt << g_settings->get("remote_media");
2397 struct SendableMedia
2403 SendableMedia(const std::string &name_="", const std::string &path_="",
2404 const std::string &data_=""):
2411 void Server::sendRequestedMedia(u16 peer_id,
2412 const std::vector<std::string> &tosend)
2414 DSTACK(FUNCTION_NAME);
2416 verbosestream<<"Server::sendRequestedMedia(): "
2417 <<"Sending files to client"<<std::endl;
2421 // Put 5kB in one bunch (this is not accurate)
2422 u32 bytes_per_bunch = 5000;
2424 std::vector< std::vector<SendableMedia> > file_bunches;
2425 file_bunches.push_back(std::vector<SendableMedia>());
2427 u32 file_size_bunch_total = 0;
2429 for(std::vector<std::string>::const_iterator i = tosend.begin();
2430 i != tosend.end(); ++i) {
2431 const std::string &name = *i;
2433 if (m_media.find(name) == m_media.end()) {
2434 errorstream<<"Server::sendRequestedMedia(): Client asked for "
2435 <<"unknown file \""<<(name)<<"\""<<std::endl;
2439 //TODO get path + name
2440 std::string tpath = m_media[name].path;
2443 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
2444 if(fis.good() == false){
2445 errorstream<<"Server::sendRequestedMedia(): Could not open \""
2446 <<tpath<<"\" for reading"<<std::endl;
2449 std::ostringstream tmp_os(std::ios_base::binary);
2453 fis.read(buf, 1024);
2454 std::streamsize len = fis.gcount();
2455 tmp_os.write(buf, len);
2456 file_size_bunch_total += len;
2465 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
2466 <<name<<"\""<<std::endl;
2469 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
2470 <<tname<<"\""<<std::endl;*/
2472 file_bunches[file_bunches.size()-1].push_back(
2473 SendableMedia(name, tpath, tmp_os.str()));
2475 // Start next bunch if got enough data
2476 if(file_size_bunch_total >= bytes_per_bunch) {
2477 file_bunches.push_back(std::vector<SendableMedia>());
2478 file_size_bunch_total = 0;
2483 /* Create and send packets */
2485 u16 num_bunches = file_bunches.size();
2486 for(u16 i = 0; i < num_bunches; i++) {
2489 u16 total number of texture bunches
2490 u16 index of this bunch
2491 u32 number of files in this bunch
2500 NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
2501 pkt << num_bunches << i << (u32) file_bunches[i].size();
2503 for(std::vector<SendableMedia>::iterator
2504 j = file_bunches[i].begin();
2505 j != file_bunches[i].end(); ++j) {
2507 pkt.putLongString(j->data);
2510 verbosestream << "Server::sendRequestedMedia(): bunch "
2511 << i << "/" << num_bunches
2512 << " files=" << file_bunches[i].size()
2513 << " size=" << pkt.getSize() << std::endl;
2518 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
2520 if(m_detached_inventories.count(name) == 0) {
2521 errorstream<<FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
2524 Inventory *inv = m_detached_inventories[name];
2525 std::ostringstream os(std::ios_base::binary);
2527 os << serializeString(name);
2531 std::string s = os.str();
2533 NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
2534 pkt.putRawString(s.c_str(), s.size());
2536 const std::string &check = m_detached_inventories_player[name];
2537 if (peer_id == PEER_ID_INEXISTENT) {
2539 return m_clients.sendToAll(0, &pkt, true);
2540 RemotePlayer *p = m_env->getPlayer(check.c_str());
2542 m_clients.send(p->peer_id, 0, &pkt, true);
2544 if (check == "" || getPlayerName(peer_id) == check)
2549 void Server::sendDetachedInventories(u16 peer_id)
2551 DSTACK(FUNCTION_NAME);
2553 for(std::map<std::string, Inventory*>::iterator
2554 i = m_detached_inventories.begin();
2555 i != m_detached_inventories.end(); ++i) {
2556 const std::string &name = i->first;
2557 //Inventory *inv = i->second;
2558 sendDetachedInventory(name, peer_id);
2566 void Server::DiePlayer(u16 peer_id)
2568 DSTACK(FUNCTION_NAME);
2569 PlayerSAO *playersao = getPlayerSAO(peer_id);
2570 // In some rare cases this can be NULL -- if the player is disconnected
2571 // when a Lua function modifies l_punch, for example
2575 infostream << "Server::DiePlayer(): Player "
2576 << playersao->getPlayer()->getName()
2577 << " dies" << std::endl;
2579 playersao->setHP(0);
2581 // Trigger scripted stuff
2582 m_script->on_dieplayer(playersao);
2584 SendPlayerHP(peer_id);
2585 SendDeathscreen(peer_id, false, v3f(0,0,0));
2588 void Server::RespawnPlayer(u16 peer_id)
2590 DSTACK(FUNCTION_NAME);
2592 PlayerSAO *playersao = getPlayerSAO(peer_id);
2595 infostream << "Server::RespawnPlayer(): Player "
2596 << playersao->getPlayer()->getName()
2597 << " respawns" << std::endl;
2599 playersao->setHP(PLAYER_MAX_HP);
2600 playersao->setBreath(PLAYER_MAX_BREATH);
2602 bool repositioned = m_script->on_respawnplayer(playersao);
2603 if (!repositioned) {
2604 v3f pos = findSpawnPos();
2605 // setPos will send the new position to client
2606 playersao->setPos(pos);
2609 SendPlayerHP(peer_id);
2613 void Server::DenySudoAccess(u16 peer_id)
2615 DSTACK(FUNCTION_NAME);
2617 NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
2622 void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
2623 const std::string &str_reason, bool reconnect)
2625 if (proto_ver >= 25) {
2626 SendAccessDenied(peer_id, reason, str_reason, reconnect);
2628 std::wstring wreason = utf8_to_wide(
2629 reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
2630 accessDeniedStrings[(u8)reason]);
2631 SendAccessDenied_Legacy(peer_id, wreason);
2634 m_clients.event(peer_id, CSE_SetDenied);
2635 m_con.DisconnectPeer(peer_id);
2639 void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
2641 DSTACK(FUNCTION_NAME);
2643 SendAccessDenied(peer_id, reason, custom_reason);
2644 m_clients.event(peer_id, CSE_SetDenied);
2645 m_con.DisconnectPeer(peer_id);
2648 // 13/03/15: remove this function when protocol version 25 will become
2649 // the minimum version for MT users, maybe in 1 year
2650 void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
2652 DSTACK(FUNCTION_NAME);
2654 SendAccessDenied_Legacy(peer_id, reason);
2655 m_clients.event(peer_id, CSE_SetDenied);
2656 m_con.DisconnectPeer(peer_id);
2659 void Server::acceptAuth(u16 peer_id, bool forSudoMode)
2661 DSTACK(FUNCTION_NAME);
2664 RemoteClient* client = getClient(peer_id, CS_Invalid);
2666 NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
2668 // Right now, the auth mechs don't change between login and sudo mode.
2669 u32 sudo_auth_mechs = client->allowed_auth_mechs;
2670 client->allowed_sudo_mechs = sudo_auth_mechs;
2672 resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
2673 << g_settings->getFloat("dedicated_server_step")
2677 m_clients.event(peer_id, CSE_AuthAccept);
2679 NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
2681 // We only support SRP right now
2682 u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
2684 resp_pkt << sudo_auth_mechs;
2686 m_clients.event(peer_id, CSE_SudoSuccess);
2690 void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
2692 DSTACK(FUNCTION_NAME);
2693 std::wstring message;
2696 Clear references to playing sounds
2698 for (UNORDERED_MAP<s32, ServerPlayingSound>::iterator
2699 i = m_playing_sounds.begin(); i != m_playing_sounds.end();) {
2700 ServerPlayingSound &psound = i->second;
2701 psound.clients.erase(peer_id);
2702 if (psound.clients.empty())
2703 m_playing_sounds.erase(i++);
2708 RemotePlayer *player = m_env->getPlayer(peer_id);
2710 /* Run scripts and remove from environment */
2711 if (player != NULL) {
2712 PlayerSAO *playersao = player->getPlayerSAO();
2715 m_script->on_leaveplayer(playersao, reason == CDR_TIMEOUT);
2717 playersao->disconnected();
2724 if(player != NULL && reason != CDR_DENY) {
2725 std::ostringstream os(std::ios_base::binary);
2726 std::vector<u16> clients = m_clients.getClientIDs();
2728 for(std::vector<u16>::iterator i = clients.begin();
2729 i != clients.end(); ++i) {
2731 RemotePlayer *player = m_env->getPlayer(*i);
2735 // Get name of player
2736 os << player->getName() << " ";
2739 std::string name = player->getName();
2740 actionstream << name << " "
2741 << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
2742 << " List of players: " << os.str() << std::endl;
2744 m_admin_chat->outgoing_queue.push_back(
2745 new ChatEventNick(CET_NICK_REMOVE, name));
2749 MutexAutoLock env_lock(m_env_mutex);
2750 m_clients.DeleteClient(peer_id);
2754 // Send leave chat message to all remaining clients
2755 if(message.length() != 0)
2756 SendChatMessage(PEER_ID_INEXISTENT,message);
2759 void Server::UpdateCrafting(RemotePlayer *player)
2761 DSTACK(FUNCTION_NAME);
2763 // Get a preview for crafting
2765 InventoryLocation loc;
2766 loc.setPlayer(player->getName());
2767 std::vector<ItemStack> output_replacements;
2768 getCraftingResult(&player->inventory, preview, output_replacements, false, this);
2769 m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(),
2770 (&player->inventory)->getList("craft"), loc);
2772 // Put the new preview in
2773 InventoryList *plist = player->inventory.getList("craftpreview");
2774 sanity_check(plist);
2775 sanity_check(plist->getSize() >= 1);
2776 plist->changeItem(0, preview);
2779 void Server::handleChatInterfaceEvent(ChatEvent *evt)
2781 if (evt->type == CET_NICK_ADD) {
2782 // The terminal informed us of its nick choice
2783 m_admin_nick = ((ChatEventNick *)evt)->nick;
2784 if (!m_script->getAuth(m_admin_nick, NULL, NULL)) {
2785 errorstream << "You haven't set up an account." << std::endl
2786 << "Please log in using the client as '"
2787 << m_admin_nick << "' with a secure password." << std::endl
2788 << "Until then, you can't execute admin tasks via the console," << std::endl
2789 << "and everybody can claim the user account instead of you," << std::endl
2790 << "giving them full control over this server." << std::endl;
2793 assert(evt->type == CET_CHAT);
2794 handleAdminChat((ChatEventChat *)evt);
2798 std::wstring Server::handleChat(const std::string &name, const std::wstring &wname,
2799 const std::wstring &wmessage, bool check_shout_priv, RemotePlayer *player)
2801 // If something goes wrong, this player is to blame
2802 RollbackScopeActor rollback_scope(m_rollback,
2803 std::string("player:") + name);
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,
2816 L"You have been kicked due to message flooding.");
2818 case RPLAYER_CHATRESULT_OK:
2821 FATAL_ERROR("Unhandled chat filtering result found.");
2825 if (m_max_chatmessage_length > 0
2826 && wmessage.length() > m_max_chatmessage_length) {
2827 return L"Your message exceed the maximum chat message limit set on the server. "
2828 L"It was refused. Send a shorter message";
2831 // Run script hook, exit if script ate the chat message
2832 if (m_script->on_chat_message(name, wide_to_utf8(wmessage)))
2837 // Whether to send line to the player that sent the message, or to all players
2838 bool broadcast_line = true;
2840 // Commands are implemented in Lua, so only catch invalid
2841 // commands that were not "eaten" and send an error back
2842 if (wmessage[0] == L'/') {
2843 std::wstring wcmd = wmessage.substr(1);
2844 broadcast_line = false;
2845 if (wcmd.length() == 0)
2846 line += L"-!- Empty command";
2848 line += L"-!- Invalid command: " + str_split(wcmd, L' ')[0];
2850 if (check_shout_priv && !checkPriv(name, "shout")) {
2851 line += L"-!- You don't have permission to shout.";
2852 broadcast_line = false;
2862 Tell calling method to send the message to sender
2864 if (!broadcast_line) {
2868 Send the message to others
2870 actionstream << "CHAT: " << wide_to_narrow(line) << std::endl;
2872 std::vector<u16> clients = m_clients.getClientIDs();
2875 Send the message back to the inital sender
2876 if they are using protocol version >= 29
2879 u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT);
2880 if (player && player->protocol_version >= 29)
2881 peer_id_to_avoid_sending = PEER_ID_INEXISTENT;
2883 for (u16 i = 0; i < clients.size(); i++) {
2884 u16 cid = clients[i];
2885 if (cid != peer_id_to_avoid_sending)
2886 SendChatMessage(cid, line);
2892 void Server::handleAdminChat(const ChatEventChat *evt)
2894 std::string name = evt->nick;
2895 std::wstring wname = utf8_to_wide(name);
2896 std::wstring wmessage = evt->evt_msg;
2898 std::wstring answer = handleChat(name, wname, wmessage);
2900 // If asked to send answer to sender
2901 if (!answer.empty()) {
2902 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", answer));
2906 RemoteClient* Server::getClient(u16 peer_id, ClientState state_min)
2908 RemoteClient *client = getClientNoEx(peer_id,state_min);
2910 throw ClientNotFoundException("Client not found");
2914 RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min)
2916 return m_clients.getClientNoEx(peer_id, state_min);
2919 std::string Server::getPlayerName(u16 peer_id)
2921 RemotePlayer *player = m_env->getPlayer(peer_id);
2923 return "[id="+itos(peer_id)+"]";
2924 return player->getName();
2927 PlayerSAO* Server::getPlayerSAO(u16 peer_id)
2929 RemotePlayer *player = m_env->getPlayer(peer_id);
2932 return player->getPlayerSAO();
2935 std::wstring Server::getStatusString()
2937 std::wostringstream os(std::ios_base::binary);
2940 os<<L"version="<<narrow_to_wide(g_version_string);
2942 os<<L", uptime="<<m_uptime.get();
2944 os<<L", max_lag="<<m_env->getMaxLagEstimate();
2945 // Information about clients
2948 std::vector<u16> clients = m_clients.getClientIDs();
2949 for (std::vector<u16>::iterator i = clients.begin(); i != clients.end(); ++i) {
2951 RemotePlayer *player = m_env->getPlayer(*i);
2952 // Get name of player
2953 std::wstring name = L"unknown";
2955 name = narrow_to_wide(player->getName());
2956 // Add name to information string
2964 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
2965 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
2966 if(g_settings->get("motd") != "")
2967 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
2971 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
2973 std::set<std::string> privs;
2974 m_script->getAuth(name, NULL, &privs);
2978 bool Server::checkPriv(const std::string &name, const std::string &priv)
2980 std::set<std::string> privs = getPlayerEffectivePrivs(name);
2981 return (privs.count(priv) != 0);
2984 void Server::reportPrivsModified(const std::string &name)
2987 std::vector<u16> clients = m_clients.getClientIDs();
2988 for(std::vector<u16>::iterator i = clients.begin();
2989 i != clients.end(); ++i) {
2990 RemotePlayer *player = m_env->getPlayer(*i);
2991 reportPrivsModified(player->getName());
2994 RemotePlayer *player = m_env->getPlayer(name.c_str());
2997 SendPlayerPrivileges(player->peer_id);
2998 PlayerSAO *sao = player->getPlayerSAO();
3001 sao->updatePrivileges(
3002 getPlayerEffectivePrivs(name),
3007 void Server::reportInventoryFormspecModified(const std::string &name)
3009 RemotePlayer *player = m_env->getPlayer(name.c_str());
3012 SendPlayerInventoryFormspec(player->peer_id);
3015 void Server::setIpBanned(const std::string &ip, const std::string &name)
3017 m_banmanager->add(ip, name);
3020 void Server::unsetIpBanned(const std::string &ip_or_name)
3022 m_banmanager->remove(ip_or_name);
3025 std::string Server::getBanDescription(const std::string &ip_or_name)
3027 return m_banmanager->getBanDescription(ip_or_name);
3030 void Server::notifyPlayer(const char *name, const std::wstring &msg)
3032 // m_env will be NULL if the server is initializing
3036 if (m_admin_nick == name && !m_admin_nick.empty()) {
3037 m_admin_chat->outgoing_queue.push_back(new ChatEventChat("", msg));
3040 RemotePlayer *player = m_env->getPlayer(name);
3045 if (player->peer_id == PEER_ID_INEXISTENT)
3048 SendChatMessage(player->peer_id, msg);
3051 bool Server::showFormspec(const char *playername, const std::string &formspec,
3052 const std::string &formname)
3054 // m_env will be NULL if the server is initializing
3058 RemotePlayer *player = m_env->getPlayer(playername);
3062 SendShowFormspecMessage(player->peer_id, formspec, formname);
3066 u32 Server::hudAdd(RemotePlayer *player, HudElement *form)
3071 u32 id = player->addHud(form);
3073 SendHUDAdd(player->peer_id, id, form);
3078 bool Server::hudRemove(RemotePlayer *player, u32 id) {
3082 HudElement* todel = player->removeHud(id);
3089 SendHUDRemove(player->peer_id, id);
3093 bool Server::hudChange(RemotePlayer *player, u32 id, HudElementStat stat, void *data)
3098 SendHUDChange(player->peer_id, id, stat, data);
3102 bool Server::hudSetFlags(RemotePlayer *player, u32 flags, u32 mask)
3107 SendHUDSetFlags(player->peer_id, flags, mask);
3108 player->hud_flags &= ~mask;
3109 player->hud_flags |= flags;
3111 PlayerSAO* playersao = player->getPlayerSAO();
3113 if (playersao == NULL)
3116 m_script->player_event(playersao, "hud_changed");
3120 bool Server::hudSetHotbarItemcount(RemotePlayer *player, s32 hotbar_itemcount)
3125 if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
3128 player->setHotbarItemcount(hotbar_itemcount);
3129 std::ostringstream os(std::ios::binary);
3130 writeS32(os, hotbar_itemcount);
3131 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
3135 void Server::hudSetHotbarImage(RemotePlayer *player, std::string name)
3140 player->setHotbarImage(name);
3141 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
3144 std::string Server::hudGetHotbarImage(RemotePlayer *player)
3148 return player->getHotbarImage();
3151 void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
3156 player->setHotbarSelectedImage(name);
3157 SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
3160 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
3161 v2s32 animation_frames[4], f32 frame_speed)
3166 player->setLocalAnimations(animation_frames, frame_speed);
3167 SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
3171 bool Server::setPlayerEyeOffset(RemotePlayer *player, v3f first, v3f third)
3176 player->eye_offset_first = first;
3177 player->eye_offset_third = third;
3178 SendEyeOffset(player->peer_id, first, third);
3182 bool Server::setSky(RemotePlayer *player, const video::SColor &bgcolor,
3183 const std::string &type, const std::vector<std::string> ¶ms)
3188 player->setSky(bgcolor, type, params);
3189 SendSetSky(player->peer_id, bgcolor, type, params);
3193 bool Server::overrideDayNightRatio(RemotePlayer *player, bool do_override,
3199 player->overrideDayNightRatio(do_override, ratio);
3200 SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
3204 void Server::notifyPlayers(const std::wstring &msg)
3206 SendChatMessage(PEER_ID_INEXISTENT,msg);
3209 void Server::spawnParticle(const std::string &playername, v3f pos,
3210 v3f velocity, v3f acceleration,
3211 float expirationtime, float size, bool
3212 collisiondetection, bool collision_removal,
3213 bool vertical, const std::string &texture,
3214 const struct TileAnimationParams &animation, u8 glow)
3216 // m_env will be NULL if the server is initializing
3220 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3221 if (playername != "") {
3222 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3225 peer_id = player->peer_id;
3226 proto_ver = player->protocol_version;
3229 SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration,
3230 expirationtime, size, collisiondetection,
3231 collision_removal, vertical, texture, animation, glow);
3234 u32 Server::addParticleSpawner(u16 amount, float spawntime,
3235 v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
3236 float minexptime, float maxexptime, float minsize, float maxsize,
3237 bool collisiondetection, bool collision_removal,
3238 ServerActiveObject *attached, bool vertical, const std::string &texture,
3239 const std::string &playername, const struct TileAnimationParams &animation,
3242 // m_env will be NULL if the server is initializing
3246 u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0;
3247 if (playername != "") {
3248 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3251 peer_id = player->peer_id;
3252 proto_ver = player->protocol_version;
3255 u16 attached_id = attached ? attached->getId() : 0;
3258 if (attached_id == 0)
3259 id = m_env->addParticleSpawner(spawntime);
3261 id = m_env->addParticleSpawner(spawntime, attached_id);
3263 SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime,
3264 minpos, maxpos, minvel, maxvel, minacc, maxacc,
3265 minexptime, maxexptime, minsize, maxsize,
3266 collisiondetection, collision_removal, attached_id, vertical,
3267 texture, id, animation, glow);
3272 void Server::deleteParticleSpawner(const std::string &playername, u32 id)
3274 // m_env will be NULL if the server is initializing
3276 throw ServerError("Can't delete particle spawners during initialisation!");
3278 u16 peer_id = PEER_ID_INEXISTENT;
3279 if (playername != "") {
3280 RemotePlayer *player = m_env->getPlayer(playername.c_str());
3283 peer_id = player->peer_id;
3286 m_env->deleteParticleSpawner(id);
3287 SendDeleteParticleSpawner(peer_id, id);
3290 Inventory* Server::createDetachedInventory(const std::string &name, const std::string &player)
3292 if(m_detached_inventories.count(name) > 0){
3293 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
3294 delete m_detached_inventories[name];
3296 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
3298 Inventory *inv = new Inventory(m_itemdef);
3300 m_detached_inventories[name] = inv;
3301 m_detached_inventories_player[name] = player;
3302 //TODO find a better way to do this
3303 sendDetachedInventory(name,PEER_ID_INEXISTENT);
3307 // actions: time-reversed list
3308 // Return value: success/failure
3309 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
3310 std::list<std::string> *log)
3312 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
3313 ServerMap *map = (ServerMap*)(&m_env->getMap());
3315 // Fail if no actions to handle
3316 if(actions.empty()){
3317 log->push_back("Nothing to do.");
3324 for(std::list<RollbackAction>::const_iterator
3325 i = actions.begin();
3326 i != actions.end(); ++i)
3328 const RollbackAction &action = *i;
3330 bool success = action.applyRevert(map, this, this);
3333 std::ostringstream os;
3334 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
3335 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3337 log->push_back(os.str());
3339 std::ostringstream os;
3340 os<<"Successfully reverted step ("<<num_tried<<") "<<action.toString();
3341 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
3343 log->push_back(os.str());
3347 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
3348 <<" failed"<<std::endl;
3350 // Call it done if less than half failed
3351 return num_failed <= num_tried/2;
3354 // IGameDef interface
3356 IItemDefManager *Server::getItemDefManager()
3361 INodeDefManager *Server::getNodeDefManager()
3366 ICraftDefManager *Server::getCraftDefManager()
3371 u16 Server::allocateUnknownNodeId(const std::string &name)
3373 return m_nodedef->allocateDummy(name);
3376 MtEventManager *Server::getEventManager()
3381 IWritableItemDefManager *Server::getWritableItemDefManager()
3386 IWritableNodeDefManager *Server::getWritableNodeDefManager()
3391 IWritableCraftDefManager *Server::getWritableCraftDefManager()
3396 const ModSpec *Server::getModSpec(const std::string &modname) const
3398 std::vector<ModSpec>::const_iterator it;
3399 for (it = m_mods.begin(); it != m_mods.end(); ++it) {
3400 const ModSpec &mod = *it;
3401 if (mod.name == modname)
3407 void Server::getModNames(std::vector<std::string> &modlist)
3409 std::vector<ModSpec>::iterator it;
3410 for (it = m_mods.begin(); it != m_mods.end(); ++it)
3411 modlist.push_back(it->name);
3414 const std::string Server::getBuiltinLuaPath()
3416 return porting::path_share + DIR_DELIM + "builtin";
3419 std::string Server::getModStoragePath() const
3421 return m_path_world + DIR_DELIM + "mod_storage";
3424 v3f Server::findSpawnPos()
3426 ServerMap &map = m_env->getServerMap();
3428 if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
3429 return nodeposf * BS;
3432 bool is_good = false;
3434 // Try to find a good place a few times
3435 for(s32 i = 0; i < 4000 && !is_good; i++) {
3437 // We're going to try to throw the player to this position
3438 v2s16 nodepos2d = v2s16(
3439 -range + (myrand() % (range * 2)),
3440 -range + (myrand() % (range * 2)));
3442 // Get spawn level at point
3443 s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
3444 // Continue if MAX_MAP_GENERATION_LIMIT was returned by
3445 // the mapgen to signify an unsuitable spawn position
3446 if (spawn_level == MAX_MAP_GENERATION_LIMIT)
3449 v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
3452 for (s32 i = 0; i < 10; i++) {
3453 v3s16 blockpos = getNodeBlockPos(nodepos);
3454 map.emergeBlock(blockpos, true);
3455 content_t c = map.getNodeNoEx(nodepos).getContent();
3456 if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
3458 if (air_count >= 2) {
3459 nodeposf = intToFloat(nodepos, BS);
3460 // Don't spawn the player outside map boundaries
3461 if (objectpos_over_limit(nodeposf))
3474 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
3476 bool newplayer = false;
3479 Try to get an existing player
3481 RemotePlayer *player = m_env->getPlayer(name);
3483 // If player is already connected, cancel
3484 if (player != NULL && player->peer_id != 0) {
3485 infostream<<"emergePlayer(): Player already connected"<<std::endl;
3490 If player with the wanted peer_id already exists, cancel.
3492 if (m_env->getPlayer(peer_id) != NULL) {
3493 infostream<<"emergePlayer(): Player with wrong name but same"
3494 " peer_id already exists"<<std::endl;
3498 // Create a new player active object
3499 PlayerSAO *playersao = new PlayerSAO(m_env, peer_id, isSingleplayer());
3500 player = m_env->loadPlayer(name, playersao);
3502 // Create player if it doesn't exist
3505 player = new RemotePlayer(name, this->idef());
3506 // Set player position
3507 infostream<<"Server: Finding spawn place for player \""
3508 <<name<<"\""<<std::endl;
3509 playersao->setBasePosition(findSpawnPos());
3511 // Make sure the player is saved
3512 player->setModified(true);
3514 // Add player to environment
3515 m_env->addPlayer(player);
3517 // If the player exists, ensure that they respawn inside legal bounds
3518 // This fixes an assert crash when the player can't be added
3519 // to the environment
3520 if (objectpos_over_limit(playersao->getBasePosition())) {
3521 actionstream << "Respawn position for player \""
3522 << name << "\" outside limits, resetting" << std::endl;
3523 playersao->setBasePosition(findSpawnPos());
3527 playersao->initialize(player, getPlayerEffectivePrivs(player->getName()));
3529 player->protocol_version = proto_version;
3531 /* Clean up old HUD elements from previous sessions */
3534 /* Add object to environment */
3535 m_env->addActiveObject(playersao);
3539 m_script->on_newplayer(playersao);
3545 bool Server::registerModStorage(ModMetadata *storage)
3547 if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
3548 errorstream << "Unable to register same mod storage twice. Storage name: "
3549 << storage->getModName() << std::endl;
3553 m_mod_storages[storage->getModName()] = storage;
3557 void Server::unregisterModStorage(const std::string &name)
3559 UNORDERED_MAP<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name);
3560 if (it != m_mod_storages.end()) {
3561 // Save unconditionaly on unregistration
3562 it->second->save(getModStoragePath());
3563 m_mod_storages.erase(name);
3567 void dedicated_server_loop(Server &server, bool &kill)
3569 DSTACK(FUNCTION_NAME);
3571 verbosestream<<"dedicated_server_loop()"<<std::endl;
3573 IntervalLimiter m_profiler_interval;
3575 static const float steplen = g_settings->getFloat("dedicated_server_step");
3576 static const float profiler_print_interval =
3577 g_settings->getFloat("profiler_print_interval");
3580 // This is kind of a hack but can be done like this
3581 // because server.step() is very light
3583 ScopeProfiler sp(g_profiler, "dedicated server sleep");
3584 sleep_ms((int)(steplen*1000.0));
3586 server.step(steplen);
3588 if(server.getShutdownRequested() || kill)
3590 infostream<<"Dedicated server quitting"<<std::endl;
3592 if(g_settings->getBool("server_announce"))
3593 ServerList::sendAnnounce("delete", server.m_bind_addr.getPort());
3601 if (profiler_print_interval != 0) {
3602 if(m_profiler_interval.step(steplen, profiler_print_interval))
3604 infostream<<"Profiler:"<<std::endl;
3605 g_profiler->print(infostream);
3606 g_profiler->clear();