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