Add setting to disable confirmation on new player registration (#8102)
[minetest.git] / src / network / clientpackethandler.cpp
blob909d336ae8cac32b08be5988278663171f16e01e
1 /*
2 Minetest
3 Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
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.
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.
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.
20 #include "client/client.h"
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"
44 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
46 infostream << "Got deprecated command "
47 << toClientCommandTable[pkt->getCommand()].name << " from peer "
48 << pkt->getPeerId() << "!" << std::endl;
51 void Client::handleCommand_Hello(NetworkPacket* pkt)
53 if (pkt->getSize() < 1)
54 return;
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;
64 // Chose an auth method we support
65 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
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;
74 if (!ser_ver_supported(serialization_ver)) {
75 infostream << "Client: TOCLIENT_HELLO: Server sent "
76 << "unsupported ser_fmt_ver"<< std::endl;
77 return;
80 m_server_ser_ver = serialization_ver;
81 m_proto_ver = proto_ver;
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
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;
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 && g_settings->getBool("enable_register_confirmation")) {
103 promptConfirmRegistration(chosen_auth_mechanism);
104 } else {
105 startAuth(chosen_auth_mechanism);
107 } else {
108 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
109 m_access_denied = true;
110 m_access_denied_reason = "Unknown";
111 m_con->Disconnect();
116 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
118 deleteAuthData();
120 v3f playerpos;
121 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
122 >> m_sudo_auth_methods;
124 playerpos -= v3f(0, BS / 2, 0);
126 // Set player position
127 LocalPlayer *player = m_env.getLocalPlayer();
128 assert(player != NULL);
129 player->setPosition(playerpos);
131 infostream << "Client: received map seed: " << m_map_seed << std::endl;
132 infostream << "Client: received recommended send interval "
133 << m_recommended_send_interval<<std::endl;
135 // Reply to server
136 std::string lang = gettext("LANG_CODE");
137 if (lang == "LANG_CODE")
138 lang = "";
140 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
141 resp_pkt << lang;
142 Send(&resp_pkt);
144 m_state = LC_Init;
146 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
148 deleteAuthData();
150 m_password = m_new_password;
152 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
154 // send packet to actually set the password
155 startAuth(AUTH_MECHANISM_FIRST_SRP);
157 // reset again
158 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
160 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
162 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
163 L"Password change denied. Password NOT changed.");
164 pushToChatQueue(chatMessage);
165 // reset everything and be sad
166 deleteAuthData();
169 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
171 // The server didn't like our password. Note, this needs
172 // to be processed even if the serialisation format has
173 // not been agreed yet, the same as TOCLIENT_INIT.
174 m_access_denied = true;
175 m_access_denied_reason = "Unknown";
177 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
178 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
179 // in some places of the server code
180 if (pkt->getSize() >= 2) {
181 std::wstring wide_reason;
182 *pkt >> wide_reason;
183 m_access_denied_reason = wide_to_utf8(wide_reason);
185 return;
188 if (pkt->getSize() < 1)
189 return;
191 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
192 *pkt >> denyCode;
193 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
194 denyCode == SERVER_ACCESSDENIED_CRASH) {
195 *pkt >> m_access_denied_reason;
196 if (m_access_denied_reason.empty()) {
197 m_access_denied_reason = accessDeniedStrings[denyCode];
199 u8 reconnect;
200 *pkt >> reconnect;
201 m_access_denied_reconnect = reconnect & 1;
202 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
203 *pkt >> m_access_denied_reason;
204 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
205 m_access_denied_reason = accessDeniedStrings[denyCode];
206 } else {
207 // Allow us to add new error messages to the
208 // protocol without raising the protocol version, if we want to.
209 // Until then (which may be never), this is outside
210 // of the defined protocol.
211 *pkt >> m_access_denied_reason;
212 if (m_access_denied_reason.empty()) {
213 m_access_denied_reason = "Unknown";
218 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
220 if (pkt->getSize() < 6)
221 return;
223 v3s16 p;
224 *pkt >> p;
225 removeNode(p);
228 void Client::handleCommand_AddNode(NetworkPacket* pkt)
230 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
231 return;
233 v3s16 p;
234 *pkt >> p;
236 MapNode n;
237 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
239 bool remove_metadata = true;
240 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
241 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
242 remove_metadata = false;
245 addNode(p, n, remove_metadata);
248 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
250 if (pkt->getSize() < 1)
251 return;
253 std::istringstream is(pkt->readLongString(), std::ios::binary);
254 std::stringstream sstr;
255 decompressZlib(is, sstr);
257 NodeMetadataList meta_updates_list(false);
258 meta_updates_list.deSerialize(sstr, m_itemdef, true);
260 Map &map = m_env.getMap();
261 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
262 i != meta_updates_list.end(); ++i) {
263 v3s16 pos = i->first;
265 if (map.isValidPosition(pos) &&
266 map.setNodeMetadata(pos, i->second))
267 continue; // Prevent from deleting metadata
269 // Meta couldn't be set, unused metadata
270 delete i->second;
274 void Client::handleCommand_BlockData(NetworkPacket* pkt)
276 // Ignore too small packet
277 if (pkt->getSize() < 6)
278 return;
280 v3s16 p;
281 *pkt >> p;
283 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
284 std::istringstream istr(datastring, std::ios_base::binary);
286 MapSector *sector;
287 MapBlock *block;
289 v2s16 p2d(p.X, p.Z);
290 sector = m_env.getMap().emergeSector(p2d);
292 assert(sector->getPos() == p2d);
294 block = sector->getBlockNoCreateNoEx(p.Y);
295 if (block) {
297 Update an existing block
299 block->deSerialize(istr, m_server_ser_ver, false);
300 block->deSerializeNetworkSpecific(istr);
302 else {
304 Create a new block
306 block = new MapBlock(&m_env.getMap(), p, this);
307 block->deSerialize(istr, m_server_ser_ver, false);
308 block->deSerializeNetworkSpecific(istr);
309 sector->insertBlock(block);
312 if (m_localdb) {
313 ServerMap::saveBlock(block, m_localdb);
317 Add it to mesh update queue and set it to be acknowledged after update.
319 addUpdateMeshTaskWithEdge(p, true);
322 void Client::handleCommand_Inventory(NetworkPacket* pkt)
324 if (pkt->getSize() < 1)
325 return;
327 std::string datastring(pkt->getString(0), pkt->getSize());
328 std::istringstream is(datastring, std::ios_base::binary);
330 LocalPlayer *player = m_env.getLocalPlayer();
331 assert(player != NULL);
333 player->inventory.deSerialize(is);
335 m_inventory_updated = true;
337 delete m_inventory_from_server;
338 m_inventory_from_server = new Inventory(player->inventory);
339 m_inventory_from_server_age = 0.0;
342 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
344 if (pkt->getSize() < 2)
345 return;
347 u16 time_of_day;
349 *pkt >> time_of_day;
351 time_of_day = time_of_day % 24000;
352 float time_speed = 0;
354 if (pkt->getSize() >= 2 + 4) {
355 *pkt >> time_speed;
357 else {
358 // Old message; try to approximate speed of time by ourselves
359 float time_of_day_f = (float)time_of_day / 24000.0f;
360 float tod_diff_f = 0;
362 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
363 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
364 else
365 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
367 m_last_time_of_day_f = time_of_day_f;
368 float time_diff = m_time_of_day_update_timer;
369 m_time_of_day_update_timer = 0;
371 if (m_time_of_day_set) {
372 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
373 infostream << "Client: Measured time_of_day speed (old format): "
374 << time_speed << " tod_diff_f=" << tod_diff_f
375 << " time_diff=" << time_diff << std::endl;
379 // Update environment
380 m_env.setTimeOfDay(time_of_day);
381 m_env.setTimeOfDaySpeed(time_speed);
382 m_time_of_day_set = true;
384 u32 dr = m_env.getDayNightRatio();
385 infostream << "Client: time_of_day=" << time_of_day
386 << " time_speed=" << time_speed
387 << " dr=" << dr << std::endl;
390 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
393 u8 version
394 u8 message_type
395 u16 sendername length
396 wstring sendername
397 u16 length
398 wstring message
401 ChatMessage *chatMessage = new ChatMessage();
402 u8 version, message_type;
403 *pkt >> version >> message_type;
405 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
406 delete chatMessage;
407 return;
410 *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
412 chatMessage->type = (ChatMessageType) message_type;
414 // @TODO send this to CSM using ChatMessage object
415 if (moddingEnabled() && m_script->on_receiving_message(
416 wide_to_utf8(chatMessage->message))) {
417 // Message was consumed by CSM and should not be handled by client
418 delete chatMessage;
419 } else {
420 pushToChatQueue(chatMessage);
424 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
427 u16 count of removed objects
428 for all removed objects {
429 u16 id
431 u16 count of added objects
432 for all added objects {
433 u16 id
434 u8 type
435 u32 initialization data length
436 string initialization data
440 try {
441 u8 type;
442 u16 removed_count, added_count, id;
444 // Read removed objects
445 *pkt >> removed_count;
447 for (u16 i = 0; i < removed_count; i++) {
448 *pkt >> id;
449 m_env.removeActiveObject(id);
452 // Read added objects
453 *pkt >> added_count;
455 for (u16 i = 0; i < added_count; i++) {
456 *pkt >> id >> type;
457 m_env.addActiveObject(id, type, pkt->readLongString());
459 } catch (PacketError &e) {
460 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
461 << ". The packet is unreliable, ignoring" << std::endl;
465 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
468 for all objects
470 u16 id
471 u16 message length
472 string message
475 std::string datastring(pkt->getString(0), pkt->getSize());
476 std::istringstream is(datastring, std::ios_base::binary);
478 try {
479 while (is.good()) {
480 u16 id = readU16(is);
481 if (!is.good())
482 break;
484 std::string message = deSerializeString(is);
486 // Pass on to the environment
487 m_env.processActiveObjectMessage(id, message);
489 } catch (SerializationError &e) {
490 errorstream << "Client::handleCommand_ActiveObjectMessages: "
491 << "caught SerializationError: " << e.what() << std::endl;
495 void Client::handleCommand_Movement(NetworkPacket* pkt)
497 LocalPlayer *player = m_env.getLocalPlayer();
498 assert(player != NULL);
500 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
502 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
503 >> lf >> lfs >> ls >> g;
505 player->movement_acceleration_default = mad * BS;
506 player->movement_acceleration_air = maa * BS;
507 player->movement_acceleration_fast = maf * BS;
508 player->movement_speed_walk = msw * BS;
509 player->movement_speed_crouch = mscr * BS;
510 player->movement_speed_fast = msf * BS;
511 player->movement_speed_climb = mscl * BS;
512 player->movement_speed_jump = msj * BS;
513 player->movement_liquid_fluidity = lf * BS;
514 player->movement_liquid_fluidity_smooth = lfs * BS;
515 player->movement_liquid_sink = ls * BS;
516 player->movement_gravity = g * BS;
519 void Client::handleCommand_HP(NetworkPacket* pkt)
522 LocalPlayer *player = m_env.getLocalPlayer();
523 assert(player != NULL);
525 u16 oldhp = player->hp;
527 u16 hp;
528 *pkt >> hp;
530 player->hp = hp;
532 if (moddingEnabled()) {
533 m_script->on_hp_modification(hp);
536 if (hp < oldhp) {
537 // Add to ClientEvent queue
538 ClientEvent *event = new ClientEvent();
539 event->type = CE_PLAYER_DAMAGE;
540 event->player_damage.amount = oldhp - hp;
541 m_client_event_queue.push(event);
545 void Client::handleCommand_Breath(NetworkPacket* pkt)
547 LocalPlayer *player = m_env.getLocalPlayer();
548 assert(player != NULL);
550 u16 breath;
552 *pkt >> breath;
554 player->setBreath(breath);
557 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
559 LocalPlayer *player = m_env.getLocalPlayer();
560 assert(player != NULL);
562 v3f pos;
563 f32 pitch, yaw;
565 *pkt >> pos >> pitch >> yaw;
567 player->setPosition(pos);
569 infostream << "Client got TOCLIENT_MOVE_PLAYER"
570 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
571 << " pitch=" << pitch
572 << " yaw=" << yaw
573 << std::endl;
576 Add to ClientEvent queue.
577 This has to be sent to the main program because otherwise
578 it would just force the pitch and yaw values to whatever
579 the camera points to.
581 ClientEvent *event = new ClientEvent();
582 event->type = CE_PLAYER_FORCE_MOVE;
583 event->player_force_move.pitch = pitch;
584 event->player_force_move.yaw = yaw;
585 m_client_event_queue.push(event);
588 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
590 bool set_camera_point_target;
591 v3f camera_point_target;
593 *pkt >> set_camera_point_target;
594 *pkt >> camera_point_target;
596 ClientEvent *event = new ClientEvent();
597 event->type = CE_DEATHSCREEN;
598 event->deathscreen.set_camera_point_target = set_camera_point_target;
599 event->deathscreen.camera_point_target_x = camera_point_target.X;
600 event->deathscreen.camera_point_target_y = camera_point_target.Y;
601 event->deathscreen.camera_point_target_z = camera_point_target.Z;
602 m_client_event_queue.push(event);
605 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
607 u16 num_files;
609 *pkt >> num_files;
611 infostream << "Client: Received media announcement: packet size: "
612 << pkt->getSize() << std::endl;
614 if (m_media_downloader == NULL ||
615 m_media_downloader->isStarted()) {
616 const char *problem = m_media_downloader ?
617 "we already saw another announcement" :
618 "all media has been received already";
619 errorstream << "Client: Received media announcement but "
620 << problem << "! "
621 << " files=" << num_files
622 << " size=" << pkt->getSize() << std::endl;
623 return;
626 // Mesh update thread must be stopped while
627 // updating content definitions
628 sanity_check(!m_mesh_update_thread.isRunning());
630 for (u16 i = 0; i < num_files; i++) {
631 std::string name, sha1_base64;
633 *pkt >> name >> sha1_base64;
635 std::string sha1_raw = base64_decode(sha1_base64);
636 m_media_downloader->addFile(name, sha1_raw);
639 try {
640 std::string str;
642 *pkt >> str;
644 Strfnd sf(str);
645 while(!sf.at_end()) {
646 std::string baseurl = trim(sf.next(","));
647 if (!baseurl.empty())
648 m_media_downloader->addRemoteServer(baseurl);
651 catch(SerializationError& e) {
652 // not supported by server or turned off
655 m_media_downloader->step(this);
658 void Client::handleCommand_Media(NetworkPacket* pkt)
661 u16 command
662 u16 total number of file bunches
663 u16 index of this bunch
664 u32 number of files in this bunch
665 for each file {
666 u16 length of name
667 string name
668 u32 length of data
669 data
672 u16 num_bunches;
673 u16 bunch_i;
674 u32 num_files;
676 *pkt >> num_bunches >> bunch_i >> num_files;
678 infostream << "Client: Received files: bunch " << bunch_i << "/"
679 << num_bunches << " files=" << num_files
680 << " size=" << pkt->getSize() << std::endl;
682 if (num_files == 0)
683 return;
685 if (!m_media_downloader || !m_media_downloader->isStarted()) {
686 const char *problem = m_media_downloader ?
687 "media has not been requested" :
688 "all media has been received already";
689 errorstream << "Client: Received media but "
690 << problem << "! "
691 << " bunch " << bunch_i << "/" << num_bunches
692 << " files=" << num_files
693 << " size=" << pkt->getSize() << std::endl;
694 return;
697 // Mesh update thread must be stopped while
698 // updating content definitions
699 sanity_check(!m_mesh_update_thread.isRunning());
701 for (u32 i=0; i < num_files; i++) {
702 std::string name;
704 *pkt >> name;
706 std::string data = pkt->readLongString();
708 m_media_downloader->conventionalTransferDone(
709 name, data, this);
713 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
715 infostream << "Client: Received node definitions: packet size: "
716 << pkt->getSize() << std::endl;
718 // Mesh update thread must be stopped while
719 // updating content definitions
720 sanity_check(!m_mesh_update_thread.isRunning());
722 // Decompress node definitions
723 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
724 std::ostringstream tmp_os;
725 decompressZlib(tmp_is, tmp_os);
727 // Deserialize node definitions
728 std::istringstream tmp_is2(tmp_os.str());
729 m_nodedef->deSerialize(tmp_is2);
730 m_nodedef_received = true;
733 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
735 infostream << "Client: Received item definitions: packet size: "
736 << pkt->getSize() << std::endl;
738 // Mesh update thread must be stopped while
739 // updating content definitions
740 sanity_check(!m_mesh_update_thread.isRunning());
742 // Decompress item definitions
743 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
744 std::ostringstream tmp_os;
745 decompressZlib(tmp_is, tmp_os);
747 // Deserialize node definitions
748 std::istringstream tmp_is2(tmp_os.str());
749 m_itemdef->deSerialize(tmp_is2);
750 m_itemdef_received = true;
753 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
756 [0] u32 server_id
757 [4] u16 name length
758 [6] char name[len]
759 [ 6 + len] f32 gain
760 [10 + len] u8 type
761 [11 + len] (f32 * 3) pos
762 [23 + len] u16 object_id
763 [25 + len] bool loop
764 [26 + len] f32 fade
765 [30 + len] f32 pitch
768 s32 server_id;
769 std::string name;
771 float gain;
772 u8 type; // 0=local, 1=positional, 2=object
773 v3f pos;
774 u16 object_id;
775 bool loop;
776 float fade = 0.0f;
777 float pitch = 1.0f;
779 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
781 try {
782 *pkt >> fade;
783 *pkt >> pitch;
784 } catch (PacketError &e) {};
786 // Start playing
787 int client_id = -1;
788 switch(type) {
789 case 0: // local
790 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
791 break;
792 case 1: // positional
793 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
794 break;
795 case 2:
796 { // object
797 ClientActiveObject *cao = m_env.getActiveObject(object_id);
798 if (cao)
799 pos = cao->getPosition();
800 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
801 // TODO: Set up sound to move with object
802 break;
804 default:
805 break;
808 if (client_id != -1) {
809 m_sounds_server_to_client[server_id] = client_id;
810 m_sounds_client_to_server[client_id] = server_id;
811 if (object_id != 0)
812 m_sounds_to_objects[client_id] = object_id;
816 void Client::handleCommand_StopSound(NetworkPacket* pkt)
818 s32 server_id;
820 *pkt >> server_id;
822 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
823 if (i != m_sounds_server_to_client.end()) {
824 int client_id = i->second;
825 m_sound->stopSound(client_id);
829 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
831 s32 sound_id;
832 float step;
833 float gain;
835 *pkt >> sound_id >> step >> gain;
837 std::unordered_map<s32, int>::const_iterator i =
838 m_sounds_server_to_client.find(sound_id);
840 if (i != m_sounds_server_to_client.end())
841 m_sound->fadeSound(i->second, step, gain);
844 void Client::handleCommand_Privileges(NetworkPacket* pkt)
846 m_privileges.clear();
847 infostream << "Client: Privileges updated: ";
848 u16 num_privileges;
850 *pkt >> num_privileges;
852 for (u16 i = 0; i < num_privileges; i++) {
853 std::string priv;
855 *pkt >> priv;
857 m_privileges.insert(priv);
858 infostream << priv << " ";
860 infostream << std::endl;
863 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
865 LocalPlayer *player = m_env.getLocalPlayer();
866 assert(player != NULL);
868 // Store formspec in LocalPlayer
869 player->inventory_formspec = pkt->readLongString();
872 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
874 std::string name;
875 bool keep_inv = true;
876 *pkt >> name >> keep_inv;
878 infostream << "Client: Detached inventory update: \"" << name
879 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
881 const auto &inv_it = m_detached_inventories.find(name);
882 if (!keep_inv) {
883 if (inv_it != m_detached_inventories.end()) {
884 delete inv_it->second;
885 m_detached_inventories.erase(inv_it);
887 return;
889 Inventory *inv = nullptr;
890 if (inv_it == m_detached_inventories.end()) {
891 inv = new Inventory(m_itemdef);
892 m_detached_inventories[name] = inv;
893 } else {
894 inv = inv_it->second;
897 std::string contents;
898 *pkt >> contents;
899 std::istringstream is(contents, std::ios::binary);
900 inv->deSerialize(is);
903 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
905 std::string formspec = pkt->readLongString();
906 std::string formname;
908 *pkt >> formname;
910 ClientEvent *event = new ClientEvent();
911 event->type = CE_SHOW_FORMSPEC;
912 // pointer is required as event is a struct only!
913 // adding a std:string to a struct isn't possible
914 event->show_formspec.formspec = new std::string(formspec);
915 event->show_formspec.formname = new std::string(formname);
916 m_client_event_queue.push(event);
919 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
921 std::string datastring(pkt->getString(0), pkt->getSize());
922 std::istringstream is(datastring, std::ios_base::binary);
924 v3f pos = readV3F32(is);
925 v3f vel = readV3F32(is);
926 v3f acc = readV3F32(is);
927 float expirationtime = readF32(is);
928 float size = readF32(is);
929 bool collisiondetection = readU8(is);
930 std::string texture = deSerializeLongString(is);
932 bool vertical = false;
933 bool collision_removal = false;
934 TileAnimationParams animation;
935 animation.type = TAT_NONE;
936 u8 glow = 0;
937 bool object_collision = false;
938 try {
939 vertical = readU8(is);
940 collision_removal = readU8(is);
941 animation.deSerialize(is, m_proto_ver);
942 glow = readU8(is);
943 object_collision = readU8(is);
944 } catch (...) {}
946 ClientEvent *event = new ClientEvent();
947 event->type = CE_SPAWN_PARTICLE;
948 event->spawn_particle.pos = new v3f (pos);
949 event->spawn_particle.vel = new v3f (vel);
950 event->spawn_particle.acc = new v3f (acc);
951 event->spawn_particle.expirationtime = expirationtime;
952 event->spawn_particle.size = size;
953 event->spawn_particle.collisiondetection = collisiondetection;
954 event->spawn_particle.collision_removal = collision_removal;
955 event->spawn_particle.object_collision = object_collision;
956 event->spawn_particle.vertical = vertical;
957 event->spawn_particle.texture = new std::string(texture);
958 event->spawn_particle.animation = animation;
959 event->spawn_particle.glow = glow;
961 m_client_event_queue.push(event);
964 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
966 u16 amount;
967 float spawntime;
968 v3f minpos;
969 v3f maxpos;
970 v3f minvel;
971 v3f maxvel;
972 v3f minacc;
973 v3f maxacc;
974 float minexptime;
975 float maxexptime;
976 float minsize;
977 float maxsize;
978 bool collisiondetection;
979 u32 server_id;
981 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
982 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
983 >> maxsize >> collisiondetection;
985 std::string texture = pkt->readLongString();
987 *pkt >> server_id;
989 bool vertical = false;
990 bool collision_removal = false;
991 u16 attached_id = 0;
992 TileAnimationParams animation;
993 animation.type = TAT_NONE;
994 u8 glow = 0;
995 bool object_collision = false;
996 try {
997 *pkt >> vertical;
998 *pkt >> collision_removal;
999 *pkt >> attached_id;
1001 // This is horrible but required (why are there two ways to deserialize pkts?)
1002 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1003 std::istringstream is(datastring, std::ios_base::binary);
1004 animation.deSerialize(is, m_proto_ver);
1005 glow = readU8(is);
1006 object_collision = readU8(is);
1007 } catch (...) {}
1009 u32 client_id = m_particle_manager.getSpawnerId();
1010 m_particles_server_to_client[server_id] = client_id;
1012 ClientEvent *event = new ClientEvent();
1013 event->type = CE_ADD_PARTICLESPAWNER;
1014 event->add_particlespawner.amount = amount;
1015 event->add_particlespawner.spawntime = spawntime;
1016 event->add_particlespawner.minpos = new v3f (minpos);
1017 event->add_particlespawner.maxpos = new v3f (maxpos);
1018 event->add_particlespawner.minvel = new v3f (minvel);
1019 event->add_particlespawner.maxvel = new v3f (maxvel);
1020 event->add_particlespawner.minacc = new v3f (minacc);
1021 event->add_particlespawner.maxacc = new v3f (maxacc);
1022 event->add_particlespawner.minexptime = minexptime;
1023 event->add_particlespawner.maxexptime = maxexptime;
1024 event->add_particlespawner.minsize = minsize;
1025 event->add_particlespawner.maxsize = maxsize;
1026 event->add_particlespawner.collisiondetection = collisiondetection;
1027 event->add_particlespawner.collision_removal = collision_removal;
1028 event->add_particlespawner.object_collision = object_collision;
1029 event->add_particlespawner.attached_id = attached_id;
1030 event->add_particlespawner.vertical = vertical;
1031 event->add_particlespawner.texture = new std::string(texture);
1032 event->add_particlespawner.id = client_id;
1033 event->add_particlespawner.animation = animation;
1034 event->add_particlespawner.glow = glow;
1036 m_client_event_queue.push(event);
1040 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1042 u32 server_id;
1043 *pkt >> server_id;
1045 u32 client_id;
1046 auto i = m_particles_server_to_client.find(server_id);
1047 if (i != m_particles_server_to_client.end())
1048 client_id = i->second;
1049 else
1050 return;
1052 ClientEvent *event = new ClientEvent();
1053 event->type = CE_DELETE_PARTICLESPAWNER;
1054 event->delete_particlespawner.id = client_id;
1056 m_client_event_queue.push(event);
1059 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1061 std::string datastring(pkt->getString(0), pkt->getSize());
1062 std::istringstream is(datastring, std::ios_base::binary);
1064 u32 server_id;
1065 u8 type;
1066 v2f pos;
1067 std::string name;
1068 v2f scale;
1069 std::string text;
1070 u32 number;
1071 u32 item;
1072 u32 dir;
1073 v2f align;
1074 v2f offset;
1075 v3f world_pos;
1076 v2s32 size;
1078 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1079 >> dir >> align >> offset;
1080 try {
1081 *pkt >> world_pos;
1083 catch(SerializationError &e) {};
1085 try {
1086 *pkt >> size;
1087 } catch(SerializationError &e) {};
1089 ClientEvent *event = new ClientEvent();
1090 event->type = CE_HUDADD;
1091 event->hudadd.server_id = server_id;
1092 event->hudadd.type = type;
1093 event->hudadd.pos = new v2f(pos);
1094 event->hudadd.name = new std::string(name);
1095 event->hudadd.scale = new v2f(scale);
1096 event->hudadd.text = new std::string(text);
1097 event->hudadd.number = number;
1098 event->hudadd.item = item;
1099 event->hudadd.dir = dir;
1100 event->hudadd.align = new v2f(align);
1101 event->hudadd.offset = new v2f(offset);
1102 event->hudadd.world_pos = new v3f(world_pos);
1103 event->hudadd.size = new v2s32(size);
1104 m_client_event_queue.push(event);
1107 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1109 u32 server_id;
1111 *pkt >> server_id;
1113 auto i = m_hud_server_to_client.find(server_id);
1114 if (i != m_hud_server_to_client.end()) {
1115 int client_id = i->second;
1116 m_hud_server_to_client.erase(i);
1118 ClientEvent *event = new ClientEvent();
1119 event->type = CE_HUDRM;
1120 event->hudrm.id = client_id;
1121 m_client_event_queue.push(event);
1125 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1127 std::string sdata;
1128 v2f v2fdata;
1129 v3f v3fdata;
1130 u32 intdata = 0;
1131 v2s32 v2s32data;
1132 u32 server_id;
1133 u8 stat;
1135 *pkt >> server_id >> stat;
1137 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1138 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1139 *pkt >> v2fdata;
1140 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1141 *pkt >> sdata;
1142 else if (stat == HUD_STAT_WORLD_POS)
1143 *pkt >> v3fdata;
1144 else if (stat == HUD_STAT_SIZE )
1145 *pkt >> v2s32data;
1146 else
1147 *pkt >> intdata;
1149 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1150 if (i != m_hud_server_to_client.end()) {
1151 ClientEvent *event = new ClientEvent();
1152 event->type = CE_HUDCHANGE;
1153 event->hudchange.id = i->second;
1154 event->hudchange.stat = (HudElementStat)stat;
1155 event->hudchange.v2fdata = new v2f(v2fdata);
1156 event->hudchange.v3fdata = new v3f(v3fdata);
1157 event->hudchange.sdata = new std::string(sdata);
1158 event->hudchange.data = intdata;
1159 event->hudchange.v2s32data = new v2s32(v2s32data);
1160 m_client_event_queue.push(event);
1164 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1166 u32 flags, mask;
1168 *pkt >> flags >> mask;
1170 LocalPlayer *player = m_env.getLocalPlayer();
1171 assert(player != NULL);
1173 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1174 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1176 player->hud_flags &= ~mask;
1177 player->hud_flags |= flags;
1179 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1180 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1182 // Hide minimap if it has been disabled by the server
1183 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1184 // defers a minimap update, therefore only call it if really
1185 // needed, by checking that minimap was visible before
1186 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1188 // Switch to surface mode if radar disabled by server
1189 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1190 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1193 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1195 u16 param; std::string value;
1197 *pkt >> param >> value;
1199 LocalPlayer *player = m_env.getLocalPlayer();
1200 assert(player != NULL);
1202 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1203 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1204 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1205 player->hud_hotbar_itemcount = hotbar_itemcount;
1207 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1208 // If value not empty verify image exists in texture source
1209 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1210 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1211 << value << "')" << std::endl;
1212 return;
1214 player->hotbar_image = value;
1216 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1217 // If value not empty verify image exists in texture source
1218 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1219 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1220 << value << "')" << std::endl;
1221 return;
1223 player->hotbar_selected_image = value;
1227 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1229 std::string datastring(pkt->getString(0), pkt->getSize());
1230 std::istringstream is(datastring, std::ios_base::binary);
1232 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1233 std::string *type = new std::string(deSerializeString(is));
1234 u16 count = readU16(is);
1235 std::vector<std::string> *params = new std::vector<std::string>;
1237 for (size_t i = 0; i < count; i++)
1238 params->push_back(deSerializeString(is));
1240 bool clouds = true;
1241 try {
1242 clouds = readU8(is);
1243 } catch (...) {}
1245 ClientEvent *event = new ClientEvent();
1246 event->type = CE_SET_SKY;
1247 event->set_sky.bgcolor = bgcolor;
1248 event->set_sky.type = type;
1249 event->set_sky.params = params;
1250 event->set_sky.clouds = clouds;
1251 m_client_event_queue.push(event);
1254 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1256 f32 density;
1257 video::SColor color_bright;
1258 video::SColor color_ambient;
1259 f32 height;
1260 f32 thickness;
1261 v2f speed;
1263 *pkt >> density >> color_bright >> color_ambient
1264 >> height >> thickness >> speed;
1266 ClientEvent *event = new ClientEvent();
1267 event->type = CE_CLOUD_PARAMS;
1268 event->cloud_params.density = density;
1269 // use the underlying u32 representation, because we can't
1270 // use struct members with constructors here, and this way
1271 // we avoid using new() and delete() for no good reason
1272 event->cloud_params.color_bright = color_bright.color;
1273 event->cloud_params.color_ambient = color_ambient.color;
1274 event->cloud_params.height = height;
1275 event->cloud_params.thickness = thickness;
1276 // same here: deconstruct to skip constructor
1277 event->cloud_params.speed_x = speed.X;
1278 event->cloud_params.speed_y = speed.Y;
1279 m_client_event_queue.push(event);
1282 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1284 bool do_override;
1285 u16 day_night_ratio_u;
1287 *pkt >> do_override >> day_night_ratio_u;
1289 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1291 ClientEvent *event = new ClientEvent();
1292 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1293 event->override_day_night_ratio.do_override = do_override;
1294 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1295 m_client_event_queue.push(event);
1298 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1300 LocalPlayer *player = m_env.getLocalPlayer();
1301 assert(player != NULL);
1303 *pkt >> player->local_animations[0];
1304 *pkt >> player->local_animations[1];
1305 *pkt >> player->local_animations[2];
1306 *pkt >> player->local_animations[3];
1307 *pkt >> player->local_animation_speed;
1310 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1312 LocalPlayer *player = m_env.getLocalPlayer();
1313 assert(player != NULL);
1315 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1318 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1320 u8 type;
1321 u16 num_players;
1322 *pkt >> type >> num_players;
1323 PlayerListModifer notice_type = (PlayerListModifer) type;
1325 for (u16 i = 0; i < num_players; i++) {
1326 std::string name;
1327 *pkt >> name;
1328 switch (notice_type) {
1329 case PLAYER_LIST_INIT:
1330 case PLAYER_LIST_ADD:
1331 m_env.addPlayerName(name);
1332 continue;
1333 case PLAYER_LIST_REMOVE:
1334 m_env.removePlayerName(name);
1335 continue;
1340 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1342 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1343 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1344 errorstream << "Client: Received SRP S_B login message,"
1345 << " but wasn't supposed to (chosen_mech="
1346 << m_chosen_auth_mech << ")." << std::endl;
1347 return;
1350 char *bytes_M = 0;
1351 size_t len_M = 0;
1352 SRPUser *usr = (SRPUser *) m_auth_data;
1353 std::string s;
1354 std::string B;
1355 *pkt >> s >> B;
1357 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1359 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1360 (const unsigned char *) B.c_str(), B.size(),
1361 (unsigned char **) &bytes_M, &len_M);
1363 if ( !bytes_M ) {
1364 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1365 return;
1368 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1369 resp_pkt << std::string(bytes_M, len_M);
1370 Send(&resp_pkt);
1373 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1375 LocalPlayer *player = m_env.getLocalPlayer();
1376 assert(player != NULL);
1378 // Store formspec in LocalPlayer
1379 *pkt >> player->formspec_prepend;
1382 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1384 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1386 // Restrictions were received -> load mods if it's enabled
1387 // Note: this should be moved after mods receptions from server instead
1388 loadMods();
1392 * Mod channels
1395 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1397 std::string channel_name, sender, channel_msg;
1398 *pkt >> channel_name >> sender >> channel_msg;
1400 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1401 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1402 << channel_msg << std::endl;
1404 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1405 verbosestream << "Server sent us messages on unregistered channel "
1406 << channel_name << ", ignoring." << std::endl;
1407 return;
1410 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1413 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1415 u8 signal_tmp;
1416 ModChannelSignal signal;
1417 std::string channel;
1419 *pkt >> signal_tmp >> channel;
1421 signal = (ModChannelSignal)signal_tmp;
1423 bool valid_signal = true;
1424 // @TODO: send Signal to Lua API
1425 switch (signal) {
1426 case MODCHANNEL_SIGNAL_JOIN_OK:
1427 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1428 infostream << "Server ack our mod channel join on channel `" << channel
1429 << "`, joining." << std::endl;
1430 break;
1431 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1432 // Unable to join, remove channel
1433 m_modchannel_mgr->leaveChannel(channel, 0);
1434 infostream << "Server refused our mod channel join on channel `" << channel
1435 << "`" << std::endl;
1436 break;
1437 case MODCHANNEL_SIGNAL_LEAVE_OK:
1438 #ifndef NDEBUG
1439 infostream << "Server ack our mod channel leave on channel " << channel
1440 << "`, leaving." << std::endl;
1441 #endif
1442 break;
1443 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1444 infostream << "Server refused our mod channel leave on channel `" << channel
1445 << "`" << std::endl;
1446 break;
1447 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1448 #ifndef NDEBUG
1449 // Generally unused, but ensure we don't do an implementation error
1450 infostream << "Server tells us we sent a message on channel `" << channel
1451 << "` but we are not registered. Message was dropped." << std::endl;
1452 #endif
1453 break;
1454 case MODCHANNEL_SIGNAL_SET_STATE: {
1455 u8 state;
1456 *pkt >> state;
1458 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1459 infostream << "Received wrong channel state " << state
1460 << ", ignoring." << std::endl;
1461 return;
1464 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1465 infostream << "Server sets mod channel `" << channel
1466 << "` in read-only mode." << std::endl;
1467 break;
1469 default:
1470 #ifndef NDEBUG
1471 warningstream << "Received unhandled mod channel signal ID "
1472 << signal << ", ignoring." << std::endl;
1473 #endif
1474 valid_signal = false;
1475 break;
1478 // If signal is valid, forward it to client side mods
1479 if (valid_signal)
1480 m_script->on_modchannel_signal(channel, signal);