client.cpp 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962
  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include <iostream>
  17. #include <algorithm>
  18. #include <sstream>
  19. #include <cmath>
  20. #include <IFileSystem.h>
  21. #include "threading/mutex_auto_lock.h"
  22. #include "client/renderingengine.h"
  23. #include "util/auth.h"
  24. #include "util/directiontables.h"
  25. #include "util/pointedthing.h"
  26. #include "util/serialize.h"
  27. #include "util/string.h"
  28. #include "util/srp.h"
  29. #include "client.h"
  30. #include "network/clientopcodes.h"
  31. #include "filesys.h"
  32. #include "mapblock_mesh.h"
  33. #include "mapblock.h"
  34. #include "minimap.h"
  35. #include "mods.h"
  36. #include "profiler.h"
  37. #include "gettext.h"
  38. #include "clientmap.h"
  39. #include "clientmedia.h"
  40. #include "version.h"
  41. #include "database-sqlite3.h"
  42. #include "serialization.h"
  43. #include "guiscalingfilter.h"
  44. #include "script/scripting_client.h"
  45. #include "game.h"
  46. extern gui::IGUIEnvironment* guienv;
  47. /*
  48. Client
  49. */
  50. Client::Client(
  51. const char *playername,
  52. const std::string &password,
  53. const std::string &address_name,
  54. MapDrawControl &control,
  55. IWritableTextureSource *tsrc,
  56. IWritableShaderSource *shsrc,
  57. IWritableItemDefManager *itemdef,
  58. IWritableNodeDefManager *nodedef,
  59. ISoundManager *sound,
  60. MtEventManager *event,
  61. bool ipv6,
  62. GameUIFlags *game_ui_flags
  63. ):
  64. m_tsrc(tsrc),
  65. m_shsrc(shsrc),
  66. m_itemdef(itemdef),
  67. m_nodedef(nodedef),
  68. m_sound(sound),
  69. m_event(event),
  70. m_mesh_update_thread(this),
  71. m_env(
  72. new ClientMap(this, control, 666),
  73. tsrc, this
  74. ),
  75. m_particle_manager(&m_env),
  76. m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
  77. m_address_name(address_name),
  78. m_server_ser_ver(SER_FMT_VER_INVALID),
  79. m_last_chat_message_sent(time(NULL)),
  80. m_password(password),
  81. m_chosen_auth_mech(AUTH_MECHANISM_NONE),
  82. m_media_downloader(new ClientMediaDownloader()),
  83. m_state(LC_Created),
  84. m_game_ui_flags(game_ui_flags)
  85. {
  86. // Add local player
  87. m_env.setLocalPlayer(new LocalPlayer(this, playername));
  88. if (g_settings->getBool("enable_minimap")) {
  89. m_minimap = new Minimap(this);
  90. }
  91. m_cache_save_interval = g_settings->getU16("server_map_save_interval");
  92. m_modding_enabled = g_settings->getBool("enable_client_modding");
  93. m_script = new ClientScripting(this);
  94. m_env.setScript(m_script);
  95. m_script->setEnv(&m_env);
  96. }
  97. void Client::loadMods()
  98. {
  99. // Load builtin
  100. scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath());
  101. // If modding is not enabled, don't load mods, just builtin
  102. if (!m_modding_enabled) {
  103. return;
  104. }
  105. ClientModConfiguration modconf(getClientModsLuaPath());
  106. m_mods = modconf.getMods();
  107. std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
  108. // complain about mods with unsatisfied dependencies
  109. if (!modconf.isConsistent()) {
  110. modconf.printUnsatisfiedModsError();
  111. }
  112. // Print mods
  113. infostream << "Client Loading mods: ";
  114. for (const ModSpec &mod : m_mods)
  115. infostream << mod.name << " ";
  116. infostream << std::endl;
  117. // Load and run "mod" scripts
  118. for (const ModSpec &mod : m_mods) {
  119. if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
  120. throw ModError("Error loading mod \"" + mod.name +
  121. "\": Mod name does not follow naming conventions: "
  122. "Only characters [a-z0-9_] are allowed.");
  123. }
  124. scanModIntoMemory(mod.name, mod.path);
  125. }
  126. }
  127. void Client::scanModSubfolder(const std::string &mod_name, const std::string &mod_path,
  128. std::string mod_subpath)
  129. {
  130. std::string full_path = mod_path + DIR_DELIM + mod_subpath;
  131. std::vector<fs::DirListNode> mod = fs::GetDirListing(full_path);
  132. for (unsigned int j=0; j < mod.size(); j++){
  133. std::string filename = mod[j].name;
  134. if (mod[j].dir) {
  135. scanModSubfolder(mod_name, mod_path, mod_subpath
  136. + filename + DIR_DELIM);
  137. continue;
  138. }
  139. std::replace( mod_subpath.begin(), mod_subpath.end(), DIR_DELIM_CHAR, '/');
  140. m_mod_files[mod_name + ":" + mod_subpath + filename] = full_path + filename;
  141. }
  142. }
  143. void Client::initMods()
  144. {
  145. m_script->loadModFromMemory(BUILTIN_MOD_NAME);
  146. // If modding is not enabled, don't load mods, just builtin
  147. if (!m_modding_enabled) {
  148. return;
  149. }
  150. // Load and run "mod" scripts
  151. for (const ModSpec &mod : m_mods)
  152. m_script->loadModFromMemory(mod.name);
  153. }
  154. const std::string &Client::getBuiltinLuaPath()
  155. {
  156. static const std::string builtin_dir = porting::path_share + DIR_DELIM + "builtin";
  157. return builtin_dir;
  158. }
  159. const std::string &Client::getClientModsLuaPath()
  160. {
  161. static const std::string clientmods_dir = porting::path_share + DIR_DELIM + "clientmods";
  162. return clientmods_dir;
  163. }
  164. const std::vector<ModSpec>& Client::getMods() const
  165. {
  166. static std::vector<ModSpec> client_modspec_temp;
  167. return client_modspec_temp;
  168. }
  169. const ModSpec* Client::getModSpec(const std::string &modname) const
  170. {
  171. return NULL;
  172. }
  173. void Client::Stop()
  174. {
  175. m_shutdown = true;
  176. // Don't disable this part when modding is disabled, it's used in builtin
  177. m_script->on_shutdown();
  178. //request all client managed threads to stop
  179. m_mesh_update_thread.stop();
  180. // Save local server map
  181. if (m_localdb) {
  182. infostream << "Local map saving ended." << std::endl;
  183. m_localdb->endSave();
  184. }
  185. delete m_script;
  186. }
  187. bool Client::isShutdown()
  188. {
  189. return m_shutdown || !m_mesh_update_thread.isRunning();
  190. }
  191. Client::~Client()
  192. {
  193. m_shutdown = true;
  194. m_con.Disconnect();
  195. m_mesh_update_thread.stop();
  196. m_mesh_update_thread.wait();
  197. while (!m_mesh_update_thread.m_queue_out.empty()) {
  198. MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
  199. delete r.mesh;
  200. }
  201. delete m_inventory_from_server;
  202. // Delete detached inventories
  203. for (std::unordered_map<std::string, Inventory*>::iterator
  204. i = m_detached_inventories.begin();
  205. i != m_detached_inventories.end(); ++i) {
  206. delete i->second;
  207. }
  208. // cleanup 3d model meshes on client shutdown
  209. while (RenderingEngine::get_mesh_cache()->getMeshCount() != 0) {
  210. scene::IAnimatedMesh *mesh = RenderingEngine::get_mesh_cache()->getMeshByIndex(0);
  211. if (mesh)
  212. RenderingEngine::get_mesh_cache()->removeMesh(mesh);
  213. }
  214. delete m_minimap;
  215. }
  216. void Client::connect(Address address, bool is_local_server)
  217. {
  218. DSTACK(FUNCTION_NAME);
  219. initLocalMapSaving(address, m_address_name, is_local_server);
  220. m_con.SetTimeoutMs(0);
  221. m_con.Connect(address);
  222. }
  223. void Client::step(float dtime)
  224. {
  225. DSTACK(FUNCTION_NAME);
  226. // Limit a bit
  227. if(dtime > 2.0)
  228. dtime = 2.0;
  229. if(m_ignore_damage_timer > dtime)
  230. m_ignore_damage_timer -= dtime;
  231. else
  232. m_ignore_damage_timer = 0.0;
  233. m_animation_time += dtime;
  234. if(m_animation_time > 60.0)
  235. m_animation_time -= 60.0;
  236. m_time_of_day_update_timer += dtime;
  237. ReceiveAll();
  238. /*
  239. Packet counter
  240. */
  241. {
  242. float &counter = m_packetcounter_timer;
  243. counter -= dtime;
  244. if(counter <= 0.0)
  245. {
  246. counter = 20.0;
  247. infostream << "Client packetcounter (" << m_packetcounter_timer
  248. << "):"<<std::endl;
  249. m_packetcounter.print(infostream);
  250. m_packetcounter.clear();
  251. }
  252. }
  253. // UGLY hack to fix 2 second startup delay caused by non existent
  254. // server client startup synchronization in local server or singleplayer mode
  255. static bool initial_step = true;
  256. if (initial_step) {
  257. initial_step = false;
  258. }
  259. else if(m_state == LC_Created) {
  260. float &counter = m_connection_reinit_timer;
  261. counter -= dtime;
  262. if(counter <= 0.0) {
  263. counter = 2.0;
  264. LocalPlayer *myplayer = m_env.getLocalPlayer();
  265. FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment.");
  266. u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ?
  267. CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN;
  268. if (proto_version_min < 25) {
  269. // Send TOSERVER_INIT_LEGACY
  270. // [0] u16 TOSERVER_INIT_LEGACY
  271. // [2] u8 SER_FMT_VER_HIGHEST_READ
  272. // [3] u8[20] player_name
  273. // [23] u8[28] password (new in some version)
  274. // [51] u16 minimum supported network protocol version (added sometime)
  275. // [53] u16 maximum supported network protocol version (added later than the previous one)
  276. char pName[PLAYERNAME_SIZE];
  277. char pPassword[PASSWORD_SIZE];
  278. memset(pName, 0, PLAYERNAME_SIZE * sizeof(char));
  279. memset(pPassword, 0, PASSWORD_SIZE * sizeof(char));
  280. std::string hashed_password = translate_password(myplayer->getName(), m_password);
  281. snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName());
  282. snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str());
  283. sendLegacyInit(pName, pPassword);
  284. }
  285. if (CLIENT_PROTOCOL_VERSION_MAX >= 25)
  286. sendInit(myplayer->getName());
  287. }
  288. // Not connected, return
  289. return;
  290. }
  291. /*
  292. Do stuff if connected
  293. */
  294. /*
  295. Run Map's timers and unload unused data
  296. */
  297. const float map_timer_and_unload_dtime = 5.25;
  298. if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) {
  299. ScopeProfiler sp(g_profiler, "Client: map timer and unload");
  300. std::vector<v3s16> deleted_blocks;
  301. m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
  302. g_settings->getFloat("client_unload_unused_data_timeout"),
  303. g_settings->getS32("client_mapblock_limit"),
  304. &deleted_blocks);
  305. /*
  306. Send info to server
  307. NOTE: This loop is intentionally iterated the way it is.
  308. */
  309. std::vector<v3s16>::iterator i = deleted_blocks.begin();
  310. std::vector<v3s16> sendlist;
  311. for(;;) {
  312. if(sendlist.size() == 255 || i == deleted_blocks.end()) {
  313. if(sendlist.empty())
  314. break;
  315. /*
  316. [0] u16 command
  317. [2] u8 count
  318. [3] v3s16 pos_0
  319. [3+6] v3s16 pos_1
  320. ...
  321. */
  322. sendDeletedBlocks(sendlist);
  323. if(i == deleted_blocks.end())
  324. break;
  325. sendlist.clear();
  326. }
  327. sendlist.push_back(*i);
  328. ++i;
  329. }
  330. }
  331. /*
  332. Send pending messages on out chat queue
  333. */
  334. if (!m_out_chat_queue.empty() && canSendChatMessage()) {
  335. sendChatMessage(m_out_chat_queue.front());
  336. m_out_chat_queue.pop();
  337. }
  338. /*
  339. Handle environment
  340. */
  341. // Control local player (0ms)
  342. LocalPlayer *player = m_env.getLocalPlayer();
  343. assert(player);
  344. player->applyControl(dtime);
  345. // Step environment
  346. m_env.step(dtime);
  347. m_sound->step(dtime);
  348. /*
  349. Get events
  350. */
  351. while (m_env.hasClientEnvEvents()) {
  352. ClientEnvEvent envEvent = m_env.getClientEnvEvent();
  353. if (envEvent.type == CEE_PLAYER_DAMAGE) {
  354. if (m_ignore_damage_timer <= 0) {
  355. u8 damage = envEvent.player_damage.amount;
  356. if (envEvent.player_damage.send_to_server)
  357. sendDamage(damage);
  358. // Add to ClientEvent queue
  359. ClientEvent event;
  360. event.type = CE_PLAYER_DAMAGE;
  361. event.player_damage.amount = damage;
  362. m_client_event_queue.push(event);
  363. }
  364. }
  365. // Protocol v29 or greater obsoleted this event
  366. else if (envEvent.type == CEE_PLAYER_BREATH && m_proto_ver < 29) {
  367. u16 breath = envEvent.player_breath.amount;
  368. sendBreath(breath);
  369. }
  370. }
  371. /*
  372. Print some info
  373. */
  374. float &counter = m_avg_rtt_timer;
  375. counter += dtime;
  376. if(counter >= 10) {
  377. counter = 0.0;
  378. // connectedAndInitialized() is true, peer exists.
  379. float avg_rtt = getRTT();
  380. infostream << "Client: avg_rtt=" << avg_rtt << std::endl;
  381. }
  382. /*
  383. Send player position to server
  384. */
  385. {
  386. float &counter = m_playerpos_send_timer;
  387. counter += dtime;
  388. if((m_state == LC_Ready) && (counter >= m_recommended_send_interval))
  389. {
  390. counter = 0.0;
  391. sendPlayerPos();
  392. }
  393. }
  394. /*
  395. Replace updated meshes
  396. */
  397. {
  398. int num_processed_meshes = 0;
  399. while (!m_mesh_update_thread.m_queue_out.empty())
  400. {
  401. num_processed_meshes++;
  402. MinimapMapblock *minimap_mapblock = NULL;
  403. bool do_mapper_update = true;
  404. MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
  405. MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
  406. if (block) {
  407. // Delete the old mesh
  408. delete block->mesh;
  409. block->mesh = nullptr;
  410. if (r.mesh) {
  411. minimap_mapblock = r.mesh->moveMinimapMapblock();
  412. if (minimap_mapblock == NULL)
  413. do_mapper_update = false;
  414. bool is_empty = true;
  415. for (int l = 0; l < MAX_TILE_LAYERS; l++)
  416. if (r.mesh->getMesh(l)->getMeshBufferCount() != 0)
  417. is_empty = false;
  418. if (is_empty)
  419. delete r.mesh;
  420. else
  421. // Replace with the new mesh
  422. block->mesh = r.mesh;
  423. }
  424. } else {
  425. delete r.mesh;
  426. }
  427. if (m_minimap && do_mapper_update)
  428. m_minimap->addBlock(r.p, minimap_mapblock);
  429. if (r.ack_block_to_server) {
  430. /*
  431. Acknowledge block
  432. [0] u8 count
  433. [1] v3s16 pos_0
  434. */
  435. sendGotBlocks(r.p);
  436. }
  437. }
  438. if (num_processed_meshes > 0)
  439. g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
  440. }
  441. /*
  442. Load fetched media
  443. */
  444. if (m_media_downloader && m_media_downloader->isStarted()) {
  445. m_media_downloader->step(this);
  446. if (m_media_downloader->isDone()) {
  447. delete m_media_downloader;
  448. m_media_downloader = NULL;
  449. }
  450. }
  451. /*
  452. If the server didn't update the inventory in a while, revert
  453. the local inventory (so the player notices the lag problem
  454. and knows something is wrong).
  455. */
  456. if(m_inventory_from_server)
  457. {
  458. float interval = 10.0;
  459. float count_before = floor(m_inventory_from_server_age / interval);
  460. m_inventory_from_server_age += dtime;
  461. float count_after = floor(m_inventory_from_server_age / interval);
  462. if(count_after != count_before)
  463. {
  464. // Do this every <interval> seconds after TOCLIENT_INVENTORY
  465. // Reset the locally changed inventory to the authoritative inventory
  466. LocalPlayer *player = m_env.getLocalPlayer();
  467. player->inventory = *m_inventory_from_server;
  468. m_inventory_updated = true;
  469. }
  470. }
  471. /*
  472. Update positions of sounds attached to objects
  473. */
  474. {
  475. for(std::unordered_map<int, u16>::iterator i = m_sounds_to_objects.begin();
  476. i != m_sounds_to_objects.end(); ++i) {
  477. int client_id = i->first;
  478. u16 object_id = i->second;
  479. ClientActiveObject *cao = m_env.getActiveObject(object_id);
  480. if(!cao)
  481. continue;
  482. v3f pos = cao->getPosition();
  483. m_sound->updateSoundPosition(client_id, pos);
  484. }
  485. }
  486. /*
  487. Handle removed remotely initiated sounds
  488. */
  489. m_removed_sounds_check_timer += dtime;
  490. if(m_removed_sounds_check_timer >= 2.32) {
  491. m_removed_sounds_check_timer = 0;
  492. // Find removed sounds and clear references to them
  493. std::vector<s32> removed_server_ids;
  494. for (std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.begin();
  495. i != m_sounds_server_to_client.end();) {
  496. s32 server_id = i->first;
  497. int client_id = i->second;
  498. ++i;
  499. if(!m_sound->soundExists(client_id)) {
  500. m_sounds_server_to_client.erase(server_id);
  501. m_sounds_client_to_server.erase(client_id);
  502. m_sounds_to_objects.erase(client_id);
  503. removed_server_ids.push_back(server_id);
  504. }
  505. }
  506. // Sync to server
  507. if(!removed_server_ids.empty()) {
  508. sendRemovedSounds(removed_server_ids);
  509. }
  510. }
  511. m_mod_storage_save_timer -= dtime;
  512. if (m_mod_storage_save_timer <= 0.0f) {
  513. verbosestream << "Saving registered mod storages." << std::endl;
  514. m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval");
  515. for (std::unordered_map<std::string, ModMetadata *>::const_iterator
  516. it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) {
  517. if (it->second->isModified()) {
  518. it->second->save(getModStoragePath());
  519. }
  520. }
  521. }
  522. // Write server map
  523. if (m_localdb && m_localdb_save_interval.step(dtime,
  524. m_cache_save_interval)) {
  525. m_localdb->endSave();
  526. m_localdb->beginSave();
  527. }
  528. }
  529. bool Client::loadMedia(const std::string &data, const std::string &filename)
  530. {
  531. // Silly irrlicht's const-incorrectness
  532. Buffer<char> data_rw(data.c_str(), data.size());
  533. std::string name;
  534. const char *image_ext[] = {
  535. ".png", ".jpg", ".bmp", ".tga",
  536. ".pcx", ".ppm", ".psd", ".wal", ".rgb",
  537. NULL
  538. };
  539. name = removeStringEnd(filename, image_ext);
  540. if(name != "")
  541. {
  542. verbosestream<<"Client: Attempting to load image "
  543. <<"file \""<<filename<<"\""<<std::endl;
  544. io::IFileSystem *irrfs = RenderingEngine::get_filesystem();
  545. video::IVideoDriver *vdrv = RenderingEngine::get_video_driver();
  546. // Create an irrlicht memory file
  547. io::IReadFile *rfile = irrfs->createMemoryReadFile(
  548. *data_rw, data_rw.getSize(), "_tempreadfile");
  549. FATAL_ERROR_IF(!rfile, "Could not create irrlicht memory file.");
  550. // Read image
  551. video::IImage *img = vdrv->createImageFromFile(rfile);
  552. if(!img){
  553. errorstream<<"Client: Cannot create image from data of "
  554. <<"file \""<<filename<<"\""<<std::endl;
  555. rfile->drop();
  556. return false;
  557. }
  558. else {
  559. m_tsrc->insertSourceImage(filename, img);
  560. img->drop();
  561. rfile->drop();
  562. return true;
  563. }
  564. }
  565. const char *sound_ext[] = {
  566. ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
  567. ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
  568. ".ogg", NULL
  569. };
  570. name = removeStringEnd(filename, sound_ext);
  571. if(name != "")
  572. {
  573. verbosestream<<"Client: Attempting to load sound "
  574. <<"file \""<<filename<<"\""<<std::endl;
  575. m_sound->loadSoundData(name, data);
  576. return true;
  577. }
  578. const char *model_ext[] = {
  579. ".x", ".b3d", ".md2", ".obj",
  580. NULL
  581. };
  582. name = removeStringEnd(filename, model_ext);
  583. if(name != "")
  584. {
  585. verbosestream<<"Client: Storing model into memory: "
  586. <<"\""<<filename<<"\""<<std::endl;
  587. if(m_mesh_data.count(filename))
  588. errorstream<<"Multiple models with name \""<<filename.c_str()
  589. <<"\" found; replacing previous model"<<std::endl;
  590. m_mesh_data[filename] = data;
  591. return true;
  592. }
  593. errorstream<<"Client: Don't know how to load file \""
  594. <<filename<<"\""<<std::endl;
  595. return false;
  596. }
  597. // Virtual methods from con::PeerHandler
  598. void Client::peerAdded(con::Peer *peer)
  599. {
  600. infostream << "Client::peerAdded(): peer->id="
  601. << peer->id << std::endl;
  602. }
  603. void Client::deletingPeer(con::Peer *peer, bool timeout)
  604. {
  605. infostream << "Client::deletingPeer(): "
  606. "Server Peer is getting deleted "
  607. << "(timeout=" << timeout << ")" << std::endl;
  608. if (timeout) {
  609. m_access_denied = true;
  610. m_access_denied_reason = gettext("Connection timed out.");
  611. }
  612. }
  613. /*
  614. u16 command
  615. u16 number of files requested
  616. for each file {
  617. u16 length of name
  618. string name
  619. }
  620. */
  621. void Client::request_media(const std::vector<std::string> &file_requests)
  622. {
  623. std::ostringstream os(std::ios_base::binary);
  624. writeU16(os, TOSERVER_REQUEST_MEDIA);
  625. size_t file_requests_size = file_requests.size();
  626. FATAL_ERROR_IF(file_requests_size > 0xFFFF, "Unsupported number of file requests");
  627. // Packet dynamicly resized
  628. NetworkPacket pkt(TOSERVER_REQUEST_MEDIA, 2 + 0);
  629. pkt << (u16) (file_requests_size & 0xFFFF);
  630. for(std::vector<std::string>::const_iterator i = file_requests.begin();
  631. i != file_requests.end(); ++i) {
  632. pkt << (*i);
  633. }
  634. Send(&pkt);
  635. infostream << "Client: Sending media request list to server ("
  636. << file_requests.size() << " files. packet size)" << std::endl;
  637. }
  638. void Client::initLocalMapSaving(const Address &address,
  639. const std::string &hostname,
  640. bool is_local_server)
  641. {
  642. if (!g_settings->getBool("enable_local_map_saving") || is_local_server) {
  643. return;
  644. }
  645. const std::string world_path = porting::path_user
  646. + DIR_DELIM + "worlds"
  647. + DIR_DELIM + "server_"
  648. + hostname + "_" + std::to_string(address.getPort());
  649. fs::CreateAllDirs(world_path);
  650. m_localdb = new MapDatabaseSQLite3(world_path);
  651. m_localdb->beginSave();
  652. actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
  653. }
  654. void Client::ReceiveAll()
  655. {
  656. DSTACK(FUNCTION_NAME);
  657. u64 start_ms = porting::getTimeMs();
  658. for(;;)
  659. {
  660. // Limit time even if there would be huge amounts of data to
  661. // process
  662. if(porting::getTimeMs() > start_ms + 100)
  663. break;
  664. try {
  665. Receive();
  666. g_profiler->graphAdd("client_received_packets", 1);
  667. }
  668. catch(con::NoIncomingDataException &e) {
  669. break;
  670. }
  671. catch(con::InvalidIncomingDataException &e) {
  672. infostream<<"Client::ReceiveAll(): "
  673. "InvalidIncomingDataException: what()="
  674. <<e.what()<<std::endl;
  675. }
  676. }
  677. }
  678. void Client::Receive()
  679. {
  680. DSTACK(FUNCTION_NAME);
  681. NetworkPacket pkt;
  682. m_con.Receive(&pkt);
  683. ProcessData(&pkt);
  684. }
  685. inline void Client::handleCommand(NetworkPacket* pkt)
  686. {
  687. const ToClientCommandHandler& opHandle = toClientCommandTable[pkt->getCommand()];
  688. (this->*opHandle.handler)(pkt);
  689. }
  690. /*
  691. sender_peer_id given to this shall be quaranteed to be a valid peer
  692. */
  693. void Client::ProcessData(NetworkPacket *pkt)
  694. {
  695. DSTACK(FUNCTION_NAME);
  696. ToClientCommand command = (ToClientCommand) pkt->getCommand();
  697. u32 sender_peer_id = pkt->getPeerId();
  698. //infostream<<"Client: received command="<<command<<std::endl;
  699. m_packetcounter.add((u16)command);
  700. /*
  701. If this check is removed, be sure to change the queue
  702. system to know the ids
  703. */
  704. if(sender_peer_id != PEER_ID_SERVER) {
  705. infostream << "Client::ProcessData(): Discarding data not "
  706. "coming from server: peer_id=" << sender_peer_id
  707. << std::endl;
  708. return;
  709. }
  710. // Command must be handled into ToClientCommandHandler
  711. if (command >= TOCLIENT_NUM_MSG_TYPES) {
  712. infostream << "Client: Ignoring unknown command "
  713. << command << std::endl;
  714. return;
  715. }
  716. /*
  717. * Those packets are handled before m_server_ser_ver is set, it's normal
  718. * But we must use the new ToClientConnectionState in the future,
  719. * as a byte mask
  720. */
  721. if(toClientCommandTable[command].state == TOCLIENT_STATE_NOT_CONNECTED) {
  722. handleCommand(pkt);
  723. return;
  724. }
  725. if(m_server_ser_ver == SER_FMT_VER_INVALID) {
  726. infostream << "Client: Server serialization"
  727. " format invalid or not initialized."
  728. " Skipping incoming command=" << command << std::endl;
  729. return;
  730. }
  731. /*
  732. Handle runtime commands
  733. */
  734. handleCommand(pkt);
  735. }
  736. void Client::Send(NetworkPacket* pkt)
  737. {
  738. m_con.Send(PEER_ID_SERVER,
  739. serverCommandFactoryTable[pkt->getCommand()].channel,
  740. pkt,
  741. serverCommandFactoryTable[pkt->getCommand()].reliable);
  742. }
  743. // Will fill up 12 + 12 + 4 + 4 + 4 bytes
  744. void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *pkt)
  745. {
  746. v3f pf = myplayer->getPosition() * 100;
  747. v3f sf = myplayer->getSpeed() * 100;
  748. s32 pitch = myplayer->getPitch() * 100;
  749. s32 yaw = myplayer->getYaw() * 100;
  750. u32 keyPressed = myplayer->keyPressed;
  751. // scaled by 80, so that pi can fit into a u8
  752. u8 fov = clientMap->getCameraFov() * 80;
  753. u8 wanted_range = MYMIN(255,
  754. std::ceil(clientMap->getControl().wanted_range / MAP_BLOCKSIZE));
  755. v3s32 position(pf.X, pf.Y, pf.Z);
  756. v3s32 speed(sf.X, sf.Y, sf.Z);
  757. /*
  758. Format:
  759. [0] v3s32 position*100
  760. [12] v3s32 speed*100
  761. [12+12] s32 pitch*100
  762. [12+12+4] s32 yaw*100
  763. [12+12+4+4] u32 keyPressed
  764. [12+12+4+4+4] u8 fov*80
  765. [12+12+4+4+4+1] u8 ceil(wanted_range / MAP_BLOCKSIZE)
  766. */
  767. *pkt << position << speed << pitch << yaw << keyPressed;
  768. *pkt << fov << wanted_range;
  769. }
  770. void Client::interact(u8 action, const PointedThing& pointed)
  771. {
  772. if(m_state != LC_Ready) {
  773. errorstream << "Client::interact() "
  774. "Canceled (not connected)"
  775. << std::endl;
  776. return;
  777. }
  778. LocalPlayer *myplayer = m_env.getLocalPlayer();
  779. if (myplayer == NULL)
  780. return;
  781. /*
  782. [0] u16 command
  783. [2] u8 action
  784. [3] u16 item
  785. [5] u32 length of the next item (plen)
  786. [9] serialized PointedThing
  787. [9 + plen] player position information
  788. actions:
  789. 0: start digging (from undersurface) or use
  790. 1: stop digging (all parameters ignored)
  791. 2: digging completed
  792. 3: place block or item (to abovesurface)
  793. 4: use item
  794. 5: perform secondary action of item
  795. */
  796. NetworkPacket pkt(TOSERVER_INTERACT, 1 + 2 + 0);
  797. pkt << action;
  798. pkt << (u16)getPlayerItem();
  799. std::ostringstream tmp_os(std::ios::binary);
  800. pointed.serialize(tmp_os);
  801. pkt.putLongString(tmp_os.str());
  802. writePlayerPos(myplayer, &m_env.getClientMap(), &pkt);
  803. Send(&pkt);
  804. }
  805. void Client::deleteAuthData()
  806. {
  807. if (!m_auth_data)
  808. return;
  809. switch (m_chosen_auth_mech) {
  810. case AUTH_MECHANISM_FIRST_SRP:
  811. break;
  812. case AUTH_MECHANISM_SRP:
  813. case AUTH_MECHANISM_LEGACY_PASSWORD:
  814. srp_user_delete((SRPUser *) m_auth_data);
  815. m_auth_data = NULL;
  816. break;
  817. case AUTH_MECHANISM_NONE:
  818. break;
  819. }
  820. m_chosen_auth_mech = AUTH_MECHANISM_NONE;
  821. }
  822. AuthMechanism Client::choseAuthMech(const u32 mechs)
  823. {
  824. if (mechs & AUTH_MECHANISM_SRP)
  825. return AUTH_MECHANISM_SRP;
  826. if (mechs & AUTH_MECHANISM_FIRST_SRP)
  827. return AUTH_MECHANISM_FIRST_SRP;
  828. if (mechs & AUTH_MECHANISM_LEGACY_PASSWORD)
  829. return AUTH_MECHANISM_LEGACY_PASSWORD;
  830. return AUTH_MECHANISM_NONE;
  831. }
  832. void Client::sendLegacyInit(const char* playerName, const char* playerPassword)
  833. {
  834. NetworkPacket pkt(TOSERVER_INIT_LEGACY,
  835. 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2);
  836. u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ?
  837. CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN;
  838. pkt << (u8) SER_FMT_VER_HIGHEST_READ;
  839. pkt.putRawString(playerName,PLAYERNAME_SIZE);
  840. pkt.putRawString(playerPassword, PASSWORD_SIZE);
  841. pkt << (u16) proto_version_min << (u16) CLIENT_PROTOCOL_VERSION_MAX;
  842. Send(&pkt);
  843. }
  844. void Client::sendInit(const std::string &playerName)
  845. {
  846. NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size()));
  847. // we don't support network compression yet
  848. u16 supp_comp_modes = NETPROTO_COMPRESSION_NONE;
  849. u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ?
  850. CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN;
  851. pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u16) supp_comp_modes;
  852. pkt << (u16) proto_version_min << (u16) CLIENT_PROTOCOL_VERSION_MAX;
  853. pkt << playerName;
  854. Send(&pkt);
  855. }
  856. void Client::startAuth(AuthMechanism chosen_auth_mechanism)
  857. {
  858. m_chosen_auth_mech = chosen_auth_mechanism;
  859. switch (chosen_auth_mechanism) {
  860. case AUTH_MECHANISM_FIRST_SRP: {
  861. // send srp verifier to server
  862. std::string verifier;
  863. std::string salt;
  864. generate_srp_verifier_and_salt(getPlayerName(), m_password,
  865. &verifier, &salt);
  866. NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0);
  867. resp_pkt << salt << verifier << (u8)((m_password == "") ? 1 : 0);
  868. Send(&resp_pkt);
  869. break;
  870. }
  871. case AUTH_MECHANISM_SRP:
  872. case AUTH_MECHANISM_LEGACY_PASSWORD: {
  873. u8 based_on = 1;
  874. if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) {
  875. m_password = translate_password(getPlayerName(), m_password);
  876. based_on = 0;
  877. }
  878. std::string playername_u = lowercase(getPlayerName());
  879. m_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048,
  880. getPlayerName().c_str(), playername_u.c_str(),
  881. (const unsigned char *) m_password.c_str(),
  882. m_password.length(), NULL, NULL);
  883. char *bytes_A = 0;
  884. size_t len_A = 0;
  885. SRP_Result res = srp_user_start_authentication(
  886. (struct SRPUser *) m_auth_data, NULL, NULL, 0,
  887. (unsigned char **) &bytes_A, &len_A);
  888. FATAL_ERROR_IF(res != SRP_OK, "Creating local SRP user failed.");
  889. NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0);
  890. resp_pkt << std::string(bytes_A, len_A) << based_on;
  891. Send(&resp_pkt);
  892. break;
  893. }
  894. case AUTH_MECHANISM_NONE:
  895. break; // not handled in this method
  896. }
  897. }
  898. void Client::sendDeletedBlocks(std::vector<v3s16> &blocks)
  899. {
  900. NetworkPacket pkt(TOSERVER_DELETEDBLOCKS, 1 + sizeof(v3s16) * blocks.size());
  901. pkt << (u8) blocks.size();
  902. u32 k = 0;
  903. for(std::vector<v3s16>::iterator
  904. j = blocks.begin();
  905. j != blocks.end(); ++j) {
  906. pkt << *j;
  907. k++;
  908. }
  909. Send(&pkt);
  910. }
  911. void Client::sendGotBlocks(v3s16 block)
  912. {
  913. NetworkPacket pkt(TOSERVER_GOTBLOCKS, 1 + 6);
  914. pkt << (u8) 1 << block;
  915. Send(&pkt);
  916. }
  917. void Client::sendRemovedSounds(std::vector<s32> &soundList)
  918. {
  919. size_t server_ids = soundList.size();
  920. assert(server_ids <= 0xFFFF);
  921. NetworkPacket pkt(TOSERVER_REMOVED_SOUNDS, 2 + server_ids * 4);
  922. pkt << (u16) (server_ids & 0xFFFF);
  923. for(std::vector<s32>::iterator i = soundList.begin();
  924. i != soundList.end(); ++i)
  925. pkt << *i;
  926. Send(&pkt);
  927. }
  928. void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
  929. const StringMap &fields)
  930. {
  931. size_t fields_size = fields.size();
  932. FATAL_ERROR_IF(fields_size > 0xFFFF, "Unsupported number of nodemeta fields");
  933. NetworkPacket pkt(TOSERVER_NODEMETA_FIELDS, 0);
  934. pkt << p << formname << (u16) (fields_size & 0xFFFF);
  935. StringMap::const_iterator it;
  936. for (it = fields.begin(); it != fields.end(); ++it) {
  937. const std::string &name = it->first;
  938. const std::string &value = it->second;
  939. pkt << name;
  940. pkt.putLongString(value);
  941. }
  942. Send(&pkt);
  943. }
  944. void Client::sendInventoryFields(const std::string &formname,
  945. const StringMap &fields)
  946. {
  947. size_t fields_size = fields.size();
  948. FATAL_ERROR_IF(fields_size > 0xFFFF, "Unsupported number of inventory fields");
  949. NetworkPacket pkt(TOSERVER_INVENTORY_FIELDS, 0);
  950. pkt << formname << (u16) (fields_size & 0xFFFF);
  951. StringMap::const_iterator it;
  952. for (it = fields.begin(); it != fields.end(); ++it) {
  953. const std::string &name = it->first;
  954. const std::string &value = it->second;
  955. pkt << name;
  956. pkt.putLongString(value);
  957. }
  958. Send(&pkt);
  959. }
  960. void Client::sendInventoryAction(InventoryAction *a)
  961. {
  962. std::ostringstream os(std::ios_base::binary);
  963. a->serialize(os);
  964. // Make data buffer
  965. std::string s = os.str();
  966. NetworkPacket pkt(TOSERVER_INVENTORY_ACTION, s.size());
  967. pkt.putRawString(s.c_str(),s.size());
  968. Send(&pkt);
  969. }
  970. bool Client::canSendChatMessage() const
  971. {
  972. u32 now = time(NULL);
  973. float time_passed = now - m_last_chat_message_sent;
  974. float virt_chat_message_allowance = m_chat_message_allowance + time_passed *
  975. (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f);
  976. if (virt_chat_message_allowance < 1.0f)
  977. return false;
  978. return true;
  979. }
  980. void Client::sendChatMessage(const std::wstring &message)
  981. {
  982. const s16 max_queue_size = g_settings->getS16("max_out_chat_queue_size");
  983. if (canSendChatMessage()) {
  984. u32 now = time(NULL);
  985. float time_passed = now - m_last_chat_message_sent;
  986. m_last_chat_message_sent = time(NULL);
  987. m_chat_message_allowance += time_passed * (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f);
  988. if (m_chat_message_allowance > CLIENT_CHAT_MESSAGE_LIMIT_PER_10S)
  989. m_chat_message_allowance = CLIENT_CHAT_MESSAGE_LIMIT_PER_10S;
  990. m_chat_message_allowance -= 1.0f;
  991. NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16));
  992. pkt << message;
  993. Send(&pkt);
  994. } else if (m_out_chat_queue.size() < (u16) max_queue_size || max_queue_size == -1) {
  995. m_out_chat_queue.push(message);
  996. } else {
  997. infostream << "Could not queue chat message because maximum out chat queue size ("
  998. << max_queue_size << ") is reached." << std::endl;
  999. }
  1000. }
  1001. void Client::clearOutChatQueue()
  1002. {
  1003. m_out_chat_queue = std::queue<std::wstring>();
  1004. }
  1005. void Client::sendChangePassword(const std::string &oldpassword,
  1006. const std::string &newpassword)
  1007. {
  1008. LocalPlayer *player = m_env.getLocalPlayer();
  1009. if (player == NULL)
  1010. return;
  1011. std::string playername = player->getName();
  1012. if (m_proto_ver >= 25) {
  1013. // get into sudo mode and then send new password to server
  1014. m_password = oldpassword;
  1015. m_new_password = newpassword;
  1016. startAuth(choseAuthMech(m_sudo_auth_methods));
  1017. } else {
  1018. std::string oldpwd = translate_password(playername, oldpassword);
  1019. std::string newpwd = translate_password(playername, newpassword);
  1020. NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE);
  1021. for (u8 i = 0; i < PASSWORD_SIZE; i++) {
  1022. pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0);
  1023. }
  1024. for (u8 i = 0; i < PASSWORD_SIZE; i++) {
  1025. pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0);
  1026. }
  1027. Send(&pkt);
  1028. }
  1029. }
  1030. void Client::sendDamage(u8 damage)
  1031. {
  1032. DSTACK(FUNCTION_NAME);
  1033. NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u8));
  1034. pkt << damage;
  1035. Send(&pkt);
  1036. }
  1037. void Client::sendBreath(u16 breath)
  1038. {
  1039. DSTACK(FUNCTION_NAME);
  1040. // Protocol v29 make this obsolete
  1041. if (m_proto_ver >= 29)
  1042. return;
  1043. NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16));
  1044. pkt << breath;
  1045. Send(&pkt);
  1046. }
  1047. void Client::sendRespawn()
  1048. {
  1049. DSTACK(FUNCTION_NAME);
  1050. NetworkPacket pkt(TOSERVER_RESPAWN, 0);
  1051. Send(&pkt);
  1052. }
  1053. void Client::sendReady()
  1054. {
  1055. DSTACK(FUNCTION_NAME);
  1056. NetworkPacket pkt(TOSERVER_CLIENT_READY,
  1057. 1 + 1 + 1 + 1 + 2 + sizeof(char) * strlen(g_version_hash));
  1058. pkt << (u8) VERSION_MAJOR << (u8) VERSION_MINOR << (u8) VERSION_PATCH
  1059. << (u8) 0 << (u16) strlen(g_version_hash);
  1060. pkt.putRawString(g_version_hash, (u16) strlen(g_version_hash));
  1061. Send(&pkt);
  1062. }
  1063. void Client::sendPlayerPos()
  1064. {
  1065. LocalPlayer *myplayer = m_env.getLocalPlayer();
  1066. if(myplayer == NULL)
  1067. return;
  1068. ClientMap &map = m_env.getClientMap();
  1069. u8 camera_fov = map.getCameraFov();
  1070. u8 wanted_range = map.getControl().wanted_range;
  1071. // Save bandwidth by only updating position when something changed
  1072. if(myplayer->last_position == myplayer->getPosition() &&
  1073. myplayer->last_speed == myplayer->getSpeed() &&
  1074. myplayer->last_pitch == myplayer->getPitch() &&
  1075. myplayer->last_yaw == myplayer->getYaw() &&
  1076. myplayer->last_keyPressed == myplayer->keyPressed &&
  1077. myplayer->last_camera_fov == camera_fov &&
  1078. myplayer->last_wanted_range == wanted_range)
  1079. return;
  1080. myplayer->last_position = myplayer->getPosition();
  1081. myplayer->last_speed = myplayer->getSpeed();
  1082. myplayer->last_pitch = myplayer->getPitch();
  1083. myplayer->last_yaw = myplayer->getYaw();
  1084. myplayer->last_keyPressed = myplayer->keyPressed;
  1085. myplayer->last_camera_fov = camera_fov;
  1086. myplayer->last_wanted_range = wanted_range;
  1087. //infostream << "Sending Player Position information" << std::endl;
  1088. u16 our_peer_id;
  1089. {
  1090. //MutexAutoLock lock(m_con_mutex); //bulk comment-out
  1091. our_peer_id = m_con.GetPeerID();
  1092. }
  1093. // Set peer id if not set already
  1094. if(myplayer->peer_id == PEER_ID_INEXISTENT)
  1095. myplayer->peer_id = our_peer_id;
  1096. assert(myplayer->peer_id == our_peer_id);
  1097. NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4 + 1 + 1);
  1098. writePlayerPos(myplayer, &map, &pkt);
  1099. Send(&pkt);
  1100. }
  1101. void Client::sendPlayerItem(u16 item)
  1102. {
  1103. LocalPlayer *myplayer = m_env.getLocalPlayer();
  1104. if(myplayer == NULL)
  1105. return;
  1106. u16 our_peer_id = m_con.GetPeerID();
  1107. // Set peer id if not set already
  1108. if(myplayer->peer_id == PEER_ID_INEXISTENT)
  1109. myplayer->peer_id = our_peer_id;
  1110. assert(myplayer->peer_id == our_peer_id);
  1111. NetworkPacket pkt(TOSERVER_PLAYERITEM, 2);
  1112. pkt << item;
  1113. Send(&pkt);
  1114. }
  1115. void Client::removeNode(v3s16 p)
  1116. {
  1117. std::map<v3s16, MapBlock*> modified_blocks;
  1118. try {
  1119. m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
  1120. }
  1121. catch(InvalidPositionException &e) {
  1122. }
  1123. for(std::map<v3s16, MapBlock *>::iterator
  1124. i = modified_blocks.begin();
  1125. i != modified_blocks.end(); ++i) {
  1126. addUpdateMeshTaskWithEdge(i->first, false, true);
  1127. }
  1128. }
  1129. MapNode Client::getNode(v3s16 p, bool *is_valid_position)
  1130. {
  1131. return m_env.getMap().getNodeNoEx(p, is_valid_position);
  1132. }
  1133. void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
  1134. {
  1135. //TimeTaker timer1("Client::addNode()");
  1136. std::map<v3s16, MapBlock*> modified_blocks;
  1137. try {
  1138. //TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
  1139. m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
  1140. }
  1141. catch(InvalidPositionException &e) {
  1142. }
  1143. for(std::map<v3s16, MapBlock *>::iterator
  1144. i = modified_blocks.begin();
  1145. i != modified_blocks.end(); ++i) {
  1146. addUpdateMeshTaskWithEdge(i->first, false, true);
  1147. }
  1148. }
  1149. void Client::setPlayerControl(PlayerControl &control)
  1150. {
  1151. LocalPlayer *player = m_env.getLocalPlayer();
  1152. assert(player);
  1153. player->control = control;
  1154. }
  1155. void Client::selectPlayerItem(u16 item)
  1156. {
  1157. m_playeritem = item;
  1158. m_inventory_updated = true;
  1159. sendPlayerItem(item);
  1160. }
  1161. // Returns true if the inventory of the local player has been
  1162. // updated from the server. If it is true, it is set to false.
  1163. bool Client::getLocalInventoryUpdated()
  1164. {
  1165. bool updated = m_inventory_updated;
  1166. m_inventory_updated = false;
  1167. return updated;
  1168. }
  1169. // Copies the inventory of the local player to parameter
  1170. void Client::getLocalInventory(Inventory &dst)
  1171. {
  1172. LocalPlayer *player = m_env.getLocalPlayer();
  1173. assert(player);
  1174. dst = player->inventory;
  1175. }
  1176. Inventory* Client::getInventory(const InventoryLocation &loc)
  1177. {
  1178. switch(loc.type){
  1179. case InventoryLocation::UNDEFINED:
  1180. {}
  1181. break;
  1182. case InventoryLocation::CURRENT_PLAYER:
  1183. {
  1184. LocalPlayer *player = m_env.getLocalPlayer();
  1185. assert(player);
  1186. return &player->inventory;
  1187. }
  1188. break;
  1189. case InventoryLocation::PLAYER:
  1190. {
  1191. // Check if we are working with local player inventory
  1192. LocalPlayer *player = m_env.getLocalPlayer();
  1193. if (!player || strcmp(player->getName(), loc.name.c_str()) != 0)
  1194. return NULL;
  1195. return &player->inventory;
  1196. }
  1197. break;
  1198. case InventoryLocation::NODEMETA:
  1199. {
  1200. NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p);
  1201. if(!meta)
  1202. return NULL;
  1203. return meta->getInventory();
  1204. }
  1205. break;
  1206. case InventoryLocation::DETACHED:
  1207. {
  1208. if (m_detached_inventories.count(loc.name) == 0)
  1209. return NULL;
  1210. return m_detached_inventories[loc.name];
  1211. }
  1212. break;
  1213. default:
  1214. FATAL_ERROR("Invalid inventory location type.");
  1215. break;
  1216. }
  1217. return NULL;
  1218. }
  1219. void Client::inventoryAction(InventoryAction *a)
  1220. {
  1221. /*
  1222. Send it to the server
  1223. */
  1224. sendInventoryAction(a);
  1225. /*
  1226. Predict some local inventory changes
  1227. */
  1228. a->clientApply(this, this);
  1229. // Remove it
  1230. delete a;
  1231. }
  1232. float Client::getAnimationTime()
  1233. {
  1234. return m_animation_time;
  1235. }
  1236. int Client::getCrackLevel()
  1237. {
  1238. return m_crack_level;
  1239. }
  1240. v3s16 Client::getCrackPos()
  1241. {
  1242. return m_crack_pos;
  1243. }
  1244. void Client::setCrack(int level, v3s16 pos)
  1245. {
  1246. int old_crack_level = m_crack_level;
  1247. v3s16 old_crack_pos = m_crack_pos;
  1248. m_crack_level = level;
  1249. m_crack_pos = pos;
  1250. if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
  1251. {
  1252. // remove old crack
  1253. addUpdateMeshTaskForNode(old_crack_pos, false, true);
  1254. }
  1255. if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
  1256. {
  1257. // add new crack
  1258. addUpdateMeshTaskForNode(pos, false, true);
  1259. }
  1260. }
  1261. u16 Client::getHP()
  1262. {
  1263. LocalPlayer *player = m_env.getLocalPlayer();
  1264. assert(player);
  1265. return player->hp;
  1266. }
  1267. bool Client::getChatMessage(std::wstring &message)
  1268. {
  1269. if(m_chat_queue.size() == 0)
  1270. return false;
  1271. message = m_chat_queue.front();
  1272. m_chat_queue.pop();
  1273. return true;
  1274. }
  1275. void Client::typeChatMessage(const std::wstring &message)
  1276. {
  1277. // Discard empty line
  1278. if(message == L"")
  1279. return;
  1280. // If message was ate by script API, don't send it to server
  1281. if (m_script->on_sending_message(wide_to_utf8(message))) {
  1282. return;
  1283. }
  1284. // Send to others
  1285. sendChatMessage(message);
  1286. // Show locally
  1287. if (message[0] != L'/')
  1288. {
  1289. // compatibility code
  1290. if (m_proto_ver < 29) {
  1291. LocalPlayer *player = m_env.getLocalPlayer();
  1292. assert(player);
  1293. std::wstring name = narrow_to_wide(player->getName());
  1294. pushToChatQueue((std::wstring)L"<" + name + L"> " + message);
  1295. }
  1296. }
  1297. }
  1298. void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
  1299. {
  1300. // Check if the block exists to begin with. In the case when a non-existing
  1301. // neighbor is automatically added, it may not. In that case we don't want
  1302. // to tell the mesh update thread about it.
  1303. MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
  1304. if (b == NULL)
  1305. return;
  1306. m_mesh_update_thread.updateBlock(&m_env.getMap(), p, ack_to_server, urgent);
  1307. }
  1308. void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
  1309. {
  1310. try{
  1311. addUpdateMeshTask(blockpos, ack_to_server, urgent);
  1312. }
  1313. catch(InvalidPositionException &e){}
  1314. // Leading edge
  1315. for (int i=0;i<6;i++)
  1316. {
  1317. try{
  1318. v3s16 p = blockpos + g_6dirs[i];
  1319. addUpdateMeshTask(p, false, urgent);
  1320. }
  1321. catch(InvalidPositionException &e){}
  1322. }
  1323. }
  1324. void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
  1325. {
  1326. {
  1327. v3s16 p = nodepos;
  1328. infostream<<"Client::addUpdateMeshTaskForNode(): "
  1329. <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
  1330. <<std::endl;
  1331. }
  1332. v3s16 blockpos = getNodeBlockPos(nodepos);
  1333. v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
  1334. try{
  1335. addUpdateMeshTask(blockpos, ack_to_server, urgent);
  1336. }
  1337. catch(InvalidPositionException &e) {}
  1338. // Leading edge
  1339. if(nodepos.X == blockpos_relative.X){
  1340. try{
  1341. v3s16 p = blockpos + v3s16(-1,0,0);
  1342. addUpdateMeshTask(p, false, urgent);
  1343. }
  1344. catch(InvalidPositionException &e){}
  1345. }
  1346. if(nodepos.Y == blockpos_relative.Y){
  1347. try{
  1348. v3s16 p = blockpos + v3s16(0,-1,0);
  1349. addUpdateMeshTask(p, false, urgent);
  1350. }
  1351. catch(InvalidPositionException &e){}
  1352. }
  1353. if(nodepos.Z == blockpos_relative.Z){
  1354. try{
  1355. v3s16 p = blockpos + v3s16(0,0,-1);
  1356. addUpdateMeshTask(p, false, urgent);
  1357. }
  1358. catch(InvalidPositionException &e){}
  1359. }
  1360. }
  1361. ClientEvent Client::getClientEvent()
  1362. {
  1363. FATAL_ERROR_IF(m_client_event_queue.empty(),
  1364. "Cannot getClientEvent, queue is empty.");
  1365. ClientEvent event = m_client_event_queue.front();
  1366. m_client_event_queue.pop();
  1367. return event;
  1368. }
  1369. float Client::mediaReceiveProgress()
  1370. {
  1371. if (m_media_downloader)
  1372. return m_media_downloader->getProgress();
  1373. else
  1374. return 1.0; // downloader only exists when not yet done
  1375. }
  1376. typedef struct TextureUpdateArgs {
  1377. gui::IGUIEnvironment *guienv;
  1378. u64 last_time_ms;
  1379. u16 last_percent;
  1380. const wchar_t* text_base;
  1381. ITextureSource *tsrc;
  1382. } TextureUpdateArgs;
  1383. void texture_update_progress(void *args, u32 progress, u32 max_progress)
  1384. {
  1385. TextureUpdateArgs* targs = (TextureUpdateArgs*) args;
  1386. u16 cur_percent = ceil(progress / (double) max_progress * 100.);
  1387. // update the loading menu -- if neccessary
  1388. bool do_draw = false;
  1389. u64 time_ms = targs->last_time_ms;
  1390. if (cur_percent != targs->last_percent) {
  1391. targs->last_percent = cur_percent;
  1392. time_ms = porting::getTimeMs();
  1393. // only draw when the user will notice something:
  1394. do_draw = (time_ms - targs->last_time_ms > 100);
  1395. }
  1396. if (do_draw) {
  1397. targs->last_time_ms = time_ms;
  1398. std::basic_stringstream<wchar_t> strm;
  1399. strm << targs->text_base << " " << targs->last_percent << "%...";
  1400. RenderingEngine::draw_load_screen(strm.str(), targs->guienv, targs->tsrc, 0,
  1401. 72 + (u16) ((18. / 100.) * (double) targs->last_percent), true);
  1402. }
  1403. }
  1404. void Client::afterContentReceived()
  1405. {
  1406. infostream<<"Client::afterContentReceived() started"<<std::endl;
  1407. assert(m_itemdef_received); // pre-condition
  1408. assert(m_nodedef_received); // pre-condition
  1409. assert(mediaReceived()); // pre-condition
  1410. const wchar_t* text = wgettext("Loading textures...");
  1411. // Clear cached pre-scaled 2D GUI images, as this cache
  1412. // might have images with the same name but different
  1413. // content from previous sessions.
  1414. guiScalingCacheClear();
  1415. // Rebuild inherited images and recreate textures
  1416. infostream<<"- Rebuilding images and textures"<<std::endl;
  1417. RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 70);
  1418. m_tsrc->rebuildImagesAndTextures();
  1419. delete[] text;
  1420. // Rebuild shaders
  1421. infostream<<"- Rebuilding shaders"<<std::endl;
  1422. text = wgettext("Rebuilding shaders...");
  1423. RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 71);
  1424. m_shsrc->rebuildShaders();
  1425. delete[] text;
  1426. // Update node aliases
  1427. infostream<<"- Updating node aliases"<<std::endl;
  1428. text = wgettext("Initializing nodes...");
  1429. RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 72);
  1430. m_nodedef->updateAliases(m_itemdef);
  1431. std::string texture_path = g_settings->get("texture_path");
  1432. if (texture_path != "" && fs::IsDir(texture_path))
  1433. m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
  1434. m_nodedef->setNodeRegistrationStatus(true);
  1435. m_nodedef->runNodeResolveCallbacks();
  1436. delete[] text;
  1437. // Update node textures and assign shaders to each tile
  1438. infostream<<"- Updating node textures"<<std::endl;
  1439. TextureUpdateArgs tu_args;
  1440. tu_args.guienv = guienv;
  1441. tu_args.last_time_ms = porting::getTimeMs();
  1442. tu_args.last_percent = 0;
  1443. tu_args.text_base = wgettext("Initializing nodes");
  1444. tu_args.tsrc = m_tsrc;
  1445. m_nodedef->updateTextures(this, texture_update_progress, &tu_args);
  1446. delete[] tu_args.text_base;
  1447. // Start mesh update thread after setting up content definitions
  1448. infostream<<"- Starting mesh update thread"<<std::endl;
  1449. m_mesh_update_thread.start();
  1450. m_state = LC_Ready;
  1451. sendReady();
  1452. if (g_settings->getBool("enable_client_modding")) {
  1453. m_script->on_client_ready(m_env.getLocalPlayer());
  1454. m_script->on_connect();
  1455. }
  1456. text = wgettext("Done!");
  1457. RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 100);
  1458. infostream<<"Client::afterContentReceived() done"<<std::endl;
  1459. delete[] text;
  1460. }
  1461. float Client::getRTT()
  1462. {
  1463. return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT);
  1464. }
  1465. float Client::getCurRate()
  1466. {
  1467. return (m_con.getLocalStat(con::CUR_INC_RATE) +
  1468. m_con.getLocalStat(con::CUR_DL_RATE));
  1469. }
  1470. void Client::makeScreenshot()
  1471. {
  1472. irr::video::IVideoDriver *driver = RenderingEngine::get_video_driver();
  1473. irr::video::IImage* const raw_image = driver->createScreenShot();
  1474. if (!raw_image)
  1475. return;
  1476. time_t t = time(NULL);
  1477. struct tm *tm = localtime(&t);
  1478. char timetstamp_c[64];
  1479. strftime(timetstamp_c, sizeof(timetstamp_c), "%Y%m%d_%H%M%S", tm);
  1480. std::string filename_base = g_settings->get("screenshot_path")
  1481. + DIR_DELIM
  1482. + std::string("screenshot_")
  1483. + std::string(timetstamp_c);
  1484. std::string filename_ext = "." + g_settings->get("screenshot_format");
  1485. std::string filename;
  1486. u32 quality = (u32)g_settings->getS32("screenshot_quality");
  1487. quality = MYMIN(MYMAX(quality, 0), 100) / 100.0 * 255;
  1488. // Try to find a unique filename
  1489. unsigned serial = 0;
  1490. while (serial < SCREENSHOT_MAX_SERIAL_TRIES) {
  1491. filename = filename_base + (serial > 0 ? ("_" + itos(serial)) : "") + filename_ext;
  1492. std::ifstream tmp(filename.c_str());
  1493. if (!tmp.good())
  1494. break; // File did not apparently exist, we'll go with it
  1495. serial++;
  1496. }
  1497. if (serial == SCREENSHOT_MAX_SERIAL_TRIES) {
  1498. infostream << "Could not find suitable filename for screenshot" << std::endl;
  1499. } else {
  1500. irr::video::IImage* const image =
  1501. driver->createImage(video::ECF_R8G8B8, raw_image->getDimension());
  1502. if (image) {
  1503. raw_image->copyTo(image);
  1504. std::ostringstream sstr;
  1505. if (driver->writeImageToFile(image, filename.c_str(), quality)) {
  1506. sstr << "Saved screenshot to '" << filename << "'";
  1507. } else {
  1508. sstr << "Failed to save screenshot '" << filename << "'";
  1509. }
  1510. pushToChatQueue(narrow_to_wide(sstr.str()));
  1511. infostream << sstr.str() << std::endl;
  1512. image->drop();
  1513. }
  1514. }
  1515. raw_image->drop();
  1516. }
  1517. bool Client::shouldShowMinimap() const
  1518. {
  1519. return !m_minimap_disabled_by_server;
  1520. }
  1521. void Client::showGameChat(const bool show)
  1522. {
  1523. m_game_ui_flags->show_chat = show;
  1524. }
  1525. void Client::showGameHud(const bool show)
  1526. {
  1527. m_game_ui_flags->show_hud = show;
  1528. }
  1529. void Client::showMinimap(const bool show)
  1530. {
  1531. m_game_ui_flags->show_minimap = show;
  1532. }
  1533. void Client::showProfiler(const bool show)
  1534. {
  1535. m_game_ui_flags->show_profiler_graph = show;
  1536. }
  1537. void Client::showGameFog(const bool show)
  1538. {
  1539. m_game_ui_flags->force_fog_off = !show;
  1540. }
  1541. void Client::showGameDebug(const bool show)
  1542. {
  1543. m_game_ui_flags->show_debug = show;
  1544. }
  1545. // IGameDef interface
  1546. // Under envlock
  1547. IItemDefManager* Client::getItemDefManager()
  1548. {
  1549. return m_itemdef;
  1550. }
  1551. INodeDefManager* Client::getNodeDefManager()
  1552. {
  1553. return m_nodedef;
  1554. }
  1555. ICraftDefManager* Client::getCraftDefManager()
  1556. {
  1557. return NULL;
  1558. //return m_craftdef;
  1559. }
  1560. ITextureSource* Client::getTextureSource()
  1561. {
  1562. return m_tsrc;
  1563. }
  1564. IShaderSource* Client::getShaderSource()
  1565. {
  1566. return m_shsrc;
  1567. }
  1568. u16 Client::allocateUnknownNodeId(const std::string &name)
  1569. {
  1570. errorstream << "Client::allocateUnknownNodeId(): "
  1571. << "Client cannot allocate node IDs" << std::endl;
  1572. FATAL_ERROR("Client allocated unknown node");
  1573. return CONTENT_IGNORE;
  1574. }
  1575. ISoundManager* Client::getSoundManager()
  1576. {
  1577. return m_sound;
  1578. }
  1579. MtEventManager* Client::getEventManager()
  1580. {
  1581. return m_event;
  1582. }
  1583. ParticleManager* Client::getParticleManager()
  1584. {
  1585. return &m_particle_manager;
  1586. }
  1587. scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
  1588. {
  1589. StringMap::const_iterator it = m_mesh_data.find(filename);
  1590. if (it == m_mesh_data.end()) {
  1591. errorstream << "Client::getMesh(): Mesh not found: \"" << filename
  1592. << "\"" << std::endl;
  1593. return NULL;
  1594. }
  1595. const std::string &data = it->second;
  1596. // Create the mesh, remove it from cache and return it
  1597. // This allows unique vertex colors and other properties for each instance
  1598. Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
  1599. io::IReadFile *rfile = RenderingEngine::get_filesystem()->createMemoryReadFile(
  1600. *data_rw, data_rw.getSize(), filename.c_str());
  1601. FATAL_ERROR_IF(!rfile, "Could not create/open RAM file");
  1602. scene::IAnimatedMesh *mesh = RenderingEngine::get_scene_manager()->getMesh(rfile);
  1603. rfile->drop();
  1604. // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
  1605. // of uniquely named instances and re-use them
  1606. mesh->grab();
  1607. RenderingEngine::get_mesh_cache()->removeMesh(mesh);
  1608. return mesh;
  1609. }
  1610. const std::string* Client::getModFile(const std::string &filename)
  1611. {
  1612. StringMap::const_iterator it = m_mod_files.find(filename);
  1613. if (it == m_mod_files.end()) {
  1614. errorstream << "Client::getModFile(): File not found: \"" << filename
  1615. << "\"" << std::endl;
  1616. return NULL;
  1617. }
  1618. return &it->second;
  1619. }
  1620. bool Client::registerModStorage(ModMetadata *storage)
  1621. {
  1622. if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {
  1623. errorstream << "Unable to register same mod storage twice. Storage name: "
  1624. << storage->getModName() << std::endl;
  1625. return false;
  1626. }
  1627. m_mod_storages[storage->getModName()] = storage;
  1628. return true;
  1629. }
  1630. void Client::unregisterModStorage(const std::string &name)
  1631. {
  1632. std::unordered_map<std::string, ModMetadata *>::const_iterator it =
  1633. m_mod_storages.find(name);
  1634. if (it != m_mod_storages.end()) {
  1635. // Save unconditionaly on unregistration
  1636. it->second->save(getModStoragePath());
  1637. m_mod_storages.erase(name);
  1638. }
  1639. }
  1640. std::string Client::getModStoragePath() const
  1641. {
  1642. return porting::path_user + DIR_DELIM + "client" + DIR_DELIM + "mod_storage";
  1643. }