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