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