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