]> git.lizzy.rs Git - minetest.git/blob - src/network/clientpackethandler.cpp
8a323872ac0c0a2a9c54fbed9bc4017a4ecb1faf
[minetest.git] / src / network / clientpackethandler.cpp
1 /*
2 Minetest
3 Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "client/client.h"
21
22 #include "util/base64.h"
23 #include "client/camera.h"
24 #include "chatmessage.h"
25 #include "client/clientmedia.h"
26 #include "log.h"
27 #include "map.h"
28 #include "mapsector.h"
29 #include "client/minimap.h"
30 #include "modchannels.h"
31 #include "nodedef.h"
32 #include "serialization.h"
33 #include "server.h"
34 #include "util/strfnd.h"
35 #include "client/clientevent.h"
36 #include "client/sound.h"
37 #include "network/clientopcodes.h"
38 #include "network/connection.h"
39 #include "script/scripting_client.h"
40 #include "util/serialize.h"
41 #include "util/srp.h"
42 #include "util/sha1.h"
43 #include "tileanimation.h"
44 #include "gettext.h"
45 #include "skyparams.h"
46 #include <memory>
47
48 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
49 {
50         infostream << "Got deprecated command "
51                         << toClientCommandTable[pkt->getCommand()].name << " from peer "
52                         << pkt->getPeerId() << "!" << std::endl;
53 }
54
55 void Client::handleCommand_Hello(NetworkPacket* pkt)
56 {
57         if (pkt->getSize() < 1)
58                 return;
59
60         u8 serialization_ver;
61         u16 proto_ver;
62         u16 compression_mode;
63         u32 auth_mechs;
64         std::string username_legacy; // for case insensitivity
65         *pkt >> serialization_ver >> compression_mode >> proto_ver
66                 >> auth_mechs >> username_legacy;
67
68         // Chose an auth method we support
69         AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
70
71         infostream << "Client: TOCLIENT_HELLO received with "
72                         << "serialization_ver=" << (u32)serialization_ver
73                         << ", auth_mechs=" << auth_mechs
74                         << ", proto_ver=" << proto_ver
75                         << ", compression_mode=" << compression_mode
76                         << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
77
78         if (!ser_ver_supported(serialization_ver)) {
79                 infostream << "Client: TOCLIENT_HELLO: Server sent "
80                                 << "unsupported ser_fmt_ver"<< std::endl;
81                 return;
82         }
83
84         m_server_ser_ver = serialization_ver;
85         m_proto_ver = proto_ver;
86
87         //TODO verify that username_legacy matches sent username, only
88         // differs in casing (make both uppercase and compare)
89         // This is only necessary though when we actually want to add casing support
90
91         if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
92                 // we received a TOCLIENT_HELLO while auth was already going on
93                 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
94                         << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
95                 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
96                                 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
97                         srp_user_delete((SRPUser *) m_auth_data);
98                         m_auth_data = 0;
99                 }
100         }
101
102         // Authenticate using that method, or abort if there wasn't any method found
103         if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
104                 bool is_register = chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP;
105                 ELoginRegister mode = is_register ? ELoginRegister::Register : ELoginRegister::Login;
106                 if (m_allow_login_or_register != ELoginRegister::Any &&
107                                 m_allow_login_or_register != mode) {
108                         m_chosen_auth_mech = AUTH_MECHANISM_NONE;
109                         m_access_denied = true;
110                         if (m_allow_login_or_register == ELoginRegister::Login) {
111                                 m_access_denied_reason =
112                                                 gettext("Name is not registered. To create an account on this server, click 'Register'");
113                         } else {
114                                 m_access_denied_reason =
115                                                 gettext("Name is taken. Please choose another name");
116                         }
117                         m_con->Disconnect();
118                 } else {
119                         startAuth(chosen_auth_mechanism);
120                 }
121         } else {
122                 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
123                 m_access_denied = true;
124                 m_access_denied_reason = "Unknown";
125                 m_con->Disconnect();
126         }
127
128 }
129
130 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
131 {
132         deleteAuthData();
133
134         v3f playerpos;
135         *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
136                 >> m_sudo_auth_methods;
137
138         playerpos -= v3f(0, BS / 2, 0);
139
140         // Set player position
141         LocalPlayer *player = m_env.getLocalPlayer();
142         assert(player != NULL);
143         player->setPosition(playerpos);
144
145         infostream << "Client: received map seed: " << m_map_seed << std::endl;
146         infostream << "Client: received recommended send interval "
147                                         << m_recommended_send_interval<<std::endl;
148
149         // Reply to server
150         /*~ DO NOT TRANSLATE THIS LITERALLY!
151         This is a special string which needs to contain the translation's
152         language code (e.g. "de" for German). */
153         std::string lang = gettext("LANG_CODE");
154         if (lang == "LANG_CODE")
155                 lang.clear();
156
157         NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
158         resp_pkt << lang;
159         Send(&resp_pkt);
160
161         m_state = LC_Init;
162 }
163 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
164 {
165         deleteAuthData();
166
167         m_password = m_new_password;
168
169         verbosestream << "Client: Received TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
170
171         // send packet to actually set the password
172         startAuth(AUTH_MECHANISM_FIRST_SRP);
173
174         // reset again
175         m_chosen_auth_mech = AUTH_MECHANISM_NONE;
176 }
177 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
178 {
179         ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
180                         L"Password change denied. Password NOT changed.");
181         pushToChatQueue(chatMessage);
182         // reset everything and be sad
183         deleteAuthData();
184 }
185
186 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
187 {
188         // The server didn't like our password. Note, this needs
189         // to be processed even if the serialization format has
190         // not been agreed yet, the same as TOCLIENT_INIT.
191         m_access_denied = true;
192         m_access_denied_reason = "Unknown";
193
194         if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
195                 // Legacy code from 0.4.12 and older but is still used
196                 // in some places of the server code
197                 if (pkt->getSize() >= 2) {
198                         std::wstring wide_reason;
199                         *pkt >> wide_reason;
200                         m_access_denied_reason = wide_to_utf8(wide_reason);
201                 }
202                 return;
203         }
204
205         if (pkt->getSize() < 1)
206                 return;
207
208         u8 denyCode;
209         *pkt >> denyCode;
210
211         if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
212                         denyCode == SERVER_ACCESSDENIED_CRASH) {
213                 *pkt >> m_access_denied_reason;
214                 if (m_access_denied_reason.empty())
215                         m_access_denied_reason = accessDeniedStrings[denyCode];
216                 u8 reconnect;
217                 *pkt >> reconnect;
218                 m_access_denied_reconnect = reconnect & 1;
219         } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
220                 *pkt >> m_access_denied_reason;
221         } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) {
222                 m_access_denied_reason = accessDeniedStrings[denyCode];
223                 m_access_denied_reconnect = true;
224         } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
225                 m_access_denied_reason = accessDeniedStrings[denyCode];
226         } else {
227                 // Allow us to add new error messages to the
228                 // protocol without raising the protocol version, if we want to.
229                 // Until then (which may be never), this is outside
230                 // of the defined protocol.
231                 *pkt >> m_access_denied_reason;
232                 if (m_access_denied_reason.empty())
233                         m_access_denied_reason = "Unknown";
234         }
235 }
236
237 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
238 {
239         if (pkt->getSize() < 6)
240                 return;
241
242         v3s16 p;
243         *pkt >> p;
244         removeNode(p);
245 }
246
247 void Client::handleCommand_AddNode(NetworkPacket* pkt)
248 {
249         if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
250                 return;
251
252         v3s16 p;
253         *pkt >> p;
254
255         MapNode n;
256         n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
257
258         bool remove_metadata = true;
259         u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
260         if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
261                 remove_metadata = false;
262         }
263
264         addNode(p, n, remove_metadata);
265 }
266
267 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
268 {
269         if (pkt->getSize() < 1)
270                 return;
271
272         std::istringstream is(pkt->readLongString(), std::ios::binary);
273         std::stringstream sstr(std::ios::binary | std::ios::in | std::ios::out);
274         decompressZlib(is, sstr);
275
276         NodeMetadataList meta_updates_list(false);
277         meta_updates_list.deSerialize(sstr, m_itemdef, true);
278
279         Map &map = m_env.getMap();
280         for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
281                         i != meta_updates_list.end(); ++i) {
282                 v3s16 pos = i->first;
283
284                 if (map.isValidPosition(pos) &&
285                                 map.setNodeMetadata(pos, i->second))
286                         continue; // Prevent from deleting metadata
287
288                 // Meta couldn't be set, unused metadata
289                 delete i->second;
290         }
291 }
292
293 void Client::handleCommand_BlockData(NetworkPacket* pkt)
294 {
295         // Ignore too small packet
296         if (pkt->getSize() < 6)
297                 return;
298
299         v3s16 p;
300         *pkt >> p;
301
302         std::string datastring(pkt->getString(6), pkt->getSize() - 6);
303         std::istringstream istr(datastring, std::ios_base::binary);
304
305         MapSector *sector;
306         MapBlock *block;
307
308         v2s16 p2d(p.X, p.Z);
309         sector = m_env.getMap().emergeSector(p2d);
310
311         assert(sector->getPos() == p2d);
312
313         block = sector->getBlockNoCreateNoEx(p.Y);
314         if (block) {
315                 /*
316                         Update an existing block
317                 */
318                 block->deSerialize(istr, m_server_ser_ver, false);
319                 block->deSerializeNetworkSpecific(istr);
320         }
321         else {
322                 /*
323                         Create a new block
324                 */
325                 block = sector->createBlankBlock(p.Y);
326                 block->deSerialize(istr, m_server_ser_ver, false);
327                 block->deSerializeNetworkSpecific(istr);
328         }
329
330         if (m_localdb) {
331                 ServerMap::saveBlock(block, m_localdb);
332         }
333
334         /*
335                 Add it to mesh update queue and set it to be acknowledged after update.
336         */
337         addUpdateMeshTaskWithEdge(p, true);
338 }
339
340 void Client::handleCommand_Inventory(NetworkPacket* pkt)
341 {
342         if (pkt->getSize() < 1)
343                 return;
344
345         std::string datastring(pkt->getString(0), pkt->getSize());
346         std::istringstream is(datastring, std::ios_base::binary);
347
348         LocalPlayer *player = m_env.getLocalPlayer();
349         assert(player != NULL);
350
351         player->inventory.deSerialize(is);
352
353         m_update_wielded_item = true;
354
355         delete m_inventory_from_server;
356         m_inventory_from_server = new Inventory(player->inventory);
357         m_inventory_from_server_age = 0.0;
358 }
359
360 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
361 {
362         if (pkt->getSize() < 2)
363                 return;
364
365         u16 time_of_day;
366
367         *pkt >> time_of_day;
368
369         time_of_day      = time_of_day % 24000;
370         float time_speed = 0;
371
372         if (pkt->getSize() >= 2 + 4) {
373                 *pkt >> time_speed;
374         }
375         else {
376                 // Old message; try to approximate speed of time by ourselves
377                 float time_of_day_f = (float)time_of_day / 24000.0f;
378                 float tod_diff_f = 0;
379
380                 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
381                         tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
382                 else
383                         tod_diff_f = time_of_day_f - m_last_time_of_day_f;
384
385                 m_last_time_of_day_f       = time_of_day_f;
386                 float time_diff            = m_time_of_day_update_timer;
387                 m_time_of_day_update_timer = 0;
388
389                 if (m_time_of_day_set) {
390                         time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
391                         infostream << "Client: Measured time_of_day speed (old format): "
392                                         << time_speed << " tod_diff_f=" << tod_diff_f
393                                         << " time_diff=" << time_diff << std::endl;
394                 }
395         }
396
397         // Update environment
398         m_env.setTimeOfDay(time_of_day);
399         m_env.setTimeOfDaySpeed(time_speed);
400         m_time_of_day_set = true;
401
402         //u32 dr = m_env.getDayNightRatio();
403         //infostream << "Client: time_of_day=" << time_of_day
404         //              << " time_speed=" << time_speed
405         //              << " dr=" << dr << std::endl;
406 }
407
408 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
409 {
410         /*
411                 u8 version
412                 u8 message_type
413                 u16 sendername length
414                 wstring sendername
415                 u16 length
416                 wstring message
417          */
418
419         ChatMessage *chatMessage = new ChatMessage();
420         u8 version, message_type;
421         *pkt >> version >> message_type;
422
423         if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
424                 delete chatMessage;
425                 return;
426         }
427
428         u64 timestamp;
429         *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
430         chatMessage->timestamp = static_cast<std::time_t>(timestamp);
431
432         chatMessage->type = (ChatMessageType) message_type;
433
434         // @TODO send this to CSM using ChatMessage object
435         if (modsLoaded() && m_script->on_receiving_message(
436                         wide_to_utf8(chatMessage->message))) {
437                 // Message was consumed by CSM and should not be handled by client
438                 delete chatMessage;
439         } else {
440                 pushToChatQueue(chatMessage);
441         }
442 }
443
444 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
445 {
446         /*
447                 u16 count of removed objects
448                 for all removed objects {
449                         u16 id
450                 }
451                 u16 count of added objects
452                 for all added objects {
453                         u16 id
454                         u8 type
455                         u32 initialization data length
456                         string initialization data
457                 }
458         */
459
460         try {
461                 u8 type;
462                 u16 removed_count, added_count, id;
463
464                 // Read removed objects
465                 *pkt >> removed_count;
466
467                 for (u16 i = 0; i < removed_count; i++) {
468                         *pkt >> id;
469                         m_env.removeActiveObject(id);
470                 }
471
472                 // Read added objects
473                 *pkt >> added_count;
474
475                 for (u16 i = 0; i < added_count; i++) {
476                         *pkt >> id >> type;
477                         m_env.addActiveObject(id, type, pkt->readLongString());
478                 }
479         } catch (PacketError &e) {
480                 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
481                                 << ". The packet is unreliable, ignoring" << std::endl;
482         }
483
484         // m_activeobjects_received is false before the first
485         // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
486         m_activeobjects_received = true;
487 }
488
489 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
490 {
491         /*
492                 for all objects
493                 {
494                         u16 id
495                         u16 message length
496                         string message
497                 }
498         */
499         std::string datastring(pkt->getString(0), pkt->getSize());
500         std::istringstream is(datastring, std::ios_base::binary);
501
502         try {
503                 while (is.good()) {
504                         u16 id = readU16(is);
505                         if (!is.good())
506                                 break;
507
508                         std::string message = deSerializeString16(is);
509
510                         // Pass on to the environment
511                         m_env.processActiveObjectMessage(id, message);
512                 }
513         } catch (SerializationError &e) {
514                 errorstream << "Client::handleCommand_ActiveObjectMessages: "
515                         << "caught SerializationError: " << e.what() << std::endl;
516         }
517 }
518
519 void Client::handleCommand_Movement(NetworkPacket* pkt)
520 {
521         LocalPlayer *player = m_env.getLocalPlayer();
522         assert(player != NULL);
523
524         float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
525
526         *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
527                 >> lf >> lfs >> ls >> g;
528
529         player->movement_acceleration_default   = mad * BS;
530         player->movement_acceleration_air       = maa * BS;
531         player->movement_acceleration_fast      = maf * BS;
532         player->movement_speed_walk             = msw * BS;
533         player->movement_speed_crouch           = mscr * BS;
534         player->movement_speed_fast             = msf * BS;
535         player->movement_speed_climb            = mscl * BS;
536         player->movement_speed_jump             = msj * BS;
537         player->movement_liquid_fluidity        = lf * BS;
538         player->movement_liquid_fluidity_smooth = lfs * BS;
539         player->movement_liquid_sink            = ls * BS;
540         player->movement_gravity                = g * BS;
541 }
542
543 void Client::handleCommand_Fov(NetworkPacket *pkt)
544 {
545         f32 fov;
546         bool is_multiplier = false;
547         f32 transition_time = 0.0f;
548
549         *pkt >> fov >> is_multiplier;
550
551         // Wrap transition_time extraction within a
552         // try-catch to preserve backwards compat
553         try {
554                 *pkt >> transition_time;
555         } catch (PacketError &e) {};
556
557         LocalPlayer *player = m_env.getLocalPlayer();
558         assert(player);
559         player->setFov({ fov, is_multiplier, transition_time });
560         m_camera->notifyFovChange();
561 }
562
563 void Client::handleCommand_HP(NetworkPacket *pkt)
564 {
565         LocalPlayer *player = m_env.getLocalPlayer();
566         assert(player != NULL);
567
568         u16 oldhp = player->hp;
569
570         u16 hp;
571         *pkt >> hp;
572         bool damage_effect = true;
573         try {
574                 *pkt >> damage_effect;
575         } catch (PacketError &e) {};
576
577         player->hp = hp;
578
579         if (modsLoaded())
580                 m_script->on_hp_modification(hp);
581
582         if (hp < oldhp) {
583                 // Add to ClientEvent queue
584                 ClientEvent *event = new ClientEvent();
585                 event->type = CE_PLAYER_DAMAGE;
586                 event->player_damage.amount = oldhp - hp;
587                 event->player_damage.effect = damage_effect;
588                 m_client_event_queue.push(event);
589         }
590 }
591
592 void Client::handleCommand_Breath(NetworkPacket* pkt)
593 {
594         LocalPlayer *player = m_env.getLocalPlayer();
595         assert(player != NULL);
596
597         u16 breath;
598
599         *pkt >> breath;
600
601         player->setBreath(breath);
602 }
603
604 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
605 {
606         LocalPlayer *player = m_env.getLocalPlayer();
607         assert(player != NULL);
608
609         v3f pos;
610         f32 pitch, yaw;
611
612         *pkt >> pos >> pitch >> yaw;
613
614         player->setPosition(pos);
615
616         infostream << "Client got TOCLIENT_MOVE_PLAYER"
617                         << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
618                         << " pitch=" << pitch
619                         << " yaw=" << yaw
620                         << std::endl;
621
622         /*
623                 Add to ClientEvent queue.
624                 This has to be sent to the main program because otherwise
625                 it would just force the pitch and yaw values to whatever
626                 the camera points to.
627         */
628         ClientEvent *event = new ClientEvent();
629         event->type = CE_PLAYER_FORCE_MOVE;
630         event->player_force_move.pitch = pitch;
631         event->player_force_move.yaw = yaw;
632         m_client_event_queue.push(event);
633 }
634
635 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
636 {
637         bool set_camera_point_target;
638         v3f camera_point_target;
639
640         *pkt >> set_camera_point_target;
641         *pkt >> camera_point_target;
642
643         ClientEvent *event = new ClientEvent();
644         event->type                                = CE_DEATHSCREEN;
645         event->deathscreen.set_camera_point_target = set_camera_point_target;
646         event->deathscreen.camera_point_target_x   = camera_point_target.X;
647         event->deathscreen.camera_point_target_y   = camera_point_target.Y;
648         event->deathscreen.camera_point_target_z   = camera_point_target.Z;
649         m_client_event_queue.push(event);
650 }
651
652 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
653 {
654         u16 num_files;
655
656         *pkt >> num_files;
657
658         infostream << "Client: Received media announcement: packet size: "
659                         << pkt->getSize() << std::endl;
660
661         if (m_media_downloader == NULL ||
662                         m_media_downloader->isStarted()) {
663                 const char *problem = m_media_downloader ?
664                         "we already saw another announcement" :
665                         "all media has been received already";
666                 errorstream << "Client: Received media announcement but "
667                         << problem << "! "
668                         << " files=" << num_files
669                         << " size=" << pkt->getSize() << std::endl;
670                 return;
671         }
672
673         // Mesh update thread must be stopped while
674         // updating content definitions
675         sanity_check(!m_mesh_update_manager.isRunning());
676
677         for (u16 i = 0; i < num_files; i++) {
678                 std::string name, sha1_base64;
679
680                 *pkt >> name >> sha1_base64;
681
682                 std::string sha1_raw = base64_decode(sha1_base64);
683                 m_media_downloader->addFile(name, sha1_raw);
684         }
685
686         {
687                 std::string str;
688                 *pkt >> str;
689
690                 Strfnd sf(str);
691                 while (!sf.at_end()) {
692                         std::string baseurl = trim(sf.next(","));
693                         if (!baseurl.empty()) {
694                                 m_remote_media_servers.emplace_back(baseurl);
695                                 m_media_downloader->addRemoteServer(baseurl);
696                         }
697                 }
698         }
699
700         m_media_downloader->step(this);
701 }
702
703 void Client::handleCommand_Media(NetworkPacket* pkt)
704 {
705         /*
706                 u16 command
707                 u16 total number of file bunches
708                 u16 index of this bunch
709                 u32 number of files in this bunch
710                 for each file {
711                         u16 length of name
712                         string name
713                         u32 length of data
714                         data
715                 }
716         */
717         u16 num_bunches;
718         u16 bunch_i;
719         u32 num_files;
720
721         *pkt >> num_bunches >> bunch_i >> num_files;
722
723         infostream << "Client: Received files: bunch " << bunch_i << "/"
724                         << num_bunches << " files=" << num_files
725                         << " size=" << pkt->getSize() << std::endl;
726
727         if (num_files == 0)
728                 return;
729
730         bool init_phase = m_media_downloader && m_media_downloader->isStarted();
731
732         if (init_phase) {
733                 // Mesh update thread must be stopped while
734                 // updating content definitions
735                 sanity_check(!m_mesh_update_manager.isRunning());
736         }
737
738         for (u32 i = 0; i < num_files; i++) {
739                 std::string name, data;
740
741                 *pkt >> name;
742                 data = pkt->readLongString();
743
744                 bool ok = false;
745                 if (init_phase) {
746                         ok = m_media_downloader->conventionalTransferDone(name, data, this);
747                 } else {
748                         // Check pending dynamic transfers, one of them must be it
749                         for (const auto &it : m_pending_media_downloads) {
750                                 if (it.second->conventionalTransferDone(name, data, this)) {
751                                         ok = true;
752                                         break;
753                                 }
754                         }
755                 }
756                 if (!ok) {
757                         errorstream << "Client: Received media \"" << name
758                                 << "\" but no downloads pending. " << num_bunches << " bunches, "
759                                 << num_files << " in this one. (init_phase=" << init_phase
760                                 << ")" << std::endl;
761                 }
762         }
763 }
764
765 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
766 {
767         infostream << "Client: Received node definitions: packet size: "
768                         << pkt->getSize() << std::endl;
769
770         // Mesh update thread must be stopped while
771         // updating content definitions
772         sanity_check(!m_mesh_update_manager.isRunning());
773
774         // Decompress node definitions
775         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
776         std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
777         decompressZlib(tmp_is, tmp_os);
778
779         // Deserialize node definitions
780         m_nodedef->deSerialize(tmp_os, m_proto_ver);
781         m_nodedef_received = true;
782 }
783
784 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
785 {
786         infostream << "Client: Received item definitions: packet size: "
787                         << pkt->getSize() << std::endl;
788
789         // Mesh update thread must be stopped while
790         // updating content definitions
791         sanity_check(!m_mesh_update_manager.isRunning());
792
793         // Decompress item definitions
794         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
795         std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
796         decompressZlib(tmp_is, tmp_os);
797
798         // Deserialize node definitions
799         m_itemdef->deSerialize(tmp_os, m_proto_ver);
800         m_itemdef_received = true;
801 }
802
803 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
804 {
805         /*
806                 [0] u32 server_id
807                 [4] u16 name length
808                 [6] char name[len]
809                 [ 6 + len] f32 gain
810                 [10 + len] u8 type
811                 [11 + len] (f32 * 3) pos
812                 [23 + len] u16 object_id
813                 [25 + len] bool loop
814                 [26 + len] f32 fade
815                 [30 + len] f32 pitch
816                 [34 + len] bool ephemeral
817         */
818
819         s32 server_id;
820
821         SimpleSoundSpec spec;
822         SoundLocation type; // 0=local, 1=positional, 2=object
823         v3f pos;
824         u16 object_id;
825         bool ephemeral = false;
826
827         *pkt >> server_id >> spec.name >> spec.gain >> (u8 &)type >> pos >> object_id >> spec.loop;
828
829         try {
830                 *pkt >> spec.fade;
831                 *pkt >> spec.pitch;
832                 *pkt >> ephemeral;
833         } catch (PacketError &e) {};
834
835         // Start playing
836         int client_id = -1;
837         switch(type) {
838         case SoundLocation::Local:
839                 client_id = m_sound->playSound(spec);
840                 break;
841         case SoundLocation::Position:
842                 client_id = m_sound->playSoundAt(spec, pos);
843                 break;
844         case SoundLocation::Object:
845                 {
846                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
847                         if (cao)
848                                 pos = cao->getPosition();
849                         client_id = m_sound->playSoundAt(spec, pos);
850                         break;
851                 }
852         }
853
854         if (client_id != -1) {
855                 // for ephemeral sounds, server_id is not meaningful
856                 if (!ephemeral) {
857                         m_sounds_server_to_client[server_id] = client_id;
858                         m_sounds_client_to_server[client_id] = server_id;
859                 }
860                 if (object_id != 0)
861                         m_sounds_to_objects[client_id] = object_id;
862         }
863 }
864
865 void Client::handleCommand_StopSound(NetworkPacket* pkt)
866 {
867         s32 server_id;
868
869         *pkt >> server_id;
870
871         std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
872         if (i != m_sounds_server_to_client.end()) {
873                 int client_id = i->second;
874                 m_sound->stopSound(client_id);
875         }
876 }
877
878 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
879 {
880         s32 sound_id;
881         float step;
882         float gain;
883
884         *pkt >> sound_id >> step >> gain;
885
886         std::unordered_map<s32, int>::const_iterator i =
887                         m_sounds_server_to_client.find(sound_id);
888
889         if (i != m_sounds_server_to_client.end())
890                 m_sound->fadeSound(i->second, step, gain);
891 }
892
893 void Client::handleCommand_Privileges(NetworkPacket* pkt)
894 {
895         m_privileges.clear();
896         infostream << "Client: Privileges updated: ";
897         u16 num_privileges;
898
899         *pkt >> num_privileges;
900
901         for (u16 i = 0; i < num_privileges; i++) {
902                 std::string priv;
903
904                 *pkt >> priv;
905
906                 m_privileges.insert(priv);
907                 infostream << priv << " ";
908         }
909         infostream << std::endl;
910 }
911
912 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
913 {
914         LocalPlayer *player = m_env.getLocalPlayer();
915         assert(player != NULL);
916
917         // Store formspec in LocalPlayer
918         player->inventory_formspec = pkt->readLongString();
919 }
920
921 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
922 {
923         std::string name;
924         bool keep_inv = true;
925         *pkt >> name >> keep_inv;
926
927         infostream << "Client: Detached inventory update: \"" << name
928                 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
929
930         const auto &inv_it = m_detached_inventories.find(name);
931         if (!keep_inv) {
932                 if (inv_it != m_detached_inventories.end()) {
933                         delete inv_it->second;
934                         m_detached_inventories.erase(inv_it);
935                 }
936                 return;
937         }
938         Inventory *inv = nullptr;
939         if (inv_it == m_detached_inventories.end()) {
940                 inv = new Inventory(m_itemdef);
941                 m_detached_inventories[name] = inv;
942         } else {
943                 inv = inv_it->second;
944         }
945
946         u16 ignore;
947         *pkt >> ignore; // this used to be the length of the following string, ignore it
948
949         std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
950         std::istringstream is(contents, std::ios::binary);
951         inv->deSerialize(is);
952 }
953
954 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
955 {
956         std::string formspec = pkt->readLongString();
957         std::string formname;
958
959         *pkt >> formname;
960
961         ClientEvent *event = new ClientEvent();
962         event->type = CE_SHOW_FORMSPEC;
963         // pointer is required as event is a struct only!
964         // adding a std:string to a struct isn't possible
965         event->show_formspec.formspec = new std::string(formspec);
966         event->show_formspec.formname = new std::string(formname);
967         m_client_event_queue.push(event);
968 }
969
970 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
971 {
972         std::string datastring(pkt->getString(0), pkt->getSize());
973         std::istringstream is(datastring, std::ios_base::binary);
974
975         ParticleParameters p;
976         p.deSerialize(is, m_proto_ver);
977
978         ClientEvent *event = new ClientEvent();
979         event->type           = CE_SPAWN_PARTICLE;
980         event->spawn_particle = new ParticleParameters(p);
981
982         m_client_event_queue.push(event);
983 }
984
985 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
986 {
987         std::string datastring(pkt->getString(0), pkt->getSize());
988         std::istringstream is(datastring, std::ios_base::binary);
989
990         ParticleSpawnerParameters p;
991         u32 server_id;
992         u16 attached_id = 0;
993
994         p.amount             = readU16(is);
995         p.time               = readF32(is);
996
997         // older protocols do not support tweening, and send only
998         // static ranges, so we can't just use the normal serialization
999         // functions for the older values.
1000         p.pos.start.legacyDeSerialize(is);
1001         p.vel.start.legacyDeSerialize(is);
1002         p.acc.start.legacyDeSerialize(is);
1003         p.exptime.start.legacyDeSerialize(is);
1004         p.size.start.legacyDeSerialize(is);
1005
1006         p.collisiondetection = readU8(is);
1007         p.texture.string     = deSerializeString32(is);
1008
1009         server_id = readU32(is);
1010
1011         p.vertical = readU8(is);
1012         p.collision_removal = readU8(is);
1013
1014         attached_id = readU16(is);
1015
1016         p.animation.deSerialize(is, m_proto_ver);
1017         p.glow = readU8(is);
1018         p.object_collision = readU8(is);
1019
1020         bool legacy_format = true;
1021
1022         // This is kinda awful
1023         do {
1024                 u16 tmp_param0 = readU16(is);
1025                 if (is.eof())
1026                         break;
1027                 p.node.param0 = tmp_param0;
1028                 p.node.param2 = readU8(is);
1029                 p.node_tile   = readU8(is);
1030
1031                 // v >= 5.6.0
1032                 f32 tmp_sbias = readF32(is);
1033                 if (is.eof())
1034                         break;
1035
1036                 // initial bias must be stored separately in the stream to preserve
1037                 // backwards compatibility with older clients, which do not support
1038                 // a bias field in their range "format"
1039                 p.pos.start.bias = tmp_sbias;
1040                 p.vel.start.bias = readF32(is);
1041                 p.acc.start.bias = readF32(is);
1042                 p.exptime.start.bias = readF32(is);
1043                 p.size.start.bias = readF32(is);
1044
1045                 p.pos.end.deSerialize(is);
1046                 p.vel.end.deSerialize(is);
1047                 p.acc.end.deSerialize(is);
1048                 p.exptime.end.deSerialize(is);
1049                 p.size.end.deSerialize(is);
1050
1051                 // properties for legacy texture field
1052                 p.texture.deSerialize(is, m_proto_ver, true);
1053
1054                 p.drag.deSerialize(is);
1055                 p.jitter.deSerialize(is);
1056                 p.bounce.deSerialize(is);
1057                 ParticleParamTypes::deSerializeParameterValue(is, p.attractor_kind);
1058                 using ParticleParamTypes::AttractorKind;
1059                 if (p.attractor_kind != AttractorKind::none) {
1060                         p.attract.deSerialize(is);
1061                         p.attractor_origin.deSerialize(is);
1062                         p.attractor_attachment = readU16(is);
1063                         /* we only check the first bit, in order to allow this value
1064                          * to be turned into a bit flag field later if needed */
1065                         p.attractor_kill = !!(readU8(is) & 1);
1066                         if (p.attractor_kind != AttractorKind::point) {
1067                                 p.attractor_direction.deSerialize(is);
1068                                 p.attractor_direction_attachment = readU16(is);
1069                         }
1070                 }
1071                 p.radius.deSerialize(is);
1072
1073                 u16 texpoolsz = readU16(is);
1074                 p.texpool.reserve(texpoolsz);
1075                 for (u16 i = 0; i < texpoolsz; ++i) {
1076                         ServerParticleTexture newtex;
1077                         newtex.deSerialize(is, m_proto_ver);
1078                         p.texpool.push_back(newtex);
1079                 }
1080
1081                 legacy_format = false;
1082         } while(0);
1083
1084         if (legacy_format) {
1085                 // there's no tweening data to be had, so we need to set the
1086                 // legacy params to constant values, otherwise everything old
1087                 // will tween to zero
1088                 p.pos.end = p.pos.start;
1089                 p.vel.end = p.vel.start;
1090                 p.acc.end = p.acc.start;
1091                 p.exptime.end = p.exptime.start;
1092                 p.size.end = p.size.start;
1093         }
1094
1095         auto event = new ClientEvent();
1096         event->type                            = CE_ADD_PARTICLESPAWNER;
1097         event->add_particlespawner.p           = new ParticleSpawnerParameters(p);
1098         event->add_particlespawner.attached_id = attached_id;
1099         event->add_particlespawner.id          = server_id;
1100
1101         m_client_event_queue.push(event);
1102 }
1103
1104
1105 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1106 {
1107         u32 server_id;
1108         *pkt >> server_id;
1109
1110         ClientEvent *event = new ClientEvent();
1111         event->type = CE_DELETE_PARTICLESPAWNER;
1112         event->delete_particlespawner.id = server_id;
1113
1114         m_client_event_queue.push(event);
1115 }
1116
1117 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1118 {
1119         u32 server_id;
1120         u8 type;
1121         v2f pos;
1122         std::string name;
1123         v2f scale;
1124         std::string text;
1125         u32 number;
1126         u32 item;
1127         u32 dir;
1128         v2f align;
1129         v2f offset;
1130         v3f world_pos;
1131         v2s32 size;
1132         s16 z_index = 0;
1133         std::string text2;
1134         u32 style = 0;
1135
1136         *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1137                 >> dir >> align >> offset;
1138         try {
1139                 *pkt >> world_pos;
1140                 *pkt >> size;
1141                 *pkt >> z_index;
1142                 *pkt >> text2;
1143                 *pkt >> style;
1144         } catch(PacketError &e) {};
1145
1146         ClientEvent *event = new ClientEvent();
1147         event->type              = CE_HUDADD;
1148         event->hudadd            = new ClientEventHudAdd();
1149         event->hudadd->server_id = server_id;
1150         event->hudadd->type      = type;
1151         event->hudadd->pos       = pos;
1152         event->hudadd->name      = name;
1153         event->hudadd->scale     = scale;
1154         event->hudadd->text      = text;
1155         event->hudadd->number    = number;
1156         event->hudadd->item      = item;
1157         event->hudadd->dir       = dir;
1158         event->hudadd->align     = align;
1159         event->hudadd->offset    = offset;
1160         event->hudadd->world_pos = world_pos;
1161         event->hudadd->size      = size;
1162         event->hudadd->z_index   = z_index;
1163         event->hudadd->text2     = text2;
1164         event->hudadd->style     = style;
1165         m_client_event_queue.push(event);
1166 }
1167
1168 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1169 {
1170         u32 server_id;
1171
1172         *pkt >> server_id;
1173
1174         ClientEvent *event = new ClientEvent();
1175         event->type     = CE_HUDRM;
1176         event->hudrm.id = server_id;
1177         m_client_event_queue.push(event);
1178 }
1179
1180 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1181 {
1182         std::string sdata;
1183         v2f v2fdata;
1184         v3f v3fdata;
1185         u32 intdata = 0;
1186         v2s32 v2s32data;
1187         u32 server_id;
1188         u8 stat;
1189
1190         *pkt >> server_id >> stat;
1191
1192         // Keep in sync with:server.cpp -> SendHUDChange
1193         switch ((HudElementStat)stat) {
1194                 case HUD_STAT_POS:
1195                 case HUD_STAT_SCALE:
1196                 case HUD_STAT_ALIGN:
1197                 case HUD_STAT_OFFSET:
1198                         *pkt >> v2fdata;
1199                         break;
1200                 case HUD_STAT_NAME:
1201                 case HUD_STAT_TEXT:
1202                 case HUD_STAT_TEXT2:
1203                         *pkt >> sdata;
1204                         break;
1205                 case HUD_STAT_WORLD_POS:
1206                         *pkt >> v3fdata;
1207                         break;
1208                 case HUD_STAT_SIZE:
1209                         *pkt >> v2s32data;
1210                         break;
1211                 default:
1212                         *pkt >> intdata;
1213                         break;
1214         }
1215
1216         ClientEvent *event = new ClientEvent();
1217         event->type                 = CE_HUDCHANGE;
1218         event->hudchange            = new ClientEventHudChange();
1219         event->hudchange->id        = server_id;
1220         event->hudchange->stat      = static_cast<HudElementStat>(stat);
1221         event->hudchange->v2fdata   = v2fdata;
1222         event->hudchange->v3fdata   = v3fdata;
1223         event->hudchange->sdata     = sdata;
1224         event->hudchange->data      = intdata;
1225         event->hudchange->v2s32data = v2s32data;
1226         m_client_event_queue.push(event);
1227 }
1228
1229 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1230 {
1231         u32 flags, mask;
1232
1233         *pkt >> flags >> mask;
1234
1235         LocalPlayer *player = m_env.getLocalPlayer();
1236         assert(player != NULL);
1237
1238         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1239         bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1240
1241         player->hud_flags &= ~mask;
1242         player->hud_flags |= flags;
1243
1244         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1245         bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1246
1247         // Not so satisying code to keep compatibility with old fixed mode system
1248         // -->
1249
1250         // Hide minimap if it has been disabled by the server
1251         if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1252                 // defers a minimap update, therefore only call it if really
1253                 // needed, by checking that minimap was visible before
1254                 m_minimap->setModeIndex(0);
1255
1256         // If radar has been disabled, try to find a non radar mode or fall back to 0
1257         if (m_minimap && m_minimap_radar_disabled_by_server
1258                         && was_minimap_radar_visible) {
1259                 while (m_minimap->getModeIndex() > 0 &&
1260                                 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1261                         m_minimap->nextMode();
1262         }
1263         // <--
1264         // End of 'not so satifying code'
1265 }
1266
1267 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1268 {
1269         u16 param; std::string value;
1270
1271         *pkt >> param >> value;
1272
1273         LocalPlayer *player = m_env.getLocalPlayer();
1274         assert(player != NULL);
1275
1276         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1277                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1278                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1279                         player->hud_hotbar_itemcount = hotbar_itemcount;
1280         }
1281         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1282                 player->hotbar_image = value;
1283         }
1284         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1285                 player->hotbar_selected_image = value;
1286         }
1287 }
1288
1289 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1290 {
1291         if (m_proto_ver < 39) {
1292                 // Handle Protocol 38 and below servers with old set_sky,
1293                 // ensuring the classic look is kept.
1294                 std::string datastring(pkt->getString(0), pkt->getSize());
1295                 std::istringstream is(datastring, std::ios_base::binary);
1296
1297                 SkyboxParams skybox;
1298                 skybox.bgcolor = video::SColor(readARGB8(is));
1299                 skybox.type = std::string(deSerializeString16(is));
1300                 u16 count = readU16(is);
1301
1302                 for (size_t i = 0; i < count; i++)
1303                         skybox.textures.emplace_back(deSerializeString16(is));
1304
1305                 skybox.clouds = true;
1306                 try {
1307                         skybox.clouds = readU8(is);
1308                 } catch (...) {}
1309
1310                 // Use default skybox settings:
1311                 SunParams sun = SkyboxDefaults::getSunDefaults();
1312                 MoonParams moon = SkyboxDefaults::getMoonDefaults();
1313                 StarParams stars = SkyboxDefaults::getStarDefaults();
1314
1315                 // Fix for "regular" skies, as color isn't kept:
1316                 if (skybox.type == "regular") {
1317                         skybox.sky_color = SkyboxDefaults::getSkyColorDefaults();
1318                         skybox.fog_tint_type = "default";
1319                         skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1320                         skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1321                 } else {
1322                         sun.visible = false;
1323                         sun.sunrise_visible = false;
1324                         moon.visible = false;
1325                         stars.visible = false;
1326                 }
1327
1328                 // Skybox, sun, moon and stars ClientEvents:
1329                 ClientEvent *sky_event = new ClientEvent();
1330                 sky_event->type = CE_SET_SKY;
1331                 sky_event->set_sky = new SkyboxParams(skybox);
1332                 m_client_event_queue.push(sky_event);
1333
1334                 ClientEvent *sun_event = new ClientEvent();
1335                 sun_event->type = CE_SET_SUN;
1336                 sun_event->sun_params = new SunParams(sun);
1337                 m_client_event_queue.push(sun_event);
1338
1339                 ClientEvent *moon_event = new ClientEvent();
1340                 moon_event->type = CE_SET_MOON;
1341                 moon_event->moon_params = new MoonParams(moon);
1342                 m_client_event_queue.push(moon_event);
1343
1344                 ClientEvent *star_event = new ClientEvent();
1345                 star_event->type = CE_SET_STARS;
1346                 star_event->star_params = new StarParams(stars);
1347                 m_client_event_queue.push(star_event);
1348         } else {
1349                 SkyboxParams skybox;
1350                 u16 texture_count;
1351                 std::string texture;
1352
1353                 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1354                         skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1355
1356                 if (skybox.type == "skybox") {
1357                         *pkt >> texture_count;
1358                         for (int i = 0; i < texture_count; i++) {
1359                                 *pkt >> texture;
1360                                 skybox.textures.emplace_back(texture);
1361                         }
1362                 }
1363                 else if (skybox.type == "regular") {
1364                         *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1365                                 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1366                                 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1367                                 >> skybox.sky_color.indoors;
1368                 }
1369
1370                 ClientEvent *event = new ClientEvent();
1371                 event->type = CE_SET_SKY;
1372                 event->set_sky = new SkyboxParams(skybox);
1373                 m_client_event_queue.push(event);
1374         }
1375 }
1376
1377 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1378 {
1379         SunParams sun;
1380
1381         *pkt >> sun.visible >> sun.texture>> sun.tonemap
1382                 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1383
1384         ClientEvent *event = new ClientEvent();
1385         event->type        = CE_SET_SUN;
1386         event->sun_params  = new SunParams(sun);
1387         m_client_event_queue.push(event);
1388 }
1389
1390 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1391 {
1392         MoonParams moon;
1393
1394         *pkt >> moon.visible >> moon.texture
1395                 >> moon.tonemap >> moon.scale;
1396
1397         ClientEvent *event = new ClientEvent();
1398         event->type        = CE_SET_MOON;
1399         event->moon_params = new MoonParams(moon);
1400         m_client_event_queue.push(event);
1401 }
1402
1403 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1404 {
1405         StarParams stars = SkyboxDefaults::getStarDefaults();
1406
1407         *pkt >> stars.visible >> stars.count
1408                 >> stars.starcolor >> stars.scale;
1409         try {
1410                 *pkt >> stars.day_opacity;
1411         } catch (PacketError &e) {};
1412
1413         ClientEvent *event = new ClientEvent();
1414         event->type        = CE_SET_STARS;
1415         event->star_params = new StarParams(stars);
1416
1417         m_client_event_queue.push(event);
1418 }
1419
1420 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1421 {
1422         f32 density;
1423         video::SColor color_bright;
1424         video::SColor color_ambient;
1425         f32 height;
1426         f32 thickness;
1427         v2f speed;
1428
1429         *pkt >> density >> color_bright >> color_ambient
1430                         >> height >> thickness >> speed;
1431
1432         ClientEvent *event = new ClientEvent();
1433         event->type                       = CE_CLOUD_PARAMS;
1434         event->cloud_params.density       = density;
1435         // use the underlying u32 representation, because we can't
1436         // use struct members with constructors here, and this way
1437         // we avoid using new() and delete() for no good reason
1438         event->cloud_params.color_bright  = color_bright.color;
1439         event->cloud_params.color_ambient = color_ambient.color;
1440         event->cloud_params.height        = height;
1441         event->cloud_params.thickness     = thickness;
1442         // same here: deconstruct to skip constructor
1443         event->cloud_params.speed_x       = speed.X;
1444         event->cloud_params.speed_y       = speed.Y;
1445         m_client_event_queue.push(event);
1446 }
1447
1448 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1449 {
1450         bool do_override;
1451         u16 day_night_ratio_u;
1452
1453         *pkt >> do_override >> day_night_ratio_u;
1454
1455         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1456
1457         ClientEvent *event = new ClientEvent();
1458         event->type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1459         event->override_day_night_ratio.do_override = do_override;
1460         event->override_day_night_ratio.ratio_f     = day_night_ratio_f;
1461         m_client_event_queue.push(event);
1462 }
1463
1464 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1465 {
1466         LocalPlayer *player = m_env.getLocalPlayer();
1467         assert(player != NULL);
1468
1469         *pkt >> player->local_animations[0];
1470         *pkt >> player->local_animations[1];
1471         *pkt >> player->local_animations[2];
1472         *pkt >> player->local_animations[3];
1473         *pkt >> player->local_animation_speed;
1474
1475         player->last_animation = -1;
1476 }
1477
1478 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1479 {
1480         LocalPlayer *player = m_env.getLocalPlayer();
1481         assert(player != NULL);
1482
1483         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1484 }
1485
1486 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1487 {
1488         u8 type;
1489         u16 num_players;
1490         *pkt >> type >> num_players;
1491         PlayerListModifer notice_type = (PlayerListModifer) type;
1492
1493         for (u16 i = 0; i < num_players; i++) {
1494                 std::string name;
1495                 *pkt >> name;
1496                 switch (notice_type) {
1497                 case PLAYER_LIST_INIT:
1498                 case PLAYER_LIST_ADD:
1499                         m_env.addPlayerName(name);
1500                         continue;
1501                 case PLAYER_LIST_REMOVE:
1502                         m_env.removePlayerName(name);
1503                         continue;
1504                 }
1505         }
1506 }
1507
1508 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1509 {
1510         if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1511                         m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1512                 errorstream << "Client: Received SRP S_B login message,"
1513                         << " but wasn't supposed to (chosen_mech="
1514                         << m_chosen_auth_mech << ")." << std::endl;
1515                 return;
1516         }
1517
1518         char *bytes_M = 0;
1519         size_t len_M = 0;
1520         SRPUser *usr = (SRPUser *) m_auth_data;
1521         std::string s;
1522         std::string B;
1523         *pkt >> s >> B;
1524
1525         infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1526
1527         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1528                 (const unsigned char *) B.c_str(), B.size(),
1529                 (unsigned char **) &bytes_M, &len_M);
1530
1531         if ( !bytes_M ) {
1532                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1533                 return;
1534         }
1535
1536         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1537         resp_pkt << std::string(bytes_M, len_M);
1538         Send(&resp_pkt);
1539 }
1540
1541 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1542 {
1543         LocalPlayer *player = m_env.getLocalPlayer();
1544         assert(player != NULL);
1545
1546         // Store formspec in LocalPlayer
1547         *pkt >> player->formspec_prepend;
1548 }
1549
1550 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1551 {
1552         *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1553
1554         // Restrictions were received -> load mods if it's enabled
1555         // Note: this should be moved after mods receptions from server instead
1556         loadMods();
1557 }
1558
1559 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1560 {
1561         v3f added_vel;
1562
1563         *pkt >> added_vel;
1564
1565         LocalPlayer *player = m_env.getLocalPlayer();
1566         assert(player != NULL);
1567         player->addVelocity(added_vel);
1568 }
1569
1570 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1571 {
1572         std::string raw_hash, filename, filedata;
1573         u32 token;
1574         bool cached;
1575
1576         *pkt >> raw_hash >> filename >> cached;
1577         if (m_proto_ver >= 40)
1578                 *pkt >> token;
1579         else
1580                 filedata = pkt->readLongString();
1581
1582         if (raw_hash.size() != 20 || filename.empty() ||
1583                         (m_proto_ver < 40 && filedata.empty()) ||
1584                         !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1585                 throw PacketError("Illegal filename, data or hash");
1586         }
1587
1588         verbosestream << "Server pushes media file \"" << filename << "\" ";
1589         if (filedata.empty())
1590                 verbosestream << "to be fetched ";
1591         else
1592                 verbosestream << "with " << filedata.size() << " bytes ";
1593         verbosestream << "(cached=" << cached << ")" << std::endl;
1594
1595         if (m_media_pushed_files.count(filename) != 0) {
1596                 // Ignore (but acknowledge). Previously this was for sync purposes,
1597                 // but even in new versions media cannot be replaced at runtime.
1598                 if (m_proto_ver >= 40)
1599                         sendHaveMedia({ token });
1600                 return;
1601         }
1602
1603         if (!filedata.empty()) {
1604                 // LEGACY CODEPATH
1605                 // Compute and check checksum of data
1606                 std::string computed_hash;
1607                 {
1608                         SHA1 ctx;
1609                         ctx.addBytes(filedata.c_str(), filedata.size());
1610                         unsigned char *buf = ctx.getDigest();
1611                         computed_hash.assign((char*) buf, 20);
1612                         free(buf);
1613                 }
1614                 if (raw_hash != computed_hash) {
1615                         verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1616                         return;
1617                 }
1618
1619                 // Actually load media
1620                 loadMedia(filedata, filename, true);
1621                 m_media_pushed_files.insert(filename);
1622
1623                 // Cache file for the next time when this client joins the same server
1624                 if (cached)
1625                         clientMediaUpdateCache(raw_hash, filedata);
1626                 return;
1627         }
1628
1629         m_media_pushed_files.insert(filename);
1630
1631         // create a downloader for this file
1632         auto downloader(std::make_shared<SingleMediaDownloader>(cached));
1633         m_pending_media_downloads.emplace_back(token, downloader);
1634         downloader->addFile(filename, raw_hash);
1635         for (const auto &baseurl : m_remote_media_servers)
1636                 downloader->addRemoteServer(baseurl);
1637
1638         downloader->step(this);
1639 }
1640
1641 /*
1642  * Mod channels
1643  */
1644
1645 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1646 {
1647         std::string channel_name, sender, channel_msg;
1648         *pkt >> channel_name >> sender >> channel_msg;
1649
1650         verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1651                 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1652                 << channel_msg << std::endl;
1653
1654         if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1655                 verbosestream << "Server sent us messages on unregistered channel "
1656                         << channel_name << ", ignoring." << std::endl;
1657                 return;
1658         }
1659
1660         m_script->on_modchannel_message(channel_name, sender, channel_msg);
1661 }
1662
1663 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1664 {
1665         u8 signal_tmp;
1666         ModChannelSignal signal;
1667         std::string channel;
1668
1669         *pkt >> signal_tmp >> channel;
1670
1671         signal = (ModChannelSignal)signal_tmp;
1672
1673         bool valid_signal = true;
1674         // @TODO: send Signal to Lua API
1675         switch (signal) {
1676                 case MODCHANNEL_SIGNAL_JOIN_OK:
1677                         m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1678                         infostream << "Server ack our mod channel join on channel `" << channel
1679                                 << "`, joining." << std::endl;
1680                         break;
1681                 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1682                         // Unable to join, remove channel
1683                         m_modchannel_mgr->leaveChannel(channel, 0);
1684                         infostream << "Server refused our mod channel join on channel `" << channel
1685                                 << "`" << std::endl;
1686                         break;
1687                 case MODCHANNEL_SIGNAL_LEAVE_OK:
1688 #ifndef NDEBUG
1689                         infostream << "Server ack our mod channel leave on channel " << channel
1690                                 << "`, leaving." << std::endl;
1691 #endif
1692                         break;
1693                 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1694                         infostream << "Server refused our mod channel leave on channel `" << channel
1695                                 << "`" << std::endl;
1696                         break;
1697                 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1698 #ifndef NDEBUG
1699                         // Generally unused, but ensure we don't do an implementation error
1700                         infostream << "Server tells us we sent a message on channel `" << channel
1701                                 << "` but we are not registered. Message was dropped." << std::endl;
1702 #endif
1703                         break;
1704                 case MODCHANNEL_SIGNAL_SET_STATE: {
1705                         u8 state;
1706                         *pkt >> state;
1707
1708                         if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1709                                 infostream << "Received wrong channel state " << state
1710                                                 << ", ignoring." << std::endl;
1711                                 return;
1712                         }
1713
1714                         m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1715                         infostream << "Server sets mod channel `" << channel
1716                                         << "` in read-only mode." << std::endl;
1717                         break;
1718                 }
1719                 default:
1720 #ifndef NDEBUG
1721                         warningstream << "Received unhandled mod channel signal ID "
1722                                 << signal << ", ignoring." << std::endl;
1723 #endif
1724                         valid_signal = false;
1725                         break;
1726         }
1727
1728         // If signal is valid, forward it to client side mods
1729         if (valid_signal)
1730                 m_script->on_modchannel_signal(channel, signal);
1731 }
1732
1733 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1734 {
1735         u16 count; // modes
1736         u16 mode;  // wanted current mode index after change
1737
1738         *pkt >> count >> mode;
1739
1740         if (m_minimap)
1741                 m_minimap->clearModes();
1742
1743         for (size_t index = 0; index < count; index++) {
1744                 u16 type;
1745                 std::string label;
1746                 u16 size;
1747                 std::string texture;
1748                 u16 scale;
1749
1750                 *pkt >> type >> label >> size >> texture >> scale;
1751
1752                 if (m_minimap)
1753                         m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1754         }
1755
1756         if (m_minimap)
1757                 m_minimap->setModeIndex(mode);
1758 }
1759
1760 void Client::handleCommand_SetLighting(NetworkPacket *pkt)
1761 {
1762         Lighting& lighting = m_env.getLocalPlayer()->getLighting();
1763
1764         if (pkt->getRemainingBytes() >= 4)
1765                 *pkt >> lighting.shadow_intensity;
1766         if (pkt->getRemainingBytes() >= 4)
1767                 *pkt >> lighting.saturation;
1768         if (pkt->getRemainingBytes() >= 24) {
1769                 *pkt >> lighting.exposure.luminance_min
1770                                 >> lighting.exposure.luminance_max
1771                                 >> lighting.exposure.exposure_correction
1772                                 >> lighting.exposure.speed_dark_bright
1773                                 >> lighting.exposure.speed_bright_dark
1774                                 >> lighting.exposure.center_weight_power;
1775         }
1776 }