]> git.lizzy.rs Git - dragonfireclient.git/blob - src/network/clientpackethandler.cpp
Fix issues with earlier CSM HUD commit (#6940)
[dragonfireclient.git] / src / network / clientpackethandler.cpp
1 /*
2 Minetest
3 Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "client.h"
21
22 #include "util/base64.h"
23 #include "chatmessage.h"
24 #include "clientmedia.h"
25 #include "log.h"
26 #include "map.h"
27 #include "mapsector.h"
28 #include "minimap.h"
29 #include "modchannels.h"
30 #include "nodedef.h"
31 #include "serialization.h"
32 #include "server.h"
33 #include "util/strfnd.h"
34 #include "client/clientevent.h"
35 #include "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 server_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 >> server_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         u32 client_id = m_particle_manager.getSpawnerId();
988         m_particles_server_to_client[server_id] = client_id;
989
990         ClientEvent *event = new ClientEvent();
991         event->type                                   = CE_ADD_PARTICLESPAWNER;
992         event->add_particlespawner.amount             = amount;
993         event->add_particlespawner.spawntime          = spawntime;
994         event->add_particlespawner.minpos             = new v3f (minpos);
995         event->add_particlespawner.maxpos             = new v3f (maxpos);
996         event->add_particlespawner.minvel             = new v3f (minvel);
997         event->add_particlespawner.maxvel             = new v3f (maxvel);
998         event->add_particlespawner.minacc             = new v3f (minacc);
999         event->add_particlespawner.maxacc             = new v3f (maxacc);
1000         event->add_particlespawner.minexptime         = minexptime;
1001         event->add_particlespawner.maxexptime         = maxexptime;
1002         event->add_particlespawner.minsize            = minsize;
1003         event->add_particlespawner.maxsize            = maxsize;
1004         event->add_particlespawner.collisiondetection = collisiondetection;
1005         event->add_particlespawner.collision_removal  = collision_removal;
1006         event->add_particlespawner.attached_id        = attached_id;
1007         event->add_particlespawner.vertical           = vertical;
1008         event->add_particlespawner.texture            = new std::string(texture);
1009         event->add_particlespawner.id                 = client_id;
1010         event->add_particlespawner.animation          = animation;
1011         event->add_particlespawner.glow               = glow;
1012
1013         m_client_event_queue.push(event);
1014 }
1015
1016
1017 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1018 {
1019         u32 server_id;
1020         *pkt >> server_id;
1021
1022         u32 client_id;
1023         auto i = m_particles_server_to_client.find(server_id);
1024         if (i != m_particles_server_to_client.end())
1025                 client_id = i->second;
1026         else
1027                 return;
1028
1029         ClientEvent *event = new ClientEvent();
1030         event->type = CE_DELETE_PARTICLESPAWNER;
1031         event->delete_particlespawner.id = client_id;
1032
1033         m_client_event_queue.push(event);
1034 }
1035
1036 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1037 {
1038         std::string datastring(pkt->getString(0), pkt->getSize());
1039         std::istringstream is(datastring, std::ios_base::binary);
1040
1041         u32 server_id;
1042         u8 type;
1043         v2f pos;
1044         std::string name;
1045         v2f scale;
1046         std::string text;
1047         u32 number;
1048         u32 item;
1049         u32 dir;
1050         v2f align;
1051         v2f offset;
1052         v3f world_pos;
1053         v2s32 size;
1054
1055         *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1056                 >> dir >> align >> offset;
1057         try {
1058                 *pkt >> world_pos;
1059         }
1060         catch(SerializationError &e) {};
1061
1062         try {
1063                 *pkt >> size;
1064         } catch(SerializationError &e) {};
1065
1066         ClientEvent *event = new ClientEvent();
1067         event->type             = CE_HUDADD;
1068         event->hudadd.server_id = server_id;
1069         event->hudadd.type      = type;
1070         event->hudadd.pos       = new v2f(pos);
1071         event->hudadd.name      = new std::string(name);
1072         event->hudadd.scale     = new v2f(scale);
1073         event->hudadd.text      = new std::string(text);
1074         event->hudadd.number    = number;
1075         event->hudadd.item      = item;
1076         event->hudadd.dir       = dir;
1077         event->hudadd.align     = new v2f(align);
1078         event->hudadd.offset    = new v2f(offset);
1079         event->hudadd.world_pos = new v3f(world_pos);
1080         event->hudadd.size      = new v2s32(size);
1081         m_client_event_queue.push(event);
1082 }
1083
1084 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1085 {
1086         u32 server_id;
1087
1088         *pkt >> server_id;
1089
1090         auto i = m_hud_server_to_client.find(server_id);
1091         if (i != m_hud_server_to_client.end()) {
1092                 int client_id = i->second;
1093                 m_hud_server_to_client.erase(i);
1094
1095                 ClientEvent *event = new ClientEvent();
1096                 event->type     = CE_HUDRM;
1097                 event->hudrm.id = client_id;
1098                 m_client_event_queue.push(event);
1099         }
1100 }
1101
1102 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1103 {
1104         std::string sdata;
1105         v2f v2fdata;
1106         v3f v3fdata;
1107         u32 intdata = 0;
1108         v2s32 v2s32data;
1109         u32 server_id;
1110         u8 stat;
1111
1112         *pkt >> server_id >> stat;
1113
1114         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1115                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1116                 *pkt >> v2fdata;
1117         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1118                 *pkt >> sdata;
1119         else if (stat == HUD_STAT_WORLD_POS)
1120                 *pkt >> v3fdata;
1121         else if (stat == HUD_STAT_SIZE )
1122                 *pkt >> v2s32data;
1123         else
1124                 *pkt >> intdata;
1125
1126         std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1127         if (i != m_hud_server_to_client.end()) {
1128                 ClientEvent *event = new ClientEvent();
1129                 event->type              = CE_HUDCHANGE;
1130                 event->hudchange.id      = i->second;
1131                 event->hudchange.stat    = (HudElementStat)stat;
1132                 event->hudchange.v2fdata = new v2f(v2fdata);
1133                 event->hudchange.v3fdata = new v3f(v3fdata);
1134                 event->hudchange.sdata   = new std::string(sdata);
1135                 event->hudchange.data    = intdata;
1136                 event->hudchange.v2s32data = new v2s32(v2s32data);
1137                 m_client_event_queue.push(event);
1138         }
1139 }
1140
1141 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1142 {
1143         u32 flags, mask;
1144
1145         *pkt >> flags >> mask;
1146
1147         LocalPlayer *player = m_env.getLocalPlayer();
1148         assert(player != NULL);
1149
1150         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1151         bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1152
1153         player->hud_flags &= ~mask;
1154         player->hud_flags |= flags;
1155
1156         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1157         bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1158
1159         // Hide minimap if it has been disabled by the server
1160         if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1161                 // defers a minimap update, therefore only call it if really
1162                 // needed, by checking that minimap was visible before
1163                 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1164
1165         // Switch to surface mode if radar disabled by server
1166         if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1167                 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1168 }
1169
1170 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1171 {
1172         u16 param; std::string value;
1173
1174         *pkt >> param >> value;
1175
1176         LocalPlayer *player = m_env.getLocalPlayer();
1177         assert(player != NULL);
1178
1179         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1180                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1181                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1182                         player->hud_hotbar_itemcount = hotbar_itemcount;
1183         }
1184         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1185                 // If value not empty verify image exists in texture source
1186                 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1187                         errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1188                                 << value << "')" << std::endl;
1189                         return;
1190                 }
1191                 player->hotbar_image = value;
1192         }
1193         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1194                 // If value not empty verify image exists in texture source
1195                 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1196                         errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1197                                         << value << "')" << std::endl;
1198                         return;
1199                 }
1200                 player->hotbar_selected_image = value;
1201         }
1202 }
1203
1204 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1205 {
1206         std::string datastring(pkt->getString(0), pkt->getSize());
1207         std::istringstream is(datastring, std::ios_base::binary);
1208
1209         video::SColor *bgcolor           = new video::SColor(readARGB8(is));
1210         std::string *type                = new std::string(deSerializeString(is));
1211         u16 count                        = readU16(is);
1212         std::vector<std::string> *params = new std::vector<std::string>;
1213
1214         for (size_t i = 0; i < count; i++)
1215                 params->push_back(deSerializeString(is));
1216
1217         bool clouds = true;
1218         try {
1219                 clouds = readU8(is);
1220         } catch (...) {}
1221
1222         ClientEvent *event = new ClientEvent();
1223         event->type            = CE_SET_SKY;
1224         event->set_sky.bgcolor = bgcolor;
1225         event->set_sky.type    = type;
1226         event->set_sky.params  = params;
1227         event->set_sky.clouds  = clouds;
1228         m_client_event_queue.push(event);
1229 }
1230
1231 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1232 {
1233         f32 density;
1234         video::SColor color_bright;
1235         video::SColor color_ambient;
1236         f32 height;
1237         f32 thickness;
1238         v2f speed;
1239
1240         *pkt >> density >> color_bright >> color_ambient
1241                         >> height >> thickness >> speed;
1242
1243         ClientEvent *event = new ClientEvent();
1244         event->type                       = CE_CLOUD_PARAMS;
1245         event->cloud_params.density       = density;
1246         // use the underlying u32 representation, because we can't
1247         // use struct members with constructors here, and this way
1248         // we avoid using new() and delete() for no good reason
1249         event->cloud_params.color_bright  = color_bright.color;
1250         event->cloud_params.color_ambient = color_ambient.color;
1251         event->cloud_params.height        = height;
1252         event->cloud_params.thickness     = thickness;
1253         // same here: deconstruct to skip constructor
1254         event->cloud_params.speed_x       = speed.X;
1255         event->cloud_params.speed_y       = speed.Y;
1256         m_client_event_queue.push(event);
1257 }
1258
1259 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1260 {
1261         bool do_override;
1262         u16 day_night_ratio_u;
1263
1264         *pkt >> do_override >> day_night_ratio_u;
1265
1266         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1267
1268         ClientEvent *event = new ClientEvent();
1269         event->type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1270         event->override_day_night_ratio.do_override = do_override;
1271         event->override_day_night_ratio.ratio_f     = day_night_ratio_f;
1272         m_client_event_queue.push(event);
1273 }
1274
1275 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1276 {
1277         LocalPlayer *player = m_env.getLocalPlayer();
1278         assert(player != NULL);
1279
1280         *pkt >> player->local_animations[0];
1281         *pkt >> player->local_animations[1];
1282         *pkt >> player->local_animations[2];
1283         *pkt >> player->local_animations[3];
1284         *pkt >> player->local_animation_speed;
1285 }
1286
1287 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1288 {
1289         LocalPlayer *player = m_env.getLocalPlayer();
1290         assert(player != NULL);
1291
1292         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1293 }
1294
1295 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1296 {
1297         u8 type;
1298         u16 num_players;
1299         *pkt >> type >> num_players;
1300         PlayerListModifer notice_type = (PlayerListModifer) type;
1301
1302         for (u16 i = 0; i < num_players; i++) {
1303                 std::string name;
1304                 *pkt >> name;
1305                 switch (notice_type) {
1306                 case PLAYER_LIST_INIT:
1307                 case PLAYER_LIST_ADD:
1308                         m_env.addPlayerName(name);
1309                         continue;
1310                 case PLAYER_LIST_REMOVE:
1311                         m_env.removePlayerName(name);
1312                         continue;
1313                 }
1314         }
1315 }
1316
1317 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1318 {
1319         if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1320                         m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1321                 errorstream << "Client: Received SRP S_B login message,"
1322                         << " but wasn't supposed to (chosen_mech="
1323                         << m_chosen_auth_mech << ")." << std::endl;
1324                 return;
1325         }
1326
1327         char *bytes_M = 0;
1328         size_t len_M = 0;
1329         SRPUser *usr = (SRPUser *) m_auth_data;
1330         std::string s;
1331         std::string B;
1332         *pkt >> s >> B;
1333
1334         infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1335
1336         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1337                 (const unsigned char *) B.c_str(), B.size(),
1338                 (unsigned char **) &bytes_M, &len_M);
1339
1340         if ( !bytes_M ) {
1341                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1342                 return;
1343         }
1344
1345         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1346         resp_pkt << std::string(bytes_M, len_M);
1347         Send(&resp_pkt);
1348 }
1349
1350 void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
1351 {
1352         *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;
1353
1354         // Now we have flavours, load mods if it's enabled
1355         // Note: this should be moved after mods receptions from server instead
1356         loadMods();
1357 }
1358
1359 /*
1360  * Mod channels
1361  */
1362
1363 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1364 {
1365         std::string channel_name, sender, channel_msg;
1366         *pkt >> channel_name >> sender >> channel_msg;
1367
1368         verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1369                 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1370                 << channel_msg << std::endl;
1371
1372         if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1373                 verbosestream << "Server sent us messages on unregistered channel "
1374                         << channel_name << ", ignoring." << std::endl;
1375                 return;
1376         }
1377
1378         m_script->on_modchannel_message(channel_name, sender, channel_msg);
1379 }
1380
1381 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1382 {
1383         u8 signal_tmp;
1384         ModChannelSignal signal;
1385         std::string channel;
1386
1387         *pkt >> signal_tmp >> channel;
1388
1389         signal = (ModChannelSignal)signal_tmp;
1390
1391         bool valid_signal = true;
1392         // @TODO: send Signal to Lua API
1393         switch (signal) {
1394                 case MODCHANNEL_SIGNAL_JOIN_OK:
1395                         m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1396                         infostream << "Server ack our mod channel join on channel `" << channel
1397                                 << "`, joining." << std::endl;
1398                         break;
1399                 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1400                         // Unable to join, remove channel
1401                         m_modchannel_mgr->leaveChannel(channel, 0);
1402                         infostream << "Server refused our mod channel join on channel `" << channel
1403                                 << "`" << std::endl;
1404                         break;
1405                 case MODCHANNEL_SIGNAL_LEAVE_OK:
1406 #ifndef NDEBUG
1407                         infostream << "Server ack our mod channel leave on channel " << channel
1408                                 << "`, leaving." << std::endl;
1409 #endif
1410                         break;
1411                 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1412                         infostream << "Server refused our mod channel leave on channel `" << channel
1413                                 << "`" << std::endl;
1414                         break;
1415                 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1416 #ifndef NDEBUG
1417                         // Generally unused, but ensure we don't do an implementation error
1418                         infostream << "Server tells us we sent a message on channel `" << channel
1419                                 << "` but we are not registered. Message was dropped." << std::endl;
1420 #endif
1421                         break;
1422                 case MODCHANNEL_SIGNAL_SET_STATE: {
1423                         u8 state;
1424                         *pkt >> state;
1425
1426                         if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1427                                 infostream << "Received wrong channel state " << state
1428                                                 << ", ignoring." << std::endl;
1429                                 return;
1430                         }
1431
1432                         m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1433                         infostream << "Server sets mod channel `" << channel
1434                                         << "` in read-only mode." << std::endl;
1435                         break;
1436                 }
1437                 default:
1438 #ifndef NDEBUG
1439                         warningstream << "Received unhandled mod channel signal ID "
1440                                 << signal << ", ignoring." << std::endl;
1441 #endif
1442                         valid_signal = false;
1443                         break;
1444         }
1445
1446         // If signal is valid, forward it to client side mods
1447         if (valid_signal)
1448                 m_script->on_modchannel_signal(channel, signal);
1449 }