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