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