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