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