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