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