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