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