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