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