]> git.lizzy.rs Git - dragonfireclient.git/blob - src/network/clientpackethandler.cpp
Use UTF-8 instead of narrow
[dragonfireclient.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 "nodedef.h"
28 #include "serialization.h"
29 #include "server.h"
30 #include "strfnd.h"
31 #include "network/clientopcodes.h"
32 #include "util/serialize.h"
33 #include "util/srp.h"
34
35 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
36 {
37         infostream << "Got deprecated command "
38                         << toClientCommandTable[pkt->getCommand()].name << " from peer "
39                         << pkt->getPeerId() << "!" << std::endl;
40 }
41
42 void Client::handleCommand_Hello(NetworkPacket* pkt)
43 {
44         if (pkt->getSize() < 1)
45                 return;
46
47         u8 serialization_ver;
48         u16 proto_ver;
49         u16 compression_mode;
50         u32 auth_mechs;
51         std::string username_legacy; // for case insensitivity
52         *pkt >> serialization_ver >> compression_mode >> proto_ver
53                 >> auth_mechs >> username_legacy;
54
55         // Chose an auth method we support
56         AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
57
58         infostream << "Client: TOCLIENT_HELLO received with "
59                         << "serialization_ver=" << (u32)serialization_ver
60                         << ", auth_mechs=" << auth_mechs
61                         << ", proto_ver=" << proto_ver
62                         << ", compression_mode=" << compression_mode
63                         << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
64
65         if (!ser_ver_supported(serialization_ver)) {
66                 infostream << "Client: TOCLIENT_HELLO: Server sent "
67                                 << "unsupported ser_fmt_ver"<< std::endl;
68                 return;
69         }
70
71         m_server_ser_ver = serialization_ver;
72         m_proto_ver = proto_ver;
73
74         //TODO verify that username_legacy matches sent username, only
75         // differs in casing (make both uppercase and compare)
76         // This is only neccessary though when we actually want to add casing support
77
78         if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
79                 // we recieved a TOCLIENT_HELLO while auth was already going on
80                 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
81                         << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
82                 if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP)
83                                 || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) {
84                         srp_user_delete((SRPUser *) m_auth_data);
85                         m_auth_data = 0;
86                 }
87         }
88
89         // Authenticate using that method, or abort if there wasn't any method found
90         if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
91                 startAuth(chosen_auth_mechanism);
92         } else {
93                 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
94                 m_access_denied = true;
95                 m_access_denied_reason = "Unknown";
96                 m_con.Disconnect();
97         }
98
99 }
100
101 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
102 {
103         m_chosen_auth_mech = AUTH_MECHANISM_NONE;
104         deleteAuthData();
105
106         v3f playerpos;
107         *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
108                 >> m_sudo_auth_methods;
109
110         playerpos -= v3f(0, BS / 2, 0);
111
112         // Set player position
113         Player *player = m_env.getLocalPlayer();
114         assert(player != NULL);
115         player->setPosition(playerpos);
116
117         infostream << "Client: received map seed: " << m_map_seed << std::endl;
118         infostream << "Client: received recommended send interval "
119                                         << m_recommended_send_interval<<std::endl;
120
121         // Reply to server
122         NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
123         Send(&resp_pkt);
124
125         m_state = LC_Init;
126 }
127 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
128 {
129         m_chosen_auth_mech = AUTH_MECHANISM_NONE;
130         deleteAuthData();
131
132         m_password = m_new_password;
133
134         verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
135
136         // send packet to actually set the password
137         startAuth(AUTH_MECHANISM_FIRST_SRP);
138
139         // reset again
140         m_chosen_auth_mech = AUTH_MECHANISM_NONE;
141 }
142 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
143 {
144         m_chat_queue.push(L"Password change denied. Password NOT changed.");
145         // reset everything and be sad
146         deleteAuthData();
147         m_chosen_auth_mech = AUTH_MECHANISM_NONE;
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         Player *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_CUSTOM_STRING) {
219                         *pkt >> m_access_denied_reason;
220                 }
221                 else if (denyCode < SERVER_ACCESSDENIED_MAX) {
222                         m_access_denied_reason = accessDeniedStrings[denyCode];
223                 }
224         }
225         // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
226         // for compat with old clients
227         else {
228                 if (pkt->getSize() >= 2) {
229                         std::wstring wide_reason;
230                         *pkt >> wide_reason;
231                         m_access_denied_reason = wide_to_utf8(wide_reason);
232                 }
233         }
234 }
235
236 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
237 {
238         if (pkt->getSize() < 6)
239                 return;
240
241         v3s16 p;
242         *pkt >> p;
243         removeNode(p);
244 }
245
246 void Client::handleCommand_AddNode(NetworkPacket* pkt)
247 {
248         if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
249                 return;
250
251         v3s16 p;
252         *pkt >> p;
253
254         MapNode n;
255         n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
256
257         bool remove_metadata = true;
258         u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
259         if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
260                 remove_metadata = false;
261         }
262
263         addNode(p, n, remove_metadata);
264 }
265 void Client::handleCommand_BlockData(NetworkPacket* pkt)
266 {
267         // Ignore too small packet
268         if (pkt->getSize() < 6)
269                 return;
270
271         v3s16 p;
272         *pkt >> p;
273
274         std::string datastring(pkt->getString(6), pkt->getSize() - 6);
275         std::istringstream istr(datastring, std::ios_base::binary);
276
277         MapSector *sector;
278         MapBlock *block;
279
280         v2s16 p2d(p.X, p.Z);
281         sector = m_env.getMap().emergeSector(p2d);
282
283         assert(sector->getPos() == p2d);
284
285         block = sector->getBlockNoCreateNoEx(p.Y);
286         if (block) {
287                 /*
288                         Update an existing block
289                 */
290                 block->deSerialize(istr, m_server_ser_ver, false);
291                 block->deSerializeNetworkSpecific(istr);
292         }
293         else {
294                 /*
295                         Create a new block
296                 */
297                 block = new MapBlock(&m_env.getMap(), p, this);
298                 block->deSerialize(istr, m_server_ser_ver, false);
299                 block->deSerializeNetworkSpecific(istr);
300                 sector->insertBlock(block);
301         }
302
303         if (m_localdb) {
304                 ServerMap::saveBlock(block, m_localdb);
305         }
306
307         /*
308                 Add it to mesh update queue and set it to be acknowledged after update.
309         */
310         addUpdateMeshTaskWithEdge(p, true);
311 }
312
313 void Client::handleCommand_Inventory(NetworkPacket* pkt)
314 {
315         if (pkt->getSize() < 1)
316                 return;
317
318         std::string datastring(pkt->getString(0), pkt->getSize());
319         std::istringstream is(datastring, std::ios_base::binary);
320
321         Player *player = m_env.getLocalPlayer();
322         assert(player != NULL);
323
324         player->inventory.deSerialize(is);
325
326         m_inventory_updated = true;
327
328         delete m_inventory_from_server;
329         m_inventory_from_server = new Inventory(player->inventory);
330         m_inventory_from_server_age = 0.0;
331 }
332
333 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
334 {
335         if (pkt->getSize() < 2)
336                 return;
337
338         u16 time_of_day;
339
340         *pkt >> time_of_day;
341
342         time_of_day      = time_of_day % 24000;
343         float time_speed = 0;
344
345         if (pkt->getSize() >= 2 + 4) {
346                 *pkt >> time_speed;
347         }
348         else {
349                 // Old message; try to approximate speed of time by ourselves
350                 float time_of_day_f = (float)time_of_day / 24000.0;
351                 float tod_diff_f = 0;
352
353                 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
354                         tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
355                 else
356                         tod_diff_f = time_of_day_f - m_last_time_of_day_f;
357
358                 m_last_time_of_day_f       = time_of_day_f;
359                 float time_diff            = m_time_of_day_update_timer;
360                 m_time_of_day_update_timer = 0;
361
362                 if (m_time_of_day_set) {
363                         time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
364                         infostream << "Client: Measured time_of_day speed (old format): "
365                                         << time_speed << " tod_diff_f=" << tod_diff_f
366                                         << " time_diff=" << time_diff << std::endl;
367                 }
368         }
369
370         // Update environment
371         m_env.setTimeOfDay(time_of_day);
372         m_env.setTimeOfDaySpeed(time_speed);
373         m_time_of_day_set = true;
374
375         u32 dr = m_env.getDayNightRatio();
376         infostream << "Client: time_of_day=" << time_of_day
377                         << " time_speed=" << time_speed
378                         << " dr=" << dr << std::endl;
379 }
380
381 void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
382 {
383         /*
384                 u16 command
385                 u16 length
386                 wstring message
387         */
388         u16 len, read_wchar;
389
390         *pkt >> len;
391
392         std::wstring message;
393         for (u32 i = 0; i < len; i++) {
394                 *pkt >> read_wchar;
395                 message += (wchar_t)read_wchar;
396         }
397
398         m_chat_queue.push(message);
399 }
400
401 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
402 {
403         /*
404                 u16 count of removed objects
405                 for all removed objects {
406                         u16 id
407                 }
408                 u16 count of added objects
409                 for all added objects {
410                         u16 id
411                         u8 type
412                         u32 initialization data length
413                         string initialization data
414                 }
415         */
416
417         try {
418                 u8 type;
419                 u16 removed_count, added_count, id;
420
421                 // Read removed objects
422                 *pkt >> removed_count;
423
424                 for (u16 i = 0; i < removed_count; i++) {
425                         *pkt >> id;
426                         m_env.removeActiveObject(id);
427                 }
428
429                 // Read added objects
430                 *pkt >> added_count;
431
432                 for (u16 i = 0; i < added_count; i++) {
433                         *pkt >> id >> type;
434                         m_env.addActiveObject(id, type, pkt->readLongString());
435                 }
436         } catch (PacketError &e) {
437                 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
438                                 << ". The packet is unreliable, ignoring" << std::endl;
439         }
440 }
441
442 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
443 {
444         /*
445                 for all objects
446                 {
447                         u16 id
448                         u16 message length
449                         string message
450                 }
451         */
452         char buf[6];
453         // Get all data except the command number
454         std::string datastring(pkt->getString(0), pkt->getSize());
455         // Throw them in an istringstream
456         std::istringstream is(datastring, std::ios_base::binary);
457
458         try {
459                 while(is.eof() == false) {
460                         is.read(buf, 2);
461                         u16 id = readU16((u8*)buf);
462                         if (is.eof())
463                                 break;
464                         is.read(buf, 2);
465                         size_t message_size = readU16((u8*)buf);
466                         std::string message;
467                         message.reserve(message_size);
468                         for (u32 i = 0; i < message_size; i++) {
469                                 is.read(buf, 1);
470                                 message.append(buf, 1);
471                         }
472                         // Pass on to the environment
473                         m_env.processActiveObjectMessage(id, message);
474                 }
475         // Packet could be unreliable then ignore it
476         } catch (PacketError &e) {
477                 infostream << "handleCommand_ActiveObjectMessages: " << e.what()
478                                         << ". The packet is unreliable, ignoring" << std::endl;
479         }
480 }
481
482 void Client::handleCommand_Movement(NetworkPacket* pkt)
483 {
484         Player *player = m_env.getLocalPlayer();
485         assert(player != NULL);
486
487         float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
488
489         *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
490                 >> lf >> lfs >> ls >> g;
491
492         player->movement_acceleration_default   = mad * BS;
493         player->movement_acceleration_air       = maa * BS;
494         player->movement_acceleration_fast      = maf * BS;
495         player->movement_speed_walk             = msw * BS;
496         player->movement_speed_crouch           = mscr * BS;
497         player->movement_speed_fast             = msf * BS;
498         player->movement_speed_climb            = mscl * BS;
499         player->movement_speed_jump             = msj * BS;
500         player->movement_liquid_fluidity        = lf * BS;
501         player->movement_liquid_fluidity_smooth = lfs * BS;
502         player->movement_liquid_sink            = ls * BS;
503         player->movement_gravity                = g * BS;
504 }
505
506 void Client::handleCommand_HP(NetworkPacket* pkt)
507 {
508
509         Player *player = m_env.getLocalPlayer();
510         assert(player != NULL);
511
512         u8 oldhp   = player->hp;
513
514         u8 hp;
515         *pkt >> hp;
516
517         player->hp = hp;
518
519         if (hp < oldhp) {
520                 // Add to ClientEvent queue
521                 ClientEvent event;
522                 event.type = CE_PLAYER_DAMAGE;
523                 event.player_damage.amount = oldhp - hp;
524                 m_client_event_queue.push(event);
525         }
526 }
527
528 void Client::handleCommand_Breath(NetworkPacket* pkt)
529 {
530         Player *player = m_env.getLocalPlayer();
531         assert(player != NULL);
532
533         u16 breath;
534
535         *pkt >> breath;
536
537         player->setBreath(breath);
538 }
539
540 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
541 {
542         Player *player = m_env.getLocalPlayer();
543         assert(player != NULL);
544
545         v3f pos;
546         f32 pitch, yaw;
547
548         *pkt >> pos >> pitch >> yaw;
549
550         player->setPosition(pos);
551
552         infostream << "Client got TOCLIENT_MOVE_PLAYER"
553                         << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
554                         << " pitch=" << pitch
555                         << " yaw=" << yaw
556                         << std::endl;
557
558         /*
559                 Add to ClientEvent queue.
560                 This has to be sent to the main program because otherwise
561                 it would just force the pitch and yaw values to whatever
562                 the camera points to.
563         */
564         ClientEvent event;
565         event.type = CE_PLAYER_FORCE_MOVE;
566         event.player_force_move.pitch = pitch;
567         event.player_force_move.yaw = yaw;
568         m_client_event_queue.push(event);
569
570         // Ignore damage for a few seconds, so that the player doesn't
571         // get damage from falling on ground
572         m_ignore_damage_timer = 3.0;
573 }
574
575 void Client::handleCommand_PlayerItem(NetworkPacket* pkt)
576 {
577         infostream << "Client: WARNING: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
578 }
579
580 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
581 {
582         bool set_camera_point_target;
583         v3f camera_point_target;
584
585         *pkt >> set_camera_point_target;
586         *pkt >> camera_point_target;
587
588         ClientEvent event;
589         event.type                                = CE_DEATHSCREEN;
590         event.deathscreen.set_camera_point_target = set_camera_point_target;
591         event.deathscreen.camera_point_target_x   = camera_point_target.X;
592         event.deathscreen.camera_point_target_y   = camera_point_target.Y;
593         event.deathscreen.camera_point_target_z   = camera_point_target.Z;
594         m_client_event_queue.push(event);
595 }
596
597 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
598 {
599         u16 num_files;
600
601         *pkt >> num_files;
602
603         infostream << "Client: Received media announcement: packet size: "
604                         << pkt->getSize() << std::endl;
605
606         if (m_media_downloader == NULL ||
607                         m_media_downloader->isStarted()) {
608                 const char *problem = m_media_downloader ?
609                         "we already saw another announcement" :
610                         "all media has been received already";
611                 errorstream << "Client: Received media announcement but "
612                         << problem << "! "
613                         << " files=" << num_files
614                         << " size=" << pkt->getSize() << std::endl;
615                 return;
616         }
617
618         // Mesh update thread must be stopped while
619         // updating content definitions
620         sanity_check(!m_mesh_update_thread.IsRunning());
621
622         for (u16 i = 0; i < num_files; i++) {
623                 std::string name, sha1_base64;
624
625                 *pkt >> name >> sha1_base64;
626
627                 std::string sha1_raw = base64_decode(sha1_base64);
628                 m_media_downloader->addFile(name, sha1_raw);
629         }
630
631         std::vector<std::string> remote_media;
632         try {
633                 std::string str;
634
635                 *pkt >> str;
636
637                 Strfnd sf(str);
638                 while(!sf.atend()) {
639                         std::string baseurl = trim(sf.next(","));
640                         if (baseurl != "")
641                                 m_media_downloader->addRemoteServer(baseurl);
642                 }
643         }
644         catch(SerializationError& e) {
645                 // not supported by server or turned off
646         }
647
648         m_media_downloader->step(this);
649 }
650
651 void Client::handleCommand_Media(NetworkPacket* pkt)
652 {
653         /*
654                 u16 command
655                 u16 total number of file bunches
656                 u16 index of this bunch
657                 u32 number of files in this bunch
658                 for each file {
659                         u16 length of name
660                         string name
661                         u32 length of data
662                         data
663                 }
664         */
665         u16 num_bunches;
666         u16 bunch_i;
667         u32 num_files;
668
669         *pkt >> num_bunches >> bunch_i >> num_files;
670
671         infostream << "Client: Received files: bunch " << bunch_i << "/"
672                         << num_bunches << " files=" << num_files
673                         << " size=" << pkt->getSize() << std::endl;
674
675         if (num_files == 0)
676                 return;
677
678         if (m_media_downloader == NULL ||
679                         !m_media_downloader->isStarted()) {
680                 const char *problem = m_media_downloader ?
681                         "media has not been requested" :
682                         "all media has been received already";
683                 errorstream << "Client: Received media but "
684                         << problem << "! "
685                         << " bunch " << bunch_i << "/" << num_bunches
686                         << " files=" << num_files
687                         << " size=" << pkt->getSize() << std::endl;
688                 return;
689         }
690
691         // Mesh update thread must be stopped while
692         // updating content definitions
693         sanity_check(!m_mesh_update_thread.IsRunning());
694
695         for (u32 i=0; i < num_files; i++) {
696                 std::string name;
697
698                 *pkt >> name;
699
700                 std::string data = pkt->readLongString();
701
702                 m_media_downloader->conventionalTransferDone(
703                                 name, data, this);
704         }
705 }
706
707 void Client::handleCommand_ToolDef(NetworkPacket* pkt)
708 {
709         infostream << "Client: WARNING: Ignoring TOCLIENT_TOOLDEF" << std::endl;
710 }
711
712 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
713 {
714         infostream << "Client: Received node definitions: packet size: "
715                         << pkt->getSize() << std::endl;
716
717         // Mesh update thread must be stopped while
718         // updating content definitions
719         sanity_check(!m_mesh_update_thread.IsRunning());
720
721         // Decompress node definitions
722         std::string datastring(pkt->getString(0), pkt->getSize());
723         std::istringstream is(datastring, std::ios_base::binary);
724         std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
725         std::ostringstream tmp_os;
726         decompressZlib(tmp_is, tmp_os);
727
728         // Deserialize node definitions
729         std::istringstream tmp_is2(tmp_os.str());
730         m_nodedef->deSerialize(tmp_is2);
731         m_nodedef_received = true;
732 }
733
734 void Client::handleCommand_CraftItemDef(NetworkPacket* pkt)
735 {
736         infostream << "Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
737 }
738
739 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
740 {
741         infostream << "Client: Received item definitions: packet size: "
742                         << pkt->getSize() << std::endl;
743
744         // Mesh update thread must be stopped while
745         // updating content definitions
746         sanity_check(!m_mesh_update_thread.IsRunning());
747
748         // Decompress item definitions
749         std::string datastring(pkt->getString(0), pkt->getSize());
750         std::istringstream is(datastring, std::ios_base::binary);
751         std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
752         std::ostringstream tmp_os;
753         decompressZlib(tmp_is, tmp_os);
754
755         // Deserialize node definitions
756         std::istringstream tmp_is2(tmp_os.str());
757         m_itemdef->deSerialize(tmp_is2);
758         m_itemdef_received = true;
759 }
760
761 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
762 {
763         s32 server_id;
764         std::string name;
765         float gain;
766         u8 type; // 0=local, 1=positional, 2=object
767         v3f pos;
768         u16 object_id;
769         bool loop;
770
771         *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
772
773         // Start playing
774         int client_id = -1;
775         switch(type) {
776                 case 0: // local
777                         client_id = m_sound->playSound(name, loop, gain);
778                         break;
779                 case 1: // positional
780                         client_id = m_sound->playSoundAt(name, loop, gain, pos);
781                         break;
782                 case 2:
783                 { // object
784                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
785                         if (cao)
786                                 pos = cao->getPosition();
787                         client_id = m_sound->playSoundAt(name, loop, gain, pos);
788                         // TODO: Set up sound to move with object
789                         break;
790                 }
791                 default:
792                         break;
793         }
794
795         if (client_id != -1) {
796                 m_sounds_server_to_client[server_id] = client_id;
797                 m_sounds_client_to_server[client_id] = server_id;
798                 if (object_id != 0)
799                         m_sounds_to_objects[client_id] = object_id;
800         }
801 }
802
803 void Client::handleCommand_StopSound(NetworkPacket* pkt)
804 {
805         s32 server_id;
806
807         *pkt >> server_id;
808
809         std::map<s32, int>::iterator i =
810                 m_sounds_server_to_client.find(server_id);
811
812         if (i != m_sounds_server_to_client.end()) {
813                 int client_id = i->second;
814                 m_sound->stopSound(client_id);
815         }
816 }
817
818 void Client::handleCommand_Privileges(NetworkPacket* pkt)
819 {
820         m_privileges.clear();
821         infostream << "Client: Privileges updated: ";
822         u16 num_privileges;
823
824         *pkt >> num_privileges;
825
826         for (u16 i = 0; i < num_privileges; i++) {
827                 std::string priv;
828
829                 *pkt >> priv;
830
831                 m_privileges.insert(priv);
832                 infostream << priv << " ";
833         }
834         infostream << std::endl;
835 }
836
837 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
838 {
839         Player *player = m_env.getLocalPlayer();
840         assert(player != NULL);
841
842         // Store formspec in LocalPlayer
843         player->inventory_formspec = pkt->readLongString();
844 }
845
846 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
847 {
848         std::string datastring(pkt->getString(0), pkt->getSize());
849         std::istringstream is(datastring, std::ios_base::binary);
850
851         std::string name = deSerializeString(is);
852
853         infostream << "Client: Detached inventory update: \"" << name
854                         << "\"" << std::endl;
855
856         Inventory *inv = NULL;
857         if (m_detached_inventories.count(name) > 0)
858                 inv = m_detached_inventories[name];
859         else {
860                 inv = new Inventory(m_itemdef);
861                 m_detached_inventories[name] = inv;
862         }
863         inv->deSerialize(is);
864 }
865
866 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
867 {
868         std::string formspec = pkt->readLongString();
869         std::string formname;
870
871         *pkt >> formname;
872
873         ClientEvent event;
874         event.type = CE_SHOW_FORMSPEC;
875         // pointer is required as event is a struct only!
876         // adding a std:string to a struct isn't possible
877         event.show_formspec.formspec = new std::string(formspec);
878         event.show_formspec.formname = new std::string(formname);
879         m_client_event_queue.push(event);
880 }
881
882 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
883 {
884         std::string datastring(pkt->getString(0), pkt->getSize());
885         std::istringstream is(datastring, std::ios_base::binary);
886
887         v3f pos                 = readV3F1000(is);
888         v3f vel                 = readV3F1000(is);
889         v3f acc                 = readV3F1000(is);
890         float expirationtime    = readF1000(is);
891         float size              = readF1000(is);
892         bool collisiondetection = readU8(is);
893         std::string texture     = deSerializeLongString(is);
894         bool vertical           = false;
895         try {
896                 vertical = 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.vertical           = vertical;
908         event.spawn_particle.texture            = new std::string(texture);
909
910         m_client_event_queue.push(event);
911 }
912
913 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
914 {
915         u16 amount;
916         float spawntime;
917         v3f minpos;
918         v3f maxpos;
919         v3f minvel;
920         v3f maxvel;
921         v3f minacc;
922         v3f maxacc;
923         float minexptime;
924         float maxexptime;
925         float minsize;
926         float maxsize;
927         bool collisiondetection;
928         u32 id;
929
930         *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
931                 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
932                 >> maxsize >> collisiondetection;
933
934         std::string texture = pkt->readLongString();
935
936         *pkt >> id;
937
938         bool vertical = false;
939         try {
940                 *pkt >> vertical;
941         } catch (...) {}
942
943         ClientEvent event;
944         event.type                                   = CE_ADD_PARTICLESPAWNER;
945         event.add_particlespawner.amount             = amount;
946         event.add_particlespawner.spawntime          = spawntime;
947         event.add_particlespawner.minpos             = new v3f (minpos);
948         event.add_particlespawner.maxpos             = new v3f (maxpos);
949         event.add_particlespawner.minvel             = new v3f (minvel);
950         event.add_particlespawner.maxvel             = new v3f (maxvel);
951         event.add_particlespawner.minacc             = new v3f (minacc);
952         event.add_particlespawner.maxacc             = new v3f (maxacc);
953         event.add_particlespawner.minexptime         = minexptime;
954         event.add_particlespawner.maxexptime         = maxexptime;
955         event.add_particlespawner.minsize            = minsize;
956         event.add_particlespawner.maxsize            = maxsize;
957         event.add_particlespawner.collisiondetection = collisiondetection;
958         event.add_particlespawner.vertical           = vertical;
959         event.add_particlespawner.texture            = new std::string(texture);
960         event.add_particlespawner.id                 = id;
961
962         m_client_event_queue.push(event);
963 }
964
965
966 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
967 {
968         u16 legacy_id;
969         u32 id;
970
971         // Modification set 13/03/15, 1 year of compat for protocol v24
972         if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
973                 *pkt >> legacy_id;
974         }
975         else {
976                 *pkt >> id;
977         }
978
979
980         ClientEvent event;
981         event.type                      = CE_DELETE_PARTICLESPAWNER;
982         event.delete_particlespawner.id =
983                         (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
984
985         m_client_event_queue.push(event);
986 }
987
988 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
989 {
990         std::string datastring(pkt->getString(0), pkt->getSize());
991         std::istringstream is(datastring, std::ios_base::binary);
992
993         u32 id;
994         u8 type;
995         v2f pos;
996         std::string name;
997         v2f scale;
998         std::string text;
999         u32 number;
1000         u32 item;
1001         u32 dir;
1002         v2f align;
1003         v2f offset;
1004         v3f world_pos;
1005         v2s32 size;
1006
1007         *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1008                 >> dir >> align >> offset;
1009         try {
1010                 *pkt >> world_pos;
1011         }
1012         catch(SerializationError &e) {};
1013
1014         try {
1015                 *pkt >> size;
1016         } catch(SerializationError &e) {};
1017
1018         ClientEvent event;
1019         event.type             = CE_HUDADD;
1020         event.hudadd.id        = id;
1021         event.hudadd.type      = type;
1022         event.hudadd.pos       = new v2f(pos);
1023         event.hudadd.name      = new std::string(name);
1024         event.hudadd.scale     = new v2f(scale);
1025         event.hudadd.text      = new std::string(text);
1026         event.hudadd.number    = number;
1027         event.hudadd.item      = item;
1028         event.hudadd.dir       = dir;
1029         event.hudadd.align     = new v2f(align);
1030         event.hudadd.offset    = new v2f(offset);
1031         event.hudadd.world_pos = new v3f(world_pos);
1032         event.hudadd.size      = new v2s32(size);
1033         m_client_event_queue.push(event);
1034 }
1035
1036 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1037 {
1038         u32 id;
1039
1040         *pkt >> id;
1041
1042         ClientEvent event;
1043         event.type     = CE_HUDRM;
1044         event.hudrm.id = id;
1045         m_client_event_queue.push(event);
1046 }
1047
1048 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1049 {
1050         std::string sdata;
1051         v2f v2fdata;
1052         v3f v3fdata;
1053         u32 intdata = 0;
1054         v2s32 v2s32data;
1055         u32 id;
1056         u8 stat;
1057
1058         *pkt >> id >> stat;
1059
1060         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1061                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1062                 *pkt >> v2fdata;
1063         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1064                 *pkt >> sdata;
1065         else if (stat == HUD_STAT_WORLD_POS)
1066                 *pkt >> v3fdata;
1067         else if (stat == HUD_STAT_SIZE )
1068                 *pkt >> v2s32data;
1069         else
1070                 *pkt >> intdata;
1071
1072         ClientEvent event;
1073         event.type              = CE_HUDCHANGE;
1074         event.hudchange.id      = id;
1075         event.hudchange.stat    = (HudElementStat)stat;
1076         event.hudchange.v2fdata = new v2f(v2fdata);
1077         event.hudchange.v3fdata = new v3f(v3fdata);
1078         event.hudchange.sdata   = new std::string(sdata);
1079         event.hudchange.data    = intdata;
1080         event.hudchange.v2s32data = new v2s32(v2s32data);
1081         m_client_event_queue.push(event);
1082 }
1083
1084 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1085 {
1086         u32 flags, mask;
1087
1088         *pkt >> flags >> mask;
1089
1090         Player *player = m_env.getLocalPlayer();
1091         assert(player != NULL);
1092
1093         player->hud_flags &= ~mask;
1094         player->hud_flags |= flags;
1095 }
1096
1097 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1098 {
1099         u16 param; std::string value;
1100
1101         *pkt >> param >> value;
1102
1103         Player *player = m_env.getLocalPlayer();
1104         assert(player != NULL);
1105
1106         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1107                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1108                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1109                         player->hud_hotbar_itemcount = hotbar_itemcount;
1110         }
1111         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1112                 ((LocalPlayer *) player)->hotbar_image = value;
1113         }
1114         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1115                 ((LocalPlayer *) player)->hotbar_selected_image = value;
1116         }
1117 }
1118
1119 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1120 {
1121         std::string datastring(pkt->getString(0), pkt->getSize());
1122         std::istringstream is(datastring, std::ios_base::binary);
1123
1124         video::SColor *bgcolor           = new video::SColor(readARGB8(is));
1125         std::string *type                = new std::string(deSerializeString(is));
1126         u16 count                        = readU16(is);
1127         std::vector<std::string> *params = new std::vector<std::string>;
1128
1129         for (size_t i = 0; i < count; i++)
1130                 params->push_back(deSerializeString(is));
1131
1132         ClientEvent event;
1133         event.type            = CE_SET_SKY;
1134         event.set_sky.bgcolor = bgcolor;
1135         event.set_sky.type    = type;
1136         event.set_sky.params  = params;
1137         m_client_event_queue.push(event);
1138 }
1139
1140 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1141 {
1142         bool do_override;
1143         u16 day_night_ratio_u;
1144
1145         *pkt >> do_override >> day_night_ratio_u;
1146
1147         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1148
1149         ClientEvent event;
1150         event.type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1151         event.override_day_night_ratio.do_override = do_override;
1152         event.override_day_night_ratio.ratio_f     = day_night_ratio_f;
1153         m_client_event_queue.push(event);
1154 }
1155
1156 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1157 {
1158         LocalPlayer *player = m_env.getLocalPlayer();
1159         assert(player != NULL);
1160
1161         *pkt >> player->local_animations[0];
1162         *pkt >> player->local_animations[1];
1163         *pkt >> player->local_animations[2];
1164         *pkt >> player->local_animations[3];
1165         *pkt >> player->local_animation_speed;
1166 }
1167
1168 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1169 {
1170         LocalPlayer *player = m_env.getLocalPlayer();
1171         assert(player != NULL);
1172
1173         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1174 }
1175
1176 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1177 {
1178         if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1179                         && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1180                 errorstream << "Client: Recieved SRP S_B login message,"
1181                         << " but wasn't supposed to (chosen_mech="
1182                         << m_chosen_auth_mech << ")." << std::endl;
1183                 return;
1184         }
1185
1186         char *bytes_M = 0;
1187         size_t len_M = 0;
1188         SRPUser *usr = (SRPUser *) m_auth_data;
1189         std::string s;
1190         std::string B;
1191         *pkt >> s >> B;
1192
1193         infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1194
1195         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1196                 (const unsigned char *) B.c_str(), B.size(),
1197                 (unsigned char **) &bytes_M, &len_M);
1198
1199         if ( !bytes_M ) {
1200                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1201                 return;
1202         }
1203
1204         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1205         resp_pkt << std::string(bytes_M, len_M);
1206         Send(&resp_pkt);
1207 }