]> git.lizzy.rs Git - dragonfireclient.git/blob - src/network/clientpackethandler.cpp
Correct ServerActiveObject's virtual getArmorGroups() to be const.
[dragonfireclient.git] / src / network / clientpackethandler.cpp
1 /*
2 Minetest
3 Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "client/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         u64 timestamp;
412         *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
413         chatMessage->timestamp = static_cast<std::time_t>(timestamp);
414
415         chatMessage->type = (ChatMessageType) message_type;
416
417         // @TODO send this to CSM using ChatMessage object
418         if (moddingEnabled() && m_script->on_receiving_message(
419                         wide_to_utf8(chatMessage->message))) {
420                 // Message was consumed by CSM and should not be handled by client
421                 delete chatMessage;
422         } else {
423                 pushToChatQueue(chatMessage);
424         }
425 }
426
427 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
428 {
429         /*
430                 u16 count of removed objects
431                 for all removed objects {
432                         u16 id
433                 }
434                 u16 count of added objects
435                 for all added objects {
436                         u16 id
437                         u8 type
438                         u32 initialization data length
439                         string initialization data
440                 }
441         */
442
443         try {
444                 u8 type;
445                 u16 removed_count, added_count, id;
446
447                 // Read removed objects
448                 *pkt >> removed_count;
449
450                 for (u16 i = 0; i < removed_count; i++) {
451                         *pkt >> id;
452                         m_env.removeActiveObject(id);
453                 }
454
455                 // Read added objects
456                 *pkt >> added_count;
457
458                 for (u16 i = 0; i < added_count; i++) {
459                         *pkt >> id >> type;
460                         m_env.addActiveObject(id, type, pkt->readLongString());
461                 }
462         } catch (PacketError &e) {
463                 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
464                                 << ". The packet is unreliable, ignoring" << std::endl;
465         }
466 }
467
468 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
469 {
470         /*
471                 for all objects
472                 {
473                         u16 id
474                         u16 message length
475                         string message
476                 }
477         */
478         std::string datastring(pkt->getString(0), pkt->getSize());
479         std::istringstream is(datastring, std::ios_base::binary);
480
481         try {
482                 while (is.good()) {
483                         u16 id = readU16(is);
484                         if (!is.good())
485                                 break;
486
487                         std::string message = deSerializeString(is);
488
489                         // Pass on to the environment
490                         m_env.processActiveObjectMessage(id, message);
491                 }
492         } catch (SerializationError &e) {
493                 errorstream << "Client::handleCommand_ActiveObjectMessages: "
494                         << "caught SerializationError: " << e.what() << std::endl;
495         }
496 }
497
498 void Client::handleCommand_Movement(NetworkPacket* pkt)
499 {
500         LocalPlayer *player = m_env.getLocalPlayer();
501         assert(player != NULL);
502
503         float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
504
505         *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
506                 >> lf >> lfs >> ls >> g;
507
508         player->movement_acceleration_default   = mad * BS;
509         player->movement_acceleration_air       = maa * BS;
510         player->movement_acceleration_fast      = maf * BS;
511         player->movement_speed_walk             = msw * BS;
512         player->movement_speed_crouch           = mscr * BS;
513         player->movement_speed_fast             = msf * BS;
514         player->movement_speed_climb            = mscl * BS;
515         player->movement_speed_jump             = msj * BS;
516         player->movement_liquid_fluidity        = lf * BS;
517         player->movement_liquid_fluidity_smooth = lfs * BS;
518         player->movement_liquid_sink            = ls * BS;
519         player->movement_gravity                = g * BS;
520 }
521
522 void Client::handleCommand_HP(NetworkPacket* pkt)
523 {
524
525         LocalPlayer *player = m_env.getLocalPlayer();
526         assert(player != NULL);
527
528         u16 oldhp   = player->hp;
529
530         u16 hp;
531         *pkt >> hp;
532
533         player->hp = hp;
534
535         if (moddingEnabled()) {
536                 m_script->on_hp_modification(hp);
537         }
538
539         if (hp < oldhp) {
540                 // Add to ClientEvent queue
541                 ClientEvent *event = new ClientEvent();
542                 event->type = CE_PLAYER_DAMAGE;
543                 event->player_damage.amount = oldhp - hp;
544                 m_client_event_queue.push(event);
545         }
546 }
547
548 void Client::handleCommand_Breath(NetworkPacket* pkt)
549 {
550         LocalPlayer *player = m_env.getLocalPlayer();
551         assert(player != NULL);
552
553         u16 breath;
554
555         *pkt >> breath;
556
557         player->setBreath(breath);
558 }
559
560 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
561 {
562         LocalPlayer *player = m_env.getLocalPlayer();
563         assert(player != NULL);
564
565         v3f pos;
566         f32 pitch, yaw;
567
568         *pkt >> pos >> pitch >> yaw;
569
570         player->setPosition(pos);
571
572         infostream << "Client got TOCLIENT_MOVE_PLAYER"
573                         << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
574                         << " pitch=" << pitch
575                         << " yaw=" << yaw
576                         << std::endl;
577
578         /*
579                 Add to ClientEvent queue.
580                 This has to be sent to the main program because otherwise
581                 it would just force the pitch and yaw values to whatever
582                 the camera points to.
583         */
584         ClientEvent *event = new ClientEvent();
585         event->type = CE_PLAYER_FORCE_MOVE;
586         event->player_force_move.pitch = pitch;
587         event->player_force_move.yaw = yaw;
588         m_client_event_queue.push(event);
589 }
590
591 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
592 {
593         bool set_camera_point_target;
594         v3f camera_point_target;
595
596         *pkt >> set_camera_point_target;
597         *pkt >> camera_point_target;
598
599         ClientEvent *event = new ClientEvent();
600         event->type                                = CE_DEATHSCREEN;
601         event->deathscreen.set_camera_point_target = set_camera_point_target;
602         event->deathscreen.camera_point_target_x   = camera_point_target.X;
603         event->deathscreen.camera_point_target_y   = camera_point_target.Y;
604         event->deathscreen.camera_point_target_z   = camera_point_target.Z;
605         m_client_event_queue.push(event);
606 }
607
608 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
609 {
610         u16 num_files;
611
612         *pkt >> num_files;
613
614         infostream << "Client: Received media announcement: packet size: "
615                         << pkt->getSize() << std::endl;
616
617         if (m_media_downloader == NULL ||
618                         m_media_downloader->isStarted()) {
619                 const char *problem = m_media_downloader ?
620                         "we already saw another announcement" :
621                         "all media has been received already";
622                 errorstream << "Client: Received media announcement but "
623                         << problem << "! "
624                         << " files=" << num_files
625                         << " size=" << pkt->getSize() << std::endl;
626                 return;
627         }
628
629         // Mesh update thread must be stopped while
630         // updating content definitions
631         sanity_check(!m_mesh_update_thread.isRunning());
632
633         for (u16 i = 0; i < num_files; i++) {
634                 std::string name, sha1_base64;
635
636                 *pkt >> name >> sha1_base64;
637
638                 std::string sha1_raw = base64_decode(sha1_base64);
639                 m_media_downloader->addFile(name, sha1_raw);
640         }
641
642         try {
643                 std::string str;
644
645                 *pkt >> str;
646
647                 Strfnd sf(str);
648                 while(!sf.at_end()) {
649                         std::string baseurl = trim(sf.next(","));
650                         if (!baseurl.empty())
651                                 m_media_downloader->addRemoteServer(baseurl);
652                 }
653         }
654         catch(SerializationError& e) {
655                 // not supported by server or turned off
656         }
657
658         m_media_downloader->step(this);
659 }
660
661 void Client::handleCommand_Media(NetworkPacket* pkt)
662 {
663         /*
664                 u16 command
665                 u16 total number of file bunches
666                 u16 index of this bunch
667                 u32 number of files in this bunch
668                 for each file {
669                         u16 length of name
670                         string name
671                         u32 length of data
672                         data
673                 }
674         */
675         u16 num_bunches;
676         u16 bunch_i;
677         u32 num_files;
678
679         *pkt >> num_bunches >> bunch_i >> num_files;
680
681         infostream << "Client: Received files: bunch " << bunch_i << "/"
682                         << num_bunches << " files=" << num_files
683                         << " size=" << pkt->getSize() << std::endl;
684
685         if (num_files == 0)
686                 return;
687
688         if (!m_media_downloader || !m_media_downloader->isStarted()) {
689                 const char *problem = m_media_downloader ?
690                         "media has not been requested" :
691                         "all media has been received already";
692                 errorstream << "Client: Received media but "
693                         << problem << "! "
694                         << " bunch " << bunch_i << "/" << num_bunches
695                         << " files=" << num_files
696                         << " size=" << pkt->getSize() << std::endl;
697                 return;
698         }
699
700         // Mesh update thread must be stopped while
701         // updating content definitions
702         sanity_check(!m_mesh_update_thread.isRunning());
703
704         for (u32 i=0; i < num_files; i++) {
705                 std::string name;
706
707                 *pkt >> name;
708
709                 std::string data = pkt->readLongString();
710
711                 m_media_downloader->conventionalTransferDone(
712                                 name, data, this);
713         }
714 }
715
716 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
717 {
718         infostream << "Client: Received node definitions: packet size: "
719                         << pkt->getSize() << std::endl;
720
721         // Mesh update thread must be stopped while
722         // updating content definitions
723         sanity_check(!m_mesh_update_thread.isRunning());
724
725         // Decompress node definitions
726         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
727         std::ostringstream tmp_os;
728         decompressZlib(tmp_is, tmp_os);
729
730         // Deserialize node definitions
731         std::istringstream tmp_is2(tmp_os.str());
732         m_nodedef->deSerialize(tmp_is2);
733         m_nodedef_received = true;
734 }
735
736 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
737 {
738         infostream << "Client: Received item definitions: packet size: "
739                         << pkt->getSize() << std::endl;
740
741         // Mesh update thread must be stopped while
742         // updating content definitions
743         sanity_check(!m_mesh_update_thread.isRunning());
744
745         // Decompress item definitions
746         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
747         std::ostringstream tmp_os;
748         decompressZlib(tmp_is, tmp_os);
749
750         // Deserialize node definitions
751         std::istringstream tmp_is2(tmp_os.str());
752         m_itemdef->deSerialize(tmp_is2);
753         m_itemdef_received = true;
754 }
755
756 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
757 {
758         /*
759                 [0] u32 server_id
760                 [4] u16 name length
761                 [6] char name[len]
762                 [ 6 + len] f32 gain
763                 [10 + len] u8 type
764                 [11 + len] (f32 * 3) pos
765                 [23 + len] u16 object_id
766                 [25 + len] bool loop
767                 [26 + len] f32 fade
768                 [30 + len] f32 pitch
769         */
770
771         s32 server_id;
772         std::string name;
773
774         float gain;
775         u8 type; // 0=local, 1=positional, 2=object
776         v3f pos;
777         u16 object_id;
778         bool loop;
779         float fade = 0.0f;
780         float pitch = 1.0f;
781
782         *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
783
784         try {
785                 *pkt >> fade;
786                 *pkt >> pitch;
787         } catch (PacketError &e) {};
788
789         // Start playing
790         int client_id = -1;
791         switch(type) {
792                 case 0: // local
793                         client_id = m_sound->playSound(name, loop, gain, fade, pitch);
794                         break;
795                 case 1: // positional
796                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
797                         break;
798                 case 2:
799                 { // object
800                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
801                         if (cao)
802                                 pos = cao->getPosition();
803                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
804                         // TODO: Set up sound to move with object
805                         break;
806                 }
807                 default:
808                         break;
809         }
810
811         if (client_id != -1) {
812                 m_sounds_server_to_client[server_id] = client_id;
813                 m_sounds_client_to_server[client_id] = server_id;
814                 if (object_id != 0)
815                         m_sounds_to_objects[client_id] = object_id;
816         }
817 }
818
819 void Client::handleCommand_StopSound(NetworkPacket* pkt)
820 {
821         s32 server_id;
822
823         *pkt >> server_id;
824
825         std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
826         if (i != m_sounds_server_to_client.end()) {
827                 int client_id = i->second;
828                 m_sound->stopSound(client_id);
829         }
830 }
831
832 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
833 {
834         s32 sound_id;
835         float step;
836         float gain;
837
838         *pkt >> sound_id >> step >> gain;
839
840         std::unordered_map<s32, int>::const_iterator i =
841                         m_sounds_server_to_client.find(sound_id);
842
843         if (i != m_sounds_server_to_client.end())
844                 m_sound->fadeSound(i->second, step, gain);
845 }
846
847 void Client::handleCommand_Privileges(NetworkPacket* pkt)
848 {
849         m_privileges.clear();
850         infostream << "Client: Privileges updated: ";
851         u16 num_privileges;
852
853         *pkt >> num_privileges;
854
855         for (u16 i = 0; i < num_privileges; i++) {
856                 std::string priv;
857
858                 *pkt >> priv;
859
860                 m_privileges.insert(priv);
861                 infostream << priv << " ";
862         }
863         infostream << std::endl;
864 }
865
866 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
867 {
868         LocalPlayer *player = m_env.getLocalPlayer();
869         assert(player != NULL);
870
871         // Store formspec in LocalPlayer
872         player->inventory_formspec = pkt->readLongString();
873 }
874
875 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
876 {
877         std::string name;
878         bool keep_inv = true;
879         *pkt >> name >> keep_inv;
880
881         infostream << "Client: Detached inventory update: \"" << name
882                 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
883
884         const auto &inv_it = m_detached_inventories.find(name);
885         if (!keep_inv) {
886                 if (inv_it != m_detached_inventories.end()) {
887                         delete inv_it->second;
888                         m_detached_inventories.erase(inv_it);
889                 }
890                 return;
891         }
892         Inventory *inv = nullptr;
893         if (inv_it == m_detached_inventories.end()) {
894                 inv = new Inventory(m_itemdef);
895                 m_detached_inventories[name] = inv;
896         } else {
897                 inv = inv_it->second;
898         }
899
900         u16 ignore;
901         *pkt >> ignore; // this used to be the length of the following string, ignore it
902
903         std::string contents = pkt->getRemainingString();
904         std::istringstream is(contents, std::ios::binary);
905         inv->deSerialize(is);
906 }
907
908 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
909 {
910         std::string formspec = pkt->readLongString();
911         std::string formname;
912
913         *pkt >> formname;
914
915         ClientEvent *event = new ClientEvent();
916         event->type = CE_SHOW_FORMSPEC;
917         // pointer is required as event is a struct only!
918         // adding a std:string to a struct isn't possible
919         event->show_formspec.formspec = new std::string(formspec);
920         event->show_formspec.formname = new std::string(formname);
921         m_client_event_queue.push(event);
922 }
923
924 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
925 {
926         std::string datastring(pkt->getString(0), pkt->getSize());
927         std::istringstream is(datastring, std::ios_base::binary);
928
929         v3f pos                 = readV3F32(is);
930         v3f vel                 = readV3F32(is);
931         v3f acc                 = readV3F32(is);
932         float expirationtime    = readF32(is);
933         float size              = readF32(is);
934         bool collisiondetection = readU8(is);
935         std::string texture     = deSerializeLongString(is);
936
937         bool vertical          = false;
938         bool collision_removal = false;
939         TileAnimationParams animation;
940         animation.type         = TAT_NONE;
941         u8 glow                = 0;
942         bool object_collision  = false;
943         try {
944                 vertical = readU8(is);
945                 collision_removal = readU8(is);
946                 animation.deSerialize(is, m_proto_ver);
947                 glow = readU8(is);
948                 object_collision = readU8(is);
949         } catch (...) {}
950
951         ClientEvent *event = new ClientEvent();
952         event->type                              = CE_SPAWN_PARTICLE;
953         event->spawn_particle.pos                = new v3f (pos);
954         event->spawn_particle.vel                = new v3f (vel);
955         event->spawn_particle.acc                = new v3f (acc);
956         event->spawn_particle.expirationtime     = expirationtime;
957         event->spawn_particle.size               = size;
958         event->spawn_particle.collisiondetection = collisiondetection;
959         event->spawn_particle.collision_removal  = collision_removal;
960         event->spawn_particle.object_collision   = object_collision;
961         event->spawn_particle.vertical           = vertical;
962         event->spawn_particle.texture            = new std::string(texture);
963         event->spawn_particle.animation          = animation;
964         event->spawn_particle.glow               = glow;
965
966         m_client_event_queue.push(event);
967 }
968
969 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
970 {
971         u16 amount;
972         float spawntime;
973         v3f minpos;
974         v3f maxpos;
975         v3f minvel;
976         v3f maxvel;
977         v3f minacc;
978         v3f maxacc;
979         float minexptime;
980         float maxexptime;
981         float minsize;
982         float maxsize;
983         bool collisiondetection;
984         u32 server_id;
985
986         *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
987                 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
988                 >> maxsize >> collisiondetection;
989
990         std::string texture = pkt->readLongString();
991
992         *pkt >> server_id;
993
994         bool vertical          = false;
995         bool collision_removal = false;
996         u16 attached_id        = 0;
997         TileAnimationParams animation;
998         animation.type         = TAT_NONE;
999         u8 glow                = 0;
1000         bool object_collision  = false;
1001         try {
1002                 *pkt >> vertical;
1003                 *pkt >> collision_removal;
1004                 *pkt >> attached_id;
1005
1006                 // This is horrible but required (why are there two ways to deserialize pkts?)
1007                 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1008                 std::istringstream is(datastring, std::ios_base::binary);
1009                 animation.deSerialize(is, m_proto_ver);
1010                 glow = readU8(is);
1011                 object_collision = readU8(is);
1012         } catch (...) {}
1013
1014         auto event = new ClientEvent();
1015         event->type                                   = CE_ADD_PARTICLESPAWNER;
1016         event->add_particlespawner.amount             = amount;
1017         event->add_particlespawner.spawntime          = spawntime;
1018         event->add_particlespawner.minpos             = new v3f (minpos);
1019         event->add_particlespawner.maxpos             = new v3f (maxpos);
1020         event->add_particlespawner.minvel             = new v3f (minvel);
1021         event->add_particlespawner.maxvel             = new v3f (maxvel);
1022         event->add_particlespawner.minacc             = new v3f (minacc);
1023         event->add_particlespawner.maxacc             = new v3f (maxacc);
1024         event->add_particlespawner.minexptime         = minexptime;
1025         event->add_particlespawner.maxexptime         = maxexptime;
1026         event->add_particlespawner.minsize            = minsize;
1027         event->add_particlespawner.maxsize            = maxsize;
1028         event->add_particlespawner.collisiondetection = collisiondetection;
1029         event->add_particlespawner.collision_removal  = collision_removal;
1030         event->add_particlespawner.object_collision   = object_collision;
1031         event->add_particlespawner.attached_id        = attached_id;
1032         event->add_particlespawner.vertical           = vertical;
1033         event->add_particlespawner.texture            = new std::string(texture);
1034         event->add_particlespawner.id                 = server_id;
1035         event->add_particlespawner.animation          = animation;
1036         event->add_particlespawner.glow               = glow;
1037
1038         m_client_event_queue.push(event);
1039 }
1040
1041
1042 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1043 {
1044         u32 server_id;
1045         *pkt >> server_id;
1046
1047         ClientEvent *event = new ClientEvent();
1048         event->type = CE_DELETE_PARTICLESPAWNER;
1049         event->delete_particlespawner.id = server_id;
1050
1051         m_client_event_queue.push(event);
1052 }
1053
1054 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1055 {
1056         std::string datastring(pkt->getString(0), pkt->getSize());
1057         std::istringstream is(datastring, std::ios_base::binary);
1058
1059         u32 server_id;
1060         u8 type;
1061         v2f pos;
1062         std::string name;
1063         v2f scale;
1064         std::string text;
1065         u32 number;
1066         u32 item;
1067         u32 dir;
1068         v2f align;
1069         v2f offset;
1070         v3f world_pos;
1071         v2s32 size;
1072
1073         *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1074                 >> dir >> align >> offset;
1075         try {
1076                 *pkt >> world_pos;
1077         }
1078         catch(SerializationError &e) {};
1079
1080         try {
1081                 *pkt >> size;
1082         } catch(SerializationError &e) {};
1083
1084         ClientEvent *event = new ClientEvent();
1085         event->type             = CE_HUDADD;
1086         event->hudadd.server_id = server_id;
1087         event->hudadd.type      = type;
1088         event->hudadd.pos       = new v2f(pos);
1089         event->hudadd.name      = new std::string(name);
1090         event->hudadd.scale     = new v2f(scale);
1091         event->hudadd.text      = new std::string(text);
1092         event->hudadd.number    = number;
1093         event->hudadd.item      = item;
1094         event->hudadd.dir       = dir;
1095         event->hudadd.align     = new v2f(align);
1096         event->hudadd.offset    = new v2f(offset);
1097         event->hudadd.world_pos = new v3f(world_pos);
1098         event->hudadd.size      = new v2s32(size);
1099         m_client_event_queue.push(event);
1100 }
1101
1102 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1103 {
1104         u32 server_id;
1105
1106         *pkt >> server_id;
1107
1108         auto i = m_hud_server_to_client.find(server_id);
1109         if (i != m_hud_server_to_client.end()) {
1110                 int client_id = i->second;
1111                 m_hud_server_to_client.erase(i);
1112
1113                 ClientEvent *event = new ClientEvent();
1114                 event->type     = CE_HUDRM;
1115                 event->hudrm.id = client_id;
1116                 m_client_event_queue.push(event);
1117         }
1118 }
1119
1120 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1121 {
1122         std::string sdata;
1123         v2f v2fdata;
1124         v3f v3fdata;
1125         u32 intdata = 0;
1126         v2s32 v2s32data;
1127         u32 server_id;
1128         u8 stat;
1129
1130         *pkt >> server_id >> stat;
1131
1132         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1133                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1134                 *pkt >> v2fdata;
1135         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1136                 *pkt >> sdata;
1137         else if (stat == HUD_STAT_WORLD_POS)
1138                 *pkt >> v3fdata;
1139         else if (stat == HUD_STAT_SIZE )
1140                 *pkt >> v2s32data;
1141         else
1142                 *pkt >> intdata;
1143
1144         std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1145         if (i != m_hud_server_to_client.end()) {
1146                 ClientEvent *event = new ClientEvent();
1147                 event->type              = CE_HUDCHANGE;
1148                 event->hudchange.id      = i->second;
1149                 event->hudchange.stat    = (HudElementStat)stat;
1150                 event->hudchange.v2fdata = new v2f(v2fdata);
1151                 event->hudchange.v3fdata = new v3f(v3fdata);
1152                 event->hudchange.sdata   = new std::string(sdata);
1153                 event->hudchange.data    = intdata;
1154                 event->hudchange.v2s32data = new v2s32(v2s32data);
1155                 m_client_event_queue.push(event);
1156         }
1157 }
1158
1159 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1160 {
1161         u32 flags, mask;
1162
1163         *pkt >> flags >> mask;
1164
1165         LocalPlayer *player = m_env.getLocalPlayer();
1166         assert(player != NULL);
1167
1168         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1169         bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1170
1171         player->hud_flags &= ~mask;
1172         player->hud_flags |= flags;
1173
1174         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1175         bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1176
1177         // Hide minimap if it has been disabled by the server
1178         if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1179                 // defers a minimap update, therefore only call it if really
1180                 // needed, by checking that minimap was visible before
1181                 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1182
1183         // Switch to surface mode if radar disabled by server
1184         if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1185                 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1186 }
1187
1188 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1189 {
1190         u16 param; std::string value;
1191
1192         *pkt >> param >> value;
1193
1194         LocalPlayer *player = m_env.getLocalPlayer();
1195         assert(player != NULL);
1196
1197         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1198                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1199                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1200                         player->hud_hotbar_itemcount = hotbar_itemcount;
1201         }
1202         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1203                 // If value not empty verify image exists in texture source
1204                 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1205                         errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1206                                 << value << "')" << std::endl;
1207                         return;
1208                 }
1209                 player->hotbar_image = value;
1210         }
1211         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1212                 // If value not empty verify image exists in texture source
1213                 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1214                         errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1215                                         << value << "')" << std::endl;
1216                         return;
1217                 }
1218                 player->hotbar_selected_image = value;
1219         }
1220 }
1221
1222 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1223 {
1224         std::string datastring(pkt->getString(0), pkt->getSize());
1225         std::istringstream is(datastring, std::ios_base::binary);
1226
1227         video::SColor *bgcolor           = new video::SColor(readARGB8(is));
1228         std::string *type                = new std::string(deSerializeString(is));
1229         u16 count                        = readU16(is);
1230         std::vector<std::string> *params = new std::vector<std::string>;
1231
1232         for (size_t i = 0; i < count; i++)
1233                 params->push_back(deSerializeString(is));
1234
1235         bool clouds = true;
1236         try {
1237                 clouds = readU8(is);
1238         } catch (...) {}
1239
1240         ClientEvent *event = new ClientEvent();
1241         event->type            = CE_SET_SKY;
1242         event->set_sky.bgcolor = bgcolor;
1243         event->set_sky.type    = type;
1244         event->set_sky.params  = params;
1245         event->set_sky.clouds  = clouds;
1246         m_client_event_queue.push(event);
1247 }
1248
1249 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1250 {
1251         f32 density;
1252         video::SColor color_bright;
1253         video::SColor color_ambient;
1254         f32 height;
1255         f32 thickness;
1256         v2f speed;
1257
1258         *pkt >> density >> color_bright >> color_ambient
1259                         >> height >> thickness >> speed;
1260
1261         ClientEvent *event = new ClientEvent();
1262         event->type                       = CE_CLOUD_PARAMS;
1263         event->cloud_params.density       = density;
1264         // use the underlying u32 representation, because we can't
1265         // use struct members with constructors here, and this way
1266         // we avoid using new() and delete() for no good reason
1267         event->cloud_params.color_bright  = color_bright.color;
1268         event->cloud_params.color_ambient = color_ambient.color;
1269         event->cloud_params.height        = height;
1270         event->cloud_params.thickness     = thickness;
1271         // same here: deconstruct to skip constructor
1272         event->cloud_params.speed_x       = speed.X;
1273         event->cloud_params.speed_y       = speed.Y;
1274         m_client_event_queue.push(event);
1275 }
1276
1277 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1278 {
1279         bool do_override;
1280         u16 day_night_ratio_u;
1281
1282         *pkt >> do_override >> day_night_ratio_u;
1283
1284         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1285
1286         ClientEvent *event = new ClientEvent();
1287         event->type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1288         event->override_day_night_ratio.do_override = do_override;
1289         event->override_day_night_ratio.ratio_f     = day_night_ratio_f;
1290         m_client_event_queue.push(event);
1291 }
1292
1293 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1294 {
1295         LocalPlayer *player = m_env.getLocalPlayer();
1296         assert(player != NULL);
1297
1298         *pkt >> player->local_animations[0];
1299         *pkt >> player->local_animations[1];
1300         *pkt >> player->local_animations[2];
1301         *pkt >> player->local_animations[3];
1302         *pkt >> player->local_animation_speed;
1303 }
1304
1305 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1306 {
1307         LocalPlayer *player = m_env.getLocalPlayer();
1308         assert(player != NULL);
1309
1310         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1311 }
1312
1313 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1314 {
1315         u8 type;
1316         u16 num_players;
1317         *pkt >> type >> num_players;
1318         PlayerListModifer notice_type = (PlayerListModifer) type;
1319
1320         for (u16 i = 0; i < num_players; i++) {
1321                 std::string name;
1322                 *pkt >> name;
1323                 switch (notice_type) {
1324                 case PLAYER_LIST_INIT:
1325                 case PLAYER_LIST_ADD:
1326                         m_env.addPlayerName(name);
1327                         continue;
1328                 case PLAYER_LIST_REMOVE:
1329                         m_env.removePlayerName(name);
1330                         continue;
1331                 }
1332         }
1333 }
1334
1335 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1336 {
1337         if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1338                         m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1339                 errorstream << "Client: Received SRP S_B login message,"
1340                         << " but wasn't supposed to (chosen_mech="
1341                         << m_chosen_auth_mech << ")." << std::endl;
1342                 return;
1343         }
1344
1345         char *bytes_M = 0;
1346         size_t len_M = 0;
1347         SRPUser *usr = (SRPUser *) m_auth_data;
1348         std::string s;
1349         std::string B;
1350         *pkt >> s >> B;
1351
1352         infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1353
1354         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1355                 (const unsigned char *) B.c_str(), B.size(),
1356                 (unsigned char **) &bytes_M, &len_M);
1357
1358         if ( !bytes_M ) {
1359                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1360                 return;
1361         }
1362
1363         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1364         resp_pkt << std::string(bytes_M, len_M);
1365         Send(&resp_pkt);
1366 }
1367
1368 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1369 {
1370         LocalPlayer *player = m_env.getLocalPlayer();
1371         assert(player != NULL);
1372
1373         // Store formspec in LocalPlayer
1374         *pkt >> player->formspec_prepend;
1375 }
1376
1377 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1378 {
1379         *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1380
1381         // Restrictions were received -> load mods if it's enabled
1382         // Note: this should be moved after mods receptions from server instead
1383         loadMods();
1384 }
1385
1386 /*
1387  * Mod channels
1388  */
1389
1390 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1391 {
1392         std::string channel_name, sender, channel_msg;
1393         *pkt >> channel_name >> sender >> channel_msg;
1394
1395         verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1396                 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1397                 << channel_msg << std::endl;
1398
1399         if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1400                 verbosestream << "Server sent us messages on unregistered channel "
1401                         << channel_name << ", ignoring." << std::endl;
1402                 return;
1403         }
1404
1405         m_script->on_modchannel_message(channel_name, sender, channel_msg);
1406 }
1407
1408 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1409 {
1410         u8 signal_tmp;
1411         ModChannelSignal signal;
1412         std::string channel;
1413
1414         *pkt >> signal_tmp >> channel;
1415
1416         signal = (ModChannelSignal)signal_tmp;
1417
1418         bool valid_signal = true;
1419         // @TODO: send Signal to Lua API
1420         switch (signal) {
1421                 case MODCHANNEL_SIGNAL_JOIN_OK:
1422                         m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1423                         infostream << "Server ack our mod channel join on channel `" << channel
1424                                 << "`, joining." << std::endl;
1425                         break;
1426                 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1427                         // Unable to join, remove channel
1428                         m_modchannel_mgr->leaveChannel(channel, 0);
1429                         infostream << "Server refused our mod channel join on channel `" << channel
1430                                 << "`" << std::endl;
1431                         break;
1432                 case MODCHANNEL_SIGNAL_LEAVE_OK:
1433 #ifndef NDEBUG
1434                         infostream << "Server ack our mod channel leave on channel " << channel
1435                                 << "`, leaving." << std::endl;
1436 #endif
1437                         break;
1438                 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1439                         infostream << "Server refused our mod channel leave on channel `" << channel
1440                                 << "`" << std::endl;
1441                         break;
1442                 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1443 #ifndef NDEBUG
1444                         // Generally unused, but ensure we don't do an implementation error
1445                         infostream << "Server tells us we sent a message on channel `" << channel
1446                                 << "` but we are not registered. Message was dropped." << std::endl;
1447 #endif
1448                         break;
1449                 case MODCHANNEL_SIGNAL_SET_STATE: {
1450                         u8 state;
1451                         *pkt >> state;
1452
1453                         if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1454                                 infostream << "Received wrong channel state " << state
1455                                                 << ", ignoring." << std::endl;
1456                                 return;
1457                         }
1458
1459                         m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1460                         infostream << "Server sets mod channel `" << channel
1461                                         << "` in read-only mode." << std::endl;
1462                         break;
1463                 }
1464                 default:
1465 #ifndef NDEBUG
1466                         warningstream << "Received unhandled mod channel signal ID "
1467                                 << signal << ", ignoring." << std::endl;
1468 #endif
1469                         valid_signal = false;
1470                         break;
1471         }
1472
1473         // If signal is valid, forward it to client side mods
1474         if (valid_signal)
1475                 m_script->on_modchannel_signal(channel, signal);
1476 }