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