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