clientpackethandler.cpp 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328
  1. /*
  2. Minetest
  3. Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
  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 "client.h"
  17. #include "util/base64.h"
  18. #include "clientmedia.h"
  19. #include "log.h"
  20. #include "map.h"
  21. #include "mapsector.h"
  22. #include "minimap.h"
  23. #include "nodedef.h"
  24. #include "serialization.h"
  25. #include "server.h"
  26. #include "util/strfnd.h"
  27. #include "network/clientopcodes.h"
  28. #include "script/scripting_client.h"
  29. #include "util/serialize.h"
  30. #include "util/srp.h"
  31. #include "tileanimation.h"
  32. void Client::handleCommand_Deprecated(NetworkPacket* pkt)
  33. {
  34. infostream << "Got deprecated command "
  35. << toClientCommandTable[pkt->getCommand()].name << " from peer "
  36. << pkt->getPeerId() << "!" << std::endl;
  37. }
  38. void Client::handleCommand_Hello(NetworkPacket* pkt)
  39. {
  40. if (pkt->getSize() < 1)
  41. return;
  42. u8 serialization_ver;
  43. u16 proto_ver;
  44. u16 compression_mode;
  45. u32 auth_mechs;
  46. std::string username_legacy; // for case insensitivity
  47. *pkt >> serialization_ver >> compression_mode >> proto_ver
  48. >> auth_mechs >> username_legacy;
  49. // Chose an auth method we support
  50. AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
  51. infostream << "Client: TOCLIENT_HELLO received with "
  52. << "serialization_ver=" << (u32)serialization_ver
  53. << ", auth_mechs=" << auth_mechs
  54. << ", proto_ver=" << proto_ver
  55. << ", compression_mode=" << compression_mode
  56. << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
  57. if (!ser_ver_supported(serialization_ver)) {
  58. infostream << "Client: TOCLIENT_HELLO: Server sent "
  59. << "unsupported ser_fmt_ver"<< std::endl;
  60. return;
  61. }
  62. m_server_ser_ver = serialization_ver;
  63. m_proto_ver = proto_ver;
  64. //TODO verify that username_legacy matches sent username, only
  65. // differs in casing (make both uppercase and compare)
  66. // This is only neccessary though when we actually want to add casing support
  67. if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
  68. // we recieved a TOCLIENT_HELLO while auth was already going on
  69. errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
  70. << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
  71. if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP)
  72. || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) {
  73. srp_user_delete((SRPUser *) m_auth_data);
  74. m_auth_data = 0;
  75. }
  76. }
  77. // Authenticate using that method, or abort if there wasn't any method found
  78. if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
  79. startAuth(chosen_auth_mechanism);
  80. } else {
  81. m_chosen_auth_mech = AUTH_MECHANISM_NONE;
  82. m_access_denied = true;
  83. m_access_denied_reason = "Unknown";
  84. m_con.Disconnect();
  85. }
  86. }
  87. void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
  88. {
  89. deleteAuthData();
  90. v3f playerpos;
  91. *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
  92. >> m_sudo_auth_methods;
  93. playerpos -= v3f(0, BS / 2, 0);
  94. // Set player position
  95. LocalPlayer *player = m_env.getLocalPlayer();
  96. assert(player != NULL);
  97. player->setPosition(playerpos);
  98. infostream << "Client: received map seed: " << m_map_seed << std::endl;
  99. infostream << "Client: received recommended send interval "
  100. << m_recommended_send_interval<<std::endl;
  101. // Reply to server
  102. NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
  103. Send(&resp_pkt);
  104. m_state = LC_Init;
  105. }
  106. void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
  107. {
  108. deleteAuthData();
  109. m_password = m_new_password;
  110. verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
  111. // send packet to actually set the password
  112. startAuth(AUTH_MECHANISM_FIRST_SRP);
  113. // reset again
  114. m_chosen_auth_mech = AUTH_MECHANISM_NONE;
  115. }
  116. void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
  117. {
  118. pushToChatQueue(L"Password change denied. Password NOT changed.");
  119. // reset everything and be sad
  120. deleteAuthData();
  121. }
  122. void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
  123. {
  124. if (pkt->getSize() < 1)
  125. return;
  126. u8 server_ser_ver;
  127. *pkt >> server_ser_ver;
  128. infostream << "Client: TOCLIENT_INIT_LEGACY received with "
  129. "server_ser_ver=" << ((int)server_ser_ver & 0xff) << std::endl;
  130. if (!ser_ver_supported(server_ser_ver)) {
  131. infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
  132. << "unsupported ser_fmt_ver"<< std::endl;
  133. return;
  134. }
  135. m_server_ser_ver = server_ser_ver;
  136. // We can be totally wrong with this guess
  137. // but we only need some value < 25.
  138. m_proto_ver = 24;
  139. // Get player position
  140. v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
  141. if (pkt->getSize() >= 1 + 6) {
  142. *pkt >> playerpos_s16;
  143. }
  144. v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
  145. // Set player position
  146. LocalPlayer *player = m_env.getLocalPlayer();
  147. assert(player != NULL);
  148. player->setPosition(playerpos_f);
  149. if (pkt->getSize() >= 1 + 6 + 8) {
  150. // Get map seed
  151. *pkt >> m_map_seed;
  152. infostream << "Client: received map seed: " << m_map_seed << std::endl;
  153. }
  154. if (pkt->getSize() >= 1 + 6 + 8 + 4) {
  155. *pkt >> m_recommended_send_interval;
  156. infostream << "Client: received recommended send interval "
  157. << m_recommended_send_interval<<std::endl;
  158. }
  159. // Reply to server
  160. NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
  161. Send(&resp_pkt);
  162. m_state = LC_Init;
  163. }
  164. void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
  165. {
  166. // The server didn't like our password. Note, this needs
  167. // to be processed even if the serialisation format has
  168. // not been agreed yet, the same as TOCLIENT_INIT.
  169. m_access_denied = true;
  170. m_access_denied_reason = "Unknown";
  171. if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
  172. if (pkt->getSize() < 1)
  173. return;
  174. u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
  175. *pkt >> denyCode;
  176. if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
  177. denyCode == SERVER_ACCESSDENIED_CRASH) {
  178. *pkt >> m_access_denied_reason;
  179. if (m_access_denied_reason == "") {
  180. m_access_denied_reason = accessDeniedStrings[denyCode];
  181. }
  182. u8 reconnect;
  183. *pkt >> reconnect;
  184. m_access_denied_reconnect = reconnect & 1;
  185. } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
  186. *pkt >> m_access_denied_reason;
  187. } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
  188. m_access_denied_reason = accessDeniedStrings[denyCode];
  189. } else {
  190. // Allow us to add new error messages to the
  191. // protocol without raising the protocol version, if we want to.
  192. // Until then (which may be never), this is outside
  193. // of the defined protocol.
  194. *pkt >> m_access_denied_reason;
  195. if (m_access_denied_reason == "") {
  196. m_access_denied_reason = "Unknown";
  197. }
  198. }
  199. }
  200. // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
  201. // for compat with old clients
  202. else {
  203. if (pkt->getSize() >= 2) {
  204. std::wstring wide_reason;
  205. *pkt >> wide_reason;
  206. m_access_denied_reason = wide_to_utf8(wide_reason);
  207. }
  208. }
  209. }
  210. void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
  211. {
  212. if (pkt->getSize() < 6)
  213. return;
  214. v3s16 p;
  215. *pkt >> p;
  216. removeNode(p);
  217. }
  218. void Client::handleCommand_AddNode(NetworkPacket* pkt)
  219. {
  220. if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
  221. return;
  222. v3s16 p;
  223. *pkt >> p;
  224. MapNode n;
  225. n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
  226. bool remove_metadata = true;
  227. u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
  228. if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
  229. remove_metadata = false;
  230. }
  231. addNode(p, n, remove_metadata);
  232. }
  233. void Client::handleCommand_BlockData(NetworkPacket* pkt)
  234. {
  235. // Ignore too small packet
  236. if (pkt->getSize() < 6)
  237. return;
  238. v3s16 p;
  239. *pkt >> p;
  240. std::string datastring(pkt->getString(6), pkt->getSize() - 6);
  241. std::istringstream istr(datastring, std::ios_base::binary);
  242. MapSector *sector;
  243. MapBlock *block;
  244. v2s16 p2d(p.X, p.Z);
  245. sector = m_env.getMap().emergeSector(p2d);
  246. assert(sector->getPos() == p2d);
  247. block = sector->getBlockNoCreateNoEx(p.Y);
  248. if (block) {
  249. /*
  250. Update an existing block
  251. */
  252. block->deSerialize(istr, m_server_ser_ver, false);
  253. block->deSerializeNetworkSpecific(istr);
  254. }
  255. else {
  256. /*
  257. Create a new block
  258. */
  259. block = new MapBlock(&m_env.getMap(), p, this);
  260. block->deSerialize(istr, m_server_ser_ver, false);
  261. block->deSerializeNetworkSpecific(istr);
  262. sector->insertBlock(block);
  263. }
  264. if (m_localdb) {
  265. ServerMap::saveBlock(block, m_localdb);
  266. }
  267. /*
  268. Add it to mesh update queue and set it to be acknowledged after update.
  269. */
  270. addUpdateMeshTaskWithEdge(p, true);
  271. }
  272. void Client::handleCommand_Inventory(NetworkPacket* pkt)
  273. {
  274. if (pkt->getSize() < 1)
  275. return;
  276. std::string datastring(pkt->getString(0), pkt->getSize());
  277. std::istringstream is(datastring, std::ios_base::binary);
  278. LocalPlayer *player = m_env.getLocalPlayer();
  279. assert(player != NULL);
  280. player->inventory.deSerialize(is);
  281. m_inventory_updated = true;
  282. delete m_inventory_from_server;
  283. m_inventory_from_server = new Inventory(player->inventory);
  284. m_inventory_from_server_age = 0.0;
  285. }
  286. void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
  287. {
  288. if (pkt->getSize() < 2)
  289. return;
  290. u16 time_of_day;
  291. *pkt >> time_of_day;
  292. time_of_day = time_of_day % 24000;
  293. float time_speed = 0;
  294. if (pkt->getSize() >= 2 + 4) {
  295. *pkt >> time_speed;
  296. }
  297. else {
  298. // Old message; try to approximate speed of time by ourselves
  299. float time_of_day_f = (float)time_of_day / 24000.0;
  300. float tod_diff_f = 0;
  301. if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
  302. tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
  303. else
  304. tod_diff_f = time_of_day_f - m_last_time_of_day_f;
  305. m_last_time_of_day_f = time_of_day_f;
  306. float time_diff = m_time_of_day_update_timer;
  307. m_time_of_day_update_timer = 0;
  308. if (m_time_of_day_set) {
  309. time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
  310. infostream << "Client: Measured time_of_day speed (old format): "
  311. << time_speed << " tod_diff_f=" << tod_diff_f
  312. << " time_diff=" << time_diff << std::endl;
  313. }
  314. }
  315. // Update environment
  316. m_env.setTimeOfDay(time_of_day);
  317. m_env.setTimeOfDaySpeed(time_speed);
  318. m_time_of_day_set = true;
  319. u32 dr = m_env.getDayNightRatio();
  320. infostream << "Client: time_of_day=" << time_of_day
  321. << " time_speed=" << time_speed
  322. << " dr=" << dr << std::endl;
  323. }
  324. void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
  325. {
  326. /*
  327. u16 command
  328. u16 length
  329. wstring message
  330. */
  331. u16 len, read_wchar;
  332. *pkt >> len;
  333. std::wstring message;
  334. for (u32 i = 0; i < len; i++) {
  335. *pkt >> read_wchar;
  336. message += (wchar_t)read_wchar;
  337. }
  338. // If chat message not consummed by client lua API
  339. if (!moddingEnabled() || !m_script->on_receiving_message(wide_to_utf8(message))) {
  340. pushToChatQueue(message);
  341. }
  342. }
  343. void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
  344. {
  345. /*
  346. u16 count of removed objects
  347. for all removed objects {
  348. u16 id
  349. }
  350. u16 count of added objects
  351. for all added objects {
  352. u16 id
  353. u8 type
  354. u32 initialization data length
  355. string initialization data
  356. }
  357. */
  358. try {
  359. u8 type;
  360. u16 removed_count, added_count, id;
  361. // Read removed objects
  362. *pkt >> removed_count;
  363. for (u16 i = 0; i < removed_count; i++) {
  364. *pkt >> id;
  365. m_env.removeActiveObject(id);
  366. }
  367. // Read added objects
  368. *pkt >> added_count;
  369. for (u16 i = 0; i < added_count; i++) {
  370. *pkt >> id >> type;
  371. m_env.addActiveObject(id, type, pkt->readLongString());
  372. }
  373. } catch (PacketError &e) {
  374. infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
  375. << ". The packet is unreliable, ignoring" << std::endl;
  376. }
  377. }
  378. void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
  379. {
  380. /*
  381. for all objects
  382. {
  383. u16 id
  384. u16 message length
  385. string message
  386. }
  387. */
  388. std::string datastring(pkt->getString(0), pkt->getSize());
  389. std::istringstream is(datastring, std::ios_base::binary);
  390. try {
  391. while (is.good()) {
  392. u16 id = readU16(is);
  393. if (!is.good())
  394. break;
  395. std::string message = deSerializeString(is);
  396. // Pass on to the environment
  397. m_env.processActiveObjectMessage(id, message);
  398. }
  399. } catch (SerializationError &e) {
  400. errorstream << "Client::handleCommand_ActiveObjectMessages: "
  401. << "caught SerializationError: " << e.what() << std::endl;
  402. }
  403. }
  404. void Client::handleCommand_Movement(NetworkPacket* pkt)
  405. {
  406. LocalPlayer *player = m_env.getLocalPlayer();
  407. assert(player != NULL);
  408. float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
  409. *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
  410. >> lf >> lfs >> ls >> g;
  411. player->movement_acceleration_default = mad * BS;
  412. player->movement_acceleration_air = maa * BS;
  413. player->movement_acceleration_fast = maf * BS;
  414. player->movement_speed_walk = msw * BS;
  415. player->movement_speed_crouch = mscr * BS;
  416. player->movement_speed_fast = msf * BS;
  417. player->movement_speed_climb = mscl * BS;
  418. player->movement_speed_jump = msj * BS;
  419. player->movement_liquid_fluidity = lf * BS;
  420. player->movement_liquid_fluidity_smooth = lfs * BS;
  421. player->movement_liquid_sink = ls * BS;
  422. player->movement_gravity = g * BS;
  423. }
  424. void Client::handleCommand_HP(NetworkPacket* pkt)
  425. {
  426. LocalPlayer *player = m_env.getLocalPlayer();
  427. assert(player != NULL);
  428. u8 oldhp = player->hp;
  429. u8 hp;
  430. *pkt >> hp;
  431. player->hp = hp;
  432. if (moddingEnabled()) {
  433. m_script->on_hp_modification(hp);
  434. }
  435. if (hp < oldhp) {
  436. // Add to ClientEvent queue
  437. ClientEvent event;
  438. event.type = CE_PLAYER_DAMAGE;
  439. event.player_damage.amount = oldhp - hp;
  440. m_client_event_queue.push(event);
  441. }
  442. }
  443. void Client::handleCommand_Breath(NetworkPacket* pkt)
  444. {
  445. LocalPlayer *player = m_env.getLocalPlayer();
  446. assert(player != NULL);
  447. u16 breath;
  448. *pkt >> breath;
  449. player->setBreath(breath);
  450. }
  451. void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
  452. {
  453. LocalPlayer *player = m_env.getLocalPlayer();
  454. assert(player != NULL);
  455. v3f pos;
  456. f32 pitch, yaw;
  457. *pkt >> pos >> pitch >> yaw;
  458. player->setPosition(pos);
  459. infostream << "Client got TOCLIENT_MOVE_PLAYER"
  460. << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
  461. << " pitch=" << pitch
  462. << " yaw=" << yaw
  463. << std::endl;
  464. /*
  465. Add to ClientEvent queue.
  466. This has to be sent to the main program because otherwise
  467. it would just force the pitch and yaw values to whatever
  468. the camera points to.
  469. */
  470. ClientEvent event;
  471. event.type = CE_PLAYER_FORCE_MOVE;
  472. event.player_force_move.pitch = pitch;
  473. event.player_force_move.yaw = yaw;
  474. m_client_event_queue.push(event);
  475. // Ignore damage for a few seconds, so that the player doesn't
  476. // get damage from falling on ground
  477. m_ignore_damage_timer = 3.0;
  478. }
  479. void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
  480. {
  481. bool set_camera_point_target;
  482. v3f camera_point_target;
  483. *pkt >> set_camera_point_target;
  484. *pkt >> camera_point_target;
  485. ClientEvent event;
  486. event.type = CE_DEATHSCREEN;
  487. event.deathscreen.set_camera_point_target = set_camera_point_target;
  488. event.deathscreen.camera_point_target_x = camera_point_target.X;
  489. event.deathscreen.camera_point_target_y = camera_point_target.Y;
  490. event.deathscreen.camera_point_target_z = camera_point_target.Z;
  491. m_client_event_queue.push(event);
  492. }
  493. void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
  494. {
  495. u16 num_files;
  496. *pkt >> num_files;
  497. infostream << "Client: Received media announcement: packet size: "
  498. << pkt->getSize() << std::endl;
  499. if (m_media_downloader == NULL ||
  500. m_media_downloader->isStarted()) {
  501. const char *problem = m_media_downloader ?
  502. "we already saw another announcement" :
  503. "all media has been received already";
  504. errorstream << "Client: Received media announcement but "
  505. << problem << "! "
  506. << " files=" << num_files
  507. << " size=" << pkt->getSize() << std::endl;
  508. return;
  509. }
  510. // Mesh update thread must be stopped while
  511. // updating content definitions
  512. sanity_check(!m_mesh_update_thread.isRunning());
  513. for (u16 i = 0; i < num_files; i++) {
  514. std::string name, sha1_base64;
  515. *pkt >> name >> sha1_base64;
  516. std::string sha1_raw = base64_decode(sha1_base64);
  517. m_media_downloader->addFile(name, sha1_raw);
  518. }
  519. try {
  520. std::string str;
  521. *pkt >> str;
  522. Strfnd sf(str);
  523. while(!sf.at_end()) {
  524. std::string baseurl = trim(sf.next(","));
  525. if (baseurl != "")
  526. m_media_downloader->addRemoteServer(baseurl);
  527. }
  528. }
  529. catch(SerializationError& e) {
  530. // not supported by server or turned off
  531. }
  532. m_media_downloader->step(this);
  533. }
  534. void Client::handleCommand_Media(NetworkPacket* pkt)
  535. {
  536. /*
  537. u16 command
  538. u16 total number of file bunches
  539. u16 index of this bunch
  540. u32 number of files in this bunch
  541. for each file {
  542. u16 length of name
  543. string name
  544. u32 length of data
  545. data
  546. }
  547. */
  548. u16 num_bunches;
  549. u16 bunch_i;
  550. u32 num_files;
  551. *pkt >> num_bunches >> bunch_i >> num_files;
  552. infostream << "Client: Received files: bunch " << bunch_i << "/"
  553. << num_bunches << " files=" << num_files
  554. << " size=" << pkt->getSize() << std::endl;
  555. if (num_files == 0)
  556. return;
  557. if (!m_media_downloader || !m_media_downloader->isStarted()) {
  558. const char *problem = m_media_downloader ?
  559. "media has not been requested" :
  560. "all media has been received already";
  561. errorstream << "Client: Received media but "
  562. << problem << "! "
  563. << " bunch " << bunch_i << "/" << num_bunches
  564. << " files=" << num_files
  565. << " size=" << pkt->getSize() << std::endl;
  566. return;
  567. }
  568. // Mesh update thread must be stopped while
  569. // updating content definitions
  570. sanity_check(!m_mesh_update_thread.isRunning());
  571. for (u32 i=0; i < num_files; i++) {
  572. std::string name;
  573. *pkt >> name;
  574. std::string data = pkt->readLongString();
  575. m_media_downloader->conventionalTransferDone(
  576. name, data, this);
  577. }
  578. }
  579. void Client::handleCommand_NodeDef(NetworkPacket* pkt)
  580. {
  581. infostream << "Client: Received node definitions: packet size: "
  582. << pkt->getSize() << std::endl;
  583. // Mesh update thread must be stopped while
  584. // updating content definitions
  585. sanity_check(!m_mesh_update_thread.isRunning());
  586. // Decompress node definitions
  587. std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
  588. std::ostringstream tmp_os;
  589. decompressZlib(tmp_is, tmp_os);
  590. // Deserialize node definitions
  591. std::istringstream tmp_is2(tmp_os.str());
  592. m_nodedef->deSerialize(tmp_is2);
  593. m_nodedef_received = true;
  594. }
  595. void Client::handleCommand_ItemDef(NetworkPacket* pkt)
  596. {
  597. infostream << "Client: Received item definitions: packet size: "
  598. << pkt->getSize() << std::endl;
  599. // Mesh update thread must be stopped while
  600. // updating content definitions
  601. sanity_check(!m_mesh_update_thread.isRunning());
  602. // Decompress item definitions
  603. std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
  604. std::ostringstream tmp_os;
  605. decompressZlib(tmp_is, tmp_os);
  606. // Deserialize node definitions
  607. std::istringstream tmp_is2(tmp_os.str());
  608. m_itemdef->deSerialize(tmp_is2);
  609. m_itemdef_received = true;
  610. }
  611. void Client::handleCommand_PlaySound(NetworkPacket* pkt)
  612. {
  613. /*
  614. [0] u32 server_id
  615. [4] u16 name length
  616. [6] char name[len]
  617. [ 6 + len] f32 gain
  618. [10 + len] u8 type
  619. [11 + len] (f32 * 3) pos
  620. [23 + len] u16 object_id
  621. [25 + len] bool loop
  622. [26 + len] f32 fade
  623. [30 + len] f32 pitch
  624. */
  625. s32 server_id;
  626. std::string name;
  627. float gain;
  628. u8 type; // 0=local, 1=positional, 2=object
  629. v3f pos;
  630. u16 object_id;
  631. bool loop;
  632. float fade = 0.0f;
  633. float pitch = 1.0f;
  634. *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
  635. try {
  636. *pkt >> fade;
  637. *pkt >> pitch;
  638. } catch (PacketError &e) {};
  639. // Start playing
  640. int client_id = -1;
  641. switch(type) {
  642. case 0: // local
  643. client_id = m_sound->playSound(name, loop, gain, fade, pitch);
  644. break;
  645. case 1: // positional
  646. client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
  647. break;
  648. case 2:
  649. { // object
  650. ClientActiveObject *cao = m_env.getActiveObject(object_id);
  651. if (cao)
  652. pos = cao->getPosition();
  653. client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
  654. // TODO: Set up sound to move with object
  655. break;
  656. }
  657. default:
  658. break;
  659. }
  660. if (client_id != -1) {
  661. m_sounds_server_to_client[server_id] = client_id;
  662. m_sounds_client_to_server[client_id] = server_id;
  663. if (object_id != 0)
  664. m_sounds_to_objects[client_id] = object_id;
  665. }
  666. }
  667. void Client::handleCommand_StopSound(NetworkPacket* pkt)
  668. {
  669. s32 server_id;
  670. *pkt >> server_id;
  671. std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
  672. if (i != m_sounds_server_to_client.end()) {
  673. int client_id = i->second;
  674. m_sound->stopSound(client_id);
  675. }
  676. }
  677. void Client::handleCommand_FadeSound(NetworkPacket *pkt)
  678. {
  679. s32 sound_id;
  680. float step;
  681. float gain;
  682. *pkt >> sound_id >> step >> gain;
  683. std::unordered_map<s32, int>::const_iterator i =
  684. m_sounds_server_to_client.find(sound_id);
  685. if (i != m_sounds_server_to_client.end())
  686. m_sound->fadeSound(i->second, step, gain);
  687. }
  688. void Client::handleCommand_Privileges(NetworkPacket* pkt)
  689. {
  690. m_privileges.clear();
  691. infostream << "Client: Privileges updated: ";
  692. u16 num_privileges;
  693. *pkt >> num_privileges;
  694. for (u16 i = 0; i < num_privileges; i++) {
  695. std::string priv;
  696. *pkt >> priv;
  697. m_privileges.insert(priv);
  698. infostream << priv << " ";
  699. }
  700. infostream << std::endl;
  701. }
  702. void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
  703. {
  704. LocalPlayer *player = m_env.getLocalPlayer();
  705. assert(player != NULL);
  706. // Store formspec in LocalPlayer
  707. player->inventory_formspec = pkt->readLongString();
  708. }
  709. void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
  710. {
  711. std::string datastring(pkt->getString(0), pkt->getSize());
  712. std::istringstream is(datastring, std::ios_base::binary);
  713. std::string name = deSerializeString(is);
  714. infostream << "Client: Detached inventory update: \"" << name
  715. << "\"" << std::endl;
  716. Inventory *inv = NULL;
  717. if (m_detached_inventories.count(name) > 0)
  718. inv = m_detached_inventories[name];
  719. else {
  720. inv = new Inventory(m_itemdef);
  721. m_detached_inventories[name] = inv;
  722. }
  723. inv->deSerialize(is);
  724. }
  725. void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
  726. {
  727. std::string formspec = pkt->readLongString();
  728. std::string formname;
  729. *pkt >> formname;
  730. ClientEvent event;
  731. event.type = CE_SHOW_FORMSPEC;
  732. // pointer is required as event is a struct only!
  733. // adding a std:string to a struct isn't possible
  734. event.show_formspec.formspec = new std::string(formspec);
  735. event.show_formspec.formname = new std::string(formname);
  736. m_client_event_queue.push(event);
  737. }
  738. void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
  739. {
  740. std::string datastring(pkt->getString(0), pkt->getSize());
  741. std::istringstream is(datastring, std::ios_base::binary);
  742. v3f pos = readV3F1000(is);
  743. v3f vel = readV3F1000(is);
  744. v3f acc = readV3F1000(is);
  745. float expirationtime = readF1000(is);
  746. float size = readF1000(is);
  747. bool collisiondetection = readU8(is);
  748. std::string texture = deSerializeLongString(is);
  749. bool vertical = false;
  750. bool collision_removal = false;
  751. struct TileAnimationParams animation;
  752. animation.type = TAT_NONE;
  753. u8 glow = 0;
  754. try {
  755. vertical = readU8(is);
  756. collision_removal = readU8(is);
  757. animation.deSerialize(is, m_proto_ver);
  758. glow = readU8(is);
  759. } catch (...) {}
  760. ClientEvent event;
  761. event.type = CE_SPAWN_PARTICLE;
  762. event.spawn_particle.pos = new v3f (pos);
  763. event.spawn_particle.vel = new v3f (vel);
  764. event.spawn_particle.acc = new v3f (acc);
  765. event.spawn_particle.expirationtime = expirationtime;
  766. event.spawn_particle.size = size;
  767. event.spawn_particle.collisiondetection = collisiondetection;
  768. event.spawn_particle.collision_removal = collision_removal;
  769. event.spawn_particle.vertical = vertical;
  770. event.spawn_particle.texture = new std::string(texture);
  771. event.spawn_particle.animation = animation;
  772. event.spawn_particle.glow = glow;
  773. m_client_event_queue.push(event);
  774. }
  775. void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
  776. {
  777. u16 amount;
  778. float spawntime;
  779. v3f minpos;
  780. v3f maxpos;
  781. v3f minvel;
  782. v3f maxvel;
  783. v3f minacc;
  784. v3f maxacc;
  785. float minexptime;
  786. float maxexptime;
  787. float minsize;
  788. float maxsize;
  789. bool collisiondetection;
  790. u32 id;
  791. *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
  792. >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
  793. >> maxsize >> collisiondetection;
  794. std::string texture = pkt->readLongString();
  795. *pkt >> id;
  796. bool vertical = false;
  797. bool collision_removal = false;
  798. struct TileAnimationParams animation;
  799. animation.type = TAT_NONE;
  800. u8 glow = 0;
  801. u16 attached_id = 0;
  802. try {
  803. *pkt >> vertical;
  804. *pkt >> collision_removal;
  805. *pkt >> attached_id;
  806. // This is horrible but required (why are there two ways to deserialize pkts?)
  807. std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
  808. std::istringstream is(datastring, std::ios_base::binary);
  809. animation.deSerialize(is, m_proto_ver);
  810. glow = readU8(is);
  811. } catch (...) {}
  812. ClientEvent event;
  813. event.type = CE_ADD_PARTICLESPAWNER;
  814. event.add_particlespawner.amount = amount;
  815. event.add_particlespawner.spawntime = spawntime;
  816. event.add_particlespawner.minpos = new v3f (minpos);
  817. event.add_particlespawner.maxpos = new v3f (maxpos);
  818. event.add_particlespawner.minvel = new v3f (minvel);
  819. event.add_particlespawner.maxvel = new v3f (maxvel);
  820. event.add_particlespawner.minacc = new v3f (minacc);
  821. event.add_particlespawner.maxacc = new v3f (maxacc);
  822. event.add_particlespawner.minexptime = minexptime;
  823. event.add_particlespawner.maxexptime = maxexptime;
  824. event.add_particlespawner.minsize = minsize;
  825. event.add_particlespawner.maxsize = maxsize;
  826. event.add_particlespawner.collisiondetection = collisiondetection;
  827. event.add_particlespawner.collision_removal = collision_removal;
  828. event.add_particlespawner.attached_id = attached_id;
  829. event.add_particlespawner.vertical = vertical;
  830. event.add_particlespawner.texture = new std::string(texture);
  831. event.add_particlespawner.id = id;
  832. event.add_particlespawner.animation = animation;
  833. event.add_particlespawner.glow = glow;
  834. m_client_event_queue.push(event);
  835. }
  836. void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
  837. {
  838. u16 legacy_id;
  839. u32 id;
  840. // Modification set 13/03/15, 1 year of compat for protocol v24
  841. if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
  842. *pkt >> legacy_id;
  843. }
  844. else {
  845. *pkt >> id;
  846. }
  847. ClientEvent event;
  848. event.type = CE_DELETE_PARTICLESPAWNER;
  849. event.delete_particlespawner.id =
  850. (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
  851. m_client_event_queue.push(event);
  852. }
  853. void Client::handleCommand_HudAdd(NetworkPacket* pkt)
  854. {
  855. std::string datastring(pkt->getString(0), pkt->getSize());
  856. std::istringstream is(datastring, std::ios_base::binary);
  857. u32 id;
  858. u8 type;
  859. v2f pos;
  860. std::string name;
  861. v2f scale;
  862. std::string text;
  863. u32 number;
  864. u32 item;
  865. u32 dir;
  866. v2f align;
  867. v2f offset;
  868. v3f world_pos;
  869. v2s32 size;
  870. *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
  871. >> dir >> align >> offset;
  872. try {
  873. *pkt >> world_pos;
  874. }
  875. catch(SerializationError &e) {};
  876. try {
  877. *pkt >> size;
  878. } catch(SerializationError &e) {};
  879. ClientEvent event;
  880. event.type = CE_HUDADD;
  881. event.hudadd.id = id;
  882. event.hudadd.type = type;
  883. event.hudadd.pos = new v2f(pos);
  884. event.hudadd.name = new std::string(name);
  885. event.hudadd.scale = new v2f(scale);
  886. event.hudadd.text = new std::string(text);
  887. event.hudadd.number = number;
  888. event.hudadd.item = item;
  889. event.hudadd.dir = dir;
  890. event.hudadd.align = new v2f(align);
  891. event.hudadd.offset = new v2f(offset);
  892. event.hudadd.world_pos = new v3f(world_pos);
  893. event.hudadd.size = new v2s32(size);
  894. m_client_event_queue.push(event);
  895. }
  896. void Client::handleCommand_HudRemove(NetworkPacket* pkt)
  897. {
  898. u32 id;
  899. *pkt >> id;
  900. ClientEvent event;
  901. event.type = CE_HUDRM;
  902. event.hudrm.id = id;
  903. m_client_event_queue.push(event);
  904. }
  905. void Client::handleCommand_HudChange(NetworkPacket* pkt)
  906. {
  907. std::string sdata;
  908. v2f v2fdata;
  909. v3f v3fdata;
  910. u32 intdata = 0;
  911. v2s32 v2s32data;
  912. u32 id;
  913. u8 stat;
  914. *pkt >> id >> stat;
  915. if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
  916. stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
  917. *pkt >> v2fdata;
  918. else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
  919. *pkt >> sdata;
  920. else if (stat == HUD_STAT_WORLD_POS)
  921. *pkt >> v3fdata;
  922. else if (stat == HUD_STAT_SIZE )
  923. *pkt >> v2s32data;
  924. else
  925. *pkt >> intdata;
  926. ClientEvent event;
  927. event.type = CE_HUDCHANGE;
  928. event.hudchange.id = id;
  929. event.hudchange.stat = (HudElementStat)stat;
  930. event.hudchange.v2fdata = new v2f(v2fdata);
  931. event.hudchange.v3fdata = new v3f(v3fdata);
  932. event.hudchange.sdata = new std::string(sdata);
  933. event.hudchange.data = intdata;
  934. event.hudchange.v2s32data = new v2s32(v2s32data);
  935. m_client_event_queue.push(event);
  936. }
  937. void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
  938. {
  939. u32 flags, mask;
  940. *pkt >> flags >> mask;
  941. LocalPlayer *player = m_env.getLocalPlayer();
  942. assert(player != NULL);
  943. bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
  944. player->hud_flags &= ~mask;
  945. player->hud_flags |= flags;
  946. m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
  947. // Hide minimap if it has been disabled by the server
  948. if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible) {
  949. // defers a minimap update, therefore only call it if really
  950. // needed, by checking that minimap was visible before
  951. m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
  952. }
  953. }
  954. void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
  955. {
  956. u16 param; std::string value;
  957. *pkt >> param >> value;
  958. LocalPlayer *player = m_env.getLocalPlayer();
  959. assert(player != NULL);
  960. if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
  961. s32 hotbar_itemcount = readS32((u8*) value.c_str());
  962. if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
  963. player->hud_hotbar_itemcount = hotbar_itemcount;
  964. }
  965. else if (param == HUD_PARAM_HOTBAR_IMAGE) {
  966. player->hotbar_image = value;
  967. }
  968. else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
  969. player->hotbar_selected_image = value;
  970. }
  971. }
  972. void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
  973. {
  974. std::string datastring(pkt->getString(0), pkt->getSize());
  975. std::istringstream is(datastring, std::ios_base::binary);
  976. video::SColor *bgcolor = new video::SColor(readARGB8(is));
  977. std::string *type = new std::string(deSerializeString(is));
  978. u16 count = readU16(is);
  979. std::vector<std::string> *params = new std::vector<std::string>;
  980. for (size_t i = 0; i < count; i++)
  981. params->push_back(deSerializeString(is));
  982. bool clouds = true;
  983. try {
  984. clouds = readU8(is);
  985. } catch (...) {}
  986. ClientEvent event;
  987. event.type = CE_SET_SKY;
  988. event.set_sky.bgcolor = bgcolor;
  989. event.set_sky.type = type;
  990. event.set_sky.params = params;
  991. event.set_sky.clouds = clouds;
  992. m_client_event_queue.push(event);
  993. }
  994. void Client::handleCommand_CloudParams(NetworkPacket* pkt)
  995. {
  996. f32 density;
  997. video::SColor color_bright;
  998. video::SColor color_ambient;
  999. f32 height;
  1000. f32 thickness;
  1001. v2f speed;
  1002. *pkt >> density >> color_bright >> color_ambient
  1003. >> height >> thickness >> speed;
  1004. ClientEvent event;
  1005. event.type = CE_CLOUD_PARAMS;
  1006. event.cloud_params.density = density;
  1007. // use the underlying u32 representation, because we can't
  1008. // use struct members with constructors here, and this way
  1009. // we avoid using new() and delete() for no good reason
  1010. event.cloud_params.color_bright = color_bright.color;
  1011. event.cloud_params.color_ambient = color_ambient.color;
  1012. event.cloud_params.height = height;
  1013. event.cloud_params.thickness = thickness;
  1014. // same here: deconstruct to skip constructor
  1015. event.cloud_params.speed_x = speed.X;
  1016. event.cloud_params.speed_y = speed.Y;
  1017. m_client_event_queue.push(event);
  1018. }
  1019. void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
  1020. {
  1021. bool do_override;
  1022. u16 day_night_ratio_u;
  1023. *pkt >> do_override >> day_night_ratio_u;
  1024. float day_night_ratio_f = (float)day_night_ratio_u / 65536;
  1025. ClientEvent event;
  1026. event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
  1027. event.override_day_night_ratio.do_override = do_override;
  1028. event.override_day_night_ratio.ratio_f = day_night_ratio_f;
  1029. m_client_event_queue.push(event);
  1030. }
  1031. void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
  1032. {
  1033. LocalPlayer *player = m_env.getLocalPlayer();
  1034. assert(player != NULL);
  1035. *pkt >> player->local_animations[0];
  1036. *pkt >> player->local_animations[1];
  1037. *pkt >> player->local_animations[2];
  1038. *pkt >> player->local_animations[3];
  1039. *pkt >> player->local_animation_speed;
  1040. }
  1041. void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
  1042. {
  1043. LocalPlayer *player = m_env.getLocalPlayer();
  1044. assert(player != NULL);
  1045. *pkt >> player->eye_offset_first >> player->eye_offset_third;
  1046. }
  1047. void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
  1048. {
  1049. u8 type;
  1050. u16 num_players;
  1051. *pkt >> type >> num_players;
  1052. PlayerListModifer notice_type = (PlayerListModifer) type;
  1053. for (u16 i = 0; i < num_players; i++) {
  1054. std::string name;
  1055. *pkt >> name;
  1056. switch (notice_type) {
  1057. case PLAYER_LIST_INIT:
  1058. case PLAYER_LIST_ADD:
  1059. m_env.addPlayerName(name);
  1060. continue;
  1061. case PLAYER_LIST_REMOVE:
  1062. m_env.removePlayerName(name);
  1063. continue;
  1064. }
  1065. }
  1066. }
  1067. void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
  1068. {
  1069. if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
  1070. && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
  1071. errorstream << "Client: Recieved SRP S_B login message,"
  1072. << " but wasn't supposed to (chosen_mech="
  1073. << m_chosen_auth_mech << ")." << std::endl;
  1074. return;
  1075. }
  1076. char *bytes_M = 0;
  1077. size_t len_M = 0;
  1078. SRPUser *usr = (SRPUser *) m_auth_data;
  1079. std::string s;
  1080. std::string B;
  1081. *pkt >> s >> B;
  1082. infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
  1083. srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
  1084. (const unsigned char *) B.c_str(), B.size(),
  1085. (unsigned char **) &bytes_M, &len_M);
  1086. if ( !bytes_M ) {
  1087. errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
  1088. return;
  1089. }
  1090. NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
  1091. resp_pkt << std::string(bytes_M, len_M);
  1092. Send(&resp_pkt);
  1093. }