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