Sound API: Add fading sounds
[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->got_teleported = true;
565         player->setPosition(pos);
566
567         infostream << "Client got TOCLIENT_MOVE_PLAYER"
568                         << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
569                         << " pitch=" << pitch
570                         << " yaw=" << yaw
571                         << std::endl;
572
573         /*
574                 Add to ClientEvent queue.
575                 This has to be sent to the main program because otherwise
576                 it would just force the pitch and yaw values to whatever
577                 the camera points to.
578         */
579         ClientEvent event;
580         event.type = CE_PLAYER_FORCE_MOVE;
581         event.player_force_move.pitch = pitch;
582         event.player_force_move.yaw = yaw;
583         m_client_event_queue.push(event);
584
585         // Ignore damage for a few seconds, so that the player doesn't
586         // get damage from falling on ground
587         m_ignore_damage_timer = 3.0;
588 }
589
590 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
591 {
592         bool set_camera_point_target;
593         v3f camera_point_target;
594
595         *pkt >> set_camera_point_target;
596         *pkt >> camera_point_target;
597
598         ClientEvent event;
599         event.type                                = CE_DEATHSCREEN;
600         event.deathscreen.set_camera_point_target = set_camera_point_target;
601         event.deathscreen.camera_point_target_x   = camera_point_target.X;
602         event.deathscreen.camera_point_target_y   = camera_point_target.Y;
603         event.deathscreen.camera_point_target_z   = camera_point_target.Z;
604         m_client_event_queue.push(event);
605 }
606
607 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
608 {
609         u16 num_files;
610
611         *pkt >> num_files;
612
613         infostream << "Client: Received media announcement: packet size: "
614                         << pkt->getSize() << std::endl;
615
616         if (m_media_downloader == NULL ||
617                         m_media_downloader->isStarted()) {
618                 const char *problem = m_media_downloader ?
619                         "we already saw another announcement" :
620                         "all media has been received already";
621                 errorstream << "Client: Received media announcement but "
622                         << problem << "! "
623                         << " files=" << num_files
624                         << " size=" << pkt->getSize() << std::endl;
625                 return;
626         }
627
628         // Mesh update thread must be stopped while
629         // updating content definitions
630         sanity_check(!m_mesh_update_thread.isRunning());
631
632         for (u16 i = 0; i < num_files; i++) {
633                 std::string name, sha1_base64;
634
635                 *pkt >> name >> sha1_base64;
636
637                 std::string sha1_raw = base64_decode(sha1_base64);
638                 m_media_downloader->addFile(name, sha1_raw);
639         }
640
641         try {
642                 std::string str;
643
644                 *pkt >> str;
645
646                 Strfnd sf(str);
647                 while(!sf.at_end()) {
648                         std::string baseurl = trim(sf.next(","));
649                         if (baseurl != "")
650                                 m_media_downloader->addRemoteServer(baseurl);
651                 }
652         }
653         catch(SerializationError& e) {
654                 // not supported by server or turned off
655         }
656
657         m_media_downloader->step(this);
658 }
659
660 void Client::handleCommand_Media(NetworkPacket* pkt)
661 {
662         /*
663                 u16 command
664                 u16 total number of file bunches
665                 u16 index of this bunch
666                 u32 number of files in this bunch
667                 for each file {
668                         u16 length of name
669                         string name
670                         u32 length of data
671                         data
672                 }
673         */
674         u16 num_bunches;
675         u16 bunch_i;
676         u32 num_files;
677
678         *pkt >> num_bunches >> bunch_i >> num_files;
679
680         infostream << "Client: Received files: bunch " << bunch_i << "/"
681                         << num_bunches << " files=" << num_files
682                         << " size=" << pkt->getSize() << std::endl;
683
684         if (num_files == 0)
685                 return;
686
687         if (m_media_downloader == NULL ||
688                         !m_media_downloader->isStarted()) {
689                 const char *problem = m_media_downloader ?
690                         "media has not been requested" :
691                         "all media has been received already";
692                 errorstream << "Client: Received media but "
693                         << problem << "! "
694                         << " bunch " << bunch_i << "/" << num_bunches
695                         << " files=" << num_files
696                         << " size=" << pkt->getSize() << std::endl;
697                 return;
698         }
699
700         // Mesh update thread must be stopped while
701         // updating content definitions
702         sanity_check(!m_mesh_update_thread.isRunning());
703
704         for (u32 i=0; i < num_files; i++) {
705                 std::string name;
706
707                 *pkt >> name;
708
709                 std::string data = pkt->readLongString();
710
711                 m_media_downloader->conventionalTransferDone(
712                                 name, data, this);
713         }
714 }
715
716 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
717 {
718         infostream << "Client: Received node definitions: packet size: "
719                         << pkt->getSize() << std::endl;
720
721         // Mesh update thread must be stopped while
722         // updating content definitions
723         sanity_check(!m_mesh_update_thread.isRunning());
724
725         // Decompress node definitions
726         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
727         std::ostringstream tmp_os;
728         decompressZlib(tmp_is, tmp_os);
729
730         // Deserialize node definitions
731         std::istringstream tmp_is2(tmp_os.str());
732         m_nodedef->deSerialize(tmp_is2);
733         m_nodedef_received = true;
734 }
735
736 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
737 {
738         infostream << "Client: Received item definitions: packet size: "
739                         << pkt->getSize() << std::endl;
740
741         // Mesh update thread must be stopped while
742         // updating content definitions
743         sanity_check(!m_mesh_update_thread.isRunning());
744
745         // Decompress item definitions
746         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
747         std::ostringstream tmp_os;
748         decompressZlib(tmp_is, tmp_os);
749
750         // Deserialize node definitions
751         std::istringstream tmp_is2(tmp_os.str());
752         m_itemdef->deSerialize(tmp_is2);
753         m_itemdef_received = true;
754 }
755
756 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
757 {
758         /*
759                 [0] u32 server_id
760                 [4] u16 name length
761                 [6] char name[len]
762                 [ 6 + len] f32 gain
763                 [10 + len] u8 type
764                 [11 + len] (f32 * 3) pos
765                 [23 + len] u16 object_id
766                 [25 + len] bool loop
767                 [26 + len] f32 fade
768         */
769
770         s32 server_id;
771         std::string name;
772
773         float gain;
774         u8 type; // 0=local, 1=positional, 2=object
775         v3f pos;
776         u16 object_id;
777         bool loop;
778         float fade = 0;
779
780         *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
781
782         try {
783                 *pkt >> fade;
784         } catch (SerializationError &e) {};
785
786         // Start playing
787         int client_id = -1;
788         switch(type) {
789                 case 0: // local
790                         client_id = m_sound->playSound(name, loop, gain, fade);
791                         break;
792                 case 1: // positional
793                         client_id = m_sound->playSoundAt(name, loop, gain, pos);
794                         break;
795                 case 2:
796                 { // object
797                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
798                         if (cao)
799                                 pos = cao->getPosition();
800                         client_id = m_sound->playSoundAt(name, loop, gain, pos);
801                         // TODO: Set up sound to move with object
802                         break;
803                 }
804                 default:
805                         break;
806         }
807
808         if (client_id != -1) {
809                 m_sounds_server_to_client[server_id] = client_id;
810                 m_sounds_client_to_server[client_id] = server_id;
811                 if (object_id != 0)
812                         m_sounds_to_objects[client_id] = object_id;
813         }
814 }
815
816 void Client::handleCommand_StopSound(NetworkPacket* pkt)
817 {
818         s32 server_id;
819
820         *pkt >> server_id;
821
822         UNORDERED_MAP<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
823         if (i != m_sounds_server_to_client.end()) {
824                 int client_id = i->second;
825                 m_sound->stopSound(client_id);
826         }
827 }
828
829 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
830 {
831         s32 sound_id;
832         float step;
833         float gain;
834
835         *pkt >> sound_id >> step >> gain;
836
837         UNORDERED_MAP<s32, int>::iterator i =
838                         m_sounds_server_to_client.find(sound_id);
839
840         if (i != m_sounds_server_to_client.end())
841                 m_sound->fadeSound(i->second, step, gain);
842 }
843
844 void Client::handleCommand_Privileges(NetworkPacket* pkt)
845 {
846         m_privileges.clear();
847         infostream << "Client: Privileges updated: ";
848         u16 num_privileges;
849
850         *pkt >> num_privileges;
851
852         for (u16 i = 0; i < num_privileges; i++) {
853                 std::string priv;
854
855                 *pkt >> priv;
856
857                 m_privileges.insert(priv);
858                 infostream << priv << " ";
859         }
860         infostream << std::endl;
861 }
862
863 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
864 {
865         LocalPlayer *player = m_env.getLocalPlayer();
866         assert(player != NULL);
867
868         // Store formspec in LocalPlayer
869         player->inventory_formspec = pkt->readLongString();
870 }
871
872 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
873 {
874         std::string datastring(pkt->getString(0), pkt->getSize());
875         std::istringstream is(datastring, std::ios_base::binary);
876
877         std::string name = deSerializeString(is);
878
879         infostream << "Client: Detached inventory update: \"" << name
880                         << "\"" << std::endl;
881
882         Inventory *inv = NULL;
883         if (m_detached_inventories.count(name) > 0)
884                 inv = m_detached_inventories[name];
885         else {
886                 inv = new Inventory(m_itemdef);
887                 m_detached_inventories[name] = inv;
888         }
889         inv->deSerialize(is);
890 }
891
892 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
893 {
894         std::string formspec = pkt->readLongString();
895         std::string formname;
896
897         *pkt >> formname;
898
899         ClientEvent event;
900         event.type = CE_SHOW_FORMSPEC;
901         // pointer is required as event is a struct only!
902         // adding a std:string to a struct isn't possible
903         event.show_formspec.formspec = new std::string(formspec);
904         event.show_formspec.formname = new std::string(formname);
905         m_client_event_queue.push(event);
906 }
907
908 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
909 {
910         std::string datastring(pkt->getString(0), pkt->getSize());
911         std::istringstream is(datastring, std::ios_base::binary);
912
913         v3f pos                 = readV3F1000(is);
914         v3f vel                 = readV3F1000(is);
915         v3f acc                 = readV3F1000(is);
916         float expirationtime    = readF1000(is);
917         float size              = readF1000(is);
918         bool collisiondetection = readU8(is);
919         std::string texture     = deSerializeLongString(is);
920         bool vertical           = false;
921         bool collision_removal  = false;
922         struct TileAnimationParams animation;
923         animation.type = TAT_NONE;
924         u8 glow = 0;
925         try {
926                 vertical = readU8(is);
927                 collision_removal = readU8(is);
928                 animation.deSerialize(is, m_proto_ver);
929                 glow = readU8(is);
930         } catch (...) {}
931
932         ClientEvent event;
933         event.type                              = CE_SPAWN_PARTICLE;
934         event.spawn_particle.pos                = new v3f (pos);
935         event.spawn_particle.vel                = new v3f (vel);
936         event.spawn_particle.acc                = new v3f (acc);
937         event.spawn_particle.expirationtime     = expirationtime;
938         event.spawn_particle.size               = size;
939         event.spawn_particle.collisiondetection = collisiondetection;
940         event.spawn_particle.collision_removal  = collision_removal;
941         event.spawn_particle.vertical           = vertical;
942         event.spawn_particle.texture            = new std::string(texture);
943         event.spawn_particle.animation          = animation;
944         event.spawn_particle.glow               = glow;
945
946         m_client_event_queue.push(event);
947 }
948
949 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
950 {
951         u16 amount;
952         float spawntime;
953         v3f minpos;
954         v3f maxpos;
955         v3f minvel;
956         v3f maxvel;
957         v3f minacc;
958         v3f maxacc;
959         float minexptime;
960         float maxexptime;
961         float minsize;
962         float maxsize;
963         bool collisiondetection;
964         u32 id;
965
966         *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
967                 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
968                 >> maxsize >> collisiondetection;
969
970         std::string texture = pkt->readLongString();
971
972         *pkt >> id;
973
974         bool vertical = false;
975         bool collision_removal = false;
976         struct TileAnimationParams animation;
977         animation.type = TAT_NONE;
978         u8 glow = 0;
979         u16 attached_id = 0;
980         try {
981                 *pkt >> vertical;
982                 *pkt >> collision_removal;
983                 *pkt >> attached_id;
984
985                 // This is horrible but required (why are there two ways to deserialize pkts?)
986                 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
987                 std::istringstream is(datastring, std::ios_base::binary);
988                 animation.deSerialize(is, m_proto_ver);
989                 glow = readU8(is);
990         } catch (...) {}
991
992         ClientEvent event;
993         event.type                                   = CE_ADD_PARTICLESPAWNER;
994         event.add_particlespawner.amount             = amount;
995         event.add_particlespawner.spawntime          = spawntime;
996         event.add_particlespawner.minpos             = new v3f (minpos);
997         event.add_particlespawner.maxpos             = new v3f (maxpos);
998         event.add_particlespawner.minvel             = new v3f (minvel);
999         event.add_particlespawner.maxvel             = new v3f (maxvel);
1000         event.add_particlespawner.minacc             = new v3f (minacc);
1001         event.add_particlespawner.maxacc             = new v3f (maxacc);
1002         event.add_particlespawner.minexptime         = minexptime;
1003         event.add_particlespawner.maxexptime         = maxexptime;
1004         event.add_particlespawner.minsize            = minsize;
1005         event.add_particlespawner.maxsize            = maxsize;
1006         event.add_particlespawner.collisiondetection = collisiondetection;
1007         event.add_particlespawner.collision_removal  = collision_removal;
1008         event.add_particlespawner.attached_id        = attached_id;
1009         event.add_particlespawner.vertical           = vertical;
1010         event.add_particlespawner.texture            = new std::string(texture);
1011         event.add_particlespawner.id                 = id;
1012         event.add_particlespawner.animation          = animation;
1013         event.add_particlespawner.glow               = glow;
1014
1015         m_client_event_queue.push(event);
1016 }
1017
1018
1019 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1020 {
1021         u16 legacy_id;
1022         u32 id;
1023
1024         // Modification set 13/03/15, 1 year of compat for protocol v24
1025         if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
1026                 *pkt >> legacy_id;
1027         }
1028         else {
1029                 *pkt >> id;
1030         }
1031
1032
1033         ClientEvent event;
1034         event.type                      = CE_DELETE_PARTICLESPAWNER;
1035         event.delete_particlespawner.id =
1036                         (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1037
1038         m_client_event_queue.push(event);
1039 }
1040
1041 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1042 {
1043         std::string datastring(pkt->getString(0), pkt->getSize());
1044         std::istringstream is(datastring, std::ios_base::binary);
1045
1046         u32 id;
1047         u8 type;
1048         v2f pos;
1049         std::string name;
1050         v2f scale;
1051         std::string text;
1052         u32 number;
1053         u32 item;
1054         u32 dir;
1055         v2f align;
1056         v2f offset;
1057         v3f world_pos;
1058         v2s32 size;
1059
1060         *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1061                 >> dir >> align >> offset;
1062         try {
1063                 *pkt >> world_pos;
1064         }
1065         catch(SerializationError &e) {};
1066
1067         try {
1068                 *pkt >> size;
1069         } catch(SerializationError &e) {};
1070
1071         ClientEvent event;
1072         event.type             = CE_HUDADD;
1073         event.hudadd.id        = id;
1074         event.hudadd.type      = type;
1075         event.hudadd.pos       = new v2f(pos);
1076         event.hudadd.name      = new std::string(name);
1077         event.hudadd.scale     = new v2f(scale);
1078         event.hudadd.text      = new std::string(text);
1079         event.hudadd.number    = number;
1080         event.hudadd.item      = item;
1081         event.hudadd.dir       = dir;
1082         event.hudadd.align     = new v2f(align);
1083         event.hudadd.offset    = new v2f(offset);
1084         event.hudadd.world_pos = new v3f(world_pos);
1085         event.hudadd.size      = new v2s32(size);
1086         m_client_event_queue.push(event);
1087 }
1088
1089 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1090 {
1091         u32 id;
1092
1093         *pkt >> id;
1094
1095         ClientEvent event;
1096         event.type     = CE_HUDRM;
1097         event.hudrm.id = id;
1098         m_client_event_queue.push(event);
1099 }
1100
1101 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1102 {
1103         std::string sdata;
1104         v2f v2fdata;
1105         v3f v3fdata;
1106         u32 intdata = 0;
1107         v2s32 v2s32data;
1108         u32 id;
1109         u8 stat;
1110
1111         *pkt >> id >> stat;
1112
1113         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1114                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1115                 *pkt >> v2fdata;
1116         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1117                 *pkt >> sdata;
1118         else if (stat == HUD_STAT_WORLD_POS)
1119                 *pkt >> v3fdata;
1120         else if (stat == HUD_STAT_SIZE )
1121                 *pkt >> v2s32data;
1122         else
1123                 *pkt >> intdata;
1124
1125         ClientEvent event;
1126         event.type              = CE_HUDCHANGE;
1127         event.hudchange.id      = id;
1128         event.hudchange.stat    = (HudElementStat)stat;
1129         event.hudchange.v2fdata = new v2f(v2fdata);
1130         event.hudchange.v3fdata = new v3f(v3fdata);
1131         event.hudchange.sdata   = new std::string(sdata);
1132         event.hudchange.data    = intdata;
1133         event.hudchange.v2s32data = new v2s32(v2s32data);
1134         m_client_event_queue.push(event);
1135 }
1136
1137 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1138 {
1139         u32 flags, mask;
1140
1141         *pkt >> flags >> mask;
1142
1143         LocalPlayer *player = m_env.getLocalPlayer();
1144         assert(player != NULL);
1145
1146         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1147
1148         player->hud_flags &= ~mask;
1149         player->hud_flags |= flags;
1150
1151         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1152
1153         // Hide minimap if it has been disabled by the server
1154         if (m_minimap_disabled_by_server && was_minimap_visible) {
1155                 // defers a minimap update, therefore only call it if really
1156                 // needed, by checking that minimap was visible before
1157                 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1158         }
1159 }
1160
1161 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1162 {
1163         u16 param; std::string value;
1164
1165         *pkt >> param >> value;
1166
1167         LocalPlayer *player = m_env.getLocalPlayer();
1168         assert(player != NULL);
1169
1170         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1171                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1172                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1173                         player->hud_hotbar_itemcount = hotbar_itemcount;
1174         }
1175         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1176                 player->hotbar_image = value;
1177         }
1178         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1179                 player->hotbar_selected_image = value;
1180         }
1181 }
1182
1183 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1184 {
1185         std::string datastring(pkt->getString(0), pkt->getSize());
1186         std::istringstream is(datastring, std::ios_base::binary);
1187
1188         video::SColor *bgcolor           = new video::SColor(readARGB8(is));
1189         std::string *type                = new std::string(deSerializeString(is));
1190         u16 count                        = readU16(is);
1191         std::vector<std::string> *params = new std::vector<std::string>;
1192
1193         for (size_t i = 0; i < count; i++)
1194                 params->push_back(deSerializeString(is));
1195
1196         ClientEvent event;
1197         event.type            = CE_SET_SKY;
1198         event.set_sky.bgcolor = bgcolor;
1199         event.set_sky.type    = type;
1200         event.set_sky.params  = params;
1201         m_client_event_queue.push(event);
1202 }
1203
1204 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1205 {
1206         f32 density;
1207         video::SColor color_bright;
1208         video::SColor color_ambient;
1209         f32 height;
1210         f32 thickness;
1211         v2f speed;
1212
1213         *pkt >> density >> color_bright >> color_ambient
1214                         >> height >> thickness >> speed;
1215
1216         ClientEvent event;
1217         event.type                       = CE_CLOUD_PARAMS;
1218         event.cloud_params.density       = density;
1219         // use the underlying u32 representation, because we can't
1220         // use struct members with constructors here, and this way
1221         // we avoid using new() and delete() for no good reason
1222         event.cloud_params.color_bright  = color_bright.color;
1223         event.cloud_params.color_ambient = color_ambient.color;
1224         event.cloud_params.height        = height;
1225         event.cloud_params.thickness     = thickness;
1226         // same here: deconstruct to skip constructor
1227         event.cloud_params.speed_x       = speed.X;
1228         event.cloud_params.speed_y       = speed.Y;
1229         m_client_event_queue.push(event);
1230 }
1231
1232 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1233 {
1234         bool do_override;
1235         u16 day_night_ratio_u;
1236
1237         *pkt >> do_override >> day_night_ratio_u;
1238
1239         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1240
1241         ClientEvent event;
1242         event.type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1243         event.override_day_night_ratio.do_override = do_override;
1244         event.override_day_night_ratio.ratio_f     = day_night_ratio_f;
1245         m_client_event_queue.push(event);
1246 }
1247
1248 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1249 {
1250         LocalPlayer *player = m_env.getLocalPlayer();
1251         assert(player != NULL);
1252
1253         *pkt >> player->local_animations[0];
1254         *pkt >> player->local_animations[1];
1255         *pkt >> player->local_animations[2];
1256         *pkt >> player->local_animations[3];
1257         *pkt >> player->local_animation_speed;
1258 }
1259
1260 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1261 {
1262         LocalPlayer *player = m_env.getLocalPlayer();
1263         assert(player != NULL);
1264
1265         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1266 }
1267
1268 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1269 {
1270         if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1271                         && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1272                 errorstream << "Client: Recieved SRP S_B login message,"
1273                         << " but wasn't supposed to (chosen_mech="
1274                         << m_chosen_auth_mech << ")." << std::endl;
1275                 return;
1276         }
1277
1278         char *bytes_M = 0;
1279         size_t len_M = 0;
1280         SRPUser *usr = (SRPUser *) m_auth_data;
1281         std::string s;
1282         std::string B;
1283         *pkt >> s >> B;
1284
1285         infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1286
1287         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1288                 (const unsigned char *) B.c_str(), B.size(),
1289                 (unsigned char **) &bytes_M, &len_M);
1290
1291         if ( !bytes_M ) {
1292                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1293                 return;
1294         }
1295
1296         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1297         resp_pkt << std::string(bytes_M, len_M);
1298         Send(&resp_pkt);
1299 }