]> git.lizzy.rs Git - minetest.git/blob - src/network/clientpackethandler.cpp
97fa93e9593a82ba9123566ed5c19fb41a900845
[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 (!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         m_script->on_hp_modification(hp);
530
531         if (hp < oldhp) {
532                 // Add to ClientEvent queue
533                 ClientEvent event;
534                 event.type = CE_PLAYER_DAMAGE;
535                 event.player_damage.amount = oldhp - hp;
536                 m_client_event_queue.push(event);
537         }
538 }
539
540 void Client::handleCommand_Breath(NetworkPacket* pkt)
541 {
542         LocalPlayer *player = m_env.getLocalPlayer();
543         assert(player != NULL);
544
545         u16 breath;
546
547         *pkt >> breath;
548
549         player->setBreath(breath);
550 }
551
552 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
553 {
554         LocalPlayer *player = m_env.getLocalPlayer();
555         assert(player != NULL);
556
557         v3f pos;
558         f32 pitch, yaw;
559
560         *pkt >> pos >> pitch >> yaw;
561
562         player->got_teleported = true;
563         player->setPosition(pos);
564
565         infostream << "Client got TOCLIENT_MOVE_PLAYER"
566                         << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
567                         << " pitch=" << pitch
568                         << " yaw=" << yaw
569                         << std::endl;
570
571         /*
572                 Add to ClientEvent queue.
573                 This has to be sent to the main program because otherwise
574                 it would just force the pitch and yaw values to whatever
575                 the camera points to.
576         */
577         ClientEvent event;
578         event.type = CE_PLAYER_FORCE_MOVE;
579         event.player_force_move.pitch = pitch;
580         event.player_force_move.yaw = yaw;
581         m_client_event_queue.push(event);
582
583         // Ignore damage for a few seconds, so that the player doesn't
584         // get damage from falling on ground
585         m_ignore_damage_timer = 3.0;
586 }
587
588 void Client::handleCommand_PlayerItem(NetworkPacket* pkt)
589 {
590         warningstream << "Client: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
591 }
592
593 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
594 {
595         bool set_camera_point_target;
596         v3f camera_point_target;
597
598         *pkt >> set_camera_point_target;
599         *pkt >> camera_point_target;
600
601         ClientEvent event;
602         event.type                                = CE_DEATHSCREEN;
603         event.deathscreen.set_camera_point_target = set_camera_point_target;
604         event.deathscreen.camera_point_target_x   = camera_point_target.X;
605         event.deathscreen.camera_point_target_y   = camera_point_target.Y;
606         event.deathscreen.camera_point_target_z   = camera_point_target.Z;
607         m_client_event_queue.push(event);
608 }
609
610 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
611 {
612         u16 num_files;
613
614         *pkt >> num_files;
615
616         infostream << "Client: Received media announcement: packet size: "
617                         << pkt->getSize() << std::endl;
618
619         if (m_media_downloader == NULL ||
620                         m_media_downloader->isStarted()) {
621                 const char *problem = m_media_downloader ?
622                         "we already saw another announcement" :
623                         "all media has been received already";
624                 errorstream << "Client: Received media announcement but "
625                         << problem << "! "
626                         << " files=" << num_files
627                         << " size=" << pkt->getSize() << std::endl;
628                 return;
629         }
630
631         // Mesh update thread must be stopped while
632         // updating content definitions
633         sanity_check(!m_mesh_update_thread.isRunning());
634
635         for (u16 i = 0; i < num_files; i++) {
636                 std::string name, sha1_base64;
637
638                 *pkt >> name >> sha1_base64;
639
640                 std::string sha1_raw = base64_decode(sha1_base64);
641                 m_media_downloader->addFile(name, sha1_raw);
642         }
643
644         try {
645                 std::string str;
646
647                 *pkt >> str;
648
649                 Strfnd sf(str);
650                 while(!sf.at_end()) {
651                         std::string baseurl = trim(sf.next(","));
652                         if (baseurl != "")
653                                 m_media_downloader->addRemoteServer(baseurl);
654                 }
655         }
656         catch(SerializationError& e) {
657                 // not supported by server or turned off
658         }
659
660         m_media_downloader->step(this);
661 }
662
663 void Client::handleCommand_Media(NetworkPacket* pkt)
664 {
665         /*
666                 u16 command
667                 u16 total number of file bunches
668                 u16 index of this bunch
669                 u32 number of files in this bunch
670                 for each file {
671                         u16 length of name
672                         string name
673                         u32 length of data
674                         data
675                 }
676         */
677         u16 num_bunches;
678         u16 bunch_i;
679         u32 num_files;
680
681         *pkt >> num_bunches >> bunch_i >> num_files;
682
683         infostream << "Client: Received files: bunch " << bunch_i << "/"
684                         << num_bunches << " files=" << num_files
685                         << " size=" << pkt->getSize() << std::endl;
686
687         if (num_files == 0)
688                 return;
689
690         if (m_media_downloader == NULL ||
691                         !m_media_downloader->isStarted()) {
692                 const char *problem = m_media_downloader ?
693                         "media has not been requested" :
694                         "all media has been received already";
695                 errorstream << "Client: Received media but "
696                         << problem << "! "
697                         << " bunch " << bunch_i << "/" << num_bunches
698                         << " files=" << num_files
699                         << " size=" << pkt->getSize() << std::endl;
700                 return;
701         }
702
703         // Mesh update thread must be stopped while
704         // updating content definitions
705         sanity_check(!m_mesh_update_thread.isRunning());
706
707         for (u32 i=0; i < num_files; i++) {
708                 std::string name;
709
710                 *pkt >> name;
711
712                 std::string data = pkt->readLongString();
713
714                 m_media_downloader->conventionalTransferDone(
715                                 name, data, this);
716         }
717 }
718
719 void Client::handleCommand_ToolDef(NetworkPacket* pkt)
720 {
721         warningstream << "Client: Ignoring TOCLIENT_TOOLDEF" << std::endl;
722 }
723
724 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
725 {
726         infostream << "Client: Received node definitions: packet size: "
727                         << pkt->getSize() << std::endl;
728
729         // Mesh update thread must be stopped while
730         // updating content definitions
731         sanity_check(!m_mesh_update_thread.isRunning());
732
733         // Decompress node definitions
734         std::string datastring(pkt->getString(0), pkt->getSize());
735         std::istringstream is(datastring, std::ios_base::binary);
736         std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
737         std::ostringstream tmp_os;
738         decompressZlib(tmp_is, tmp_os);
739
740         // Deserialize node definitions
741         std::istringstream tmp_is2(tmp_os.str());
742         m_nodedef->deSerialize(tmp_is2);
743         m_nodedef_received = true;
744 }
745
746 void Client::handleCommand_CraftItemDef(NetworkPacket* pkt)
747 {
748         warningstream << "Client: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
749 }
750
751 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
752 {
753         infostream << "Client: Received item definitions: packet size: "
754                         << pkt->getSize() << std::endl;
755
756         // Mesh update thread must be stopped while
757         // updating content definitions
758         sanity_check(!m_mesh_update_thread.isRunning());
759
760         // Decompress item definitions
761         std::string datastring(pkt->getString(0), pkt->getSize());
762         std::istringstream is(datastring, std::ios_base::binary);
763         std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
764         std::ostringstream tmp_os;
765         decompressZlib(tmp_is, tmp_os);
766
767         // Deserialize node definitions
768         std::istringstream tmp_is2(tmp_os.str());
769         m_itemdef->deSerialize(tmp_is2);
770         m_itemdef_received = true;
771 }
772
773 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
774 {
775         s32 server_id;
776         std::string name;
777         float gain;
778         u8 type; // 0=local, 1=positional, 2=object
779         v3f pos;
780         u16 object_id;
781         bool loop;
782
783         *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
784
785         // Start playing
786         int client_id = -1;
787         switch(type) {
788                 case 0: // local
789                         client_id = m_sound->playSound(name, loop, gain);
790                         break;
791                 case 1: // positional
792                         client_id = m_sound->playSoundAt(name, loop, gain, pos);
793                         break;
794                 case 2:
795                 { // object
796                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
797                         if (cao)
798                                 pos = cao->getPosition();
799                         client_id = m_sound->playSoundAt(name, loop, gain, pos);
800                         // TODO: Set up sound to move with object
801                         break;
802                 }
803                 default:
804                         break;
805         }
806
807         if (client_id != -1) {
808                 m_sounds_server_to_client[server_id] = client_id;
809                 m_sounds_client_to_server[client_id] = server_id;
810                 if (object_id != 0)
811                         m_sounds_to_objects[client_id] = object_id;
812         }
813 }
814
815 void Client::handleCommand_StopSound(NetworkPacket* pkt)
816 {
817         s32 server_id;
818
819         *pkt >> server_id;
820
821         UNORDERED_MAP<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
822         if (i != m_sounds_server_to_client.end()) {
823                 int client_id = i->second;
824                 m_sound->stopSound(client_id);
825         }
826 }
827
828 void Client::handleCommand_Privileges(NetworkPacket* pkt)
829 {
830         m_privileges.clear();
831         infostream << "Client: Privileges updated: ";
832         u16 num_privileges;
833
834         *pkt >> num_privileges;
835
836         for (u16 i = 0; i < num_privileges; i++) {
837                 std::string priv;
838
839                 *pkt >> priv;
840
841                 m_privileges.insert(priv);
842                 infostream << priv << " ";
843         }
844         infostream << std::endl;
845 }
846
847 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
848 {
849         LocalPlayer *player = m_env.getLocalPlayer();
850         assert(player != NULL);
851
852         // Store formspec in LocalPlayer
853         player->inventory_formspec = pkt->readLongString();
854 }
855
856 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
857 {
858         std::string datastring(pkt->getString(0), pkt->getSize());
859         std::istringstream is(datastring, std::ios_base::binary);
860
861         std::string name = deSerializeString(is);
862
863         infostream << "Client: Detached inventory update: \"" << name
864                         << "\"" << std::endl;
865
866         Inventory *inv = NULL;
867         if (m_detached_inventories.count(name) > 0)
868                 inv = m_detached_inventories[name];
869         else {
870                 inv = new Inventory(m_itemdef);
871                 m_detached_inventories[name] = inv;
872         }
873         inv->deSerialize(is);
874 }
875
876 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
877 {
878         std::string formspec = pkt->readLongString();
879         std::string formname;
880
881         *pkt >> formname;
882
883         ClientEvent event;
884         event.type = CE_SHOW_FORMSPEC;
885         // pointer is required as event is a struct only!
886         // adding a std:string to a struct isn't possible
887         event.show_formspec.formspec = new std::string(formspec);
888         event.show_formspec.formname = new std::string(formname);
889         m_client_event_queue.push(event);
890 }
891
892 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
893 {
894         std::string datastring(pkt->getString(0), pkt->getSize());
895         std::istringstream is(datastring, std::ios_base::binary);
896
897         v3f pos                 = readV3F1000(is);
898         v3f vel                 = readV3F1000(is);
899         v3f acc                 = readV3F1000(is);
900         float expirationtime    = readF1000(is);
901         float size              = readF1000(is);
902         bool collisiondetection = readU8(is);
903         std::string texture     = deSerializeLongString(is);
904         bool vertical           = false;
905         bool collision_removal  = false;
906         struct TileAnimationParams animation;
907         animation.type = TAT_NONE;
908         u8 glow = 0;
909         try {
910                 vertical = readU8(is);
911                 collision_removal = readU8(is);
912                 animation.deSerialize(is, m_proto_ver);
913                 glow = readU8(is);
914         } catch (...) {}
915
916         ClientEvent event;
917         event.type                              = CE_SPAWN_PARTICLE;
918         event.spawn_particle.pos                = new v3f (pos);
919         event.spawn_particle.vel                = new v3f (vel);
920         event.spawn_particle.acc                = new v3f (acc);
921         event.spawn_particle.expirationtime     = expirationtime;
922         event.spawn_particle.size               = size;
923         event.spawn_particle.collisiondetection = collisiondetection;
924         event.spawn_particle.collision_removal  = collision_removal;
925         event.spawn_particle.vertical           = vertical;
926         event.spawn_particle.texture            = new std::string(texture);
927         event.spawn_particle.animation          = animation;
928         event.spawn_particle.glow               = glow;
929
930         m_client_event_queue.push(event);
931 }
932
933 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
934 {
935         u16 amount;
936         float spawntime;
937         v3f minpos;
938         v3f maxpos;
939         v3f minvel;
940         v3f maxvel;
941         v3f minacc;
942         v3f maxacc;
943         float minexptime;
944         float maxexptime;
945         float minsize;
946         float maxsize;
947         bool collisiondetection;
948         u32 id;
949
950         *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
951                 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
952                 >> maxsize >> collisiondetection;
953
954         std::string texture = pkt->readLongString();
955
956         *pkt >> id;
957
958         bool vertical = false;
959         bool collision_removal = false;
960         struct TileAnimationParams animation;
961         animation.type = TAT_NONE;
962         u8 glow = 0;
963         u16 attached_id = 0;
964         try {
965                 *pkt >> vertical;
966                 *pkt >> collision_removal;
967                 *pkt >> attached_id;
968
969                 // This is horrible but required (why are there two ways to deserialize pkts?)
970                 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
971                 std::istringstream is(datastring, std::ios_base::binary);
972                 animation.deSerialize(is, m_proto_ver);
973                 glow = readU8(is);
974         } catch (...) {}
975
976         ClientEvent event;
977         event.type                                   = CE_ADD_PARTICLESPAWNER;
978         event.add_particlespawner.amount             = amount;
979         event.add_particlespawner.spawntime          = spawntime;
980         event.add_particlespawner.minpos             = new v3f (minpos);
981         event.add_particlespawner.maxpos             = new v3f (maxpos);
982         event.add_particlespawner.minvel             = new v3f (minvel);
983         event.add_particlespawner.maxvel             = new v3f (maxvel);
984         event.add_particlespawner.minacc             = new v3f (minacc);
985         event.add_particlespawner.maxacc             = new v3f (maxacc);
986         event.add_particlespawner.minexptime         = minexptime;
987         event.add_particlespawner.maxexptime         = maxexptime;
988         event.add_particlespawner.minsize            = minsize;
989         event.add_particlespawner.maxsize            = maxsize;
990         event.add_particlespawner.collisiondetection = collisiondetection;
991         event.add_particlespawner.collision_removal  = collision_removal;
992         event.add_particlespawner.attached_id        = attached_id;
993         event.add_particlespawner.vertical           = vertical;
994         event.add_particlespawner.texture            = new std::string(texture);
995         event.add_particlespawner.id                 = id;
996         event.add_particlespawner.animation          = animation;
997         event.add_particlespawner.glow               = glow;
998
999         m_client_event_queue.push(event);
1000 }
1001
1002
1003 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1004 {
1005         u16 legacy_id;
1006         u32 id;
1007
1008         // Modification set 13/03/15, 1 year of compat for protocol v24
1009         if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
1010                 *pkt >> legacy_id;
1011         }
1012         else {
1013                 *pkt >> id;
1014         }
1015
1016
1017         ClientEvent event;
1018         event.type                      = CE_DELETE_PARTICLESPAWNER;
1019         event.delete_particlespawner.id =
1020                         (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1021
1022         m_client_event_queue.push(event);
1023 }
1024
1025 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1026 {
1027         std::string datastring(pkt->getString(0), pkt->getSize());
1028         std::istringstream is(datastring, std::ios_base::binary);
1029
1030         u32 id;
1031         u8 type;
1032         v2f pos;
1033         std::string name;
1034         v2f scale;
1035         std::string text;
1036         u32 number;
1037         u32 item;
1038         u32 dir;
1039         v2f align;
1040         v2f offset;
1041         v3f world_pos;
1042         v2s32 size;
1043
1044         *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1045                 >> dir >> align >> offset;
1046         try {
1047                 *pkt >> world_pos;
1048         }
1049         catch(SerializationError &e) {};
1050
1051         try {
1052                 *pkt >> size;
1053         } catch(SerializationError &e) {};
1054
1055         ClientEvent event;
1056         event.type             = CE_HUDADD;
1057         event.hudadd.id        = id;
1058         event.hudadd.type      = type;
1059         event.hudadd.pos       = new v2f(pos);
1060         event.hudadd.name      = new std::string(name);
1061         event.hudadd.scale     = new v2f(scale);
1062         event.hudadd.text      = new std::string(text);
1063         event.hudadd.number    = number;
1064         event.hudadd.item      = item;
1065         event.hudadd.dir       = dir;
1066         event.hudadd.align     = new v2f(align);
1067         event.hudadd.offset    = new v2f(offset);
1068         event.hudadd.world_pos = new v3f(world_pos);
1069         event.hudadd.size      = new v2s32(size);
1070         m_client_event_queue.push(event);
1071 }
1072
1073 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1074 {
1075         u32 id;
1076
1077         *pkt >> id;
1078
1079         ClientEvent event;
1080         event.type     = CE_HUDRM;
1081         event.hudrm.id = id;
1082         m_client_event_queue.push(event);
1083 }
1084
1085 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1086 {
1087         std::string sdata;
1088         v2f v2fdata;
1089         v3f v3fdata;
1090         u32 intdata = 0;
1091         v2s32 v2s32data;
1092         u32 id;
1093         u8 stat;
1094
1095         *pkt >> id >> stat;
1096
1097         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1098                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1099                 *pkt >> v2fdata;
1100         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1101                 *pkt >> sdata;
1102         else if (stat == HUD_STAT_WORLD_POS)
1103                 *pkt >> v3fdata;
1104         else if (stat == HUD_STAT_SIZE )
1105                 *pkt >> v2s32data;
1106         else
1107                 *pkt >> intdata;
1108
1109         ClientEvent event;
1110         event.type              = CE_HUDCHANGE;
1111         event.hudchange.id      = id;
1112         event.hudchange.stat    = (HudElementStat)stat;
1113         event.hudchange.v2fdata = new v2f(v2fdata);
1114         event.hudchange.v3fdata = new v3f(v3fdata);
1115         event.hudchange.sdata   = new std::string(sdata);
1116         event.hudchange.data    = intdata;
1117         event.hudchange.v2s32data = new v2s32(v2s32data);
1118         m_client_event_queue.push(event);
1119 }
1120
1121 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1122 {
1123         u32 flags, mask;
1124
1125         *pkt >> flags >> mask;
1126
1127         LocalPlayer *player = m_env.getLocalPlayer();
1128         assert(player != NULL);
1129
1130         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1131
1132         player->hud_flags &= ~mask;
1133         player->hud_flags |= flags;
1134
1135         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1136
1137         // Hide minimap if it has been disabled by the server
1138         if (m_minimap_disabled_by_server && was_minimap_visible) {
1139                 // defers a minimap update, therefore only call it if really
1140                 // needed, by checking that minimap was visible before
1141                 m_mapper->setMinimapMode(MINIMAP_MODE_OFF);
1142         }
1143 }
1144
1145 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1146 {
1147         u16 param; std::string value;
1148
1149         *pkt >> param >> value;
1150
1151         LocalPlayer *player = m_env.getLocalPlayer();
1152         assert(player != NULL);
1153
1154         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1155                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1156                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1157                         player->hud_hotbar_itemcount = hotbar_itemcount;
1158         }
1159         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1160                 player->hotbar_image = value;
1161         }
1162         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1163                 player->hotbar_selected_image = value;
1164         }
1165 }
1166
1167 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1168 {
1169         std::string datastring(pkt->getString(0), pkt->getSize());
1170         std::istringstream is(datastring, std::ios_base::binary);
1171
1172         video::SColor *bgcolor           = new video::SColor(readARGB8(is));
1173         std::string *type                = new std::string(deSerializeString(is));
1174         u16 count                        = readU16(is);
1175         std::vector<std::string> *params = new std::vector<std::string>;
1176
1177         for (size_t i = 0; i < count; i++)
1178                 params->push_back(deSerializeString(is));
1179
1180         ClientEvent event;
1181         event.type            = CE_SET_SKY;
1182         event.set_sky.bgcolor = bgcolor;
1183         event.set_sky.type    = type;
1184         event.set_sky.params  = params;
1185         m_client_event_queue.push(event);
1186 }
1187
1188 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1189 {
1190         bool do_override;
1191         u16 day_night_ratio_u;
1192
1193         *pkt >> do_override >> day_night_ratio_u;
1194
1195         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1196
1197         ClientEvent event;
1198         event.type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1199         event.override_day_night_ratio.do_override = do_override;
1200         event.override_day_night_ratio.ratio_f     = day_night_ratio_f;
1201         m_client_event_queue.push(event);
1202 }
1203
1204 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1205 {
1206         LocalPlayer *player = m_env.getLocalPlayer();
1207         assert(player != NULL);
1208
1209         *pkt >> player->local_animations[0];
1210         *pkt >> player->local_animations[1];
1211         *pkt >> player->local_animations[2];
1212         *pkt >> player->local_animations[3];
1213         *pkt >> player->local_animation_speed;
1214 }
1215
1216 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1217 {
1218         LocalPlayer *player = m_env.getLocalPlayer();
1219         assert(player != NULL);
1220
1221         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1222 }
1223
1224 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1225 {
1226         if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1227                         && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1228                 errorstream << "Client: Recieved SRP S_B login message,"
1229                         << " but wasn't supposed to (chosen_mech="
1230                         << m_chosen_auth_mech << ")." << std::endl;
1231                 return;
1232         }
1233
1234         char *bytes_M = 0;
1235         size_t len_M = 0;
1236         SRPUser *usr = (SRPUser *) m_auth_data;
1237         std::string s;
1238         std::string B;
1239         *pkt >> s >> B;
1240
1241         infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1242
1243         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1244                 (const unsigned char *) B.c_str(), B.size(),
1245                 (unsigned char **) &bytes_M, &len_M);
1246
1247         if ( !bytes_M ) {
1248                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1249                 return;
1250         }
1251
1252         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1253         resp_pkt << std::string(bytes_M, len_M);
1254         Send(&resp_pkt);
1255 }