]> 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
49 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
50 {
51         infostream << "Got deprecated command "
52                         << toClientCommandTable[pkt->getCommand()].name << " from peer "
53                         << pkt->getPeerId() << "!" << std::endl;
54 }
55
56 void Client::handleCommand_Hello(NetworkPacket* pkt)
57 {
58         if (pkt->getSize() < 1)
59                 return;
60
61         u8 serialization_ver;
62         u16 proto_ver;
63         u16 compression_mode;
64         u32 auth_mechs;
65         std::string username_legacy; // for case insensitivity
66         *pkt >> serialization_ver >> compression_mode >> proto_ver
67                 >> auth_mechs >> username_legacy;
68
69         // Chose an auth method we support
70         AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
71
72         infostream << "Client: TOCLIENT_HELLO received with "
73                         << "serialization_ver=" << (u32)serialization_ver
74                         << ", auth_mechs=" << auth_mechs
75                         << ", proto_ver=" << proto_ver
76                         << ", compression_mode=" << compression_mode
77                         << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
78
79         if (!ser_ver_supported(serialization_ver)) {
80                 infostream << "Client: TOCLIENT_HELLO: Server sent "
81                                 << "unsupported ser_fmt_ver"<< std::endl;
82                 return;
83         }
84
85         m_server_ser_ver = serialization_ver;
86         m_proto_ver = proto_ver;
87
88         //TODO verify that username_legacy matches sent username, only
89         // differs in casing (make both uppercase and compare)
90         // This is only neccessary though when we actually want to add casing support
91
92         if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
93                 // we recieved a TOCLIENT_HELLO while auth was already going on
94                 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
95                         << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
96                 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
97                                 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
98                         srp_user_delete((SRPUser *) m_auth_data);
99                         m_auth_data = 0;
100                 }
101         }
102
103         // Authenticate using that method, or abort if there wasn't any method found
104         if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
105                 if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
106                                 !m_simple_singleplayer_mode &&
107                                 !getServerAddress().isLocalhost() &&
108                                 g_settings->getBool("enable_register_confirmation")) {
109                         promptConfirmRegistration(chosen_auth_mechanism);
110                 } else {
111                         startAuth(chosen_auth_mechanism);
112                 }
113         } else {
114                 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
115                 m_access_denied = true;
116                 m_access_denied_reason = "Unknown";
117                 m_con->Disconnect();
118         }
119
120 }
121
122 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
123 {
124         deleteAuthData();
125
126         v3f playerpos;
127         *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
128                 >> m_sudo_auth_methods;
129
130         playerpos -= v3f(0, BS / 2, 0);
131
132         // Set player position
133         LocalPlayer *player = m_env.getLocalPlayer();
134         assert(player != NULL);
135         player->setPosition(playerpos);
136
137         infostream << "Client: received map seed: " << m_map_seed << std::endl;
138         infostream << "Client: received recommended send interval "
139                                         << m_recommended_send_interval<<std::endl;
140
141         // Reply to server
142         /*~ DO NOT TRANSLATE THIS LITERALLY!
143         This is a special string which needs to contain the translation's
144         language code (e.g. "de" for German). */
145         std::string lang = gettext("LANG_CODE");
146         if (lang == "LANG_CODE")
147                 lang = "";
148
149         NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
150         resp_pkt << lang;
151         Send(&resp_pkt);
152
153         m_state = LC_Init;
154 }
155 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
156 {
157         deleteAuthData();
158
159         m_password = m_new_password;
160
161         verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
162
163         // send packet to actually set the password
164         startAuth(AUTH_MECHANISM_FIRST_SRP);
165
166         // reset again
167         m_chosen_auth_mech = AUTH_MECHANISM_NONE;
168 }
169 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
170 {
171         ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
172                         L"Password change denied. Password NOT changed.");
173         pushToChatQueue(chatMessage);
174         // reset everything and be sad
175         deleteAuthData();
176 }
177
178 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
179 {
180         // The server didn't like our password. Note, this needs
181         // to be processed even if the serialisation format has
182         // not been agreed yet, the same as TOCLIENT_INIT.
183         m_access_denied = true;
184         m_access_denied_reason = "Unknown";
185
186         if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
187                 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
188                 // in some places of the server code
189                 if (pkt->getSize() >= 2) {
190                         std::wstring wide_reason;
191                         *pkt >> wide_reason;
192                         m_access_denied_reason = wide_to_utf8(wide_reason);
193                 }
194                 return;
195         }
196
197         if (pkt->getSize() < 1)
198                 return;
199
200         u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
201         *pkt >> denyCode;
202         if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
203                         denyCode == SERVER_ACCESSDENIED_CRASH) {
204                 *pkt >> m_access_denied_reason;
205                 if (m_access_denied_reason.empty()) {
206                         m_access_denied_reason = accessDeniedStrings[denyCode];
207                 }
208                 u8 reconnect;
209                 *pkt >> reconnect;
210                 m_access_denied_reconnect = reconnect & 1;
211         } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
212                 *pkt >> m_access_denied_reason;
213         } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) {
214                 m_access_denied_reason = accessDeniedStrings[denyCode];
215                 m_access_denied_reconnect = true;
216         } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
217                 m_access_denied_reason = accessDeniedStrings[denyCode];
218         } else {
219                 // Allow us to add new error messages to the
220                 // protocol without raising the protocol version, if we want to.
221                 // Until then (which may be never), this is outside
222                 // of the defined protocol.
223                 *pkt >> m_access_denied_reason;
224                 if (m_access_denied_reason.empty()) {
225                         m_access_denied_reason = "Unknown";
226                 }
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;
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         try {
688                 std::string str;
689
690                 *pkt >> str;
691
692                 Strfnd sf(str);
693                 while(!sf.at_end()) {
694                         std::string baseurl = trim(sf.next(","));
695                         if (!baseurl.empty())
696                                 m_media_downloader->addRemoteServer(baseurl);
697                 }
698         }
699         catch(SerializationError& e) {
700                 // not supported by server or turned off
701         }
702
703         m_media_downloader->step(this);
704 }
705
706 void Client::handleCommand_Media(NetworkPacket* pkt)
707 {
708         /*
709                 u16 command
710                 u16 total number of file bunches
711                 u16 index of this bunch
712                 u32 number of files in this bunch
713                 for each file {
714                         u16 length of name
715                         string name
716                         u32 length of data
717                         data
718                 }
719         */
720         u16 num_bunches;
721         u16 bunch_i;
722         u32 num_files;
723
724         *pkt >> num_bunches >> bunch_i >> num_files;
725
726         infostream << "Client: Received files: bunch " << bunch_i << "/"
727                         << num_bunches << " files=" << num_files
728                         << " size=" << pkt->getSize() << std::endl;
729
730         if (num_files == 0)
731                 return;
732
733         if (!m_media_downloader || !m_media_downloader->isStarted()) {
734                 const char *problem = m_media_downloader ?
735                         "media has not been requested" :
736                         "all media has been received already";
737                 errorstream << "Client: Received media but "
738                         << problem << "! "
739                         << " bunch " << bunch_i << "/" << num_bunches
740                         << " files=" << num_files
741                         << " size=" << pkt->getSize() << std::endl;
742                 return;
743         }
744
745         // Mesh update thread must be stopped while
746         // updating content definitions
747         sanity_check(!m_mesh_update_thread.isRunning());
748
749         for (u32 i=0; i < num_files; i++) {
750                 std::string name;
751
752                 *pkt >> name;
753
754                 std::string data = pkt->readLongString();
755
756                 m_media_downloader->conventionalTransferDone(
757                                 name, data, this);
758         }
759 }
760
761 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
762 {
763         infostream << "Client: Received node definitions: packet size: "
764                         << pkt->getSize() << std::endl;
765
766         // Mesh update thread must be stopped while
767         // updating content definitions
768         sanity_check(!m_mesh_update_thread.isRunning());
769
770         // Decompress node definitions
771         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
772         std::ostringstream tmp_os;
773         decompressZlib(tmp_is, tmp_os);
774
775         // Deserialize node definitions
776         std::istringstream tmp_is2(tmp_os.str());
777         m_nodedef->deSerialize(tmp_is2);
778         m_nodedef_received = true;
779 }
780
781 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
782 {
783         infostream << "Client: Received item definitions: packet size: "
784                         << pkt->getSize() << std::endl;
785
786         // Mesh update thread must be stopped while
787         // updating content definitions
788         sanity_check(!m_mesh_update_thread.isRunning());
789
790         // Decompress item definitions
791         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
792         std::ostringstream tmp_os;
793         decompressZlib(tmp_is, tmp_os);
794
795         // Deserialize node definitions
796         std::istringstream tmp_is2(tmp_os.str());
797         m_itemdef->deSerialize(tmp_is2);
798         m_itemdef_received = true;
799 }
800
801 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
802 {
803         /*
804                 [0] u32 server_id
805                 [4] u16 name length
806                 [6] char name[len]
807                 [ 6 + len] f32 gain
808                 [10 + len] u8 type
809                 [11 + len] (f32 * 3) pos
810                 [23 + len] u16 object_id
811                 [25 + len] bool loop
812                 [26 + len] f32 fade
813                 [30 + len] f32 pitch
814                 [34 + len] bool ephemeral
815         */
816
817         s32 server_id;
818         std::string name;
819
820         float gain;
821         u8 type; // 0=local, 1=positional, 2=object
822         v3f pos;
823         u16 object_id;
824         bool loop;
825         float fade = 0.0f;
826         float pitch = 1.0f;
827         bool ephemeral = false;
828
829         *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
830
831         try {
832                 *pkt >> fade;
833                 *pkt >> pitch;
834                 *pkt >> ephemeral;
835         } catch (PacketError &e) {};
836
837         SimpleSoundSpec sound_spec(name, gain, fade, pitch);
838
839         if (m_mods_loaded && m_script->on_play_sound(sound_spec))
840                 return;
841
842         // Start playing
843         int client_id = -1;
844         switch(type) {
845                 case 0: // local
846                         client_id = m_sound->playSound(name, loop, gain, fade, pitch);
847                         break;
848                 case 1: // positional
849                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
850                         break;
851                 case 2:
852                 { // object
853                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
854                         if (cao)
855                                 pos = cao->getPosition();
856                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
857                         break;
858                 }
859                 default:
860                         break;
861         }
862
863         if (client_id != -1) {
864                 // for ephemeral sounds, server_id is not meaningful
865                 if (!ephemeral) {
866                         m_sounds_server_to_client[server_id] = client_id;
867                         m_sounds_client_to_server[client_id] = server_id;
868                 }
869                 if (object_id != 0)
870                         m_sounds_to_objects[client_id] = object_id;
871         }
872 }
873
874 void Client::handleCommand_StopSound(NetworkPacket* pkt)
875 {
876         s32 server_id;
877
878         *pkt >> server_id;
879
880         std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
881         if (i != m_sounds_server_to_client.end()) {
882                 int client_id = i->second;
883                 m_sound->stopSound(client_id);
884         }
885 }
886
887 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
888 {
889         s32 sound_id;
890         float step;
891         float gain;
892
893         *pkt >> sound_id >> step >> gain;
894
895         std::unordered_map<s32, int>::const_iterator i =
896                         m_sounds_server_to_client.find(sound_id);
897
898         if (i != m_sounds_server_to_client.end())
899                 m_sound->fadeSound(i->second, step, gain);
900 }
901
902 void Client::handleCommand_Privileges(NetworkPacket* pkt)
903 {
904         m_privileges.clear();
905         infostream << "Client: Privileges updated: ";
906         u16 num_privileges;
907
908         *pkt >> num_privileges;
909
910         for (u16 i = 0; i < num_privileges; i++) {
911                 std::string priv;
912
913                 *pkt >> priv;
914
915                 m_privileges.insert(priv);
916                 infostream << priv << " ";
917         }
918         infostream << std::endl;
919 }
920
921 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
922 {
923         LocalPlayer *player = m_env.getLocalPlayer();
924         assert(player != NULL);
925
926         // Store formspec in LocalPlayer
927         player->inventory_formspec = pkt->readLongString();
928 }
929
930 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
931 {
932         std::string name;
933         bool keep_inv = true;
934         *pkt >> name >> keep_inv;
935
936         infostream << "Client: Detached inventory update: \"" << name
937                 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
938
939         const auto &inv_it = m_detached_inventories.find(name);
940         if (!keep_inv) {
941                 if (inv_it != m_detached_inventories.end()) {
942                         delete inv_it->second;
943                         m_detached_inventories.erase(inv_it);
944                 }
945                 return;
946         }
947         Inventory *inv = nullptr;
948         if (inv_it == m_detached_inventories.end()) {
949                 inv = new Inventory(m_itemdef);
950                 m_detached_inventories[name] = inv;
951         } else {
952                 inv = inv_it->second;
953         }
954
955         u16 ignore;
956         *pkt >> ignore; // this used to be the length of the following string, ignore it
957
958         std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
959         std::istringstream is(contents, std::ios::binary);
960         inv->deSerialize(is);
961 }
962
963 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
964 {
965         std::string formspec = pkt->readLongString();
966         std::string formname;
967
968         *pkt >> formname;
969
970         ClientEvent *event = new ClientEvent();
971         event->type = CE_SHOW_FORMSPEC;
972         // pointer is required as event is a struct only!
973         // adding a std:string to a struct isn't possible
974         event->show_formspec.formspec = new std::string(formspec);
975         event->show_formspec.formname = new std::string(formname);
976         m_client_event_queue.push(event);
977 }
978
979 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
980 {
981         std::string datastring(pkt->getString(0), pkt->getSize());
982         std::istringstream is(datastring, std::ios_base::binary);
983
984         ParticleParameters p;
985         p.deSerialize(is, m_proto_ver);
986
987         ClientEvent *event = new ClientEvent();
988         event->type           = CE_SPAWN_PARTICLE;
989         event->spawn_particle = new ParticleParameters(p);
990
991         if (m_mods_loaded && m_script->on_spawn_particle(*event->spawn_particle))
992                 return;
993
994         m_client_event_queue.push(event);
995 }
996
997 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
998 {
999         std::string datastring(pkt->getString(0), pkt->getSize());
1000         std::istringstream is(datastring, std::ios_base::binary);
1001
1002         ParticleSpawnerParameters p;
1003         u32 server_id;
1004         u16 attached_id = 0;
1005
1006         p.amount             = readU16(is);
1007         p.time               = readF32(is);
1008         p.minpos             = readV3F32(is);
1009         p.maxpos             = readV3F32(is);
1010         p.minvel             = readV3F32(is);
1011         p.maxvel             = readV3F32(is);
1012         p.minacc             = readV3F32(is);
1013         p.maxacc             = readV3F32(is);
1014         p.minexptime         = readF32(is);
1015         p.maxexptime         = readF32(is);
1016         p.minsize            = readF32(is);
1017         p.maxsize            = readF32(is);
1018         p.collisiondetection = readU8(is);
1019         p.texture            = deSerializeString32(is);
1020
1021         server_id = readU32(is);
1022
1023         p.vertical = readU8(is);
1024         p.collision_removal = readU8(is);
1025
1026         attached_id = readU16(is);
1027
1028         p.animation.deSerialize(is, m_proto_ver);
1029         p.glow = readU8(is);
1030         p.object_collision = readU8(is);
1031
1032         // This is kinda awful
1033         do {
1034                 u16 tmp_param0 = readU16(is);
1035                 if (is.eof())
1036                         break;
1037                 p.node.param0 = tmp_param0;
1038                 p.node.param2 = readU8(is);
1039                 p.node_tile   = readU8(is);
1040         } while (0);
1041
1042         auto event = new ClientEvent();
1043         event->type                            = CE_ADD_PARTICLESPAWNER;
1044         event->add_particlespawner.p           = new ParticleSpawnerParameters(p);
1045         event->add_particlespawner.attached_id = attached_id;
1046         event->add_particlespawner.id          = server_id;
1047
1048         m_client_event_queue.push(event);
1049 }
1050
1051
1052 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1053 {
1054         u32 server_id;
1055         *pkt >> server_id;
1056
1057         ClientEvent *event = new ClientEvent();
1058         event->type = CE_DELETE_PARTICLESPAWNER;
1059         event->delete_particlespawner.id = server_id;
1060
1061         m_client_event_queue.push(event);
1062 }
1063
1064 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1065 {
1066         u32 server_id;
1067         u8 type;
1068         v2f pos;
1069         std::string name;
1070         v2f scale;
1071         std::string text;
1072         u32 number;
1073         u32 item;
1074         u32 dir;
1075         v2f align;
1076         v2f offset;
1077         v3f world_pos;
1078         v2s32 size;
1079         s16 z_index = 0;
1080         std::string text2;
1081
1082         *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1083                 >> dir >> align >> offset;
1084         try {
1085                 *pkt >> world_pos;
1086                 *pkt >> size;
1087                 *pkt >> z_index;
1088                 *pkt >> text2;
1089         } catch(PacketError &e) {};
1090
1091         ClientEvent *event = new ClientEvent();
1092         event->type              = CE_HUDADD;
1093         event->hudadd            = new ClientEventHudAdd();
1094         event->hudadd->server_id = server_id;
1095         event->hudadd->type      = type;
1096         event->hudadd->pos       = pos;
1097         event->hudadd->name      = name;
1098         event->hudadd->scale     = scale;
1099         event->hudadd->text      = text;
1100         event->hudadd->number    = number;
1101         event->hudadd->item      = item;
1102         event->hudadd->dir       = dir;
1103         event->hudadd->align     = align;
1104         event->hudadd->offset    = offset;
1105         event->hudadd->world_pos = world_pos;
1106         event->hudadd->size      = size;
1107         event->hudadd->z_index   = z_index;
1108         event->hudadd->text2     = text2;
1109         m_client_event_queue.push(event);
1110 }
1111
1112 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1113 {
1114         u32 server_id;
1115
1116         *pkt >> server_id;
1117
1118         ClientEvent *event = new ClientEvent();
1119         event->type     = CE_HUDRM;
1120         event->hudrm.id = server_id;
1121         m_client_event_queue.push(event);
1122 }
1123
1124 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1125 {
1126         std::string sdata;
1127         v2f v2fdata;
1128         v3f v3fdata;
1129         u32 intdata = 0;
1130         v2s32 v2s32data;
1131         u32 server_id;
1132         u8 stat;
1133
1134         *pkt >> server_id >> stat;
1135
1136         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1137                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1138                 *pkt >> v2fdata;
1139         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1140                 *pkt >> sdata;
1141         else if (stat == HUD_STAT_WORLD_POS)
1142                 *pkt >> v3fdata;
1143         else if (stat == HUD_STAT_SIZE )
1144                 *pkt >> v2s32data;
1145         else
1146                 *pkt >> intdata;
1147
1148         ClientEvent *event = new ClientEvent();
1149         event->type                 = CE_HUDCHANGE;
1150         event->hudchange            = new ClientEventHudChange();
1151         event->hudchange->id        = server_id;
1152         event->hudchange->stat      = static_cast<HudElementStat>(stat);
1153         event->hudchange->v2fdata   = v2fdata;
1154         event->hudchange->v3fdata   = v3fdata;
1155         event->hudchange->sdata     = sdata;
1156         event->hudchange->data      = intdata;
1157         event->hudchange->v2s32data = v2s32data;
1158         m_client_event_queue.push(event);
1159 }
1160
1161 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1162 {
1163         u32 flags, mask;
1164
1165         *pkt >> flags >> mask;
1166
1167         LocalPlayer *player = m_env.getLocalPlayer();
1168         assert(player != NULL);
1169
1170         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1171         bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1172
1173         player->hud_flags &= ~mask;
1174         player->hud_flags |= flags;
1175
1176         if (g_settings->getBool("hud_flags_bypass"))
1177                 player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE     | HUD_FLAG_HEALTHBAR_VISIBLE |
1178                         HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
1179                         HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE   |
1180                         HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1181
1182         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1183         bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1184
1185         // Not so satisying code to keep compatibility with old fixed mode system
1186         // -->
1187
1188         // Hide minimap if it has been disabled by the server
1189         if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1190                 // defers a minimap update, therefore only call it if really
1191                 // needed, by checking that minimap was visible before
1192                 m_minimap->setModeIndex(0);
1193
1194         // If radar has been disabled, try to find a non radar mode or fall back to 0
1195         if (m_minimap && m_minimap_radar_disabled_by_server
1196                         && was_minimap_radar_visible) {
1197                 while (m_minimap->getModeIndex() > 0 &&
1198                                 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1199                         m_minimap->nextMode();
1200         }
1201         // <--
1202         // End of 'not so satifying code'
1203 }
1204
1205 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1206 {
1207         u16 param; std::string value;
1208
1209         *pkt >> param >> value;
1210
1211         LocalPlayer *player = m_env.getLocalPlayer();
1212         assert(player != NULL);
1213
1214         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1215                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1216                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1217                         player->hud_hotbar_itemcount = hotbar_itemcount;
1218         }
1219         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1220                 player->hotbar_image = value;
1221         }
1222         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1223                 player->hotbar_selected_image = value;
1224         }
1225 }
1226
1227 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1228 {
1229         if (m_proto_ver < 39) {
1230                 // Handle Protocol 38 and below servers with old set_sky,
1231                 // ensuring the classic look is kept.
1232                 std::string datastring(pkt->getString(0), pkt->getSize());
1233                 std::istringstream is(datastring, std::ios_base::binary);
1234
1235                 SkyboxParams skybox;
1236                 skybox.bgcolor = video::SColor(readARGB8(is));
1237                 skybox.type = std::string(deSerializeString16(is));
1238                 u16 count = readU16(is);
1239
1240                 for (size_t i = 0; i < count; i++)
1241                         skybox.textures.emplace_back(deSerializeString16(is));
1242
1243                 skybox.clouds = true;
1244                 try {
1245                         skybox.clouds = readU8(is);
1246                 } catch (...) {}
1247
1248                 // Use default skybox settings:
1249                 SkyboxDefaults sky_defaults;
1250                 SunParams sun = sky_defaults.getSunDefaults();
1251                 MoonParams moon = sky_defaults.getMoonDefaults();
1252                 StarParams stars = sky_defaults.getStarDefaults();
1253
1254                 // Fix for "regular" skies, as color isn't kept:
1255                 if (skybox.type == "regular") {
1256                         skybox.sky_color = sky_defaults.getSkyColorDefaults();
1257                         skybox.fog_tint_type = "default";
1258                         skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1259                         skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1260                 }
1261                 else {
1262                         sun.visible = false;
1263                         sun.sunrise_visible = false;
1264                         moon.visible = false;
1265                         stars.visible = false;
1266                 }
1267
1268                 // Skybox, sun, moon and stars ClientEvents:
1269                 ClientEvent *sky_event = new ClientEvent();
1270                 sky_event->type = CE_SET_SKY;
1271                 sky_event->set_sky = new SkyboxParams(skybox);
1272                 m_client_event_queue.push(sky_event);
1273
1274                 ClientEvent *sun_event = new ClientEvent();
1275                 sun_event->type = CE_SET_SUN;
1276                 sun_event->sun_params = new SunParams(sun);
1277                 m_client_event_queue.push(sun_event);
1278
1279                 ClientEvent *moon_event = new ClientEvent();
1280                 moon_event->type = CE_SET_MOON;
1281                 moon_event->moon_params = new MoonParams(moon);
1282                 m_client_event_queue.push(moon_event);
1283
1284                 ClientEvent *star_event = new ClientEvent();
1285                 star_event->type = CE_SET_STARS;
1286                 star_event->star_params = new StarParams(stars);
1287                 m_client_event_queue.push(star_event);
1288         } else {
1289                 SkyboxParams skybox;
1290                 u16 texture_count;
1291                 std::string texture;
1292
1293                 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1294                         skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1295
1296                 if (skybox.type == "skybox") {
1297                         *pkt >> texture_count;
1298                         for (int i = 0; i < texture_count; i++) {
1299                                 *pkt >> texture;
1300                                 skybox.textures.emplace_back(texture);
1301                         }
1302                 }
1303                 else if (skybox.type == "regular") {
1304                         *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1305                                 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1306                                 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1307                                 >> skybox.sky_color.indoors;
1308                 }
1309
1310                 ClientEvent *event = new ClientEvent();
1311                 event->type = CE_SET_SKY;
1312                 event->set_sky = new SkyboxParams(skybox);
1313                 m_client_event_queue.push(event);
1314         }
1315 }
1316
1317 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1318 {
1319         SunParams sun;
1320
1321         *pkt >> sun.visible >> sun.texture>> sun.tonemap
1322                 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1323
1324         ClientEvent *event = new ClientEvent();
1325         event->type        = CE_SET_SUN;
1326         event->sun_params  = new SunParams(sun);
1327         m_client_event_queue.push(event);
1328 }
1329
1330 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1331 {
1332         MoonParams moon;
1333
1334         *pkt >> moon.visible >> moon.texture
1335                 >> moon.tonemap >> moon.scale;
1336
1337         ClientEvent *event = new ClientEvent();
1338         event->type        = CE_SET_MOON;
1339         event->moon_params = new MoonParams(moon);
1340         m_client_event_queue.push(event);
1341 }
1342
1343 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1344 {
1345         StarParams stars;
1346
1347         *pkt >> stars.visible >> stars.count
1348                 >> stars.starcolor >> stars.scale;
1349
1350         ClientEvent *event = new ClientEvent();
1351         event->type        = CE_SET_STARS;
1352         event->star_params = new StarParams(stars);
1353
1354         m_client_event_queue.push(event);
1355 }
1356
1357 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1358 {
1359         f32 density;
1360         video::SColor color_bright;
1361         video::SColor color_ambient;
1362         f32 height;
1363         f32 thickness;
1364         v2f speed;
1365
1366         *pkt >> density >> color_bright >> color_ambient
1367                         >> height >> thickness >> speed;
1368
1369         ClientEvent *event = new ClientEvent();
1370         event->type                       = CE_CLOUD_PARAMS;
1371         event->cloud_params.density       = density;
1372         // use the underlying u32 representation, because we can't
1373         // use struct members with constructors here, and this way
1374         // we avoid using new() and delete() for no good reason
1375         event->cloud_params.color_bright  = color_bright.color;
1376         event->cloud_params.color_ambient = color_ambient.color;
1377         event->cloud_params.height        = height;
1378         event->cloud_params.thickness     = thickness;
1379         // same here: deconstruct to skip constructor
1380         event->cloud_params.speed_x       = speed.X;
1381         event->cloud_params.speed_y       = speed.Y;
1382         m_client_event_queue.push(event);
1383 }
1384
1385 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1386 {
1387         bool do_override;
1388         u16 day_night_ratio_u;
1389
1390         *pkt >> do_override >> day_night_ratio_u;
1391
1392         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1393
1394         ClientEvent *event = new ClientEvent();
1395         event->type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1396         event->override_day_night_ratio.do_override = do_override;
1397         event->override_day_night_ratio.ratio_f     = day_night_ratio_f;
1398         m_client_event_queue.push(event);
1399 }
1400
1401 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1402 {
1403         LocalPlayer *player = m_env.getLocalPlayer();
1404         assert(player != NULL);
1405
1406         *pkt >> player->local_animations[0];
1407         *pkt >> player->local_animations[1];
1408         *pkt >> player->local_animations[2];
1409         *pkt >> player->local_animations[3];
1410         *pkt >> player->local_animation_speed;
1411 }
1412
1413 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1414 {
1415         LocalPlayer *player = m_env.getLocalPlayer();
1416         assert(player != NULL);
1417
1418         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1419 }
1420
1421 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1422 {
1423         u8 type;
1424         u16 num_players;
1425         *pkt >> type >> num_players;
1426         PlayerListModifer notice_type = (PlayerListModifer) type;
1427
1428         for (u16 i = 0; i < num_players; i++) {
1429                 std::string name;
1430                 *pkt >> name;
1431                 switch (notice_type) {
1432                 case PLAYER_LIST_INIT:
1433                 case PLAYER_LIST_ADD:
1434                         m_env.addPlayerName(name);
1435                         continue;
1436                 case PLAYER_LIST_REMOVE:
1437                         m_env.removePlayerName(name);
1438                         continue;
1439                 }
1440         }
1441 }
1442
1443 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1444 {
1445         if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1446                         m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1447                 errorstream << "Client: Received SRP S_B login message,"
1448                         << " but wasn't supposed to (chosen_mech="
1449                         << m_chosen_auth_mech << ")." << std::endl;
1450                 return;
1451         }
1452
1453         char *bytes_M = 0;
1454         size_t len_M = 0;
1455         SRPUser *usr = (SRPUser *) m_auth_data;
1456         std::string s;
1457         std::string B;
1458         *pkt >> s >> B;
1459
1460         infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1461
1462         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1463                 (const unsigned char *) B.c_str(), B.size(),
1464                 (unsigned char **) &bytes_M, &len_M);
1465
1466         if ( !bytes_M ) {
1467                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1468                 return;
1469         }
1470
1471         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1472         resp_pkt << std::string(bytes_M, len_M);
1473         Send(&resp_pkt);
1474 }
1475
1476 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1477 {
1478         LocalPlayer *player = m_env.getLocalPlayer();
1479         assert(player != NULL);
1480
1481         // Store formspec in LocalPlayer
1482         *pkt >> player->formspec_prepend;
1483 }
1484
1485 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1486 {
1487         *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1488
1489         // Restrictions were received -> load mods if it's enabled
1490         // Note: this should be moved after mods receptions from server instead
1491         loadMods();
1492 }
1493
1494 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1495 {
1496         if (g_settings->getBool("antiknockback"))
1497                 return;
1498         v3f added_vel;
1499
1500         *pkt >> added_vel;
1501
1502         LocalPlayer *player = m_env.getLocalPlayer();
1503         assert(player != NULL);
1504         player->addVelocity(added_vel);
1505 }
1506
1507 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1508 {
1509         std::string raw_hash, filename, filedata;
1510         bool cached;
1511
1512         *pkt >> raw_hash >> filename >> cached;
1513         filedata = pkt->readLongString();
1514
1515         if (raw_hash.size() != 20 || filedata.empty() || filename.empty() ||
1516                         !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1517                 throw PacketError("Illegal filename, data or hash");
1518         }
1519
1520         verbosestream << "Server pushes media file \"" << filename << "\" with "
1521                 << filedata.size() << " bytes of data (cached=" << cached
1522                 << ")" << std::endl;
1523
1524         if (m_media_pushed_files.count(filename) != 0) {
1525                 // Silently ignore for synchronization purposes
1526                 return;
1527         }
1528
1529         // Compute and check checksum of data
1530         std::string computed_hash;
1531         {
1532                 SHA1 ctx;
1533                 ctx.addBytes(filedata.c_str(), filedata.size());
1534                 unsigned char *buf = ctx.getDigest();
1535                 computed_hash.assign((char*) buf, 20);
1536                 free(buf);
1537         }
1538         if (raw_hash != computed_hash) {
1539                 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1540                 return;
1541         }
1542
1543         // Actually load media
1544         loadMedia(filedata, filename, true);
1545         m_media_pushed_files.insert(filename);
1546
1547         // Cache file for the next time when this client joins the same server
1548         if (cached)
1549                 clientMediaUpdateCache(raw_hash, filedata);
1550 }
1551
1552 /*
1553  * Mod channels
1554  */
1555
1556 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1557 {
1558         std::string channel_name, sender, channel_msg;
1559         *pkt >> channel_name >> sender >> channel_msg;
1560
1561         verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1562                 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1563                 << channel_msg << std::endl;
1564
1565         if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1566                 verbosestream << "Server sent us messages on unregistered channel "
1567                         << channel_name << ", ignoring." << std::endl;
1568                 return;
1569         }
1570
1571         m_script->on_modchannel_message(channel_name, sender, channel_msg);
1572 }
1573
1574 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1575 {
1576         u8 signal_tmp;
1577         ModChannelSignal signal;
1578         std::string channel;
1579
1580         *pkt >> signal_tmp >> channel;
1581
1582         signal = (ModChannelSignal)signal_tmp;
1583
1584         bool valid_signal = true;
1585         // @TODO: send Signal to Lua API
1586         switch (signal) {
1587                 case MODCHANNEL_SIGNAL_JOIN_OK:
1588                         m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1589                         infostream << "Server ack our mod channel join on channel `" << channel
1590                                 << "`, joining." << std::endl;
1591                         break;
1592                 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1593                         // Unable to join, remove channel
1594                         m_modchannel_mgr->leaveChannel(channel, 0);
1595                         infostream << "Server refused our mod channel join on channel `" << channel
1596                                 << "`" << std::endl;
1597                         break;
1598                 case MODCHANNEL_SIGNAL_LEAVE_OK:
1599 #ifndef NDEBUG
1600                         infostream << "Server ack our mod channel leave on channel " << channel
1601                                 << "`, leaving." << std::endl;
1602 #endif
1603                         break;
1604                 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1605                         infostream << "Server refused our mod channel leave on channel `" << channel
1606                                 << "`" << std::endl;
1607                         break;
1608                 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1609 #ifndef NDEBUG
1610                         // Generally unused, but ensure we don't do an implementation error
1611                         infostream << "Server tells us we sent a message on channel `" << channel
1612                                 << "` but we are not registered. Message was dropped." << std::endl;
1613 #endif
1614                         break;
1615                 case MODCHANNEL_SIGNAL_SET_STATE: {
1616                         u8 state;
1617                         *pkt >> state;
1618
1619                         if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1620                                 infostream << "Received wrong channel state " << state
1621                                                 << ", ignoring." << std::endl;
1622                                 return;
1623                         }
1624
1625                         m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1626                         infostream << "Server sets mod channel `" << channel
1627                                         << "` in read-only mode." << std::endl;
1628                         break;
1629                 }
1630                 default:
1631 #ifndef NDEBUG
1632                         warningstream << "Received unhandled mod channel signal ID "
1633                                 << signal << ", ignoring." << std::endl;
1634 #endif
1635                         valid_signal = false;
1636                         break;
1637         }
1638
1639         // If signal is valid, forward it to client side mods
1640         if (valid_signal)
1641                 m_script->on_modchannel_signal(channel, signal);
1642 }
1643
1644 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1645 {
1646         u16 count; // modes
1647         u16 mode;  // wanted current mode index after change
1648
1649         *pkt >> count >> mode;
1650
1651         if (m_minimap)
1652                 m_minimap->clearModes();
1653
1654         for (size_t index = 0; index < count; index++) {
1655                 u16 type;
1656                 std::string label;
1657                 u16 size;
1658                 std::string texture;
1659                 u16 scale;
1660
1661                 *pkt >> type >> label >> size >> texture >> scale;
1662
1663                 if (m_minimap)
1664                         m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1665         }
1666
1667         if (m_minimap)
1668                 m_minimap->setModeIndex(mode);
1669 }