]> git.lizzy.rs Git - minetest.git/blob - src/network/clientpackethandler.cpp
a8ae8a5ef46f8eb9bd8fd002d66423917762201f
[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
468 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
469 {
470         /*
471                 for all objects
472                 {
473                         u16 id
474                         u16 message length
475                         string message
476                 }
477         */
478         std::string datastring(pkt->getString(0), pkt->getSize());
479         std::istringstream is(datastring, std::ios_base::binary);
480
481         try {
482                 while (is.good()) {
483                         u16 id = readU16(is);
484                         if (!is.good())
485                                 break;
486
487                         std::string message = deSerializeString(is);
488
489                         // Pass on to the environment
490                         m_env.processActiveObjectMessage(id, message);
491                 }
492         } catch (SerializationError &e) {
493                 errorstream << "Client::handleCommand_ActiveObjectMessages: "
494                         << "caught SerializationError: " << e.what() << std::endl;
495         }
496 }
497
498 void Client::handleCommand_Movement(NetworkPacket* pkt)
499 {
500         LocalPlayer *player = m_env.getLocalPlayer();
501         assert(player != NULL);
502
503         float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
504
505         *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
506                 >> lf >> lfs >> ls >> g;
507
508         player->movement_acceleration_default   = mad * BS;
509         player->movement_acceleration_air       = maa * BS;
510         player->movement_acceleration_fast      = maf * BS;
511         player->movement_speed_walk             = msw * BS;
512         player->movement_speed_crouch           = mscr * BS;
513         player->movement_speed_fast             = msf * BS;
514         player->movement_speed_climb            = mscl * BS;
515         player->movement_speed_jump             = msj * BS;
516         player->movement_liquid_fluidity        = lf * BS;
517         player->movement_liquid_fluidity_smooth = lfs * BS;
518         player->movement_liquid_sink            = ls * BS;
519         player->movement_gravity                = g * BS;
520 }
521
522 void Client::handleCommand_HP(NetworkPacket* pkt)
523 {
524
525         LocalPlayer *player = m_env.getLocalPlayer();
526         assert(player != NULL);
527
528         u16 oldhp   = player->hp;
529
530         u16 hp;
531         *pkt >> hp;
532
533         player->hp = hp;
534
535         if (modsLoaded())
536                 m_script->on_hp_modification(hp);
537
538         if (hp < oldhp) {
539                 // Add to ClientEvent queue
540                 ClientEvent *event = new ClientEvent();
541                 event->type = CE_PLAYER_DAMAGE;
542                 event->player_damage.amount = oldhp - hp;
543                 m_client_event_queue.push(event);
544         }
545 }
546
547 void Client::handleCommand_Breath(NetworkPacket* pkt)
548 {
549         LocalPlayer *player = m_env.getLocalPlayer();
550         assert(player != NULL);
551
552         u16 breath;
553
554         *pkt >> breath;
555
556         player->setBreath(breath);
557 }
558
559 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
560 {
561         LocalPlayer *player = m_env.getLocalPlayer();
562         assert(player != NULL);
563
564         v3f pos;
565         f32 pitch, yaw;
566
567         *pkt >> pos >> pitch >> yaw;
568
569         player->setPosition(pos);
570
571         infostream << "Client got TOCLIENT_MOVE_PLAYER"
572                         << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
573                         << " pitch=" << pitch
574                         << " yaw=" << yaw
575                         << std::endl;
576
577         /*
578                 Add to ClientEvent queue.
579                 This has to be sent to the main program because otherwise
580                 it would just force the pitch and yaw values to whatever
581                 the camera points to.
582         */
583         ClientEvent *event = new ClientEvent();
584         event->type = CE_PLAYER_FORCE_MOVE;
585         event->player_force_move.pitch = pitch;
586         event->player_force_move.yaw = yaw;
587         m_client_event_queue.push(event);
588 }
589
590 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
591 {
592         bool set_camera_point_target;
593         v3f camera_point_target;
594
595         *pkt >> set_camera_point_target;
596         *pkt >> camera_point_target;
597
598         ClientEvent *event = new ClientEvent();
599         event->type                                = CE_DEATHSCREEN;
600         event->deathscreen.set_camera_point_target = set_camera_point_target;
601         event->deathscreen.camera_point_target_x   = camera_point_target.X;
602         event->deathscreen.camera_point_target_y   = camera_point_target.Y;
603         event->deathscreen.camera_point_target_z   = camera_point_target.Z;
604         m_client_event_queue.push(event);
605 }
606
607 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
608 {
609         u16 num_files;
610
611         *pkt >> num_files;
612
613         infostream << "Client: Received media announcement: packet size: "
614                         << pkt->getSize() << std::endl;
615
616         if (m_media_downloader == NULL ||
617                         m_media_downloader->isStarted()) {
618                 const char *problem = m_media_downloader ?
619                         "we already saw another announcement" :
620                         "all media has been received already";
621                 errorstream << "Client: Received media announcement but "
622                         << problem << "! "
623                         << " files=" << num_files
624                         << " size=" << pkt->getSize() << std::endl;
625                 return;
626         }
627
628         // Mesh update thread must be stopped while
629         // updating content definitions
630         sanity_check(!m_mesh_update_thread.isRunning());
631
632         for (u16 i = 0; i < num_files; i++) {
633                 std::string name, sha1_base64;
634
635                 *pkt >> name >> sha1_base64;
636
637                 std::string sha1_raw = base64_decode(sha1_base64);
638                 m_media_downloader->addFile(name, sha1_raw);
639         }
640
641         try {
642                 std::string str;
643
644                 *pkt >> str;
645
646                 Strfnd sf(str);
647                 while(!sf.at_end()) {
648                         std::string baseurl = trim(sf.next(","));
649                         if (!baseurl.empty())
650                                 m_media_downloader->addRemoteServer(baseurl);
651                 }
652         }
653         catch(SerializationError& e) {
654                 // not supported by server or turned off
655         }
656
657         m_media_downloader->step(this);
658 }
659
660 void Client::handleCommand_Media(NetworkPacket* pkt)
661 {
662         /*
663                 u16 command
664                 u16 total number of file bunches
665                 u16 index of this bunch
666                 u32 number of files in this bunch
667                 for each file {
668                         u16 length of name
669                         string name
670                         u32 length of data
671                         data
672                 }
673         */
674         u16 num_bunches;
675         u16 bunch_i;
676         u32 num_files;
677
678         *pkt >> num_bunches >> bunch_i >> num_files;
679
680         infostream << "Client: Received files: bunch " << bunch_i << "/"
681                         << num_bunches << " files=" << num_files
682                         << " size=" << pkt->getSize() << std::endl;
683
684         if (num_files == 0)
685                 return;
686
687         if (!m_media_downloader || !m_media_downloader->isStarted()) {
688                 const char *problem = m_media_downloader ?
689                         "media has not been requested" :
690                         "all media has been received already";
691                 errorstream << "Client: Received media but "
692                         << problem << "! "
693                         << " bunch " << bunch_i << "/" << num_bunches
694                         << " files=" << num_files
695                         << " size=" << pkt->getSize() << std::endl;
696                 return;
697         }
698
699         // Mesh update thread must be stopped while
700         // updating content definitions
701         sanity_check(!m_mesh_update_thread.isRunning());
702
703         for (u32 i=0; i < num_files; i++) {
704                 std::string name;
705
706                 *pkt >> name;
707
708                 std::string data = pkt->readLongString();
709
710                 m_media_downloader->conventionalTransferDone(
711                                 name, data, this);
712         }
713 }
714
715 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
716 {
717         infostream << "Client: Received node definitions: packet size: "
718                         << pkt->getSize() << std::endl;
719
720         // Mesh update thread must be stopped while
721         // updating content definitions
722         sanity_check(!m_mesh_update_thread.isRunning());
723
724         // Decompress node definitions
725         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
726         std::ostringstream tmp_os;
727         decompressZlib(tmp_is, tmp_os);
728
729         // Deserialize node definitions
730         std::istringstream tmp_is2(tmp_os.str());
731         m_nodedef->deSerialize(tmp_is2);
732         m_nodedef_received = true;
733 }
734
735 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
736 {
737         infostream << "Client: Received item definitions: packet size: "
738                         << pkt->getSize() << std::endl;
739
740         // Mesh update thread must be stopped while
741         // updating content definitions
742         sanity_check(!m_mesh_update_thread.isRunning());
743
744         // Decompress item definitions
745         std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
746         std::ostringstream tmp_os;
747         decompressZlib(tmp_is, tmp_os);
748
749         // Deserialize node definitions
750         std::istringstream tmp_is2(tmp_os.str());
751         m_itemdef->deSerialize(tmp_is2);
752         m_itemdef_received = true;
753 }
754
755 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
756 {
757         /*
758                 [0] u32 server_id
759                 [4] u16 name length
760                 [6] char name[len]
761                 [ 6 + len] f32 gain
762                 [10 + len] u8 type
763                 [11 + len] (f32 * 3) pos
764                 [23 + len] u16 object_id
765                 [25 + len] bool loop
766                 [26 + len] f32 fade
767                 [30 + len] f32 pitch
768         */
769
770         s32 server_id;
771         std::string name;
772
773         float gain;
774         u8 type; // 0=local, 1=positional, 2=object
775         v3f pos;
776         u16 object_id;
777         bool loop;
778         float fade = 0.0f;
779         float pitch = 1.0f;
780
781         *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
782
783         try {
784                 *pkt >> fade;
785                 *pkt >> pitch;
786         } catch (PacketError &e) {};
787
788         // Start playing
789         int client_id = -1;
790         switch(type) {
791                 case 0: // local
792                         client_id = m_sound->playSound(name, loop, gain, fade, pitch);
793                         break;
794                 case 1: // positional
795                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
796                         break;
797                 case 2:
798                 { // object
799                         ClientActiveObject *cao = m_env.getActiveObject(object_id);
800                         if (cao)
801                                 pos = cao->getPosition();
802                         client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
803                         // TODO: Set up sound to move with object
804                         break;
805                 }
806                 default:
807                         break;
808         }
809
810         if (client_id != -1) {
811                 m_sounds_server_to_client[server_id] = client_id;
812                 m_sounds_client_to_server[client_id] = server_id;
813                 if (object_id != 0)
814                         m_sounds_to_objects[client_id] = object_id;
815         }
816 }
817
818 void Client::handleCommand_StopSound(NetworkPacket* pkt)
819 {
820         s32 server_id;
821
822         *pkt >> server_id;
823
824         std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
825         if (i != m_sounds_server_to_client.end()) {
826                 int client_id = i->second;
827                 m_sound->stopSound(client_id);
828         }
829 }
830
831 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
832 {
833         s32 sound_id;
834         float step;
835         float gain;
836
837         *pkt >> sound_id >> step >> gain;
838
839         std::unordered_map<s32, int>::const_iterator i =
840                         m_sounds_server_to_client.find(sound_id);
841
842         if (i != m_sounds_server_to_client.end())
843                 m_sound->fadeSound(i->second, step, gain);
844 }
845
846 void Client::handleCommand_Privileges(NetworkPacket* pkt)
847 {
848         m_privileges.clear();
849         infostream << "Client: Privileges updated: ";
850         u16 num_privileges;
851
852         *pkt >> num_privileges;
853
854         for (u16 i = 0; i < num_privileges; i++) {
855                 std::string priv;
856
857                 *pkt >> priv;
858
859                 m_privileges.insert(priv);
860                 infostream << priv << " ";
861         }
862         infostream << std::endl;
863 }
864
865 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
866 {
867         LocalPlayer *player = m_env.getLocalPlayer();
868         assert(player != NULL);
869
870         // Store formspec in LocalPlayer
871         player->inventory_formspec = pkt->readLongString();
872 }
873
874 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
875 {
876         std::string name;
877         bool keep_inv = true;
878         *pkt >> name >> keep_inv;
879
880         infostream << "Client: Detached inventory update: \"" << name
881                 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
882
883         const auto &inv_it = m_detached_inventories.find(name);
884         if (!keep_inv) {
885                 if (inv_it != m_detached_inventories.end()) {
886                         delete inv_it->second;
887                         m_detached_inventories.erase(inv_it);
888                 }
889                 return;
890         }
891         Inventory *inv = nullptr;
892         if (inv_it == m_detached_inventories.end()) {
893                 inv = new Inventory(m_itemdef);
894                 m_detached_inventories[name] = inv;
895         } else {
896                 inv = inv_it->second;
897         }
898
899         u16 ignore;
900         *pkt >> ignore; // this used to be the length of the following string, ignore it
901
902         std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
903         std::istringstream is(contents, std::ios::binary);
904         inv->deSerialize(is);
905 }
906
907 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
908 {
909         std::string formspec = pkt->readLongString();
910         std::string formname;
911
912         *pkt >> formname;
913
914         ClientEvent *event = new ClientEvent();
915         event->type = CE_SHOW_FORMSPEC;
916         // pointer is required as event is a struct only!
917         // adding a std:string to a struct isn't possible
918         event->show_formspec.formspec = new std::string(formspec);
919         event->show_formspec.formname = new std::string(formname);
920         m_client_event_queue.push(event);
921 }
922
923 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
924 {
925         std::string datastring(pkt->getString(0), pkt->getSize());
926         std::istringstream is(datastring, std::ios_base::binary);
927
928         v3f pos                 = readV3F32(is);
929         v3f vel                 = readV3F32(is);
930         v3f acc                 = readV3F32(is);
931         float expirationtime    = readF32(is);
932         float size              = readF32(is);
933         bool collisiondetection = readU8(is);
934         std::string texture     = deSerializeLongString(is);
935
936         bool vertical          = false;
937         bool collision_removal = false;
938         TileAnimationParams animation;
939         animation.type         = TAT_NONE;
940         u8 glow                = 0;
941         bool object_collision  = false;
942         try {
943                 vertical = readU8(is);
944                 collision_removal = readU8(is);
945                 animation.deSerialize(is, m_proto_ver);
946                 glow = readU8(is);
947                 object_collision = readU8(is);
948         } catch (...) {}
949
950         ClientEvent *event = new ClientEvent();
951         event->type                              = CE_SPAWN_PARTICLE;
952         event->spawn_particle.pos                = new v3f (pos);
953         event->spawn_particle.vel                = new v3f (vel);
954         event->spawn_particle.acc                = new v3f (acc);
955         event->spawn_particle.expirationtime     = expirationtime;
956         event->spawn_particle.size               = size;
957         event->spawn_particle.collisiondetection = collisiondetection;
958         event->spawn_particle.collision_removal  = collision_removal;
959         event->spawn_particle.object_collision   = object_collision;
960         event->spawn_particle.vertical           = vertical;
961         event->spawn_particle.texture            = new std::string(texture);
962         event->spawn_particle.animation          = animation;
963         event->spawn_particle.glow               = glow;
964
965         m_client_event_queue.push(event);
966 }
967
968 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
969 {
970         u16 amount;
971         float spawntime;
972         v3f minpos;
973         v3f maxpos;
974         v3f minvel;
975         v3f maxvel;
976         v3f minacc;
977         v3f maxacc;
978         float minexptime;
979         float maxexptime;
980         float minsize;
981         float maxsize;
982         bool collisiondetection;
983         u32 server_id;
984
985         *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
986                 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
987                 >> maxsize >> collisiondetection;
988
989         std::string texture = pkt->readLongString();
990
991         *pkt >> server_id;
992
993         bool vertical          = false;
994         bool collision_removal = false;
995         u16 attached_id        = 0;
996         TileAnimationParams animation;
997         animation.type         = TAT_NONE;
998         u8 glow                = 0;
999         bool object_collision  = false;
1000         try {
1001                 *pkt >> vertical;
1002                 *pkt >> collision_removal;
1003                 *pkt >> attached_id;
1004
1005                 // This is horrible but required (why are there two ways to deserialize pkts?)
1006                 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1007                 std::istringstream is(datastring, std::ios_base::binary);
1008                 animation.deSerialize(is, m_proto_ver);
1009                 glow = readU8(is);
1010                 object_collision = readU8(is);
1011         } catch (...) {}
1012
1013         auto event = new ClientEvent();
1014         event->type                                   = CE_ADD_PARTICLESPAWNER;
1015         event->add_particlespawner.amount             = amount;
1016         event->add_particlespawner.spawntime          = spawntime;
1017         event->add_particlespawner.minpos             = new v3f (minpos);
1018         event->add_particlespawner.maxpos             = new v3f (maxpos);
1019         event->add_particlespawner.minvel             = new v3f (minvel);
1020         event->add_particlespawner.maxvel             = new v3f (maxvel);
1021         event->add_particlespawner.minacc             = new v3f (minacc);
1022         event->add_particlespawner.maxacc             = new v3f (maxacc);
1023         event->add_particlespawner.minexptime         = minexptime;
1024         event->add_particlespawner.maxexptime         = maxexptime;
1025         event->add_particlespawner.minsize            = minsize;
1026         event->add_particlespawner.maxsize            = maxsize;
1027         event->add_particlespawner.collisiondetection = collisiondetection;
1028         event->add_particlespawner.collision_removal  = collision_removal;
1029         event->add_particlespawner.object_collision   = object_collision;
1030         event->add_particlespawner.attached_id        = attached_id;
1031         event->add_particlespawner.vertical           = vertical;
1032         event->add_particlespawner.texture            = new std::string(texture);
1033         event->add_particlespawner.id                 = server_id;
1034         event->add_particlespawner.animation          = animation;
1035         event->add_particlespawner.glow               = glow;
1036
1037         m_client_event_queue.push(event);
1038 }
1039
1040
1041 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1042 {
1043         u32 server_id;
1044         *pkt >> server_id;
1045
1046         ClientEvent *event = new ClientEvent();
1047         event->type = CE_DELETE_PARTICLESPAWNER;
1048         event->delete_particlespawner.id = server_id;
1049
1050         m_client_event_queue.push(event);
1051 }
1052
1053 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1054 {
1055         std::string datastring(pkt->getString(0), pkt->getSize());
1056         std::istringstream is(datastring, std::ios_base::binary);
1057
1058         u32 server_id;
1059         u8 type;
1060         v2f pos;
1061         std::string name;
1062         v2f scale;
1063         std::string text;
1064         u32 number;
1065         u32 item;
1066         u32 dir;
1067         v2f align;
1068         v2f offset;
1069         v3f world_pos;
1070         v2s32 size;
1071
1072         *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1073                 >> dir >> align >> offset;
1074         try {
1075                 *pkt >> world_pos;
1076         }
1077         catch(SerializationError &e) {};
1078
1079         try {
1080                 *pkt >> size;
1081         } catch(SerializationError &e) {};
1082
1083         ClientEvent *event = new ClientEvent();
1084         event->type             = CE_HUDADD;
1085         event->hudadd.server_id = server_id;
1086         event->hudadd.type      = type;
1087         event->hudadd.pos       = new v2f(pos);
1088         event->hudadd.name      = new std::string(name);
1089         event->hudadd.scale     = new v2f(scale);
1090         event->hudadd.text      = new std::string(text);
1091         event->hudadd.number    = number;
1092         event->hudadd.item      = item;
1093         event->hudadd.dir       = dir;
1094         event->hudadd.align     = new v2f(align);
1095         event->hudadd.offset    = new v2f(offset);
1096         event->hudadd.world_pos = new v3f(world_pos);
1097         event->hudadd.size      = new v2s32(size);
1098         m_client_event_queue.push(event);
1099 }
1100
1101 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1102 {
1103         u32 server_id;
1104
1105         *pkt >> server_id;
1106
1107         auto i = m_hud_server_to_client.find(server_id);
1108         if (i != m_hud_server_to_client.end()) {
1109                 int client_id = i->second;
1110                 m_hud_server_to_client.erase(i);
1111
1112                 ClientEvent *event = new ClientEvent();
1113                 event->type     = CE_HUDRM;
1114                 event->hudrm.id = client_id;
1115                 m_client_event_queue.push(event);
1116         }
1117 }
1118
1119 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1120 {
1121         std::string sdata;
1122         v2f v2fdata;
1123         v3f v3fdata;
1124         u32 intdata = 0;
1125         v2s32 v2s32data;
1126         u32 server_id;
1127         u8 stat;
1128
1129         *pkt >> server_id >> stat;
1130
1131         if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1132                 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1133                 *pkt >> v2fdata;
1134         else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1135                 *pkt >> sdata;
1136         else if (stat == HUD_STAT_WORLD_POS)
1137                 *pkt >> v3fdata;
1138         else if (stat == HUD_STAT_SIZE )
1139                 *pkt >> v2s32data;
1140         else
1141                 *pkt >> intdata;
1142
1143         std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1144         if (i != m_hud_server_to_client.end()) {
1145                 ClientEvent *event = new ClientEvent();
1146                 event->type              = CE_HUDCHANGE;
1147                 event->hudchange.id      = i->second;
1148                 event->hudchange.stat    = (HudElementStat)stat;
1149                 event->hudchange.v2fdata = new v2f(v2fdata);
1150                 event->hudchange.v3fdata = new v3f(v3fdata);
1151                 event->hudchange.sdata   = new std::string(sdata);
1152                 event->hudchange.data    = intdata;
1153                 event->hudchange.v2s32data = new v2s32(v2s32data);
1154                 m_client_event_queue.push(event);
1155         }
1156 }
1157
1158 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1159 {
1160         u32 flags, mask;
1161
1162         *pkt >> flags >> mask;
1163
1164         LocalPlayer *player = m_env.getLocalPlayer();
1165         assert(player != NULL);
1166
1167         bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1168         bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1169
1170         player->hud_flags &= ~mask;
1171         player->hud_flags |= flags;
1172
1173         m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1174         bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1175
1176         // Hide minimap if it has been disabled by the server
1177         if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1178                 // defers a minimap update, therefore only call it if really
1179                 // needed, by checking that minimap was visible before
1180                 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1181
1182         // Switch to surface mode if radar disabled by server
1183         if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1184                 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1185 }
1186
1187 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1188 {
1189         u16 param; std::string value;
1190
1191         *pkt >> param >> value;
1192
1193         LocalPlayer *player = m_env.getLocalPlayer();
1194         assert(player != NULL);
1195
1196         if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1197                 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1198                 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1199                         player->hud_hotbar_itemcount = hotbar_itemcount;
1200         }
1201         else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1202                 // If value not empty verify image exists in texture source
1203                 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1204                         errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1205                                 << value << "')" << std::endl;
1206                         return;
1207                 }
1208                 player->hotbar_image = value;
1209         }
1210         else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1211                 // If value not empty verify image exists in texture source
1212                 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1213                         errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1214                                         << value << "')" << std::endl;
1215                         return;
1216                 }
1217                 player->hotbar_selected_image = value;
1218         }
1219 }
1220
1221 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1222 {
1223         std::string datastring(pkt->getString(0), pkt->getSize());
1224         std::istringstream is(datastring, std::ios_base::binary);
1225
1226         video::SColor *bgcolor           = new video::SColor(readARGB8(is));
1227         std::string *type                = new std::string(deSerializeString(is));
1228         u16 count                        = readU16(is);
1229         std::vector<std::string> *params = new std::vector<std::string>;
1230
1231         for (size_t i = 0; i < count; i++)
1232                 params->push_back(deSerializeString(is));
1233
1234         bool clouds = true;
1235         try {
1236                 clouds = readU8(is);
1237         } catch (...) {}
1238
1239         ClientEvent *event = new ClientEvent();
1240         event->type            = CE_SET_SKY;
1241         event->set_sky.bgcolor = bgcolor;
1242         event->set_sky.type    = type;
1243         event->set_sky.params  = params;
1244         event->set_sky.clouds  = clouds;
1245         m_client_event_queue.push(event);
1246 }
1247
1248 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1249 {
1250         f32 density;
1251         video::SColor color_bright;
1252         video::SColor color_ambient;
1253         f32 height;
1254         f32 thickness;
1255         v2f speed;
1256
1257         *pkt >> density >> color_bright >> color_ambient
1258                         >> height >> thickness >> speed;
1259
1260         ClientEvent *event = new ClientEvent();
1261         event->type                       = CE_CLOUD_PARAMS;
1262         event->cloud_params.density       = density;
1263         // use the underlying u32 representation, because we can't
1264         // use struct members with constructors here, and this way
1265         // we avoid using new() and delete() for no good reason
1266         event->cloud_params.color_bright  = color_bright.color;
1267         event->cloud_params.color_ambient = color_ambient.color;
1268         event->cloud_params.height        = height;
1269         event->cloud_params.thickness     = thickness;
1270         // same here: deconstruct to skip constructor
1271         event->cloud_params.speed_x       = speed.X;
1272         event->cloud_params.speed_y       = speed.Y;
1273         m_client_event_queue.push(event);
1274 }
1275
1276 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1277 {
1278         bool do_override;
1279         u16 day_night_ratio_u;
1280
1281         *pkt >> do_override >> day_night_ratio_u;
1282
1283         float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1284
1285         ClientEvent *event = new ClientEvent();
1286         event->type                                 = CE_OVERRIDE_DAY_NIGHT_RATIO;
1287         event->override_day_night_ratio.do_override = do_override;
1288         event->override_day_night_ratio.ratio_f     = day_night_ratio_f;
1289         m_client_event_queue.push(event);
1290 }
1291
1292 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1293 {
1294         LocalPlayer *player = m_env.getLocalPlayer();
1295         assert(player != NULL);
1296
1297         *pkt >> player->local_animations[0];
1298         *pkt >> player->local_animations[1];
1299         *pkt >> player->local_animations[2];
1300         *pkt >> player->local_animations[3];
1301         *pkt >> player->local_animation_speed;
1302 }
1303
1304 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1305 {
1306         LocalPlayer *player = m_env.getLocalPlayer();
1307         assert(player != NULL);
1308
1309         *pkt >> player->eye_offset_first >> player->eye_offset_third;
1310 }
1311
1312 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1313 {
1314         u8 type;
1315         u16 num_players;
1316         *pkt >> type >> num_players;
1317         PlayerListModifer notice_type = (PlayerListModifer) type;
1318
1319         for (u16 i = 0; i < num_players; i++) {
1320                 std::string name;
1321                 *pkt >> name;
1322                 switch (notice_type) {
1323                 case PLAYER_LIST_INIT:
1324                 case PLAYER_LIST_ADD:
1325                         m_env.addPlayerName(name);
1326                         continue;
1327                 case PLAYER_LIST_REMOVE:
1328                         m_env.removePlayerName(name);
1329                         continue;
1330                 }
1331         }
1332 }
1333
1334 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1335 {
1336         if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1337                         m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1338                 errorstream << "Client: Received SRP S_B login message,"
1339                         << " but wasn't supposed to (chosen_mech="
1340                         << m_chosen_auth_mech << ")." << std::endl;
1341                 return;
1342         }
1343
1344         char *bytes_M = 0;
1345         size_t len_M = 0;
1346         SRPUser *usr = (SRPUser *) m_auth_data;
1347         std::string s;
1348         std::string B;
1349         *pkt >> s >> B;
1350
1351         infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1352
1353         srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1354                 (const unsigned char *) B.c_str(), B.size(),
1355                 (unsigned char **) &bytes_M, &len_M);
1356
1357         if ( !bytes_M ) {
1358                 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1359                 return;
1360         }
1361
1362         NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1363         resp_pkt << std::string(bytes_M, len_M);
1364         Send(&resp_pkt);
1365 }
1366
1367 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1368 {
1369         LocalPlayer *player = m_env.getLocalPlayer();
1370         assert(player != NULL);
1371
1372         // Store formspec in LocalPlayer
1373         *pkt >> player->formspec_prepend;
1374 }
1375
1376 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1377 {
1378         *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1379
1380         // Restrictions were received -> load mods if it's enabled
1381         // Note: this should be moved after mods receptions from server instead
1382         loadMods();
1383 }
1384
1385 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1386 {
1387         v3f added_vel;
1388
1389         *pkt >> added_vel;
1390
1391         LocalPlayer *player = m_env.getLocalPlayer();
1392         assert(player != NULL);
1393         player->addVelocity(added_vel);
1394 }
1395
1396 /*
1397  * Mod channels
1398  */
1399
1400 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1401 {
1402         std::string channel_name, sender, channel_msg;
1403         *pkt >> channel_name >> sender >> channel_msg;
1404
1405         verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1406                 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1407                 << channel_msg << std::endl;
1408
1409         if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1410                 verbosestream << "Server sent us messages on unregistered channel "
1411                         << channel_name << ", ignoring." << std::endl;
1412                 return;
1413         }
1414
1415         m_script->on_modchannel_message(channel_name, sender, channel_msg);
1416 }
1417
1418 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1419 {
1420         u8 signal_tmp;
1421         ModChannelSignal signal;
1422         std::string channel;
1423
1424         *pkt >> signal_tmp >> channel;
1425
1426         signal = (ModChannelSignal)signal_tmp;
1427
1428         bool valid_signal = true;
1429         // @TODO: send Signal to Lua API
1430         switch (signal) {
1431                 case MODCHANNEL_SIGNAL_JOIN_OK:
1432                         m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1433                         infostream << "Server ack our mod channel join on channel `" << channel
1434                                 << "`, joining." << std::endl;
1435                         break;
1436                 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1437                         // Unable to join, remove channel
1438                         m_modchannel_mgr->leaveChannel(channel, 0);
1439                         infostream << "Server refused our mod channel join on channel `" << channel
1440                                 << "`" << std::endl;
1441                         break;
1442                 case MODCHANNEL_SIGNAL_LEAVE_OK:
1443 #ifndef NDEBUG
1444                         infostream << "Server ack our mod channel leave on channel " << channel
1445                                 << "`, leaving." << std::endl;
1446 #endif
1447                         break;
1448                 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1449                         infostream << "Server refused our mod channel leave on channel `" << channel
1450                                 << "`" << std::endl;
1451                         break;
1452                 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1453 #ifndef NDEBUG
1454                         // Generally unused, but ensure we don't do an implementation error
1455                         infostream << "Server tells us we sent a message on channel `" << channel
1456                                 << "` but we are not registered. Message was dropped." << std::endl;
1457 #endif
1458                         break;
1459                 case MODCHANNEL_SIGNAL_SET_STATE: {
1460                         u8 state;
1461                         *pkt >> state;
1462
1463                         if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1464                                 infostream << "Received wrong channel state " << state
1465                                                 << ", ignoring." << std::endl;
1466                                 return;
1467                         }
1468
1469                         m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1470                         infostream << "Server sets mod channel `" << channel
1471                                         << "` in read-only mode." << std::endl;
1472                         break;
1473                 }
1474                 default:
1475 #ifndef NDEBUG
1476                         warningstream << "Received unhandled mod channel signal ID "
1477                                 << signal << ", ignoring." << std::endl;
1478 #endif
1479                         valid_signal = false;
1480                         break;
1481         }
1482
1483         // If signal is valid, forward it to client side mods
1484         if (valid_signal)
1485                 m_script->on_modchannel_signal(channel, signal);
1486 }