]> git.lizzy.rs Git - minetest.git/blob - src/network/clientpackethandler.cpp
Sky API: Rename *_tint to fog_*_tint for consistency
[minetest.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
1106         *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1107                 >> dir >> align >> offset;
1108         try {
1109                 *pkt >> world_pos;
1110         }
1111         catch(SerializationError &e) {};
1112
1113         try {
1114                 *pkt >> size;
1115         } catch(SerializationError &e) {};
1116
1117         try {
1118                 *pkt >> z_index;
1119         }
1120         catch(PacketError &e) {}
1121
1122         ClientEvent *event = new ClientEvent();
1123         event->type             = CE_HUDADD;
1124         event->hudadd.server_id = server_id;
1125         event->hudadd.type      = type;
1126         event->hudadd.pos       = new v2f(pos);
1127         event->hudadd.name      = new std::string(name);
1128         event->hudadd.scale     = new v2f(scale);
1129         event->hudadd.text      = new std::string(text);
1130         event->hudadd.number    = number;
1131         event->hudadd.item      = item;
1132         event->hudadd.dir       = dir;
1133         event->hudadd.align     = new v2f(align);
1134         event->hudadd.offset    = new v2f(offset);
1135         event->hudadd.world_pos = new v3f(world_pos);
1136         event->hudadd.size      = new v2s32(size);
1137         event->hudadd.z_index   = z_index;
1138         m_client_event_queue.push(event);
1139 }
1140
1141 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1142 {
1143         u32 server_id;
1144
1145         *pkt >> server_id;
1146
1147         auto i = m_hud_server_to_client.find(server_id);
1148         if (i != m_hud_server_to_client.end()) {
1149                 int client_id = i->second;
1150                 m_hud_server_to_client.erase(i);
1151
1152                 ClientEvent *event = new ClientEvent();
1153                 event->type     = CE_HUDRM;
1154                 event->hudrm.id = client_id;
1155                 m_client_event_queue.push(event);
1156         }
1157 }
1158
1159 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1160 {
1161         std::string sdata;
1162         v2f v2fdata;
1163         v3f v3fdata;
1164         u32 intdata = 0;
1165         v2s32 v2s32data;
1166         u32 server_id;
1167         u8 stat;
1168
1169         *pkt >> server_id >> stat;
1170
1171         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1172                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1173                 *pkt >> v2fdata;
1174         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1175                 *pkt >> sdata;
1176         else if (stat == HUD_STAT_WORLD_POS)
1177                 *pkt >> v3fdata;
1178         else if (stat == HUD_STAT_SIZE )
1179                 *pkt >> v2s32data;
1180         else
1181                 *pkt >> intdata;
1182
1183         std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1184         if (i != m_hud_server_to_client.end()) {
1185                 ClientEvent *event = new ClientEvent();
1186                 event->type              = CE_HUDCHANGE;
1187                 event->hudchange.id      = i->second;
1188                 event->hudchange.stat    = (HudElementStat)stat;
1189                 event->hudchange.v2fdata = new v2f(v2fdata);
1190                 event->hudchange.v3fdata = new v3f(v3fdata);
1191                 event->hudchange.sdata   = new std::string(sdata);
1192                 event->hudchange.data    = intdata;
1193                 event->hudchange.v2s32data = new v2s32(v2s32data);
1194                 m_client_event_queue.push(event);
1195         }
1196 }
1197
1198 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1199 {
1200         u32 flags, mask;
1201
1202         *pkt >> flags >> mask;
1203
1204         LocalPlayer *player = m_env.getLocalPlayer();
1205         assert(player != NULL);
1206
1207         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1208         bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1209
1210         player->hud_flags &= ~mask;
1211         player->hud_flags |= flags;
1212
1213         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1214         bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1215
1216         // Hide minimap if it has been disabled by the server
1217         if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1218                 // defers a minimap update, therefore only call it if really
1219                 // needed, by checking that minimap was visible before
1220                 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1221
1222         // Switch to surface mode if radar disabled by server
1223         if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1224                 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1225 }
1226
1227 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1228 {
1229         u16 param; std::string value;
1230
1231         *pkt >> param >> value;
1232
1233         LocalPlayer *player = m_env.getLocalPlayer();
1234         assert(player != NULL);
1235
1236         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1237                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1238                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1239                         player->hud_hotbar_itemcount = hotbar_itemcount;
1240         }
1241         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1242                 player->hotbar_image = value;
1243         }
1244         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1245                 player->hotbar_selected_image = value;
1246         }
1247 }
1248
1249 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1250 {
1251         if (m_proto_ver < 39) {
1252                 // Handle Protocol 38 and below servers with old set_sky,
1253                 // ensuring the classic look is kept.
1254                 std::string datastring(pkt->getString(0), pkt->getSize());
1255                 std::istringstream is(datastring, std::ios_base::binary);
1256
1257                 SkyboxParams skybox;
1258                 skybox.bgcolor = video::SColor(readARGB8(is));
1259                 skybox.type = std::string(deSerializeString(is));
1260                 u16 count = readU16(is);
1261
1262                 for (size_t i = 0; i < count; i++)
1263                         skybox.textures.emplace_back(deSerializeString(is));
1264
1265                 skybox.clouds = true;
1266                 try {
1267                         skybox.clouds = readU8(is);
1268                 } catch (...) {}
1269
1270                 // Use default skybox settings:
1271                 SkyboxDefaults sky_defaults;
1272                 SunParams sun = sky_defaults.getSunDefaults();
1273                 MoonParams moon = sky_defaults.getMoonDefaults();
1274                 StarParams stars = sky_defaults.getStarDefaults();
1275
1276                 // Fix for "regular" skies, as color isn't kept:
1277                 if (skybox.type == "regular") {
1278                         skybox.sky_color = sky_defaults.getSkyColorDefaults();
1279                         skybox.fog_tint_type = "default";
1280                         skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1281                         skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1282                 }
1283                 else {
1284                         sun.visible = false;
1285                         sun.sunrise_visible = false;
1286                         moon.visible = false;
1287                         stars.visible = false;
1288                 }
1289
1290                 // Skybox, sun, moon and stars ClientEvents:
1291                 ClientEvent *sky_event = new ClientEvent();
1292                 sky_event->type = CE_SET_SKY;
1293                 sky_event->set_sky = new SkyboxParams(skybox);
1294                 m_client_event_queue.push(sky_event);
1295
1296                 ClientEvent *sun_event = new ClientEvent();
1297                 sun_event->type = CE_SET_SUN;
1298                 sun_event->sun_params = new SunParams(sun);
1299                 m_client_event_queue.push(sun_event);
1300
1301                 ClientEvent *moon_event = new ClientEvent();
1302                 moon_event->type = CE_SET_MOON;
1303                 moon_event->moon_params = new MoonParams(moon);
1304                 m_client_event_queue.push(moon_event);
1305
1306                 ClientEvent *star_event = new ClientEvent();
1307                 star_event->type = CE_SET_STARS;
1308                 star_event->star_params = new StarParams(stars);
1309                 m_client_event_queue.push(star_event);
1310         } else {
1311                 SkyboxParams skybox;
1312                 u16 texture_count;
1313                 std::string texture;
1314
1315                 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1316                         skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1317
1318                 if (skybox.type == "skybox") {
1319                         *pkt >> texture_count;
1320                         for (int i = 0; i < texture_count; i++) {
1321                                 *pkt >> texture;
1322                                 skybox.textures.emplace_back(texture);
1323                         }
1324                 }
1325                 else if (skybox.type == "regular") {
1326                         *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1327                                 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1328                                 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1329                                 >> skybox.sky_color.indoors;
1330                 }
1331
1332                 ClientEvent *event = new ClientEvent();
1333                 event->type = CE_SET_SKY;
1334                 event->set_sky = new SkyboxParams(skybox);
1335                 m_client_event_queue.push(event);
1336         }
1337 }
1338
1339 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1340 {
1341         SunParams sun;
1342
1343         *pkt >> sun.visible >> sun.texture>> sun.tonemap
1344                 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1345
1346         ClientEvent *event = new ClientEvent();
1347         event->type        = CE_SET_SUN;
1348         event->sun_params  = new SunParams(sun);
1349         m_client_event_queue.push(event);
1350 }
1351
1352 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1353 {
1354         MoonParams moon;
1355
1356         *pkt >> moon.visible >> moon.texture
1357                 >> moon.tonemap >> moon.scale;
1358
1359         ClientEvent *event = new ClientEvent();
1360         event->type        = CE_SET_MOON;
1361         event->moon_params = new MoonParams(moon);
1362         m_client_event_queue.push(event);
1363 }
1364
1365 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1366 {
1367         StarParams stars;
1368
1369         *pkt >> stars.visible >> stars.count
1370                 >> stars.starcolor >> stars.scale;
1371
1372         ClientEvent *event = new ClientEvent();
1373         event->type        = CE_SET_STARS;
1374         event->star_params = new StarParams(stars);
1375
1376         m_client_event_queue.push(event);
1377 }
1378
1379 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1380 {
1381         f32 density;
1382         video::SColor color_bright;
1383         video::SColor color_ambient;
1384         f32 height;
1385         f32 thickness;
1386         v2f speed;
1387
1388         *pkt >> density >> color_bright >> color_ambient
1389                         >> height >> thickness >> speed;
1390
1391         ClientEvent *event = new ClientEvent();
1392         event->type                       = CE_CLOUD_PARAMS;
1393         event->cloud_params.density       = density;
1394         // use the underlying u32 representation, because we can't
1395         // use struct members with constructors here, and this way
1396         // we avoid using new() and delete() for no good reason
1397         event->cloud_params.color_bright  = color_bright.color;
1398         event->cloud_params.color_ambient = color_ambient.color;
1399         event->cloud_params.height        = height;
1400         event->cloud_params.thickness     = thickness;
1401         // same here: deconstruct to skip constructor
1402         event->cloud_params.speed_x       = speed.X;
1403         event->cloud_params.speed_y       = speed.Y;
1404         m_client_event_queue.push(event);
1405 }
1406
1407 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1408 {
1409         bool do_override;
1410         u16 day_night_ratio_u;
1411
1412         *pkt >> do_override >> day_night_ratio_u;
1413
1414         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1415
1416         ClientEvent *event = new ClientEvent();
1417         event->type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1418         event->override_day_night_ratio.do_override = do_override;
1419         event->override_day_night_ratio.ratio_f     = day_night_ratio_f;
1420         m_client_event_queue.push(event);
1421 }
1422
1423 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1424 {
1425         LocalPlayer *player = m_env.getLocalPlayer();
1426         assert(player != NULL);
1427
1428         *pkt >> player->local_animations[0];
1429         *pkt >> player->local_animations[1];
1430         *pkt >> player->local_animations[2];
1431         *pkt >> player->local_animations[3];
1432         *pkt >> player->local_animation_speed;
1433 }
1434
1435 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1436 {
1437         LocalPlayer *player = m_env.getLocalPlayer();
1438         assert(player != NULL);
1439
1440         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1441 }
1442
1443 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1444 {
1445         u8 type;
1446         u16 num_players;
1447         *pkt >> type >> num_players;
1448         PlayerListModifer notice_type = (PlayerListModifer) type;
1449
1450         for (u16 i = 0; i < num_players; i++) {
1451                 std::string name;
1452                 *pkt >> name;
1453                 switch (notice_type) {
1454                 case PLAYER_LIST_INIT:
1455                 case PLAYER_LIST_ADD:
1456                         m_env.addPlayerName(name);
1457                         continue;
1458                 case PLAYER_LIST_REMOVE:
1459                         m_env.removePlayerName(name);
1460                         continue;
1461                 }
1462         }
1463 }
1464
1465 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1466 {
1467         if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1468                         m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1469                 errorstream << "Client: Received SRP S_B login message,"
1470                         << " but wasn't supposed to (chosen_mech="
1471                         << m_chosen_auth_mech << ")." << std::endl;
1472                 return;
1473         }
1474
1475         char *bytes_M = 0;
1476         size_t len_M = 0;
1477         SRPUser *usr = (SRPUser *) m_auth_data;
1478         std::string s;
1479         std::string B;
1480         *pkt >> s >> B;
1481
1482         infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1483
1484         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1485                 (const unsigned char *) B.c_str(), B.size(),
1486                 (unsigned char **) &bytes_M, &len_M);
1487
1488         if ( !bytes_M ) {
1489                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1490                 return;
1491         }
1492
1493         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1494         resp_pkt << std::string(bytes_M, len_M);
1495         Send(&resp_pkt);
1496 }
1497
1498 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1499 {
1500         LocalPlayer *player = m_env.getLocalPlayer();
1501         assert(player != NULL);
1502
1503         // Store formspec in LocalPlayer
1504         *pkt >> player->formspec_prepend;
1505 }
1506
1507 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1508 {
1509         *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1510
1511         // Restrictions were received -> load mods if it's enabled
1512         // Note: this should be moved after mods receptions from server instead
1513         loadMods();
1514 }
1515
1516 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1517 {
1518         v3f added_vel;
1519
1520         *pkt >> added_vel;
1521
1522         LocalPlayer *player = m_env.getLocalPlayer();
1523         assert(player != NULL);
1524         player->addVelocity(added_vel);
1525 }
1526
1527 /*
1528  * Mod channels
1529  */
1530
1531 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1532 {
1533         std::string channel_name, sender, channel_msg;
1534         *pkt >> channel_name >> sender >> channel_msg;
1535
1536         verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1537                 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1538                 << channel_msg << std::endl;
1539
1540         if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1541                 verbosestream << "Server sent us messages on unregistered channel "
1542                         << channel_name << ", ignoring." << std::endl;
1543                 return;
1544         }
1545
1546         m_script->on_modchannel_message(channel_name, sender, channel_msg);
1547 }
1548
1549 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1550 {
1551         u8 signal_tmp;
1552         ModChannelSignal signal;
1553         std::string channel;
1554
1555         *pkt >> signal_tmp >> channel;
1556
1557         signal = (ModChannelSignal)signal_tmp;
1558
1559         bool valid_signal = true;
1560         // @TODO: send Signal to Lua API
1561         switch (signal) {
1562                 case MODCHANNEL_SIGNAL_JOIN_OK:
1563                         m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1564                         infostream << "Server ack our mod channel join on channel `" << channel
1565                                 << "`, joining." << std::endl;
1566                         break;
1567                 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1568                         // Unable to join, remove channel
1569                         m_modchannel_mgr->leaveChannel(channel, 0);
1570                         infostream << "Server refused our mod channel join on channel `" << channel
1571                                 << "`" << std::endl;
1572                         break;
1573                 case MODCHANNEL_SIGNAL_LEAVE_OK:
1574 #ifndef NDEBUG
1575                         infostream << "Server ack our mod channel leave on channel " << channel
1576                                 << "`, leaving." << std::endl;
1577 #endif
1578                         break;
1579                 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1580                         infostream << "Server refused our mod channel leave on channel `" << channel
1581                                 << "`" << std::endl;
1582                         break;
1583                 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1584 #ifndef NDEBUG
1585                         // Generally unused, but ensure we don't do an implementation error
1586                         infostream << "Server tells us we sent a message on channel `" << channel
1587                                 << "` but we are not registered. Message was dropped." << std::endl;
1588 #endif
1589                         break;
1590                 case MODCHANNEL_SIGNAL_SET_STATE: {
1591                         u8 state;
1592                         *pkt >> state;
1593
1594                         if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1595                                 infostream << "Received wrong channel state " << state
1596                                                 << ", ignoring." << std::endl;
1597                                 return;
1598                         }
1599
1600                         m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1601                         infostream << "Server sets mod channel `" << channel
1602                                         << "` in read-only mode." << std::endl;
1603                         break;
1604                 }
1605                 default:
1606 #ifndef NDEBUG
1607                         warningstream << "Received unhandled mod channel signal ID "
1608                                 << signal << ", ignoring." << std::endl;
1609 #endif
1610                         valid_signal = false;
1611                         break;
1612         }
1613
1614         // If signal is valid, forward it to client side mods
1615         if (valid_signal)
1616                 m_script->on_modchannel_signal(channel, signal);
1617 }