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