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