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