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