Verify HudSetParams input when hotbar textures are set (#6013)
[oweals/minetest.git] / src / network / clientpackethandler.cpp
1 /*
2 Minetest
3 Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "client.h"
21
22 #include "util/base64.h"
23 #include "clientmedia.h"
24 #include "log.h"
25 #include "map.h"
26 #include "mapsector.h"
27 #include "minimap.h"
28 #include "nodedef.h"
29 #include "serialization.h"
30 #include "server.h"
31 #include "util/strfnd.h"
32 #include "network/clientopcodes.h"
33 #include "script/scripting_client.h"
34 #include "util/serialize.h"
35 #include "util/srp.h"
36 #include "tileanimation.h"
37
38 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
39 {
40         infostream << "Got deprecated command "
41                         << toClientCommandTable[pkt->getCommand()].name << " from peer "
42                         << pkt->getPeerId() << "!" << std::endl;
43 }
44
45 void Client::handleCommand_Hello(NetworkPacket* pkt)
46 {
47         if (pkt->getSize() < 1)
48                 return;
49
50         u8 serialization_ver;
51         u16 proto_ver;
52         u16 compression_mode;
53         u32 auth_mechs;
54         std::string username_legacy; // for case insensitivity
55         *pkt >> serialization_ver >> compression_mode >> proto_ver
56                 >> auth_mechs >> username_legacy;
57
58         // Chose an auth method we support
59         AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
60
61         infostream << "Client: TOCLIENT_HELLO received with "
62                         << "serialization_ver=" << (u32)serialization_ver
63                         << ", auth_mechs=" << auth_mechs
64                         << ", proto_ver=" << proto_ver
65                         << ", compression_mode=" << compression_mode
66                         << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
67
68         if (!ser_ver_supported(serialization_ver)) {
69                 infostream << "Client: TOCLIENT_HELLO: Server sent "
70                                 << "unsupported ser_fmt_ver"<< std::endl;
71                 return;
72         }
73
74         m_server_ser_ver = serialization_ver;
75         m_proto_ver = proto_ver;
76
77         //TODO verify that username_legacy matches sent username, only
78         // differs in casing (make both uppercase and compare)
79         // This is only neccessary though when we actually want to add casing support
80
81         if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
82                 // we recieved a TOCLIENT_HELLO while auth was already going on
83                 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
84                         << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
85                 if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP)
86                                 || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) {
87                         srp_user_delete((SRPUser *) m_auth_data);
88                         m_auth_data = 0;
89                 }
90         }
91
92         // Authenticate using that method, or abort if there wasn't any method found
93         if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
94                 startAuth(chosen_auth_mechanism);
95         } else {
96                 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
97                 m_access_denied = true;
98                 m_access_denied_reason = "Unknown";
99                 m_con.Disconnect();
100         }
101
102 }
103
104 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
105 {
106         deleteAuthData();
107
108         v3f playerpos;
109         *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
110                 >> m_sudo_auth_methods;
111
112         playerpos -= v3f(0, BS / 2, 0);
113
114         // Set player position
115         LocalPlayer *player = m_env.getLocalPlayer();
116         assert(player != NULL);
117         player->setPosition(playerpos);
118
119         infostream << "Client: received map seed: " << m_map_seed << std::endl;
120         infostream << "Client: received recommended send interval "
121                                         << m_recommended_send_interval<<std::endl;
122
123         // Reply to server
124         NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
125         Send(&resp_pkt);
126
127         m_state = LC_Init;
128 }
129 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
130 {
131         deleteAuthData();
132
133         m_password = m_new_password;
134
135         verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
136
137         // send packet to actually set the password
138         startAuth(AUTH_MECHANISM_FIRST_SRP);
139
140         // reset again
141         m_chosen_auth_mech = AUTH_MECHANISM_NONE;
142 }
143 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
144 {
145         pushToChatQueue(L"Password change denied. Password NOT changed.");
146         // reset everything and be sad
147         deleteAuthData();
148 }
149 void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
150 {
151         if (pkt->getSize() < 1)
152                 return;
153
154         u8 server_ser_ver;
155         *pkt >> server_ser_ver;
156
157         infostream << "Client: TOCLIENT_INIT_LEGACY received with "
158                 "server_ser_ver=" << ((int)server_ser_ver & 0xff) << std::endl;
159
160         if (!ser_ver_supported(server_ser_ver)) {
161                 infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
162                                 << "unsupported ser_fmt_ver"<< std::endl;
163                 return;
164         }
165
166         m_server_ser_ver = server_ser_ver;
167
168         // We can be totally wrong with this guess
169         // but we only need some value < 25.
170         m_proto_ver = 24;
171
172         // Get player position
173         v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
174         if (pkt->getSize() >= 1 + 6) {
175                 *pkt >> playerpos_s16;
176         }
177         v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
178
179
180         // Set player position
181         LocalPlayer *player = m_env.getLocalPlayer();
182         assert(player != NULL);
183         player->setPosition(playerpos_f);
184
185         if (pkt->getSize() >= 1 + 6 + 8) {
186                 // Get map seed
187                 *pkt >> m_map_seed;
188                 infostream << "Client: received map seed: " << m_map_seed << std::endl;
189         }
190
191         if (pkt->getSize() >= 1 + 6 + 8 + 4) {
192                 *pkt >> m_recommended_send_interval;
193                 infostream << "Client: received recommended send interval "
194                                 << m_recommended_send_interval<<std::endl;
195         }
196
197         // Reply to server
198         NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
199         Send(&resp_pkt);
200
201         m_state = LC_Init;
202 }
203
204 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
205 {
206         // The server didn't like our password. Note, this needs
207         // to be processed even if the serialisation format has
208         // not been agreed yet, the same as TOCLIENT_INIT.
209         m_access_denied = true;
210         m_access_denied_reason = "Unknown";
211
212         if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
213                 if (pkt->getSize() < 1)
214                         return;
215
216                 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
217                 *pkt >> denyCode;
218                 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
219                                 denyCode == SERVER_ACCESSDENIED_CRASH) {
220                         *pkt >> m_access_denied_reason;
221                         if (m_access_denied_reason == "") {
222                                 m_access_denied_reason = accessDeniedStrings[denyCode];
223                         }
224                         u8 reconnect;
225                         *pkt >> reconnect;
226                         m_access_denied_reconnect = reconnect & 1;
227                 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
228                         *pkt >> m_access_denied_reason;
229                 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
230                         m_access_denied_reason = accessDeniedStrings[denyCode];
231                 } else {
232                         // Allow us to add new error messages to the
233                         // protocol without raising the protocol version, if we want to.
234                         // Until then (which may be never), this is outside
235                         // of the defined protocol.
236                         *pkt >> m_access_denied_reason;
237                         if (m_access_denied_reason == "") {
238                                 m_access_denied_reason = "Unknown";
239                         }
240                 }
241         }
242         // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
243         // for compat with old clients
244         else {
245                 if (pkt->getSize() >= 2) {
246                         std::wstring wide_reason;
247                         *pkt >> wide_reason;
248                         m_access_denied_reason = wide_to_utf8(wide_reason);
249                 }
250         }
251 }
252
253 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
254 {
255         if (pkt->getSize() < 6)
256                 return;
257
258         v3s16 p;
259         *pkt >> p;
260         removeNode(p);
261 }
262
263 void Client::handleCommand_AddNode(NetworkPacket* pkt)
264 {
265         if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
266                 return;
267
268         v3s16 p;
269         *pkt >> p;
270
271         MapNode n;
272         n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
273
274         bool remove_metadata = true;
275         u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
276         if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
277                 remove_metadata = false;
278         }
279
280         addNode(p, n, remove_metadata);
281 }
282 void Client::handleCommand_BlockData(NetworkPacket* pkt)
283 {
284         // Ignore too small packet
285         if (pkt->getSize() < 6)
286                 return;
287
288         v3s16 p;
289         *pkt >> p;
290
291         std::string datastring(pkt->getString(6), pkt->getSize() - 6);
292         std::istringstream istr(datastring, std::ios_base::binary);
293
294         MapSector *sector;
295         MapBlock *block;
296
297         v2s16 p2d(p.X, p.Z);
298         sector = m_env.getMap().emergeSector(p2d);
299
300         assert(sector->getPos() == p2d);
301
302         block = sector->getBlockNoCreateNoEx(p.Y);
303         if (block) {
304                 /*
305                         Update an existing block
306                 */
307                 block->deSerialize(istr, m_server_ser_ver, false);
308                 block->deSerializeNetworkSpecific(istr);
309         }
310         else {
311                 /*
312                         Create a new block
313                 */
314                 block = new MapBlock(&m_env.getMap(), p, this);
315                 block->deSerialize(istr, m_server_ser_ver, false);
316                 block->deSerializeNetworkSpecific(istr);
317                 sector->insertBlock(block);
318         }
319
320         if (m_localdb) {
321                 ServerMap::saveBlock(block, m_localdb);
322         }
323
324         /*
325                 Add it to mesh update queue and set it to be acknowledged after update.
326         */
327         addUpdateMeshTaskWithEdge(p, true);
328 }
329
330 void Client::handleCommand_Inventory(NetworkPacket* pkt)
331 {
332         if (pkt->getSize() < 1)
333                 return;
334
335         std::string datastring(pkt->getString(0), pkt->getSize());
336         std::istringstream is(datastring, std::ios_base::binary);
337
338         LocalPlayer *player = m_env.getLocalPlayer();
339         assert(player != NULL);
340
341         player->inventory.deSerialize(is);
342
343         m_inventory_updated = true;
344
345         delete m_inventory_from_server;
346         m_inventory_from_server = new Inventory(player->inventory);
347         m_inventory_from_server_age = 0.0;
348 }
349
350 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
351 {
352         if (pkt->getSize() < 2)
353                 return;
354
355         u16 time_of_day;
356
357         *pkt >> time_of_day;
358
359         time_of_day      = time_of_day % 24000;
360         float time_speed = 0;
361
362         if (pkt->getSize() >= 2 + 4) {
363                 *pkt >> time_speed;
364         }
365         else {
366                 // Old message; try to approximate speed of time by ourselves
367                 float time_of_day_f = (float)time_of_day / 24000.0;
368                 float tod_diff_f = 0;
369
370                 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
371                         tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
372                 else
373                         tod_diff_f = time_of_day_f - m_last_time_of_day_f;
374
375                 m_last_time_of_day_f       = time_of_day_f;
376                 float time_diff            = m_time_of_day_update_timer;
377                 m_time_of_day_update_timer = 0;
378
379                 if (m_time_of_day_set) {
380                         time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
381                         infostream << "Client: Measured time_of_day speed (old format): "
382                                         << time_speed << " tod_diff_f=" << tod_diff_f
383                                         << " time_diff=" << time_diff << std::endl;
384                 }
385         }
386
387         // Update environment
388         m_env.setTimeOfDay(time_of_day);
389         m_env.setTimeOfDaySpeed(time_speed);
390         m_time_of_day_set = true;
391
392         u32 dr = m_env.getDayNightRatio();
393         infostream << "Client: time_of_day=" << time_of_day
394                         << " time_speed=" << time_speed
395                         << " dr=" << dr << std::endl;
396 }
397
398 void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
399 {
400         /*
401                 u16 command
402                 u16 length
403                 wstring message
404         */
405         u16 len, read_wchar;
406
407         *pkt >> len;
408
409         std::wstring message;
410         for (u32 i = 0; i < len; i++) {
411                 *pkt >> read_wchar;
412                 message += (wchar_t)read_wchar;
413         }
414
415         // If chat message not consummed by client lua API
416         if (!moddingEnabled() || !m_script->on_receiving_message(wide_to_utf8(message))) {
417                 pushToChatQueue(message);
418         }
419 }
420
421 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
422 {
423         /*
424                 u16 count of removed objects
425                 for all removed objects {
426                         u16 id
427                 }
428                 u16 count of added objects
429                 for all added objects {
430                         u16 id
431                         u8 type
432                         u32 initialization data length
433                         string initialization data
434                 }
435         */
436
437         try {
438                 u8 type;
439                 u16 removed_count, added_count, id;
440
441                 // Read removed objects
442                 *pkt >> removed_count;
443
444                 for (u16 i = 0; i < removed_count; i++) {
445                         *pkt >> id;
446                         m_env.removeActiveObject(id);
447                 }
448
449                 // Read added objects
450                 *pkt >> added_count;
451
452                 for (u16 i = 0; i < added_count; i++) {
453                         *pkt >> id >> type;
454                         m_env.addActiveObject(id, type, pkt->readLongString());
455                 }
456         } catch (PacketError &e) {
457                 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
458                                 << ". The packet is unreliable, ignoring" << std::endl;
459         }
460 }
461
462 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
463 {
464         /*
465                 for all objects
466                 {
467                         u16 id
468                         u16 message length
469                         string message
470                 }
471         */
472         std::string datastring(pkt->getString(0), pkt->getSize());
473         std::istringstream is(datastring, std::ios_base::binary);
474
475         try {
476                 while (is.good()) {
477                         u16 id = readU16(is);
478                         if (!is.good())
479                                 break;
480
481                         std::string message = deSerializeString(is);
482
483                         // Pass on to the environment
484                         m_env.processActiveObjectMessage(id, message);
485                 }
486         } catch (SerializationError &e) {
487                 errorstream << "Client::handleCommand_ActiveObjectMessages: "
488                         << "caught SerializationError: " << e.what() << std::endl;
489         }
490 }
491
492 void Client::handleCommand_Movement(NetworkPacket* pkt)
493 {
494         LocalPlayer *player = m_env.getLocalPlayer();
495         assert(player != NULL);
496
497         float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
498
499         *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
500                 >> lf >> lfs >> ls >> g;
501
502         player->movement_acceleration_default   = mad * BS;
503         player->movement_acceleration_air       = maa * BS;
504         player->movement_acceleration_fast      = maf * BS;
505         player->movement_speed_walk             = msw * BS;
506         player->movement_speed_crouch           = mscr * BS;
507         player->movement_speed_fast             = msf * BS;
508         player->movement_speed_climb            = mscl * BS;
509         player->movement_speed_jump             = msj * BS;
510         player->movement_liquid_fluidity        = lf * BS;
511         player->movement_liquid_fluidity_smooth = lfs * BS;
512         player->movement_liquid_sink            = ls * BS;
513         player->movement_gravity                = g * BS;
514 }
515
516 void Client::handleCommand_HP(NetworkPacket* pkt)
517 {
518
519         LocalPlayer *player = m_env.getLocalPlayer();
520         assert(player != NULL);
521
522         u8 oldhp   = player->hp;
523
524         u8 hp;
525         *pkt >> hp;
526
527         player->hp = hp;
528
529         if (moddingEnabled()) {
530                 m_script->on_hp_modification(hp);
531         }
532
533         if (hp < oldhp) {
534                 // Add to ClientEvent queue
535                 ClientEvent event;
536                 event.type = CE_PLAYER_DAMAGE;
537                 event.player_damage.amount = oldhp - hp;
538                 m_client_event_queue.push(event);
539         }
540 }
541
542 void Client::handleCommand_Breath(NetworkPacket* pkt)
543 {
544         LocalPlayer *player = m_env.getLocalPlayer();
545         assert(player != NULL);
546
547         u16 breath;
548
549         *pkt >> breath;
550
551         player->setBreath(breath);
552 }
553
554 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
555 {
556         LocalPlayer *player = m_env.getLocalPlayer();
557         assert(player != NULL);
558
559         v3f pos;
560         f32 pitch, yaw;
561
562         *pkt >> pos >> pitch >> yaw;
563
564         player->setPosition(pos);
565
566         infostream << "Client got TOCLIENT_MOVE_PLAYER"
567                         << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
568                         << " pitch=" << pitch
569                         << " yaw=" << yaw
570                         << std::endl;
571
572         /*
573                 Add to ClientEvent queue.
574                 This has to be sent to the main program because otherwise
575                 it would just force the pitch and yaw values to whatever
576                 the camera points to.
577         */
578         ClientEvent event;
579         event.type = CE_PLAYER_FORCE_MOVE;
580         event.player_force_move.pitch = pitch;
581         event.player_force_move.yaw = yaw;
582         m_client_event_queue.push(event);
583
584         // Ignore damage for a few seconds, so that the player doesn't
585         // get damage from falling on ground
586         m_ignore_damage_timer = 3.0;
587 }
588
589 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
590 {
591         bool set_camera_point_target;
592         v3f camera_point_target;
593
594         *pkt >> set_camera_point_target;
595         *pkt >> camera_point_target;
596
597         ClientEvent event;
598         event.type                                = CE_DEATHSCREEN;
599         event.deathscreen.set_camera_point_target = set_camera_point_target;
600         event.deathscreen.camera_point_target_x   = camera_point_target.X;
601         event.deathscreen.camera_point_target_y   = camera_point_target.Y;
602         event.deathscreen.camera_point_target_z   = camera_point_target.Z;
603         m_client_event_queue.push(event);
604 }
605
606 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
607 {
608         u16 num_files;
609
610         *pkt >> num_files;
611
612         infostream << "Client: Received media announcement: packet size: "
613                         << pkt->getSize() << std::endl;
614
615         if (m_media_downloader == NULL ||
616                         m_media_downloader->isStarted()) {
617                 const char *problem = m_media_downloader ?
618                         "we already saw another announcement" :
619                         "all media has been received already";
620                 errorstream << "Client: Received media announcement but "
621                         << problem << "! "
622                         << " files=" << num_files
623                         << " size=" << pkt->getSize() << std::endl;
624                 return;
625         }
626
627         // Mesh update thread must be stopped while
628         // updating content definitions
629         sanity_check(!m_mesh_update_thread.isRunning());
630
631         for (u16 i = 0; i < num_files; i++) {
632                 std::string name, sha1_base64;
633
634                 *pkt >> name >> sha1_base64;
635
636                 std::string sha1_raw = base64_decode(sha1_base64);
637                 m_media_downloader->addFile(name, sha1_raw);
638         }
639
640         try {
641                 std::string str;
642
643                 *pkt >> str;
644
645                 Strfnd sf(str);
646                 while(!sf.at_end()) {
647                         std::string baseurl = trim(sf.next(","));
648                         if (baseurl != "")
649                                 m_media_downloader->addRemoteServer(baseurl);
650                 }
651         }
652         catch(SerializationError& e) {
653                 // not supported by server or turned off
654         }
655
656         m_media_downloader->step(this);
657 }
658
659 void Client::handleCommand_Media(NetworkPacket* pkt)
660 {
661         /*
662                 u16 command
663                 u16 total number of file bunches
664                 u16 index of this bunch
665                 u32 number of files in this bunch
666                 for each file {
667                         u16 length of name
668                         string name
669                         u32 length of data
670                         data
671                 }
672         */
673         u16 num_bunches;
674         u16 bunch_i;
675         u32 num_files;
676
677         *pkt >> num_bunches >> bunch_i >> num_files;
678
679         infostream << "Client: Received files: bunch " << bunch_i << "/"
680                         << num_bunches << " files=" << num_files
681                         << " size=" << pkt->getSize() << std::endl;
682
683         if (num_files == 0)
684                 return;
685
686         if (!m_media_downloader || !m_media_downloader->isStarted()) {
687                 const char *problem = m_media_downloader ?
688                         "media has not been requested" :
689                         "all media has been received already";
690                 errorstream << "Client: Received media but "
691                         << problem << "! "
692                         << " bunch " << bunch_i << "/" << num_bunches
693                         << " files=" << num_files
694                         << " size=" << pkt->getSize() << std::endl;
695                 return;
696         }
697
698         // Mesh update thread must be stopped while
699         // updating content definitions
700         sanity_check(!m_mesh_update_thread.isRunning());
701
702         for (u32 i=0; i < num_files; i++) {
703                 std::string name;
704
705                 *pkt >> name;
706
707                 std::string data = pkt->readLongString();
708
709                 m_media_downloader->conventionalTransferDone(
710                                 name, data, this);
711         }
712 }
713
714 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
715 {
716         infostream << "Client: Received node definitions: packet size: "
717                         << pkt->getSize() << std::endl;
718
719         // Mesh update thread must be stopped while
720         // updating content definitions
721         sanity_check(!m_mesh_update_thread.isRunning());
722
723         // Decompress node definitions
724         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
725         std::ostringstream tmp_os;
726         decompressZlib(tmp_is, tmp_os);
727
728         // Deserialize node definitions
729         std::istringstream tmp_is2(tmp_os.str());
730         m_nodedef->deSerialize(tmp_is2);
731         m_nodedef_received = true;
732 }
733
734 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
735 {
736         infostream << "Client: Received item definitions: packet size: "
737                         << pkt->getSize() << std::endl;
738
739         // Mesh update thread must be stopped while
740         // updating content definitions
741         sanity_check(!m_mesh_update_thread.isRunning());
742
743         // Decompress item definitions
744         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
745         std::ostringstream tmp_os;
746         decompressZlib(tmp_is, tmp_os);
747
748         // Deserialize node definitions
749         std::istringstream tmp_is2(tmp_os.str());
750         m_itemdef->deSerialize(tmp_is2);
751         m_itemdef_received = true;
752 }
753
754 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
755 {
756         /*
757                 [0] u32 server_id
758                 [4] u16 name length
759                 [6] char name[len]
760                 [ 6 + len] f32 gain
761                 [10 + len] u8 type
762                 [11 + len] (f32 * 3) pos
763                 [23 + len] u16 object_id
764                 [25 + len] bool loop
765                 [26 + len] f32 fade
766                 [30 + len] f32 pitch
767         */
768
769         s32 server_id;
770         std::string name;
771
772         float gain;
773         u8 type; // 0=local, 1=positional, 2=object
774         v3f pos;
775         u16 object_id;
776         bool loop;
777         float fade = 0.0f;
778         float pitch = 1.0f;
779
780         *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
781
782         try {
783                 *pkt >> fade;
784                 *pkt >> pitch;
785         } catch (PacketError &e) {};
786
787         // Start playing
788         int client_id = -1;
789         switch(type) {
790                 case 0: // local
791                         client_id = m_sound->playSound(name, loop, gain, fade, pitch);
792                         break;
793                 case 1: // positional
794                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
795                         break;
796                 case 2:
797                 { // object
798                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
799                         if (cao)
800                                 pos = cao->getPosition();
801                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
802                         // TODO: Set up sound to move with object
803                         break;
804                 }
805                 default:
806                         break;
807         }
808
809         if (client_id != -1) {
810                 m_sounds_server_to_client[server_id] = client_id;
811                 m_sounds_client_to_server[client_id] = server_id;
812                 if (object_id != 0)
813                         m_sounds_to_objects[client_id] = object_id;
814         }
815 }
816
817 void Client::handleCommand_StopSound(NetworkPacket* pkt)
818 {
819         s32 server_id;
820
821         *pkt >> server_id;
822
823         std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
824         if (i != m_sounds_server_to_client.end()) {
825                 int client_id = i->second;
826                 m_sound->stopSound(client_id);
827         }
828 }
829
830 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
831 {
832         s32 sound_id;
833         float step;
834         float gain;
835
836         *pkt >> sound_id >> step >> gain;
837
838         std::unordered_map<s32, int>::const_iterator i =
839                         m_sounds_server_to_client.find(sound_id);
840
841         if (i != m_sounds_server_to_client.end())
842                 m_sound->fadeSound(i->second, step, gain);
843 }
844
845 void Client::handleCommand_Privileges(NetworkPacket* pkt)
846 {
847         m_privileges.clear();
848         infostream << "Client: Privileges updated: ";
849         u16 num_privileges;
850
851         *pkt >> num_privileges;
852
853         for (u16 i = 0; i < num_privileges; i++) {
854                 std::string priv;
855
856                 *pkt >> priv;
857
858                 m_privileges.insert(priv);
859                 infostream << priv << " ";
860         }
861         infostream << std::endl;
862 }
863
864 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
865 {
866         LocalPlayer *player = m_env.getLocalPlayer();
867         assert(player != NULL);
868
869         // Store formspec in LocalPlayer
870         player->inventory_formspec = pkt->readLongString();
871 }
872
873 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
874 {
875         std::string datastring(pkt->getString(0), pkt->getSize());
876         std::istringstream is(datastring, std::ios_base::binary);
877
878         std::string name = deSerializeString(is);
879
880         infostream << "Client: Detached inventory update: \"" << name
881                         << "\"" << std::endl;
882
883         Inventory *inv = NULL;
884         if (m_detached_inventories.count(name) > 0)
885                 inv = m_detached_inventories[name];
886         else {
887                 inv = new Inventory(m_itemdef);
888                 m_detached_inventories[name] = inv;
889         }
890         inv->deSerialize(is);
891 }
892
893 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
894 {
895         std::string formspec = pkt->readLongString();
896         std::string formname;
897
898         *pkt >> formname;
899
900         ClientEvent event;
901         event.type = CE_SHOW_FORMSPEC;
902         // pointer is required as event is a struct only!
903         // adding a std:string to a struct isn't possible
904         event.show_formspec.formspec = new std::string(formspec);
905         event.show_formspec.formname = new std::string(formname);
906         m_client_event_queue.push(event);
907 }
908
909 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
910 {
911         std::string datastring(pkt->getString(0), pkt->getSize());
912         std::istringstream is(datastring, std::ios_base::binary);
913
914         v3f pos                 = readV3F1000(is);
915         v3f vel                 = readV3F1000(is);
916         v3f acc                 = readV3F1000(is);
917         float expirationtime    = readF1000(is);
918         float size              = readF1000(is);
919         bool collisiondetection = readU8(is);
920         std::string texture     = deSerializeLongString(is);
921         bool vertical           = false;
922         bool collision_removal  = false;
923         struct TileAnimationParams animation;
924         animation.type = TAT_NONE;
925         u8 glow = 0;
926         try {
927                 vertical = readU8(is);
928                 collision_removal = readU8(is);
929                 animation.deSerialize(is, m_proto_ver);
930                 glow = readU8(is);
931         } catch (...) {}
932
933         ClientEvent event;
934         event.type                              = CE_SPAWN_PARTICLE;
935         event.spawn_particle.pos                = new v3f (pos);
936         event.spawn_particle.vel                = new v3f (vel);
937         event.spawn_particle.acc                = new v3f (acc);
938         event.spawn_particle.expirationtime     = expirationtime;
939         event.spawn_particle.size               = size;
940         event.spawn_particle.collisiondetection = collisiondetection;
941         event.spawn_particle.collision_removal  = collision_removal;
942         event.spawn_particle.vertical           = vertical;
943         event.spawn_particle.texture            = new std::string(texture);
944         event.spawn_particle.animation          = animation;
945         event.spawn_particle.glow               = glow;
946
947         m_client_event_queue.push(event);
948 }
949
950 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
951 {
952         u16 amount;
953         float spawntime;
954         v3f minpos;
955         v3f maxpos;
956         v3f minvel;
957         v3f maxvel;
958         v3f minacc;
959         v3f maxacc;
960         float minexptime;
961         float maxexptime;
962         float minsize;
963         float maxsize;
964         bool collisiondetection;
965         u32 id;
966
967         *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
968                 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
969                 >> maxsize >> collisiondetection;
970
971         std::string texture = pkt->readLongString();
972
973         *pkt >> id;
974
975         bool vertical = false;
976         bool collision_removal = false;
977         struct TileAnimationParams animation;
978         animation.type = TAT_NONE;
979         u8 glow = 0;
980         u16 attached_id = 0;
981         try {
982                 *pkt >> vertical;
983                 *pkt >> collision_removal;
984                 *pkt >> attached_id;
985
986                 // This is horrible but required (why are there two ways to deserialize pkts?)
987                 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
988                 std::istringstream is(datastring, std::ios_base::binary);
989                 animation.deSerialize(is, m_proto_ver);
990                 glow = readU8(is);
991         } catch (...) {}
992
993         ClientEvent event;
994         event.type                                   = CE_ADD_PARTICLESPAWNER;
995         event.add_particlespawner.amount             = amount;
996         event.add_particlespawner.spawntime          = spawntime;
997         event.add_particlespawner.minpos             = new v3f (minpos);
998         event.add_particlespawner.maxpos             = new v3f (maxpos);
999         event.add_particlespawner.minvel             = new v3f (minvel);
1000         event.add_particlespawner.maxvel             = new v3f (maxvel);
1001         event.add_particlespawner.minacc             = new v3f (minacc);
1002         event.add_particlespawner.maxacc             = new v3f (maxacc);
1003         event.add_particlespawner.minexptime         = minexptime;
1004         event.add_particlespawner.maxexptime         = maxexptime;
1005         event.add_particlespawner.minsize            = minsize;
1006         event.add_particlespawner.maxsize            = maxsize;
1007         event.add_particlespawner.collisiondetection = collisiondetection;
1008         event.add_particlespawner.collision_removal  = collision_removal;
1009         event.add_particlespawner.attached_id        = attached_id;
1010         event.add_particlespawner.vertical           = vertical;
1011         event.add_particlespawner.texture            = new std::string(texture);
1012         event.add_particlespawner.id                 = id;
1013         event.add_particlespawner.animation          = animation;
1014         event.add_particlespawner.glow               = glow;
1015
1016         m_client_event_queue.push(event);
1017 }
1018
1019
1020 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1021 {
1022         u16 legacy_id;
1023         u32 id;
1024
1025         // Modification set 13/03/15, 1 year of compat for protocol v24
1026         if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
1027                 *pkt >> legacy_id;
1028         }
1029         else {
1030                 *pkt >> id;
1031         }
1032
1033
1034         ClientEvent event;
1035         event.type                      = CE_DELETE_PARTICLESPAWNER;
1036         event.delete_particlespawner.id =
1037                         (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1038
1039         m_client_event_queue.push(event);
1040 }
1041
1042 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1043 {
1044         std::string datastring(pkt->getString(0), pkt->getSize());
1045         std::istringstream is(datastring, std::ios_base::binary);
1046
1047         u32 id;
1048         u8 type;
1049         v2f pos;
1050         std::string name;
1051         v2f scale;
1052         std::string text;
1053         u32 number;
1054         u32 item;
1055         u32 dir;
1056         v2f align;
1057         v2f offset;
1058         v3f world_pos;
1059         v2s32 size;
1060
1061         *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1062                 >> dir >> align >> offset;
1063         try {
1064                 *pkt >> world_pos;
1065         }
1066         catch(SerializationError &e) {};
1067
1068         try {
1069                 *pkt >> size;
1070         } catch(SerializationError &e) {};
1071
1072         ClientEvent event;
1073         event.type             = CE_HUDADD;
1074         event.hudadd.id        = id;
1075         event.hudadd.type      = type;
1076         event.hudadd.pos       = new v2f(pos);
1077         event.hudadd.name      = new std::string(name);
1078         event.hudadd.scale     = new v2f(scale);
1079         event.hudadd.text      = new std::string(text);
1080         event.hudadd.number    = number;
1081         event.hudadd.item      = item;
1082         event.hudadd.dir       = dir;
1083         event.hudadd.align     = new v2f(align);
1084         event.hudadd.offset    = new v2f(offset);
1085         event.hudadd.world_pos = new v3f(world_pos);
1086         event.hudadd.size      = new v2s32(size);
1087         m_client_event_queue.push(event);
1088 }
1089
1090 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1091 {
1092         u32 id;
1093
1094         *pkt >> id;
1095
1096         ClientEvent event;
1097         event.type     = CE_HUDRM;
1098         event.hudrm.id = id;
1099         m_client_event_queue.push(event);
1100 }
1101
1102 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1103 {
1104         std::string sdata;
1105         v2f v2fdata;
1106         v3f v3fdata;
1107         u32 intdata = 0;
1108         v2s32 v2s32data;
1109         u32 id;
1110         u8 stat;
1111
1112         *pkt >> id >> stat;
1113
1114         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1115                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1116                 *pkt >> v2fdata;
1117         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1118                 *pkt >> sdata;
1119         else if (stat == HUD_STAT_WORLD_POS)
1120                 *pkt >> v3fdata;
1121         else if (stat == HUD_STAT_SIZE )
1122                 *pkt >> v2s32data;
1123         else
1124                 *pkt >> intdata;
1125
1126         ClientEvent event;
1127         event.type              = CE_HUDCHANGE;
1128         event.hudchange.id      = id;
1129         event.hudchange.stat    = (HudElementStat)stat;
1130         event.hudchange.v2fdata = new v2f(v2fdata);
1131         event.hudchange.v3fdata = new v3f(v3fdata);
1132         event.hudchange.sdata   = new std::string(sdata);
1133         event.hudchange.data    = intdata;
1134         event.hudchange.v2s32data = new v2s32(v2s32data);
1135         m_client_event_queue.push(event);
1136 }
1137
1138 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1139 {
1140         u32 flags, mask;
1141
1142         *pkt >> flags >> mask;
1143
1144         LocalPlayer *player = m_env.getLocalPlayer();
1145         assert(player != NULL);
1146
1147         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1148
1149         player->hud_flags &= ~mask;
1150         player->hud_flags |= flags;
1151
1152         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1153
1154         // Hide minimap if it has been disabled by the server
1155         if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible) {
1156                 // defers a minimap update, therefore only call it if really
1157                 // needed, by checking that minimap was visible before
1158                 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1159         }
1160 }
1161
1162 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1163 {
1164         u16 param; std::string value;
1165
1166         *pkt >> param >> value;
1167
1168         LocalPlayer *player = m_env.getLocalPlayer();
1169         assert(player != NULL);
1170
1171         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1172                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1173                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1174                         player->hud_hotbar_itemcount = hotbar_itemcount;
1175         }
1176         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1177                 // If value not empty verify image exists in texture source
1178                 if (value != "" && !getTextureSource()->isKnownSourceImage(value)) {
1179                         errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1180                                 << value << "')" << std::endl;
1181                         return;
1182                 }
1183                 player->hotbar_image = value;
1184         }
1185         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1186                 // If value not empty verify image exists in texture source
1187                 if (value != "" && !getTextureSource()->isKnownSourceImage(value)) {
1188                         errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1189                                         << value << "')" << std::endl;
1190                         return;
1191                 }
1192                 player->hotbar_selected_image = value;
1193         }
1194 }
1195
1196 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1197 {
1198         std::string datastring(pkt->getString(0), pkt->getSize());
1199         std::istringstream is(datastring, std::ios_base::binary);
1200
1201         video::SColor *bgcolor           = new video::SColor(readARGB8(is));
1202         std::string *type                = new std::string(deSerializeString(is));
1203         u16 count                        = readU16(is);
1204         std::vector<std::string> *params = new std::vector<std::string>;
1205
1206         for (size_t i = 0; i < count; i++)
1207                 params->push_back(deSerializeString(is));
1208
1209         bool clouds = true;
1210         try {
1211                 clouds = readU8(is);
1212         } catch (...) {}
1213
1214         ClientEvent event;
1215         event.type            = CE_SET_SKY;
1216         event.set_sky.bgcolor = bgcolor;
1217         event.set_sky.type    = type;
1218         event.set_sky.params  = params;
1219         event.set_sky.clouds  = clouds;
1220         m_client_event_queue.push(event);
1221 }
1222
1223 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1224 {
1225         f32 density;
1226         video::SColor color_bright;
1227         video::SColor color_ambient;
1228         f32 height;
1229         f32 thickness;
1230         v2f speed;
1231
1232         *pkt >> density >> color_bright >> color_ambient
1233                         >> height >> thickness >> speed;
1234
1235         ClientEvent event;
1236         event.type                       = CE_CLOUD_PARAMS;
1237         event.cloud_params.density       = density;
1238         // use the underlying u32 representation, because we can't
1239         // use struct members with constructors here, and this way
1240         // we avoid using new() and delete() for no good reason
1241         event.cloud_params.color_bright  = color_bright.color;
1242         event.cloud_params.color_ambient = color_ambient.color;
1243         event.cloud_params.height        = height;
1244         event.cloud_params.thickness     = thickness;
1245         // same here: deconstruct to skip constructor
1246         event.cloud_params.speed_x       = speed.X;
1247         event.cloud_params.speed_y       = speed.Y;
1248         m_client_event_queue.push(event);
1249 }
1250
1251 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1252 {
1253         bool do_override;
1254         u16 day_night_ratio_u;
1255
1256         *pkt >> do_override >> day_night_ratio_u;
1257
1258         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1259
1260         ClientEvent event;
1261         event.type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1262         event.override_day_night_ratio.do_override = do_override;
1263         event.override_day_night_ratio.ratio_f     = day_night_ratio_f;
1264         m_client_event_queue.push(event);
1265 }
1266
1267 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1268 {
1269         LocalPlayer *player = m_env.getLocalPlayer();
1270         assert(player != NULL);
1271
1272         *pkt >> player->local_animations[0];
1273         *pkt >> player->local_animations[1];
1274         *pkt >> player->local_animations[2];
1275         *pkt >> player->local_animations[3];
1276         *pkt >> player->local_animation_speed;
1277 }
1278
1279 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1280 {
1281         LocalPlayer *player = m_env.getLocalPlayer();
1282         assert(player != NULL);
1283
1284         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1285 }
1286
1287 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1288 {
1289         u8 type;
1290         u16 num_players;
1291         *pkt >> type >> num_players;
1292         PlayerListModifer notice_type = (PlayerListModifer) type;
1293
1294         for (u16 i = 0; i < num_players; i++) {
1295                 std::string name;
1296                 *pkt >> name;
1297                 switch (notice_type) {
1298                 case PLAYER_LIST_INIT:
1299                 case PLAYER_LIST_ADD:
1300                         m_env.addPlayerName(name);
1301                         continue;
1302                 case PLAYER_LIST_REMOVE:
1303                         m_env.removePlayerName(name);
1304                         continue;
1305                 }
1306         }
1307 }
1308
1309 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1310 {
1311         if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1312                         && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1313                 errorstream << "Client: Recieved SRP S_B login message,"
1314                         << " but wasn't supposed to (chosen_mech="
1315                         << m_chosen_auth_mech << ")." << std::endl;
1316                 return;
1317         }
1318
1319         char *bytes_M = 0;
1320         size_t len_M = 0;
1321         SRPUser *usr = (SRPUser *) m_auth_data;
1322         std::string s;
1323         std::string B;
1324         *pkt >> s >> B;
1325
1326         infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1327
1328         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1329                 (const unsigned char *) B.c_str(), B.size(),
1330                 (unsigned char **) &bytes_M, &len_M);
1331
1332         if ( !bytes_M ) {
1333                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1334                 return;
1335         }
1336
1337         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1338         resp_pkt << std::string(bytes_M, len_M);
1339         Send(&resp_pkt);
1340 }