]> git.lizzy.rs Git - minetest.git/blob - src/network/clientpackethandler.cpp
Network:Remove old opcodes and fix documentation. (#5573)
[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 (!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         s32 server_id;
759         std::string name;
760         float gain;
761         u8 type; // 0=local, 1=positional, 2=object
762         v3f pos;
763         u16 object_id;
764         bool loop;
765
766         *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
767
768         // Start playing
769         int client_id = -1;
770         switch(type) {
771                 case 0: // local
772                         client_id = m_sound->playSound(name, loop, gain);
773                         break;
774                 case 1: // positional
775                         client_id = m_sound->playSoundAt(name, loop, gain, pos);
776                         break;
777                 case 2:
778                 { // object
779                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
780                         if (cao)
781                                 pos = cao->getPosition();
782                         client_id = m_sound->playSoundAt(name, loop, gain, pos);
783                         // TODO: Set up sound to move with object
784                         break;
785                 }
786                 default:
787                         break;
788         }
789
790         if (client_id != -1) {
791                 m_sounds_server_to_client[server_id] = client_id;
792                 m_sounds_client_to_server[client_id] = server_id;
793                 if (object_id != 0)
794                         m_sounds_to_objects[client_id] = object_id;
795         }
796 }
797
798 void Client::handleCommand_StopSound(NetworkPacket* pkt)
799 {
800         s32 server_id;
801
802         *pkt >> server_id;
803
804         UNORDERED_MAP<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
805         if (i != m_sounds_server_to_client.end()) {
806                 int client_id = i->second;
807                 m_sound->stopSound(client_id);
808         }
809 }
810
811 void Client::handleCommand_Privileges(NetworkPacket* pkt)
812 {
813         m_privileges.clear();
814         infostream << "Client: Privileges updated: ";
815         u16 num_privileges;
816
817         *pkt >> num_privileges;
818
819         for (u16 i = 0; i < num_privileges; i++) {
820                 std::string priv;
821
822                 *pkt >> priv;
823
824                 m_privileges.insert(priv);
825                 infostream << priv << " ";
826         }
827         infostream << std::endl;
828 }
829
830 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
831 {
832         LocalPlayer *player = m_env.getLocalPlayer();
833         assert(player != NULL);
834
835         // Store formspec in LocalPlayer
836         player->inventory_formspec = pkt->readLongString();
837 }
838
839 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
840 {
841         std::string datastring(pkt->getString(0), pkt->getSize());
842         std::istringstream is(datastring, std::ios_base::binary);
843
844         std::string name = deSerializeString(is);
845
846         infostream << "Client: Detached inventory update: \"" << name
847                         << "\"" << std::endl;
848
849         Inventory *inv = NULL;
850         if (m_detached_inventories.count(name) > 0)
851                 inv = m_detached_inventories[name];
852         else {
853                 inv = new Inventory(m_itemdef);
854                 m_detached_inventories[name] = inv;
855         }
856         inv->deSerialize(is);
857 }
858
859 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
860 {
861         std::string formspec = pkt->readLongString();
862         std::string formname;
863
864         *pkt >> formname;
865
866         ClientEvent event;
867         event.type = CE_SHOW_FORMSPEC;
868         // pointer is required as event is a struct only!
869         // adding a std:string to a struct isn't possible
870         event.show_formspec.formspec = new std::string(formspec);
871         event.show_formspec.formname = new std::string(formname);
872         m_client_event_queue.push(event);
873 }
874
875 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
876 {
877         std::string datastring(pkt->getString(0), pkt->getSize());
878         std::istringstream is(datastring, std::ios_base::binary);
879
880         v3f pos                 = readV3F1000(is);
881         v3f vel                 = readV3F1000(is);
882         v3f acc                 = readV3F1000(is);
883         float expirationtime    = readF1000(is);
884         float size              = readF1000(is);
885         bool collisiondetection = readU8(is);
886         std::string texture     = deSerializeLongString(is);
887         bool vertical           = false;
888         bool collision_removal  = false;
889         struct TileAnimationParams animation;
890         animation.type = TAT_NONE;
891         u8 glow = 0;
892         try {
893                 vertical = readU8(is);
894                 collision_removal = readU8(is);
895                 animation.deSerialize(is, m_proto_ver);
896                 glow = readU8(is);
897         } catch (...) {}
898
899         ClientEvent event;
900         event.type                              = CE_SPAWN_PARTICLE;
901         event.spawn_particle.pos                = new v3f (pos);
902         event.spawn_particle.vel                = new v3f (vel);
903         event.spawn_particle.acc                = new v3f (acc);
904         event.spawn_particle.expirationtime     = expirationtime;
905         event.spawn_particle.size               = size;
906         event.spawn_particle.collisiondetection = collisiondetection;
907         event.spawn_particle.collision_removal  = collision_removal;
908         event.spawn_particle.vertical           = vertical;
909         event.spawn_particle.texture            = new std::string(texture);
910         event.spawn_particle.animation          = animation;
911         event.spawn_particle.glow               = glow;
912
913         m_client_event_queue.push(event);
914 }
915
916 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
917 {
918         u16 amount;
919         float spawntime;
920         v3f minpos;
921         v3f maxpos;
922         v3f minvel;
923         v3f maxvel;
924         v3f minacc;
925         v3f maxacc;
926         float minexptime;
927         float maxexptime;
928         float minsize;
929         float maxsize;
930         bool collisiondetection;
931         u32 id;
932
933         *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
934                 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
935                 >> maxsize >> collisiondetection;
936
937         std::string texture = pkt->readLongString();
938
939         *pkt >> id;
940
941         bool vertical = false;
942         bool collision_removal = false;
943         struct TileAnimationParams animation;
944         animation.type = TAT_NONE;
945         u8 glow = 0;
946         u16 attached_id = 0;
947         try {
948                 *pkt >> vertical;
949                 *pkt >> collision_removal;
950                 *pkt >> attached_id;
951
952                 // This is horrible but required (why are there two ways to deserialize pkts?)
953                 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
954                 std::istringstream is(datastring, std::ios_base::binary);
955                 animation.deSerialize(is, m_proto_ver);
956                 glow = readU8(is);
957         } catch (...) {}
958
959         ClientEvent event;
960         event.type                                   = CE_ADD_PARTICLESPAWNER;
961         event.add_particlespawner.amount             = amount;
962         event.add_particlespawner.spawntime          = spawntime;
963         event.add_particlespawner.minpos             = new v3f (minpos);
964         event.add_particlespawner.maxpos             = new v3f (maxpos);
965         event.add_particlespawner.minvel             = new v3f (minvel);
966         event.add_particlespawner.maxvel             = new v3f (maxvel);
967         event.add_particlespawner.minacc             = new v3f (minacc);
968         event.add_particlespawner.maxacc             = new v3f (maxacc);
969         event.add_particlespawner.minexptime         = minexptime;
970         event.add_particlespawner.maxexptime         = maxexptime;
971         event.add_particlespawner.minsize            = minsize;
972         event.add_particlespawner.maxsize            = maxsize;
973         event.add_particlespawner.collisiondetection = collisiondetection;
974         event.add_particlespawner.collision_removal  = collision_removal;
975         event.add_particlespawner.attached_id        = attached_id;
976         event.add_particlespawner.vertical           = vertical;
977         event.add_particlespawner.texture            = new std::string(texture);
978         event.add_particlespawner.id                 = id;
979         event.add_particlespawner.animation          = animation;
980         event.add_particlespawner.glow               = glow;
981
982         m_client_event_queue.push(event);
983 }
984
985
986 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
987 {
988         u16 legacy_id;
989         u32 id;
990
991         // Modification set 13/03/15, 1 year of compat for protocol v24
992         if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
993                 *pkt >> legacy_id;
994         }
995         else {
996                 *pkt >> id;
997         }
998
999
1000         ClientEvent event;
1001         event.type                      = CE_DELETE_PARTICLESPAWNER;
1002         event.delete_particlespawner.id =
1003                         (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1004
1005         m_client_event_queue.push(event);
1006 }
1007
1008 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1009 {
1010         std::string datastring(pkt->getString(0), pkt->getSize());
1011         std::istringstream is(datastring, std::ios_base::binary);
1012
1013         u32 id;
1014         u8 type;
1015         v2f pos;
1016         std::string name;
1017         v2f scale;
1018         std::string text;
1019         u32 number;
1020         u32 item;
1021         u32 dir;
1022         v2f align;
1023         v2f offset;
1024         v3f world_pos;
1025         v2s32 size;
1026
1027         *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1028                 >> dir >> align >> offset;
1029         try {
1030                 *pkt >> world_pos;
1031         }
1032         catch(SerializationError &e) {};
1033
1034         try {
1035                 *pkt >> size;
1036         } catch(SerializationError &e) {};
1037
1038         ClientEvent event;
1039         event.type             = CE_HUDADD;
1040         event.hudadd.id        = id;
1041         event.hudadd.type      = type;
1042         event.hudadd.pos       = new v2f(pos);
1043         event.hudadd.name      = new std::string(name);
1044         event.hudadd.scale     = new v2f(scale);
1045         event.hudadd.text      = new std::string(text);
1046         event.hudadd.number    = number;
1047         event.hudadd.item      = item;
1048         event.hudadd.dir       = dir;
1049         event.hudadd.align     = new v2f(align);
1050         event.hudadd.offset    = new v2f(offset);
1051         event.hudadd.world_pos = new v3f(world_pos);
1052         event.hudadd.size      = new v2s32(size);
1053         m_client_event_queue.push(event);
1054 }
1055
1056 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1057 {
1058         u32 id;
1059
1060         *pkt >> id;
1061
1062         ClientEvent event;
1063         event.type     = CE_HUDRM;
1064         event.hudrm.id = id;
1065         m_client_event_queue.push(event);
1066 }
1067
1068 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1069 {
1070         std::string sdata;
1071         v2f v2fdata;
1072         v3f v3fdata;
1073         u32 intdata = 0;
1074         v2s32 v2s32data;
1075         u32 id;
1076         u8 stat;
1077
1078         *pkt >> id >> stat;
1079
1080         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1081                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1082                 *pkt >> v2fdata;
1083         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1084                 *pkt >> sdata;
1085         else if (stat == HUD_STAT_WORLD_POS)
1086                 *pkt >> v3fdata;
1087         else if (stat == HUD_STAT_SIZE )
1088                 *pkt >> v2s32data;
1089         else
1090                 *pkt >> intdata;
1091
1092         ClientEvent event;
1093         event.type              = CE_HUDCHANGE;
1094         event.hudchange.id      = id;
1095         event.hudchange.stat    = (HudElementStat)stat;
1096         event.hudchange.v2fdata = new v2f(v2fdata);
1097         event.hudchange.v3fdata = new v3f(v3fdata);
1098         event.hudchange.sdata   = new std::string(sdata);
1099         event.hudchange.data    = intdata;
1100         event.hudchange.v2s32data = new v2s32(v2s32data);
1101         m_client_event_queue.push(event);
1102 }
1103
1104 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1105 {
1106         u32 flags, mask;
1107
1108         *pkt >> flags >> mask;
1109
1110         LocalPlayer *player = m_env.getLocalPlayer();
1111         assert(player != NULL);
1112
1113         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1114
1115         player->hud_flags &= ~mask;
1116         player->hud_flags |= flags;
1117
1118         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1119
1120         // Hide minimap if it has been disabled by the server
1121         if (m_minimap_disabled_by_server && was_minimap_visible) {
1122                 // defers a minimap update, therefore only call it if really
1123                 // needed, by checking that minimap was visible before
1124                 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1125         }
1126 }
1127
1128 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1129 {
1130         u16 param; std::string value;
1131
1132         *pkt >> param >> value;
1133
1134         LocalPlayer *player = m_env.getLocalPlayer();
1135         assert(player != NULL);
1136
1137         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1138                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1139                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1140                         player->hud_hotbar_itemcount = hotbar_itemcount;
1141         }
1142         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1143                 player->hotbar_image = value;
1144         }
1145         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1146                 player->hotbar_selected_image = value;
1147         }
1148 }
1149
1150 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1151 {
1152         std::string datastring(pkt->getString(0), pkt->getSize());
1153         std::istringstream is(datastring, std::ios_base::binary);
1154
1155         video::SColor *bgcolor           = new video::SColor(readARGB8(is));
1156         std::string *type                = new std::string(deSerializeString(is));
1157         u16 count                        = readU16(is);
1158         std::vector<std::string> *params = new std::vector<std::string>;
1159
1160         for (size_t i = 0; i < count; i++)
1161                 params->push_back(deSerializeString(is));
1162
1163         ClientEvent event;
1164         event.type            = CE_SET_SKY;
1165         event.set_sky.bgcolor = bgcolor;
1166         event.set_sky.type    = type;
1167         event.set_sky.params  = params;
1168         m_client_event_queue.push(event);
1169 }
1170
1171 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1172 {
1173         bool do_override;
1174         u16 day_night_ratio_u;
1175
1176         *pkt >> do_override >> day_night_ratio_u;
1177
1178         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1179
1180         ClientEvent event;
1181         event.type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1182         event.override_day_night_ratio.do_override = do_override;
1183         event.override_day_night_ratio.ratio_f     = day_night_ratio_f;
1184         m_client_event_queue.push(event);
1185 }
1186
1187 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1188 {
1189         LocalPlayer *player = m_env.getLocalPlayer();
1190         assert(player != NULL);
1191
1192         *pkt >> player->local_animations[0];
1193         *pkt >> player->local_animations[1];
1194         *pkt >> player->local_animations[2];
1195         *pkt >> player->local_animations[3];
1196         *pkt >> player->local_animation_speed;
1197 }
1198
1199 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1200 {
1201         LocalPlayer *player = m_env.getLocalPlayer();
1202         assert(player != NULL);
1203
1204         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1205 }
1206
1207 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1208 {
1209         if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1210                         && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1211                 errorstream << "Client: Recieved SRP S_B login message,"
1212                         << " but wasn't supposed to (chosen_mech="
1213                         << m_chosen_auth_mech << ")." << std::endl;
1214                 return;
1215         }
1216
1217         char *bytes_M = 0;
1218         size_t len_M = 0;
1219         SRPUser *usr = (SRPUser *) m_auth_data;
1220         std::string s;
1221         std::string B;
1222         *pkt >> s >> B;
1223
1224         infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1225
1226         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1227                 (const unsigned char *) B.c_str(), B.size(),
1228                 (unsigned char **) &bytes_M, &len_M);
1229
1230         if ( !bytes_M ) {
1231                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1232                 return;
1233         }
1234
1235         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1236         resp_pkt << std::string(bytes_M, len_M);
1237         Send(&resp_pkt);
1238 }