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