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