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