]> git.lizzy.rs Git - minetest.git/blob - src/network/clientpackethandler.cpp
f42f9219b78b6d8bf3249e8ca23492c25fd5992d
[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 "chatmessage.h"
24 #include "clientmedia.h"
25 #include "log.h"
26 #include "map.h"
27 #include "mapsector.h"
28 #include "minimap.h"
29 #include "nodedef.h"
30 #include "serialization.h"
31 #include "server.h"
32 #include "util/strfnd.h"
33 #include "network/clientopcodes.h"
34 #include "network/connection.h"
35 #include "script/scripting_client.h"
36 #include "util/serialize.h"
37 #include "util/srp.h"
38 #include "tileanimation.h"
39 #include "gettext.h"
40
41 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
42 {
43         infostream << "Got deprecated command "
44                         << toClientCommandTable[pkt->getCommand()].name << " from peer "
45                         << pkt->getPeerId() << "!" << std::endl;
46 }
47
48 void Client::handleCommand_Hello(NetworkPacket* pkt)
49 {
50         if (pkt->getSize() < 1)
51                 return;
52
53         u8 serialization_ver;
54         u16 proto_ver;
55         u16 compression_mode;
56         u32 auth_mechs;
57         std::string username_legacy; // for case insensitivity
58         *pkt >> serialization_ver >> compression_mode >> proto_ver
59                 >> auth_mechs >> username_legacy;
60
61         // Chose an auth method we support
62         AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
63
64         infostream << "Client: TOCLIENT_HELLO received with "
65                         << "serialization_ver=" << (u32)serialization_ver
66                         << ", auth_mechs=" << auth_mechs
67                         << ", proto_ver=" << proto_ver
68                         << ", compression_mode=" << compression_mode
69                         << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
70
71         if (!ser_ver_supported(serialization_ver)) {
72                 infostream << "Client: TOCLIENT_HELLO: Server sent "
73                                 << "unsupported ser_fmt_ver"<< std::endl;
74                 return;
75         }
76
77         m_server_ser_ver = serialization_ver;
78         m_proto_ver = proto_ver;
79
80         //TODO verify that username_legacy matches sent username, only
81         // differs in casing (make both uppercase and compare)
82         // This is only neccessary though when we actually want to add casing support
83
84         if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
85                 // we recieved a TOCLIENT_HELLO while auth was already going on
86                 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
87                         << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
88                 if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP)
89                                 || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) {
90                         srp_user_delete((SRPUser *) m_auth_data);
91                         m_auth_data = 0;
92                 }
93         }
94
95         // Authenticate using that method, or abort if there wasn't any method found
96         if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
97                 startAuth(chosen_auth_mechanism);
98         } else {
99                 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
100                 m_access_denied = true;
101                 m_access_denied_reason = "Unknown";
102                 m_con->Disconnect();
103         }
104
105 }
106
107 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
108 {
109         deleteAuthData();
110
111         v3f playerpos;
112         *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
113                 >> m_sudo_auth_methods;
114
115         playerpos -= v3f(0, BS / 2, 0);
116
117         // Set player position
118         LocalPlayer *player = m_env.getLocalPlayer();
119         assert(player != NULL);
120         player->setPosition(playerpos);
121
122         infostream << "Client: received map seed: " << m_map_seed << std::endl;
123         infostream << "Client: received recommended send interval "
124                                         << m_recommended_send_interval<<std::endl;
125
126         // Reply to server
127         std::string lang = gettext("LANG_CODE");
128         if (lang == "LANG_CODE")
129                 lang = "";
130
131         NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
132         resp_pkt << lang;
133         Send(&resp_pkt);
134
135         m_state = LC_Init;
136 }
137 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
138 {
139         deleteAuthData();
140
141         m_password = m_new_password;
142
143         verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
144
145         // send packet to actually set the password
146         startAuth(AUTH_MECHANISM_FIRST_SRP);
147
148         // reset again
149         m_chosen_auth_mech = AUTH_MECHANISM_NONE;
150 }
151 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
152 {
153         ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
154                         L"Password change denied. Password NOT changed.");
155         pushToChatQueue(chatMessage);
156         // reset everything and be sad
157         deleteAuthData();
158 }
159 void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
160 {
161         if (pkt->getSize() < 1)
162                 return;
163
164         u8 server_ser_ver;
165         *pkt >> server_ser_ver;
166
167         infostream << "Client: TOCLIENT_INIT_LEGACY received with "
168                 "server_ser_ver=" << ((int)server_ser_ver & 0xff) << std::endl;
169
170         if (!ser_ver_supported(server_ser_ver)) {
171                 infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
172                                 << "unsupported ser_fmt_ver"<< std::endl;
173                 return;
174         }
175
176         m_server_ser_ver = server_ser_ver;
177
178         // We can be totally wrong with this guess
179         // but we only need some value < 25.
180         m_proto_ver = 24;
181
182         // Get player position
183         v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
184         if (pkt->getSize() >= 1 + 6) {
185                 *pkt >> playerpos_s16;
186         }
187         v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
188
189
190         // Set player position
191         LocalPlayer *player = m_env.getLocalPlayer();
192         assert(player != NULL);
193         player->setPosition(playerpos_f);
194
195         if (pkt->getSize() >= 1 + 6 + 8) {
196                 // Get map seed
197                 *pkt >> m_map_seed;
198                 infostream << "Client: received map seed: " << m_map_seed << std::endl;
199         }
200
201         if (pkt->getSize() >= 1 + 6 + 8 + 4) {
202                 *pkt >> m_recommended_send_interval;
203                 infostream << "Client: received recommended send interval "
204                                 << m_recommended_send_interval<<std::endl;
205         }
206
207         // Reply to server
208         NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
209         Send(&resp_pkt);
210
211         m_state = LC_Init;
212 }
213
214 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
215 {
216         // The server didn't like our password. Note, this needs
217         // to be processed even if the serialisation format has
218         // not been agreed yet, the same as TOCLIENT_INIT.
219         m_access_denied = true;
220         m_access_denied_reason = "Unknown";
221
222         if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
223                 if (pkt->getSize() < 1)
224                         return;
225
226                 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
227                 *pkt >> denyCode;
228                 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
229                                 denyCode == SERVER_ACCESSDENIED_CRASH) {
230                         *pkt >> m_access_denied_reason;
231                         if (m_access_denied_reason.empty()) {
232                                 m_access_denied_reason = accessDeniedStrings[denyCode];
233                         }
234                         u8 reconnect;
235                         *pkt >> reconnect;
236                         m_access_denied_reconnect = reconnect & 1;
237                 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
238                         *pkt >> m_access_denied_reason;
239                 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
240                         m_access_denied_reason = accessDeniedStrings[denyCode];
241                 } else {
242                         // Allow us to add new error messages to the
243                         // protocol without raising the protocol version, if we want to.
244                         // Until then (which may be never), this is outside
245                         // of the defined protocol.
246                         *pkt >> m_access_denied_reason;
247                         if (m_access_denied_reason.empty()) {
248                                 m_access_denied_reason = "Unknown";
249                         }
250                 }
251         }
252         // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
253         // for compat with old clients
254         else {
255                 if (pkt->getSize() >= 2) {
256                         std::wstring wide_reason;
257                         *pkt >> wide_reason;
258                         m_access_denied_reason = wide_to_utf8(wide_reason);
259                 }
260         }
261 }
262
263 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
264 {
265         if (pkt->getSize() < 6)
266                 return;
267
268         v3s16 p;
269         *pkt >> p;
270         removeNode(p);
271 }
272
273 void Client::handleCommand_AddNode(NetworkPacket* pkt)
274 {
275         if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
276                 return;
277
278         v3s16 p;
279         *pkt >> p;
280
281         MapNode n;
282         n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
283
284         bool remove_metadata = true;
285         u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
286         if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
287                 remove_metadata = false;
288         }
289
290         addNode(p, n, remove_metadata);
291 }
292 void Client::handleCommand_BlockData(NetworkPacket* pkt)
293 {
294         // Ignore too small packet
295         if (pkt->getSize() < 6)
296                 return;
297
298         v3s16 p;
299         *pkt >> p;
300
301         std::string datastring(pkt->getString(6), pkt->getSize() - 6);
302         std::istringstream istr(datastring, std::ios_base::binary);
303
304         MapSector *sector;
305         MapBlock *block;
306
307         v2s16 p2d(p.X, p.Z);
308         sector = m_env.getMap().emergeSector(p2d);
309
310         assert(sector->getPos() == p2d);
311
312         block = sector->getBlockNoCreateNoEx(p.Y);
313         if (block) {
314                 /*
315                         Update an existing block
316                 */
317                 block->deSerialize(istr, m_server_ser_ver, false);
318                 block->deSerializeNetworkSpecific(istr);
319         }
320         else {
321                 /*
322                         Create a new block
323                 */
324                 block = new MapBlock(&m_env.getMap(), p, this);
325                 block->deSerialize(istr, m_server_ser_ver, false);
326                 block->deSerializeNetworkSpecific(istr);
327                 sector->insertBlock(block);
328         }
329
330         if (m_localdb) {
331                 ServerMap::saveBlock(block, m_localdb);
332         }
333
334         /*
335                 Add it to mesh update queue and set it to be acknowledged after update.
336         */
337         addUpdateMeshTaskWithEdge(p, true);
338 }
339
340 void Client::handleCommand_Inventory(NetworkPacket* pkt)
341 {
342         if (pkt->getSize() < 1)
343                 return;
344
345         std::string datastring(pkt->getString(0), pkt->getSize());
346         std::istringstream is(datastring, std::ios_base::binary);
347
348         LocalPlayer *player = m_env.getLocalPlayer();
349         assert(player != NULL);
350
351         player->inventory.deSerialize(is);
352
353         m_inventory_updated = true;
354
355         delete m_inventory_from_server;
356         m_inventory_from_server = new Inventory(player->inventory);
357         m_inventory_from_server_age = 0.0;
358 }
359
360 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
361 {
362         if (pkt->getSize() < 2)
363                 return;
364
365         u16 time_of_day;
366
367         *pkt >> time_of_day;
368
369         time_of_day      = time_of_day % 24000;
370         float time_speed = 0;
371
372         if (pkt->getSize() >= 2 + 4) {
373                 *pkt >> time_speed;
374         }
375         else {
376                 // Old message; try to approximate speed of time by ourselves
377                 float time_of_day_f = (float)time_of_day / 24000.0;
378                 float tod_diff_f = 0;
379
380                 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
381                         tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
382                 else
383                         tod_diff_f = time_of_day_f - m_last_time_of_day_f;
384
385                 m_last_time_of_day_f       = time_of_day_f;
386                 float time_diff            = m_time_of_day_update_timer;
387                 m_time_of_day_update_timer = 0;
388
389                 if (m_time_of_day_set) {
390                         time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
391                         infostream << "Client: Measured time_of_day speed (old format): "
392                                         << time_speed << " tod_diff_f=" << tod_diff_f
393                                         << " time_diff=" << time_diff << std::endl;
394                 }
395         }
396
397         // Update environment
398         m_env.setTimeOfDay(time_of_day);
399         m_env.setTimeOfDaySpeed(time_speed);
400         m_time_of_day_set = true;
401
402         u32 dr = m_env.getDayNightRatio();
403         infostream << "Client: time_of_day=" << time_of_day
404                         << " time_speed=" << time_speed
405                         << " dr=" << dr << std::endl;
406 }
407
408 void Client::handleCommand_ChatMessageOld(NetworkPacket *pkt)
409 {
410         /*
411                 u16 command
412                 u16 length
413                 wstring message
414         */
415         u16 len, read_wchar;
416
417         *pkt >> len;
418
419         std::wstring message;
420         for (u32 i = 0; i < len; i++) {
421                 *pkt >> read_wchar;
422                 message += (wchar_t)read_wchar;
423         }
424
425         // If chat message not consummed by client lua API
426         // @TODO send this to CSM using ChatMessage object
427         if (!moddingEnabled() || !m_script->on_receiving_message(wide_to_utf8(message))) {
428                 pushToChatQueue(new ChatMessage(message));
429         }
430 }
431
432 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
433 {
434         /*
435                 u8 version
436                 u8 message_type
437                 u16 sendername length
438                 wstring sendername
439                 u16 length
440                 wstring message
441          */
442
443         ChatMessage *chatMessage = new ChatMessage();
444         u8 version, message_type;
445         *pkt >> version >> message_type;
446
447         if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
448                 delete chatMessage;
449                 return;
450         }
451
452         *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
453
454         chatMessage->type = (ChatMessageType) message_type;
455
456         // @TODO send this to CSM using ChatMessage object
457         if (!moddingEnabled() || !m_script->on_receiving_message(
458                         wide_to_utf8(chatMessage->message))) {
459                 pushToChatQueue(chatMessage);
460         } else {
461                 // Message was consumed by CSM and should not handled by client, destroying
462                 delete chatMessage;
463         }
464 }
465
466 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
467 {
468         /*
469                 u16 count of removed objects
470                 for all removed objects {
471                         u16 id
472                 }
473                 u16 count of added objects
474                 for all added objects {
475                         u16 id
476                         u8 type
477                         u32 initialization data length
478                         string initialization data
479                 }
480         */
481
482         try {
483                 u8 type;
484                 u16 removed_count, added_count, id;
485
486                 // Read removed objects
487                 *pkt >> removed_count;
488
489                 for (u16 i = 0; i < removed_count; i++) {
490                         *pkt >> id;
491                         m_env.removeActiveObject(id);
492                 }
493
494                 // Read added objects
495                 *pkt >> added_count;
496
497                 for (u16 i = 0; i < added_count; i++) {
498                         *pkt >> id >> type;
499                         m_env.addActiveObject(id, type, pkt->readLongString());
500                 }
501         } catch (PacketError &e) {
502                 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
503                                 << ". The packet is unreliable, ignoring" << std::endl;
504         }
505 }
506
507 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
508 {
509         /*
510                 for all objects
511                 {
512                         u16 id
513                         u16 message length
514                         string message
515                 }
516         */
517         std::string datastring(pkt->getString(0), pkt->getSize());
518         std::istringstream is(datastring, std::ios_base::binary);
519
520         try {
521                 while (is.good()) {
522                         u16 id = readU16(is);
523                         if (!is.good())
524                                 break;
525
526                         std::string message = deSerializeString(is);
527
528                         // Pass on to the environment
529                         m_env.processActiveObjectMessage(id, message);
530                 }
531         } catch (SerializationError &e) {
532                 errorstream << "Client::handleCommand_ActiveObjectMessages: "
533                         << "caught SerializationError: " << e.what() << std::endl;
534         }
535 }
536
537 void Client::handleCommand_Movement(NetworkPacket* pkt)
538 {
539         LocalPlayer *player = m_env.getLocalPlayer();
540         assert(player != NULL);
541
542         float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
543
544         *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
545                 >> lf >> lfs >> ls >> g;
546
547         player->movement_acceleration_default   = mad * BS;
548         player->movement_acceleration_air       = maa * BS;
549         player->movement_acceleration_fast      = maf * BS;
550         player->movement_speed_walk             = msw * BS;
551         player->movement_speed_crouch           = mscr * BS;
552         player->movement_speed_fast             = msf * BS;
553         player->movement_speed_climb            = mscl * BS;
554         player->movement_speed_jump             = msj * BS;
555         player->movement_liquid_fluidity        = lf * BS;
556         player->movement_liquid_fluidity_smooth = lfs * BS;
557         player->movement_liquid_sink            = ls * BS;
558         player->movement_gravity                = g * BS;
559 }
560
561 void Client::handleCommand_HP(NetworkPacket* pkt)
562 {
563
564         LocalPlayer *player = m_env.getLocalPlayer();
565         assert(player != NULL);
566
567         u8 oldhp   = player->hp;
568
569         u8 hp;
570         *pkt >> hp;
571
572         player->hp = hp;
573
574         if (moddingEnabled()) {
575                 m_script->on_hp_modification(hp);
576         }
577
578         if (hp < oldhp) {
579                 // Add to ClientEvent queue
580                 ClientEvent event;
581                 event.type = CE_PLAYER_DAMAGE;
582                 event.player_damage.amount = oldhp - hp;
583                 m_client_event_queue.push(event);
584         }
585 }
586
587 void Client::handleCommand_Breath(NetworkPacket* pkt)
588 {
589         LocalPlayer *player = m_env.getLocalPlayer();
590         assert(player != NULL);
591
592         u16 breath;
593
594         *pkt >> breath;
595
596         player->setBreath(breath);
597 }
598
599 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
600 {
601         LocalPlayer *player = m_env.getLocalPlayer();
602         assert(player != NULL);
603
604         v3f pos;
605         f32 pitch, yaw;
606
607         *pkt >> pos >> pitch >> yaw;
608
609         player->setPosition(pos);
610
611         infostream << "Client got TOCLIENT_MOVE_PLAYER"
612                         << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
613                         << " pitch=" << pitch
614                         << " yaw=" << yaw
615                         << std::endl;
616
617         /*
618                 Add to ClientEvent queue.
619                 This has to be sent to the main program because otherwise
620                 it would just force the pitch and yaw values to whatever
621                 the camera points to.
622         */
623         ClientEvent event;
624         event.type = CE_PLAYER_FORCE_MOVE;
625         event.player_force_move.pitch = pitch;
626         event.player_force_move.yaw = yaw;
627         m_client_event_queue.push(event);
628
629         // Ignore damage for a few seconds, so that the player doesn't
630         // get damage from falling on ground
631         m_ignore_damage_timer = 3.0;
632 }
633
634 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
635 {
636         bool set_camera_point_target;
637         v3f camera_point_target;
638
639         *pkt >> set_camera_point_target;
640         *pkt >> camera_point_target;
641
642         ClientEvent event;
643         event.type                                = CE_DEATHSCREEN;
644         event.deathscreen.set_camera_point_target = set_camera_point_target;
645         event.deathscreen.camera_point_target_x   = camera_point_target.X;
646         event.deathscreen.camera_point_target_y   = camera_point_target.Y;
647         event.deathscreen.camera_point_target_z   = camera_point_target.Z;
648         m_client_event_queue.push(event);
649 }
650
651 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
652 {
653         u16 num_files;
654
655         *pkt >> num_files;
656
657         infostream << "Client: Received media announcement: packet size: "
658                         << pkt->getSize() << std::endl;
659
660         if (m_media_downloader == NULL ||
661                         m_media_downloader->isStarted()) {
662                 const char *problem = m_media_downloader ?
663                         "we already saw another announcement" :
664                         "all media has been received already";
665                 errorstream << "Client: Received media announcement but "
666                         << problem << "! "
667                         << " files=" << num_files
668                         << " size=" << pkt->getSize() << std::endl;
669                 return;
670         }
671
672         // Mesh update thread must be stopped while
673         // updating content definitions
674         sanity_check(!m_mesh_update_thread.isRunning());
675
676         for (u16 i = 0; i < num_files; i++) {
677                 std::string name, sha1_base64;
678
679                 *pkt >> name >> sha1_base64;
680
681                 std::string sha1_raw = base64_decode(sha1_base64);
682                 m_media_downloader->addFile(name, sha1_raw);
683         }
684
685         try {
686                 std::string str;
687
688                 *pkt >> str;
689
690                 Strfnd sf(str);
691                 while(!sf.at_end()) {
692                         std::string baseurl = trim(sf.next(","));
693                         if (!baseurl.empty())
694                                 m_media_downloader->addRemoteServer(baseurl);
695                 }
696         }
697         catch(SerializationError& e) {
698                 // not supported by server or turned off
699         }
700
701         m_media_downloader->step(this);
702 }
703
704 void Client::handleCommand_Media(NetworkPacket* pkt)
705 {
706         /*
707                 u16 command
708                 u16 total number of file bunches
709                 u16 index of this bunch
710                 u32 number of files in this bunch
711                 for each file {
712                         u16 length of name
713                         string name
714                         u32 length of data
715                         data
716                 }
717         */
718         u16 num_bunches;
719         u16 bunch_i;
720         u32 num_files;
721
722         *pkt >> num_bunches >> bunch_i >> num_files;
723
724         infostream << "Client: Received files: bunch " << bunch_i << "/"
725                         << num_bunches << " files=" << num_files
726                         << " size=" << pkt->getSize() << std::endl;
727
728         if (num_files == 0)
729                 return;
730
731         if (!m_media_downloader || !m_media_downloader->isStarted()) {
732                 const char *problem = m_media_downloader ?
733                         "media has not been requested" :
734                         "all media has been received already";
735                 errorstream << "Client: Received media but "
736                         << problem << "! "
737                         << " bunch " << bunch_i << "/" << num_bunches
738                         << " files=" << num_files
739                         << " size=" << pkt->getSize() << std::endl;
740                 return;
741         }
742
743         // Mesh update thread must be stopped while
744         // updating content definitions
745         sanity_check(!m_mesh_update_thread.isRunning());
746
747         for (u32 i=0; i < num_files; i++) {
748                 std::string name;
749
750                 *pkt >> name;
751
752                 std::string data = pkt->readLongString();
753
754                 m_media_downloader->conventionalTransferDone(
755                                 name, data, this);
756         }
757 }
758
759 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
760 {
761         infostream << "Client: Received node definitions: packet size: "
762                         << pkt->getSize() << std::endl;
763
764         // Mesh update thread must be stopped while
765         // updating content definitions
766         sanity_check(!m_mesh_update_thread.isRunning());
767
768         // Decompress node definitions
769         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
770         std::ostringstream tmp_os;
771         decompressZlib(tmp_is, tmp_os);
772
773         // Deserialize node definitions
774         std::istringstream tmp_is2(tmp_os.str());
775         m_nodedef->deSerialize(tmp_is2);
776         m_nodedef_received = true;
777 }
778
779 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
780 {
781         infostream << "Client: Received item definitions: packet size: "
782                         << pkt->getSize() << std::endl;
783
784         // Mesh update thread must be stopped while
785         // updating content definitions
786         sanity_check(!m_mesh_update_thread.isRunning());
787
788         // Decompress item definitions
789         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
790         std::ostringstream tmp_os;
791         decompressZlib(tmp_is, tmp_os);
792
793         // Deserialize node definitions
794         std::istringstream tmp_is2(tmp_os.str());
795         m_itemdef->deSerialize(tmp_is2);
796         m_itemdef_received = true;
797 }
798
799 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
800 {
801         /*
802                 [0] u32 server_id
803                 [4] u16 name length
804                 [6] char name[len]
805                 [ 6 + len] f32 gain
806                 [10 + len] u8 type
807                 [11 + len] (f32 * 3) pos
808                 [23 + len] u16 object_id
809                 [25 + len] bool loop
810                 [26 + len] f32 fade
811                 [30 + len] f32 pitch
812         */
813
814         s32 server_id;
815         std::string name;
816
817         float gain;
818         u8 type; // 0=local, 1=positional, 2=object
819         v3f pos;
820         u16 object_id;
821         bool loop;
822         float fade = 0.0f;
823         float pitch = 1.0f;
824
825         *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
826
827         try {
828                 *pkt >> fade;
829                 *pkt >> pitch;
830         } catch (PacketError &e) {};
831
832         // Start playing
833         int client_id = -1;
834         switch(type) {
835                 case 0: // local
836                         client_id = m_sound->playSound(name, loop, gain, fade, pitch);
837                         break;
838                 case 1: // positional
839                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
840                         break;
841                 case 2:
842                 { // object
843                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
844                         if (cao)
845                                 pos = cao->getPosition();
846                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
847                         // TODO: Set up sound to move with object
848                         break;
849                 }
850                 default:
851                         break;
852         }
853
854         if (client_id != -1) {
855                 m_sounds_server_to_client[server_id] = client_id;
856                 m_sounds_client_to_server[client_id] = server_id;
857                 if (object_id != 0)
858                         m_sounds_to_objects[client_id] = object_id;
859         }
860 }
861
862 void Client::handleCommand_StopSound(NetworkPacket* pkt)
863 {
864         s32 server_id;
865
866         *pkt >> server_id;
867
868         std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
869         if (i != m_sounds_server_to_client.end()) {
870                 int client_id = i->second;
871                 m_sound->stopSound(client_id);
872         }
873 }
874
875 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
876 {
877         s32 sound_id;
878         float step;
879         float gain;
880
881         *pkt >> sound_id >> step >> gain;
882
883         std::unordered_map<s32, int>::const_iterator i =
884                         m_sounds_server_to_client.find(sound_id);
885
886         if (i != m_sounds_server_to_client.end())
887                 m_sound->fadeSound(i->second, step, gain);
888 }
889
890 void Client::handleCommand_Privileges(NetworkPacket* pkt)
891 {
892         m_privileges.clear();
893         infostream << "Client: Privileges updated: ";
894         u16 num_privileges;
895
896         *pkt >> num_privileges;
897
898         for (u16 i = 0; i < num_privileges; i++) {
899                 std::string priv;
900
901                 *pkt >> priv;
902
903                 m_privileges.insert(priv);
904                 infostream << priv << " ";
905         }
906         infostream << std::endl;
907 }
908
909 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
910 {
911         LocalPlayer *player = m_env.getLocalPlayer();
912         assert(player != NULL);
913
914         // Store formspec in LocalPlayer
915         player->inventory_formspec = pkt->readLongString();
916 }
917
918 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
919 {
920         std::string datastring(pkt->getString(0), pkt->getSize());
921         std::istringstream is(datastring, std::ios_base::binary);
922
923         std::string name = deSerializeString(is);
924
925         infostream << "Client: Detached inventory update: \"" << name
926                         << "\"" << std::endl;
927
928         Inventory *inv = NULL;
929         if (m_detached_inventories.count(name) > 0)
930                 inv = m_detached_inventories[name];
931         else {
932                 inv = new Inventory(m_itemdef);
933                 m_detached_inventories[name] = inv;
934         }
935         inv->deSerialize(is);
936 }
937
938 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
939 {
940         std::string formspec = pkt->readLongString();
941         std::string formname;
942
943         *pkt >> formname;
944
945         ClientEvent event;
946         event.type = CE_SHOW_FORMSPEC;
947         // pointer is required as event is a struct only!
948         // adding a std:string to a struct isn't possible
949         event.show_formspec.formspec = new std::string(formspec);
950         event.show_formspec.formname = new std::string(formname);
951         m_client_event_queue.push(event);
952 }
953
954 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
955 {
956         std::string datastring(pkt->getString(0), pkt->getSize());
957         std::istringstream is(datastring, std::ios_base::binary);
958
959         v3f pos                 = readV3F1000(is);
960         v3f vel                 = readV3F1000(is);
961         v3f acc                 = readV3F1000(is);
962         float expirationtime    = readF1000(is);
963         float size              = readF1000(is);
964         bool collisiondetection = readU8(is);
965         std::string texture     = deSerializeLongString(is);
966         bool vertical           = false;
967         bool collision_removal  = false;
968         struct TileAnimationParams animation;
969         animation.type = TAT_NONE;
970         u8 glow = 0;
971         try {
972                 vertical = readU8(is);
973                 collision_removal = readU8(is);
974                 animation.deSerialize(is, m_proto_ver);
975                 glow = readU8(is);
976         } catch (...) {}
977
978         ClientEvent event;
979         event.type                              = CE_SPAWN_PARTICLE;
980         event.spawn_particle.pos                = new v3f (pos);
981         event.spawn_particle.vel                = new v3f (vel);
982         event.spawn_particle.acc                = new v3f (acc);
983         event.spawn_particle.expirationtime     = expirationtime;
984         event.spawn_particle.size               = size;
985         event.spawn_particle.collisiondetection = collisiondetection;
986         event.spawn_particle.collision_removal  = collision_removal;
987         event.spawn_particle.vertical           = vertical;
988         event.spawn_particle.texture            = new std::string(texture);
989         event.spawn_particle.animation          = animation;
990         event.spawn_particle.glow               = glow;
991
992         m_client_event_queue.push(event);
993 }
994
995 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
996 {
997         u16 amount;
998         float spawntime;
999         v3f minpos;
1000         v3f maxpos;
1001         v3f minvel;
1002         v3f maxvel;
1003         v3f minacc;
1004         v3f maxacc;
1005         float minexptime;
1006         float maxexptime;
1007         float minsize;
1008         float maxsize;
1009         bool collisiondetection;
1010         u32 id;
1011
1012         *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
1013                 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
1014                 >> maxsize >> collisiondetection;
1015
1016         std::string texture = pkt->readLongString();
1017
1018         *pkt >> id;
1019
1020         bool vertical = false;
1021         bool collision_removal = false;
1022         struct TileAnimationParams animation;
1023         animation.type = TAT_NONE;
1024         u8 glow = 0;
1025         u16 attached_id = 0;
1026         try {
1027                 *pkt >> vertical;
1028                 *pkt >> collision_removal;
1029                 *pkt >> attached_id;
1030
1031                 // This is horrible but required (why are there two ways to deserialize pkts?)
1032                 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1033                 std::istringstream is(datastring, std::ios_base::binary);
1034                 animation.deSerialize(is, m_proto_ver);
1035                 glow = readU8(is);
1036         } catch (...) {}
1037
1038         ClientEvent event;
1039         event.type                                   = CE_ADD_PARTICLESPAWNER;
1040         event.add_particlespawner.amount             = amount;
1041         event.add_particlespawner.spawntime          = spawntime;
1042         event.add_particlespawner.minpos             = new v3f (minpos);
1043         event.add_particlespawner.maxpos             = new v3f (maxpos);
1044         event.add_particlespawner.minvel             = new v3f (minvel);
1045         event.add_particlespawner.maxvel             = new v3f (maxvel);
1046         event.add_particlespawner.minacc             = new v3f (minacc);
1047         event.add_particlespawner.maxacc             = new v3f (maxacc);
1048         event.add_particlespawner.minexptime         = minexptime;
1049         event.add_particlespawner.maxexptime         = maxexptime;
1050         event.add_particlespawner.minsize            = minsize;
1051         event.add_particlespawner.maxsize            = maxsize;
1052         event.add_particlespawner.collisiondetection = collisiondetection;
1053         event.add_particlespawner.collision_removal  = collision_removal;
1054         event.add_particlespawner.attached_id        = attached_id;
1055         event.add_particlespawner.vertical           = vertical;
1056         event.add_particlespawner.texture            = new std::string(texture);
1057         event.add_particlespawner.id                 = id;
1058         event.add_particlespawner.animation          = animation;
1059         event.add_particlespawner.glow               = glow;
1060
1061         m_client_event_queue.push(event);
1062 }
1063
1064
1065 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1066 {
1067         u16 legacy_id;
1068         u32 id;
1069
1070         // Modification set 13/03/15, 1 year of compat for protocol v24
1071         if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
1072                 *pkt >> legacy_id;
1073         }
1074         else {
1075                 *pkt >> id;
1076         }
1077
1078
1079         ClientEvent event;
1080         event.type                      = CE_DELETE_PARTICLESPAWNER;
1081         event.delete_particlespawner.id =
1082                         (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1083
1084         m_client_event_queue.push(event);
1085 }
1086
1087 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1088 {
1089         std::string datastring(pkt->getString(0), pkt->getSize());
1090         std::istringstream is(datastring, std::ios_base::binary);
1091
1092         u32 id;
1093         u8 type;
1094         v2f pos;
1095         std::string name;
1096         v2f scale;
1097         std::string text;
1098         u32 number;
1099         u32 item;
1100         u32 dir;
1101         v2f align;
1102         v2f offset;
1103         v3f world_pos;
1104         v2s32 size;
1105
1106         *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1107                 >> dir >> align >> offset;
1108         try {
1109                 *pkt >> world_pos;
1110         }
1111         catch(SerializationError &e) {};
1112
1113         try {
1114                 *pkt >> size;
1115         } catch(SerializationError &e) {};
1116
1117         ClientEvent event;
1118         event.type             = CE_HUDADD;
1119         event.hudadd.id        = id;
1120         event.hudadd.type      = type;
1121         event.hudadd.pos       = new v2f(pos);
1122         event.hudadd.name      = new std::string(name);
1123         event.hudadd.scale     = new v2f(scale);
1124         event.hudadd.text      = new std::string(text);
1125         event.hudadd.number    = number;
1126         event.hudadd.item      = item;
1127         event.hudadd.dir       = dir;
1128         event.hudadd.align     = new v2f(align);
1129         event.hudadd.offset    = new v2f(offset);
1130         event.hudadd.world_pos = new v3f(world_pos);
1131         event.hudadd.size      = new v2s32(size);
1132         m_client_event_queue.push(event);
1133 }
1134
1135 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1136 {
1137         u32 id;
1138
1139         *pkt >> id;
1140
1141         ClientEvent event;
1142         event.type     = CE_HUDRM;
1143         event.hudrm.id = id;
1144         m_client_event_queue.push(event);
1145 }
1146
1147 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1148 {
1149         std::string sdata;
1150         v2f v2fdata;
1151         v3f v3fdata;
1152         u32 intdata = 0;
1153         v2s32 v2s32data;
1154         u32 id;
1155         u8 stat;
1156
1157         *pkt >> id >> stat;
1158
1159         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1160                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1161                 *pkt >> v2fdata;
1162         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1163                 *pkt >> sdata;
1164         else if (stat == HUD_STAT_WORLD_POS)
1165                 *pkt >> v3fdata;
1166         else if (stat == HUD_STAT_SIZE )
1167                 *pkt >> v2s32data;
1168         else
1169                 *pkt >> intdata;
1170
1171         ClientEvent event;
1172         event.type              = CE_HUDCHANGE;
1173         event.hudchange.id      = id;
1174         event.hudchange.stat    = (HudElementStat)stat;
1175         event.hudchange.v2fdata = new v2f(v2fdata);
1176         event.hudchange.v3fdata = new v3f(v3fdata);
1177         event.hudchange.sdata   = new std::string(sdata);
1178         event.hudchange.data    = intdata;
1179         event.hudchange.v2s32data = new v2s32(v2s32data);
1180         m_client_event_queue.push(event);
1181 }
1182
1183 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1184 {
1185         u32 flags, mask;
1186
1187         *pkt >> flags >> mask;
1188
1189         LocalPlayer *player = m_env.getLocalPlayer();
1190         assert(player != NULL);
1191
1192         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1193         bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1194
1195         player->hud_flags &= ~mask;
1196         player->hud_flags |= flags;
1197
1198         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1199         bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1200
1201         // Hide minimap if it has been disabled by the server
1202         if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1203                 // defers a minimap update, therefore only call it if really
1204                 // needed, by checking that minimap was visible before
1205                 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1206
1207         // Switch to surface mode if radar disabled by server
1208         if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1209                 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1210 }
1211
1212 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1213 {
1214         u16 param; std::string value;
1215
1216         *pkt >> param >> value;
1217
1218         LocalPlayer *player = m_env.getLocalPlayer();
1219         assert(player != NULL);
1220
1221         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1222                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1223                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1224                         player->hud_hotbar_itemcount = hotbar_itemcount;
1225         }
1226         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1227                 // If value not empty verify image exists in texture source
1228                 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1229                         errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1230                                 << value << "')" << std::endl;
1231                         return;
1232                 }
1233                 player->hotbar_image = value;
1234         }
1235         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1236                 // If value not empty verify image exists in texture source
1237                 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1238                         errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1239                                         << value << "')" << std::endl;
1240                         return;
1241                 }
1242                 player->hotbar_selected_image = value;
1243         }
1244 }
1245
1246 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1247 {
1248         std::string datastring(pkt->getString(0), pkt->getSize());
1249         std::istringstream is(datastring, std::ios_base::binary);
1250
1251         video::SColor *bgcolor           = new video::SColor(readARGB8(is));
1252         std::string *type                = new std::string(deSerializeString(is));
1253         u16 count                        = readU16(is);
1254         std::vector<std::string> *params = new std::vector<std::string>;
1255
1256         for (size_t i = 0; i < count; i++)
1257                 params->push_back(deSerializeString(is));
1258
1259         bool clouds = true;
1260         try {
1261                 clouds = readU8(is);
1262         } catch (...) {}
1263
1264         ClientEvent event;
1265         event.type            = CE_SET_SKY;
1266         event.set_sky.bgcolor = bgcolor;
1267         event.set_sky.type    = type;
1268         event.set_sky.params  = params;
1269         event.set_sky.clouds  = clouds;
1270         m_client_event_queue.push(event);
1271 }
1272
1273 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1274 {
1275         f32 density;
1276         video::SColor color_bright;
1277         video::SColor color_ambient;
1278         f32 height;
1279         f32 thickness;
1280         v2f speed;
1281
1282         *pkt >> density >> color_bright >> color_ambient
1283                         >> height >> thickness >> speed;
1284
1285         ClientEvent event;
1286         event.type                       = CE_CLOUD_PARAMS;
1287         event.cloud_params.density       = density;
1288         // use the underlying u32 representation, because we can't
1289         // use struct members with constructors here, and this way
1290         // we avoid using new() and delete() for no good reason
1291         event.cloud_params.color_bright  = color_bright.color;
1292         event.cloud_params.color_ambient = color_ambient.color;
1293         event.cloud_params.height        = height;
1294         event.cloud_params.thickness     = thickness;
1295         // same here: deconstruct to skip constructor
1296         event.cloud_params.speed_x       = speed.X;
1297         event.cloud_params.speed_y       = speed.Y;
1298         m_client_event_queue.push(event);
1299 }
1300
1301 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1302 {
1303         bool do_override;
1304         u16 day_night_ratio_u;
1305
1306         *pkt >> do_override >> day_night_ratio_u;
1307
1308         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1309
1310         ClientEvent event;
1311         event.type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1312         event.override_day_night_ratio.do_override = do_override;
1313         event.override_day_night_ratio.ratio_f     = day_night_ratio_f;
1314         m_client_event_queue.push(event);
1315 }
1316
1317 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1318 {
1319         LocalPlayer *player = m_env.getLocalPlayer();
1320         assert(player != NULL);
1321
1322         *pkt >> player->local_animations[0];
1323         *pkt >> player->local_animations[1];
1324         *pkt >> player->local_animations[2];
1325         *pkt >> player->local_animations[3];
1326         *pkt >> player->local_animation_speed;
1327 }
1328
1329 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1330 {
1331         LocalPlayer *player = m_env.getLocalPlayer();
1332         assert(player != NULL);
1333
1334         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1335 }
1336
1337 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1338 {
1339         u8 type;
1340         u16 num_players;
1341         *pkt >> type >> num_players;
1342         PlayerListModifer notice_type = (PlayerListModifer) type;
1343
1344         for (u16 i = 0; i < num_players; i++) {
1345                 std::string name;
1346                 *pkt >> name;
1347                 switch (notice_type) {
1348                 case PLAYER_LIST_INIT:
1349                 case PLAYER_LIST_ADD:
1350                         m_env.addPlayerName(name);
1351                         continue;
1352                 case PLAYER_LIST_REMOVE:
1353                         m_env.removePlayerName(name);
1354                         continue;
1355                 }
1356         }
1357 }
1358
1359 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1360 {
1361         if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1362                         && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1363                 errorstream << "Client: Recieved SRP S_B login message,"
1364                         << " but wasn't supposed to (chosen_mech="
1365                         << m_chosen_auth_mech << ")." << std::endl;
1366                 return;
1367         }
1368
1369         char *bytes_M = 0;
1370         size_t len_M = 0;
1371         SRPUser *usr = (SRPUser *) m_auth_data;
1372         std::string s;
1373         std::string B;
1374         *pkt >> s >> B;
1375
1376         infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1377
1378         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1379                 (const unsigned char *) B.c_str(), B.size(),
1380                 (unsigned char **) &bytes_M, &len_M);
1381
1382         if ( !bytes_M ) {
1383                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1384                 return;
1385         }
1386
1387         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1388         resp_pkt << std::string(bytes_M, len_M);
1389         Send(&resp_pkt);
1390 }
1391
1392 void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
1393 {
1394         *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;
1395 }