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