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