]> git.lizzy.rs Git - dragonfireclient.git/blob - src/network/clientpackethandler.cpp
Merge branch 'master' into master
[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 = deSerializeString(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         // Start playing
838         int client_id = -1;
839         switch(type) {
840                 case 0: // local
841                         client_id = m_sound->playSound(name, loop, gain, fade, pitch);
842                         break;
843                 case 1: // positional
844                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
845                         break;
846                 case 2:
847                 { // object
848                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
849                         if (cao)
850                                 pos = cao->getPosition();
851                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
852                         break;
853                 }
854                 default:
855                         break;
856         }
857
858         if (client_id != -1) {
859                 // for ephemeral sounds, server_id is not meaningful
860                 if (!ephemeral) {
861                         m_sounds_server_to_client[server_id] = client_id;
862                         m_sounds_client_to_server[client_id] = server_id;
863                 }
864                 if (object_id != 0)
865                         m_sounds_to_objects[client_id] = object_id;
866         }
867 }
868
869 void Client::handleCommand_StopSound(NetworkPacket* pkt)
870 {
871         s32 server_id;
872
873         *pkt >> server_id;
874
875         std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
876         if (i != m_sounds_server_to_client.end()) {
877                 int client_id = i->second;
878                 m_sound->stopSound(client_id);
879         }
880 }
881
882 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
883 {
884         s32 sound_id;
885         float step;
886         float gain;
887
888         *pkt >> sound_id >> step >> gain;
889
890         std::unordered_map<s32, int>::const_iterator i =
891                         m_sounds_server_to_client.find(sound_id);
892
893         if (i != m_sounds_server_to_client.end())
894                 m_sound->fadeSound(i->second, step, gain);
895 }
896
897 void Client::handleCommand_Privileges(NetworkPacket* pkt)
898 {
899         m_privileges.clear();
900         infostream << "Client: Privileges updated: ";
901         u16 num_privileges;
902
903         *pkt >> num_privileges;
904
905         for (u16 i = 0; i < num_privileges; i++) {
906                 std::string priv;
907
908                 *pkt >> priv;
909
910                 m_privileges.insert(priv);
911                 infostream << priv << " ";
912         }
913         infostream << std::endl;
914 }
915
916 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
917 {
918         LocalPlayer *player = m_env.getLocalPlayer();
919         assert(player != NULL);
920
921         // Store formspec in LocalPlayer
922         player->inventory_formspec = pkt->readLongString();
923 }
924
925 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
926 {
927         std::string name;
928         bool keep_inv = true;
929         *pkt >> name >> keep_inv;
930
931         infostream << "Client: Detached inventory update: \"" << name
932                 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
933
934         const auto &inv_it = m_detached_inventories.find(name);
935         if (!keep_inv) {
936                 if (inv_it != m_detached_inventories.end()) {
937                         delete inv_it->second;
938                         m_detached_inventories.erase(inv_it);
939                 }
940                 return;
941         }
942         Inventory *inv = nullptr;
943         if (inv_it == m_detached_inventories.end()) {
944                 inv = new Inventory(m_itemdef);
945                 m_detached_inventories[name] = inv;
946         } else {
947                 inv = inv_it->second;
948         }
949
950         u16 ignore;
951         *pkt >> ignore; // this used to be the length of the following string, ignore it
952
953         std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
954         std::istringstream is(contents, std::ios::binary);
955         inv->deSerialize(is);
956 }
957
958 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
959 {
960         std::string formspec = pkt->readLongString();
961         std::string formname;
962
963         *pkt >> formname;
964
965         ClientEvent *event = new ClientEvent();
966         event->type = CE_SHOW_FORMSPEC;
967         // pointer is required as event is a struct only!
968         // adding a std:string to a struct isn't possible
969         event->show_formspec.formspec = new std::string(formspec);
970         event->show_formspec.formname = new std::string(formname);
971         m_client_event_queue.push(event);
972 }
973
974 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
975 {
976         std::string datastring(pkt->getString(0), pkt->getSize());
977         std::istringstream is(datastring, std::ios_base::binary);
978
979         ParticleParameters p;
980         p.deSerialize(is, m_proto_ver);
981
982         ClientEvent *event = new ClientEvent();
983         event->type           = CE_SPAWN_PARTICLE;
984         event->spawn_particle = new ParticleParameters(p);
985         
986         if (g_settings->getBool("log_particles")) {
987                 std::cout << p.pos.X << " " << p.pos.Y << " " << p.pos.Z << std::endl;
988         }
989         
990         m_client_event_queue.push(event);
991 }
992
993 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
994 {
995         std::string datastring(pkt->getString(0), pkt->getSize());
996         std::istringstream is(datastring, std::ios_base::binary);
997
998         ParticleSpawnerParameters p;
999         u32 server_id;
1000         u16 attached_id = 0;
1001
1002         p.amount             = readU16(is);
1003         p.time               = readF32(is);
1004         p.minpos             = readV3F32(is);
1005         p.maxpos             = readV3F32(is);
1006         p.minvel             = readV3F32(is);
1007         p.maxvel             = readV3F32(is);
1008         p.minacc             = readV3F32(is);
1009         p.maxacc             = readV3F32(is);
1010         p.minexptime         = readF32(is);
1011         p.maxexptime         = readF32(is);
1012         p.minsize            = readF32(is);
1013         p.maxsize            = readF32(is);
1014         p.collisiondetection = readU8(is);
1015         p.texture            = deSerializeLongString(is);
1016
1017         server_id = readU32(is);
1018
1019         p.vertical = readU8(is);
1020         p.collision_removal = readU8(is);
1021
1022         attached_id = readU16(is);
1023
1024         p.animation.deSerialize(is, m_proto_ver);
1025         p.glow = readU8(is);
1026         p.object_collision = readU8(is);
1027
1028         // This is kinda awful
1029         do {
1030                 u16 tmp_param0 = readU16(is);
1031                 if (is.eof())
1032                         break;
1033                 p.node.param0 = tmp_param0;
1034                 p.node.param2 = readU8(is);
1035                 p.node_tile   = readU8(is);
1036         } while (0);
1037
1038         auto event = new ClientEvent();
1039         event->type                            = CE_ADD_PARTICLESPAWNER;
1040         event->add_particlespawner.p           = new ParticleSpawnerParameters(p);
1041         event->add_particlespawner.attached_id = attached_id;
1042         event->add_particlespawner.id          = server_id;
1043
1044         m_client_event_queue.push(event);
1045 }
1046
1047
1048 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1049 {
1050         u32 server_id;
1051         *pkt >> server_id;
1052
1053         ClientEvent *event = new ClientEvent();
1054         event->type = CE_DELETE_PARTICLESPAWNER;
1055         event->delete_particlespawner.id = server_id;
1056
1057         m_client_event_queue.push(event);
1058 }
1059
1060 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1061 {
1062         std::string datastring(pkt->getString(0), pkt->getSize());
1063         std::istringstream is(datastring, std::ios_base::binary);
1064
1065         u32 server_id;
1066         u8 type;
1067         v2f pos;
1068         std::string name;
1069         v2f scale;
1070         std::string text;
1071         u32 number;
1072         u32 item;
1073         u32 dir;
1074         v2f align;
1075         v2f offset;
1076         v3f world_pos;
1077         v2s32 size;
1078         s16 z_index = 0;
1079         std::string text2;
1080
1081         *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1082                 >> dir >> align >> offset;
1083         try {
1084                 *pkt >> world_pos;
1085                 *pkt >> size;
1086                 *pkt >> z_index;
1087                 *pkt >> text2;
1088         } catch(PacketError &e) {};
1089
1090         ClientEvent *event = new ClientEvent();
1091         event->type             = CE_HUDADD;
1092         event->hudadd.server_id = server_id;
1093         event->hudadd.type      = type;
1094         event->hudadd.pos       = new v2f(pos);
1095         event->hudadd.name      = new std::string(name);
1096         event->hudadd.scale     = new v2f(scale);
1097         event->hudadd.text      = new std::string(text);
1098         event->hudadd.number    = number;
1099         event->hudadd.item      = item;
1100         event->hudadd.dir       = dir;
1101         event->hudadd.align     = new v2f(align);
1102         event->hudadd.offset    = new v2f(offset);
1103         event->hudadd.world_pos = new v3f(world_pos);
1104         event->hudadd.size      = new v2s32(size);
1105         event->hudadd.z_index   = z_index;
1106         event->hudadd.text2     = new std::string(text2);
1107         m_client_event_queue.push(event);
1108 }
1109
1110 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1111 {
1112         u32 server_id;
1113
1114         *pkt >> server_id;
1115
1116         auto i = m_hud_server_to_client.find(server_id);
1117         if (i != m_hud_server_to_client.end()) {
1118                 int client_id = i->second;
1119                 m_hud_server_to_client.erase(i);
1120
1121                 ClientEvent *event = new ClientEvent();
1122                 event->type     = CE_HUDRM;
1123                 event->hudrm.id = client_id;
1124                 m_client_event_queue.push(event);
1125         }
1126 }
1127
1128 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1129 {
1130         std::string sdata;
1131         v2f v2fdata;
1132         v3f v3fdata;
1133         u32 intdata = 0;
1134         v2s32 v2s32data;
1135         u32 server_id;
1136         u8 stat;
1137
1138         *pkt >> server_id >> stat;
1139
1140         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1141                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1142                 *pkt >> v2fdata;
1143         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1144                 *pkt >> sdata;
1145         else if (stat == HUD_STAT_WORLD_POS)
1146                 *pkt >> v3fdata;
1147         else if (stat == HUD_STAT_SIZE )
1148                 *pkt >> v2s32data;
1149         else
1150                 *pkt >> intdata;
1151
1152         std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1153         if (i != m_hud_server_to_client.end()) {
1154                 ClientEvent *event = new ClientEvent();
1155                 event->type              = CE_HUDCHANGE;
1156                 event->hudchange.id      = i->second;
1157                 event->hudchange.stat    = (HudElementStat)stat;
1158                 event->hudchange.v2fdata = new v2f(v2fdata);
1159                 event->hudchange.v3fdata = new v3f(v3fdata);
1160                 event->hudchange.sdata   = new std::string(sdata);
1161                 event->hudchange.data    = intdata;
1162                 event->hudchange.v2s32data = new v2s32(v2s32data);
1163                 m_client_event_queue.push(event);
1164         }
1165 }
1166
1167 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1168 {
1169         u32 flags, mask;
1170
1171         *pkt >> flags >> mask;
1172
1173         LocalPlayer *player = m_env.getLocalPlayer();
1174         assert(player != NULL);
1175
1176         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1177         bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1178
1179         player->hud_flags &= ~mask;
1180         player->hud_flags |= flags;
1181
1182         if (g_settings->getBool("hud_flags_bypass"))
1183                 player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE    | HUD_FLAG_HEALTHBAR_VISIBLE |
1184                         HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
1185                         HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE   |
1186                         HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1187         
1188
1189         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1190         bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1191
1192         // Hide minimap if it has been disabled by the server
1193         if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1194                 // defers a minimap update, therefore only call it if really
1195                 // needed, by checking that minimap was visible before
1196                 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1197
1198         // Switch to surface mode if radar disabled by server
1199         if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1200                 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1201                 
1202
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(deSerializeString(is));
1238                 u16 count = readU16(is);
1239
1240                 for (size_t i = 0; i < count; i++)
1241                         skybox.textures.emplace_back(deSerializeString(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 }