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