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