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