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