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.
21 #include "client/client.h"
23 #include "util/base64.h"
24 #include "client/camera.h"
25 #include "chatmessage.h"
26 #include "client/clientmedia.h"
29 #include "mapsector.h"
30 #include "client/minimap.h"
31 #include "modchannels.h"
33 #include "serialization.h"
35 #include "util/strfnd.h"
36 #include "client/clientevent.h"
37 #include "client/content_cao.h"
38 #include "client/sound.h"
39 #include "network/clientopcodes.h"
40 #include "network/connection.h"
41 #include "script/scripting_client.h"
42 #include "util/serialize.h"
44 #include "util/sha1.h"
45 #include "tileanimation.h"
47 #include "skyparams.h"
49 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
51 infostream << "Got deprecated command "
52 << toClientCommandTable[pkt->getCommand()].name << " from peer "
53 << pkt->getPeerId() << "!" << std::endl;
56 void Client::handleCommand_Hello(NetworkPacket* pkt)
58 if (pkt->getSize() < 1)
65 std::string username_legacy; // for case insensitivity
66 *pkt >> serialization_ver >> compression_mode >> proto_ver
67 >> auth_mechs >> username_legacy;
69 // Chose an auth method we support
70 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
72 infostream << "Client: TOCLIENT_HELLO received with "
73 << "serialization_ver=" << (u32)serialization_ver
74 << ", auth_mechs=" << auth_mechs
75 << ", proto_ver=" << proto_ver
76 << ", compression_mode=" << compression_mode
77 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
79 if (!ser_ver_supported(serialization_ver)) {
80 infostream << "Client: TOCLIENT_HELLO: Server sent "
81 << "unsupported ser_fmt_ver"<< std::endl;
85 m_server_ser_ver = serialization_ver;
86 m_proto_ver = proto_ver;
88 //TODO verify that username_legacy matches sent username, only
89 // differs in casing (make both uppercase and compare)
90 // This is only neccessary though when we actually want to add casing support
92 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
93 // we recieved a TOCLIENT_HELLO while auth was already going on
94 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
95 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
96 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
97 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
98 srp_user_delete((SRPUser *) m_auth_data);
103 // Authenticate using that method, or abort if there wasn't any method found
104 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
105 if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
106 !m_simple_singleplayer_mode &&
107 !getServerAddress().isLocalhost() &&
108 g_settings->getBool("enable_register_confirmation")) {
109 promptConfirmRegistration(chosen_auth_mechanism);
111 startAuth(chosen_auth_mechanism);
114 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
115 m_access_denied = true;
116 m_access_denied_reason = "Unknown";
122 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
127 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
128 >> m_sudo_auth_methods;
130 playerpos -= v3f(0, BS / 2, 0);
132 // Set player position
133 LocalPlayer *player = m_env.getLocalPlayer();
134 assert(player != NULL);
135 player->setPosition(playerpos);
137 infostream << "Client: received map seed: " << m_map_seed << std::endl;
138 infostream << "Client: received recommended send interval "
139 << m_recommended_send_interval<<std::endl;
142 /*~ DO NOT TRANSLATE THIS LITERALLY!
143 This is a special string which needs to contain the translation's
144 language code (e.g. "de" for German). */
145 std::string lang = gettext("LANG_CODE");
146 if (lang == "LANG_CODE")
149 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
155 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
159 m_password = m_new_password;
161 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
163 // send packet to actually set the password
164 startAuth(AUTH_MECHANISM_FIRST_SRP);
167 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
169 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
171 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
172 L"Password change denied. Password NOT changed.");
173 pushToChatQueue(chatMessage);
174 // reset everything and be sad
178 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
180 // The server didn't like our password. Note, this needs
181 // to be processed even if the serialisation format has
182 // not been agreed yet, the same as TOCLIENT_INIT.
183 m_access_denied = true;
184 m_access_denied_reason = "Unknown";
186 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
187 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
188 // in some places of the server code
189 if (pkt->getSize() >= 2) {
190 std::wstring wide_reason;
192 m_access_denied_reason = wide_to_utf8(wide_reason);
197 if (pkt->getSize() < 1)
200 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
202 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
203 denyCode == SERVER_ACCESSDENIED_CRASH) {
204 *pkt >> m_access_denied_reason;
205 if (m_access_denied_reason.empty()) {
206 m_access_denied_reason = accessDeniedStrings[denyCode];
210 m_access_denied_reconnect = reconnect & 1;
211 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
212 *pkt >> m_access_denied_reason;
213 } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) {
214 m_access_denied_reason = accessDeniedStrings[denyCode];
215 m_access_denied_reconnect = true;
216 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
217 m_access_denied_reason = accessDeniedStrings[denyCode];
219 // Allow us to add new error messages to the
220 // protocol without raising the protocol version, if we want to.
221 // Until then (which may be never), this is outside
222 // of the defined protocol.
223 *pkt >> m_access_denied_reason;
224 if (m_access_denied_reason.empty()) {
225 m_access_denied_reason = "Unknown";
230 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
232 if (pkt->getSize() < 6)
240 void Client::handleCommand_AddNode(NetworkPacket* pkt)
242 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
249 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
251 bool remove_metadata = true;
252 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
253 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
254 remove_metadata = false;
257 addNode(p, n, remove_metadata);
260 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
262 if (pkt->getSize() < 1)
265 std::istringstream is(pkt->readLongString(), std::ios::binary);
266 std::stringstream sstr;
267 decompressZlib(is, sstr);
269 NodeMetadataList meta_updates_list(false);
270 meta_updates_list.deSerialize(sstr, m_itemdef, true);
272 Map &map = m_env.getMap();
273 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
274 i != meta_updates_list.end(); ++i) {
275 v3s16 pos = i->first;
277 if (map.isValidPosition(pos) &&
278 map.setNodeMetadata(pos, i->second))
279 continue; // Prevent from deleting metadata
281 // Meta couldn't be set, unused metadata
286 void Client::handleCommand_BlockData(NetworkPacket* pkt)
288 // Ignore too small packet
289 if (pkt->getSize() < 6)
295 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
296 std::istringstream istr(datastring, std::ios_base::binary);
302 sector = m_env.getMap().emergeSector(p2d);
304 assert(sector->getPos() == p2d);
306 block = sector->getBlockNoCreateNoEx(p.Y);
309 Update an existing block
311 block->deSerialize(istr, m_server_ser_ver, false);
312 block->deSerializeNetworkSpecific(istr);
318 block = new MapBlock(&m_env.getMap(), p, this);
319 block->deSerialize(istr, m_server_ser_ver, false);
320 block->deSerializeNetworkSpecific(istr);
321 sector->insertBlock(block);
325 ServerMap::saveBlock(block, m_localdb);
329 Add it to mesh update queue and set it to be acknowledged after update.
331 addUpdateMeshTaskWithEdge(p, true);
334 void Client::handleCommand_Inventory(NetworkPacket* pkt)
336 if (pkt->getSize() < 1)
339 std::string datastring(pkt->getString(0), pkt->getSize());
340 std::istringstream is(datastring, std::ios_base::binary);
342 LocalPlayer *player = m_env.getLocalPlayer();
343 assert(player != NULL);
345 player->inventory.deSerialize(is);
347 m_update_wielded_item = true;
349 delete m_inventory_from_server;
350 m_inventory_from_server = new Inventory(player->inventory);
351 m_inventory_from_server_age = 0.0;
354 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
356 if (pkt->getSize() < 2)
363 time_of_day = time_of_day % 24000;
364 float time_speed = 0;
366 if (pkt->getSize() >= 2 + 4) {
370 // Old message; try to approximate speed of time by ourselves
371 float time_of_day_f = (float)time_of_day / 24000.0f;
372 float tod_diff_f = 0;
374 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
375 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
377 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
379 m_last_time_of_day_f = time_of_day_f;
380 float time_diff = m_time_of_day_update_timer;
381 m_time_of_day_update_timer = 0;
383 if (m_time_of_day_set) {
384 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
385 infostream << "Client: Measured time_of_day speed (old format): "
386 << time_speed << " tod_diff_f=" << tod_diff_f
387 << " time_diff=" << time_diff << std::endl;
391 // Update environment
392 m_env.setTimeOfDay(time_of_day);
393 m_env.setTimeOfDaySpeed(time_speed);
394 m_time_of_day_set = true;
396 //u32 dr = m_env.getDayNightRatio();
397 //infostream << "Client: time_of_day=" << time_of_day
398 // << " time_speed=" << time_speed
399 // << " dr=" << dr << std::endl;
402 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
407 u16 sendername length
413 ChatMessage *chatMessage = new ChatMessage();
414 u8 version, message_type;
415 *pkt >> version >> message_type;
417 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
423 *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
424 chatMessage->timestamp = static_cast<std::time_t>(timestamp);
426 chatMessage->type = (ChatMessageType) message_type;
428 // @TODO send this to CSM using ChatMessage object
429 if (modsLoaded() && m_script->on_receiving_message(
430 wide_to_utf8(chatMessage->message))) {
431 // Message was consumed by CSM and should not be handled by client
434 pushToChatQueue(chatMessage);
438 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
441 u16 count of removed objects
442 for all removed objects {
445 u16 count of added objects
446 for all added objects {
449 u32 initialization data length
450 string initialization data
454 LocalPlayer *player = m_env.getLocalPlayer();
455 bool try_reattach = player && player->isWaitingForReattach();
459 u16 removed_count, added_count, id;
461 // Read removed objects
462 *pkt >> removed_count;
464 for (u16 i = 0; i < removed_count; i++) {
466 m_env.removeActiveObject(id);
469 // Read added objects
472 for (u16 i = 0; i < added_count; i++) {
474 m_env.addActiveObject(id, type, pkt->readLongString());
476 player->tryReattach(id);
478 } catch (PacketError &e) {
479 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
480 << ". The packet is unreliable, ignoring" << std::endl;
483 // m_activeobjects_received is false before the first
484 // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
485 m_activeobjects_received = true;
488 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
498 std::string datastring(pkt->getString(0), pkt->getSize());
499 std::istringstream is(datastring, std::ios_base::binary);
503 u16 id = readU16(is);
507 std::string message = deSerializeString(is);
509 // Pass on to the environment
510 m_env.processActiveObjectMessage(id, message);
512 } catch (SerializationError &e) {
513 errorstream << "Client::handleCommand_ActiveObjectMessages: "
514 << "caught SerializationError: " << e.what() << std::endl;
518 void Client::handleCommand_Movement(NetworkPacket* pkt)
520 LocalPlayer *player = m_env.getLocalPlayer();
521 assert(player != NULL);
523 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
525 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
526 >> lf >> lfs >> ls >> g;
528 player->movement_acceleration_default = mad * BS;
529 player->movement_acceleration_air = maa * BS;
530 player->movement_acceleration_fast = maf * BS;
531 player->movement_speed_walk = msw * BS;
532 player->movement_speed_crouch = mscr * BS;
533 player->movement_speed_fast = msf * BS;
534 player->movement_speed_climb = mscl * BS;
535 player->movement_speed_jump = msj * BS;
536 player->movement_liquid_fluidity = lf * BS;
537 player->movement_liquid_fluidity_smooth = lfs * BS;
538 player->movement_liquid_sink = ls * BS;
539 player->movement_gravity = g * BS;
542 void Client::handleCommand_Fov(NetworkPacket *pkt)
545 bool is_multiplier = false;
546 f32 transition_time = 0.0f;
548 *pkt >> fov >> is_multiplier;
550 // Wrap transition_time extraction within a
551 // try-catch to preserve backwards compat
553 *pkt >> transition_time;
554 } catch (PacketError &e) {};
556 LocalPlayer *player = m_env.getLocalPlayer();
558 player->setFov({ fov, is_multiplier, transition_time });
559 m_camera->notifyFovChange();
562 void Client::handleCommand_HP(NetworkPacket *pkt)
564 LocalPlayer *player = m_env.getLocalPlayer();
565 assert(player != NULL);
567 u16 oldhp = player->hp;
575 m_script->on_hp_modification(hp);
578 // Add to ClientEvent queue
579 ClientEvent *event = new ClientEvent();
580 event->type = CE_PLAYER_DAMAGE;
581 event->player_damage.amount = oldhp - hp;
582 m_client_event_queue.push(event);
586 void Client::handleCommand_Breath(NetworkPacket* pkt)
588 LocalPlayer *player = m_env.getLocalPlayer();
589 assert(player != NULL);
595 player->setBreath(breath);
598 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
600 LocalPlayer *player = m_env.getLocalPlayer();
601 assert(player != NULL);
603 if ((player->getCAO() && player->getCAO()->getParentId()) || player->isWaitingForReattach())
609 *pkt >> pos >> pitch >> yaw;
611 player->setLegitPosition(pos);
613 infostream << "Client got TOCLIENT_MOVE_PLAYER"
614 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
615 << " pitch=" << pitch
620 Add to ClientEvent queue.
621 This has to be sent to the main program because otherwise
622 it would just force the pitch and yaw values to whatever
623 the camera points to.
626 if (g_settings->getBool("no_force_rotate"))
629 ClientEvent *event = new ClientEvent();
630 event->type = CE_PLAYER_FORCE_MOVE;
631 event->player_force_move.pitch = pitch;
632 event->player_force_move.yaw = yaw;
633 m_client_event_queue.push(event);
636 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
638 bool set_camera_point_target;
639 v3f camera_point_target;
641 *pkt >> set_camera_point_target;
642 *pkt >> camera_point_target;
644 ClientEvent *event = new ClientEvent();
645 event->type = CE_DEATHSCREEN;
646 event->deathscreen.set_camera_point_target = set_camera_point_target;
647 event->deathscreen.camera_point_target_x = camera_point_target.X;
648 event->deathscreen.camera_point_target_y = camera_point_target.Y;
649 event->deathscreen.camera_point_target_z = camera_point_target.Z;
650 m_client_event_queue.push(event);
653 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
659 infostream << "Client: Received media announcement: packet size: "
660 << pkt->getSize() << std::endl;
662 if (m_media_downloader == NULL ||
663 m_media_downloader->isStarted()) {
664 const char *problem = m_media_downloader ?
665 "we already saw another announcement" :
666 "all media has been received already";
667 errorstream << "Client: Received media announcement but "
669 << " files=" << num_files
670 << " size=" << pkt->getSize() << std::endl;
674 // Mesh update thread must be stopped while
675 // updating content definitions
676 sanity_check(!m_mesh_update_thread.isRunning());
678 for (u16 i = 0; i < num_files; i++) {
679 std::string name, sha1_base64;
681 *pkt >> name >> sha1_base64;
683 std::string sha1_raw = base64_decode(sha1_base64);
684 m_media_downloader->addFile(name, sha1_raw);
693 while(!sf.at_end()) {
694 std::string baseurl = trim(sf.next(","));
695 if (!baseurl.empty())
696 m_media_downloader->addRemoteServer(baseurl);
699 catch(SerializationError& e) {
700 // not supported by server or turned off
703 m_media_downloader->step(this);
706 void Client::handleCommand_Media(NetworkPacket* pkt)
710 u16 total number of file bunches
711 u16 index of this bunch
712 u32 number of files in this bunch
724 *pkt >> num_bunches >> bunch_i >> num_files;
726 infostream << "Client: Received files: bunch " << bunch_i << "/"
727 << num_bunches << " files=" << num_files
728 << " size=" << pkt->getSize() << std::endl;
733 if (!m_media_downloader || !m_media_downloader->isStarted()) {
734 const char *problem = m_media_downloader ?
735 "media has not been requested" :
736 "all media has been received already";
737 errorstream << "Client: Received media but "
739 << " bunch " << bunch_i << "/" << num_bunches
740 << " files=" << num_files
741 << " size=" << pkt->getSize() << std::endl;
745 // Mesh update thread must be stopped while
746 // updating content definitions
747 sanity_check(!m_mesh_update_thread.isRunning());
749 for (u32 i=0; i < num_files; i++) {
754 std::string data = pkt->readLongString();
756 m_media_downloader->conventionalTransferDone(
761 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
763 infostream << "Client: Received node definitions: packet size: "
764 << pkt->getSize() << std::endl;
766 // Mesh update thread must be stopped while
767 // updating content definitions
768 sanity_check(!m_mesh_update_thread.isRunning());
770 // Decompress node definitions
771 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
772 std::ostringstream tmp_os;
773 decompressZlib(tmp_is, tmp_os);
775 // Deserialize node definitions
776 std::istringstream tmp_is2(tmp_os.str());
777 m_nodedef->deSerialize(tmp_is2);
778 m_nodedef_received = true;
781 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
783 infostream << "Client: Received item definitions: packet size: "
784 << pkt->getSize() << std::endl;
786 // Mesh update thread must be stopped while
787 // updating content definitions
788 sanity_check(!m_mesh_update_thread.isRunning());
790 // Decompress item definitions
791 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
792 std::ostringstream tmp_os;
793 decompressZlib(tmp_is, tmp_os);
795 // Deserialize node definitions
796 std::istringstream tmp_is2(tmp_os.str());
797 m_itemdef->deSerialize(tmp_is2);
798 m_itemdef_received = true;
801 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
809 [11 + len] (f32 * 3) pos
810 [23 + len] u16 object_id
814 [34 + len] bool ephemeral
821 u8 type; // 0=local, 1=positional, 2=object
827 bool ephemeral = false;
829 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
835 } catch (PacketError &e) {};
841 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
843 case 1: // positional
844 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
848 ClientActiveObject *cao = m_env.getActiveObject(object_id);
850 pos = cao->getPosition();
851 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
858 if (client_id != -1) {
859 // for ephemeral sounds, server_id is not meaningful
861 m_sounds_server_to_client[server_id] = client_id;
862 m_sounds_client_to_server[client_id] = server_id;
865 m_sounds_to_objects[client_id] = object_id;
869 void Client::handleCommand_StopSound(NetworkPacket* pkt)
875 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
876 if (i != m_sounds_server_to_client.end()) {
877 int client_id = i->second;
878 m_sound->stopSound(client_id);
882 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
888 *pkt >> sound_id >> step >> gain;
890 std::unordered_map<s32, int>::const_iterator i =
891 m_sounds_server_to_client.find(sound_id);
893 if (i != m_sounds_server_to_client.end())
894 m_sound->fadeSound(i->second, step, gain);
897 void Client::handleCommand_Privileges(NetworkPacket* pkt)
899 m_privileges.clear();
900 infostream << "Client: Privileges updated: ";
903 *pkt >> num_privileges;
905 for (u16 i = 0; i < num_privileges; i++) {
910 m_privileges.insert(priv);
911 infostream << priv << " ";
913 infostream << std::endl;
916 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
918 LocalPlayer *player = m_env.getLocalPlayer();
919 assert(player != NULL);
921 // Store formspec in LocalPlayer
922 player->inventory_formspec = pkt->readLongString();
925 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
928 bool keep_inv = true;
929 *pkt >> name >> keep_inv;
931 infostream << "Client: Detached inventory update: \"" << name
932 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
934 const auto &inv_it = m_detached_inventories.find(name);
936 if (inv_it != m_detached_inventories.end()) {
937 delete inv_it->second;
938 m_detached_inventories.erase(inv_it);
942 Inventory *inv = nullptr;
943 if (inv_it == m_detached_inventories.end()) {
944 inv = new Inventory(m_itemdef);
945 m_detached_inventories[name] = inv;
947 inv = inv_it->second;
951 *pkt >> ignore; // this used to be the length of the following string, ignore it
953 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
954 std::istringstream is(contents, std::ios::binary);
955 inv->deSerialize(is);
958 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
960 std::string formspec = pkt->readLongString();
961 std::string formname;
965 ClientEvent *event = new ClientEvent();
966 event->type = CE_SHOW_FORMSPEC;
967 // pointer is required as event is a struct only!
968 // adding a std:string to a struct isn't possible
969 event->show_formspec.formspec = new std::string(formspec);
970 event->show_formspec.formname = new std::string(formname);
971 m_client_event_queue.push(event);
974 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
976 std::string datastring(pkt->getString(0), pkt->getSize());
977 std::istringstream is(datastring, std::ios_base::binary);
979 ParticleParameters p;
980 p.deSerialize(is, m_proto_ver);
982 ClientEvent *event = new ClientEvent();
983 event->type = CE_SPAWN_PARTICLE;
984 event->spawn_particle = new ParticleParameters(p);
986 if (g_settings->getBool("log_particles")) {
987 std::cout << p.pos.X << " " << p.pos.Y << " " << p.pos.Z << std::endl;
990 m_client_event_queue.push(event);
993 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
995 std::string datastring(pkt->getString(0), pkt->getSize());
996 std::istringstream is(datastring, std::ios_base::binary);
998 ParticleSpawnerParameters p;
1000 u16 attached_id = 0;
1002 p.amount = readU16(is);
1003 p.time = readF32(is);
1004 p.minpos = readV3F32(is);
1005 p.maxpos = readV3F32(is);
1006 p.minvel = readV3F32(is);
1007 p.maxvel = readV3F32(is);
1008 p.minacc = readV3F32(is);
1009 p.maxacc = readV3F32(is);
1010 p.minexptime = readF32(is);
1011 p.maxexptime = readF32(is);
1012 p.minsize = readF32(is);
1013 p.maxsize = readF32(is);
1014 p.collisiondetection = readU8(is);
1015 p.texture = deSerializeLongString(is);
1017 server_id = readU32(is);
1019 p.vertical = readU8(is);
1020 p.collision_removal = readU8(is);
1022 attached_id = readU16(is);
1024 p.animation.deSerialize(is, m_proto_ver);
1025 p.glow = readU8(is);
1026 p.object_collision = readU8(is);
1028 // This is kinda awful
1030 u16 tmp_param0 = readU16(is);
1033 p.node.param0 = tmp_param0;
1034 p.node.param2 = readU8(is);
1035 p.node_tile = readU8(is);
1038 auto event = new ClientEvent();
1039 event->type = CE_ADD_PARTICLESPAWNER;
1040 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1041 event->add_particlespawner.attached_id = attached_id;
1042 event->add_particlespawner.id = server_id;
1044 m_client_event_queue.push(event);
1048 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1053 ClientEvent *event = new ClientEvent();
1054 event->type = CE_DELETE_PARTICLESPAWNER;
1055 event->delete_particlespawner.id = server_id;
1057 m_client_event_queue.push(event);
1060 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1062 std::string datastring(pkt->getString(0), pkt->getSize());
1063 std::istringstream is(datastring, std::ios_base::binary);
1081 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1082 >> dir >> align >> offset;
1088 } catch(PacketError &e) {};
1090 ClientEvent *event = new ClientEvent();
1091 event->type = CE_HUDADD;
1092 event->hudadd.server_id = server_id;
1093 event->hudadd.type = type;
1094 event->hudadd.pos = new v2f(pos);
1095 event->hudadd.name = new std::string(name);
1096 event->hudadd.scale = new v2f(scale);
1097 event->hudadd.text = new std::string(text);
1098 event->hudadd.number = number;
1099 event->hudadd.item = item;
1100 event->hudadd.dir = dir;
1101 event->hudadd.align = new v2f(align);
1102 event->hudadd.offset = new v2f(offset);
1103 event->hudadd.world_pos = new v3f(world_pos);
1104 event->hudadd.size = new v2s32(size);
1105 event->hudadd.z_index = z_index;
1106 event->hudadd.text2 = new std::string(text2);
1107 m_client_event_queue.push(event);
1110 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1116 auto i = m_hud_server_to_client.find(server_id);
1117 if (i != m_hud_server_to_client.end()) {
1118 int client_id = i->second;
1119 m_hud_server_to_client.erase(i);
1121 ClientEvent *event = new ClientEvent();
1122 event->type = CE_HUDRM;
1123 event->hudrm.id = client_id;
1124 m_client_event_queue.push(event);
1128 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1138 *pkt >> server_id >> stat;
1140 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1141 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1143 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1145 else if (stat == HUD_STAT_WORLD_POS)
1147 else if (stat == HUD_STAT_SIZE )
1152 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1153 if (i != m_hud_server_to_client.end()) {
1154 ClientEvent *event = new ClientEvent();
1155 event->type = CE_HUDCHANGE;
1156 event->hudchange.id = i->second;
1157 event->hudchange.stat = (HudElementStat)stat;
1158 event->hudchange.v2fdata = new v2f(v2fdata);
1159 event->hudchange.v3fdata = new v3f(v3fdata);
1160 event->hudchange.sdata = new std::string(sdata);
1161 event->hudchange.data = intdata;
1162 event->hudchange.v2s32data = new v2s32(v2s32data);
1163 m_client_event_queue.push(event);
1167 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1171 *pkt >> flags >> mask;
1173 LocalPlayer *player = m_env.getLocalPlayer();
1174 assert(player != NULL);
1176 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1177 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1179 player->hud_flags &= ~mask;
1180 player->hud_flags |= flags;
1182 if (g_settings->getBool("hud_flags_bypass"))
1183 player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE |
1184 HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
1185 HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE |
1186 HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1189 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1190 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1192 // Hide minimap if it has been disabled by the server
1193 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1194 // defers a minimap update, therefore only call it if really
1195 // needed, by checking that minimap was visible before
1196 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1198 // Switch to surface mode if radar disabled by server
1199 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1200 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1205 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1207 u16 param; std::string value;
1209 *pkt >> param >> value;
1211 LocalPlayer *player = m_env.getLocalPlayer();
1212 assert(player != NULL);
1214 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1215 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1216 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1217 player->hud_hotbar_itemcount = hotbar_itemcount;
1219 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1220 player->hotbar_image = value;
1222 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1223 player->hotbar_selected_image = value;
1227 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1229 if (m_proto_ver < 39) {
1230 // Handle Protocol 38 and below servers with old set_sky,
1231 // ensuring the classic look is kept.
1232 std::string datastring(pkt->getString(0), pkt->getSize());
1233 std::istringstream is(datastring, std::ios_base::binary);
1235 SkyboxParams skybox;
1236 skybox.bgcolor = video::SColor(readARGB8(is));
1237 skybox.type = std::string(deSerializeString(is));
1238 u16 count = readU16(is);
1240 for (size_t i = 0; i < count; i++)
1241 skybox.textures.emplace_back(deSerializeString(is));
1243 skybox.clouds = true;
1245 skybox.clouds = readU8(is);
1248 // Use default skybox settings:
1249 SkyboxDefaults sky_defaults;
1250 SunParams sun = sky_defaults.getSunDefaults();
1251 MoonParams moon = sky_defaults.getMoonDefaults();
1252 StarParams stars = sky_defaults.getStarDefaults();
1254 // Fix for "regular" skies, as color isn't kept:
1255 if (skybox.type == "regular") {
1256 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1257 skybox.fog_tint_type = "default";
1258 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1259 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1262 sun.visible = false;
1263 sun.sunrise_visible = false;
1264 moon.visible = false;
1265 stars.visible = false;
1268 // Skybox, sun, moon and stars ClientEvents:
1269 ClientEvent *sky_event = new ClientEvent();
1270 sky_event->type = CE_SET_SKY;
1271 sky_event->set_sky = new SkyboxParams(skybox);
1272 m_client_event_queue.push(sky_event);
1274 ClientEvent *sun_event = new ClientEvent();
1275 sun_event->type = CE_SET_SUN;
1276 sun_event->sun_params = new SunParams(sun);
1277 m_client_event_queue.push(sun_event);
1279 ClientEvent *moon_event = new ClientEvent();
1280 moon_event->type = CE_SET_MOON;
1281 moon_event->moon_params = new MoonParams(moon);
1282 m_client_event_queue.push(moon_event);
1284 ClientEvent *star_event = new ClientEvent();
1285 star_event->type = CE_SET_STARS;
1286 star_event->star_params = new StarParams(stars);
1287 m_client_event_queue.push(star_event);
1289 SkyboxParams skybox;
1291 std::string texture;
1293 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1294 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1296 if (skybox.type == "skybox") {
1297 *pkt >> texture_count;
1298 for (int i = 0; i < texture_count; i++) {
1300 skybox.textures.emplace_back(texture);
1303 else if (skybox.type == "regular") {
1304 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1305 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1306 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1307 >> skybox.sky_color.indoors;
1310 ClientEvent *event = new ClientEvent();
1311 event->type = CE_SET_SKY;
1312 event->set_sky = new SkyboxParams(skybox);
1313 m_client_event_queue.push(event);
1317 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1321 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1322 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1324 ClientEvent *event = new ClientEvent();
1325 event->type = CE_SET_SUN;
1326 event->sun_params = new SunParams(sun);
1327 m_client_event_queue.push(event);
1330 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1334 *pkt >> moon.visible >> moon.texture
1335 >> moon.tonemap >> moon.scale;
1337 ClientEvent *event = new ClientEvent();
1338 event->type = CE_SET_MOON;
1339 event->moon_params = new MoonParams(moon);
1340 m_client_event_queue.push(event);
1343 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1347 *pkt >> stars.visible >> stars.count
1348 >> stars.starcolor >> stars.scale;
1350 ClientEvent *event = new ClientEvent();
1351 event->type = CE_SET_STARS;
1352 event->star_params = new StarParams(stars);
1354 m_client_event_queue.push(event);
1357 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1360 video::SColor color_bright;
1361 video::SColor color_ambient;
1366 *pkt >> density >> color_bright >> color_ambient
1367 >> height >> thickness >> speed;
1369 ClientEvent *event = new ClientEvent();
1370 event->type = CE_CLOUD_PARAMS;
1371 event->cloud_params.density = density;
1372 // use the underlying u32 representation, because we can't
1373 // use struct members with constructors here, and this way
1374 // we avoid using new() and delete() for no good reason
1375 event->cloud_params.color_bright = color_bright.color;
1376 event->cloud_params.color_ambient = color_ambient.color;
1377 event->cloud_params.height = height;
1378 event->cloud_params.thickness = thickness;
1379 // same here: deconstruct to skip constructor
1380 event->cloud_params.speed_x = speed.X;
1381 event->cloud_params.speed_y = speed.Y;
1382 m_client_event_queue.push(event);
1385 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1388 u16 day_night_ratio_u;
1390 *pkt >> do_override >> day_night_ratio_u;
1392 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1394 ClientEvent *event = new ClientEvent();
1395 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1396 event->override_day_night_ratio.do_override = do_override;
1397 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1398 m_client_event_queue.push(event);
1401 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1403 LocalPlayer *player = m_env.getLocalPlayer();
1404 assert(player != NULL);
1406 *pkt >> player->local_animations[0];
1407 *pkt >> player->local_animations[1];
1408 *pkt >> player->local_animations[2];
1409 *pkt >> player->local_animations[3];
1410 *pkt >> player->local_animation_speed;
1413 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1415 LocalPlayer *player = m_env.getLocalPlayer();
1416 assert(player != NULL);
1418 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1421 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1425 *pkt >> type >> num_players;
1426 PlayerListModifer notice_type = (PlayerListModifer) type;
1428 for (u16 i = 0; i < num_players; i++) {
1431 switch (notice_type) {
1432 case PLAYER_LIST_INIT:
1433 case PLAYER_LIST_ADD:
1434 m_env.addPlayerName(name);
1436 case PLAYER_LIST_REMOVE:
1437 m_env.removePlayerName(name);
1443 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1445 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1446 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1447 errorstream << "Client: Received SRP S_B login message,"
1448 << " but wasn't supposed to (chosen_mech="
1449 << m_chosen_auth_mech << ")." << std::endl;
1455 SRPUser *usr = (SRPUser *) m_auth_data;
1460 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1462 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1463 (const unsigned char *) B.c_str(), B.size(),
1464 (unsigned char **) &bytes_M, &len_M);
1467 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1471 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1472 resp_pkt << std::string(bytes_M, len_M);
1476 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1478 LocalPlayer *player = m_env.getLocalPlayer();
1479 assert(player != NULL);
1481 // Store formspec in LocalPlayer
1482 *pkt >> player->formspec_prepend;
1485 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1487 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1489 // Restrictions were received -> load mods if it's enabled
1490 // Note: this should be moved after mods receptions from server instead
1494 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1496 if (g_settings->getBool("antiknockback"))
1502 LocalPlayer *player = m_env.getLocalPlayer();
1503 assert(player != NULL);
1504 player->addVelocity(added_vel);
1507 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1509 std::string raw_hash, filename, filedata;
1512 *pkt >> raw_hash >> filename >> cached;
1513 filedata = pkt->readLongString();
1515 if (raw_hash.size() != 20 || filedata.empty() || filename.empty() ||
1516 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1517 throw PacketError("Illegal filename, data or hash");
1520 verbosestream << "Server pushes media file \"" << filename << "\" with "
1521 << filedata.size() << " bytes of data (cached=" << cached
1522 << ")" << std::endl;
1524 if (m_media_pushed_files.count(filename) != 0) {
1525 // Silently ignore for synchronization purposes
1529 // Compute and check checksum of data
1530 std::string computed_hash;
1533 ctx.addBytes(filedata.c_str(), filedata.size());
1534 unsigned char *buf = ctx.getDigest();
1535 computed_hash.assign((char*) buf, 20);
1538 if (raw_hash != computed_hash) {
1539 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1543 // Actually load media
1544 loadMedia(filedata, filename, true);
1545 m_media_pushed_files.insert(filename);
1547 // Cache file for the next time when this client joins the same server
1549 clientMediaUpdateCache(raw_hash, filedata);
1556 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1558 std::string channel_name, sender, channel_msg;
1559 *pkt >> channel_name >> sender >> channel_msg;
1561 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1562 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1563 << channel_msg << std::endl;
1565 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1566 verbosestream << "Server sent us messages on unregistered channel "
1567 << channel_name << ", ignoring." << std::endl;
1571 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1574 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1577 ModChannelSignal signal;
1578 std::string channel;
1580 *pkt >> signal_tmp >> channel;
1582 signal = (ModChannelSignal)signal_tmp;
1584 bool valid_signal = true;
1585 // @TODO: send Signal to Lua API
1587 case MODCHANNEL_SIGNAL_JOIN_OK:
1588 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1589 infostream << "Server ack our mod channel join on channel `" << channel
1590 << "`, joining." << std::endl;
1592 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1593 // Unable to join, remove channel
1594 m_modchannel_mgr->leaveChannel(channel, 0);
1595 infostream << "Server refused our mod channel join on channel `" << channel
1596 << "`" << std::endl;
1598 case MODCHANNEL_SIGNAL_LEAVE_OK:
1600 infostream << "Server ack our mod channel leave on channel " << channel
1601 << "`, leaving." << std::endl;
1604 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1605 infostream << "Server refused our mod channel leave on channel `" << channel
1606 << "`" << std::endl;
1608 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1610 // Generally unused, but ensure we don't do an implementation error
1611 infostream << "Server tells us we sent a message on channel `" << channel
1612 << "` but we are not registered. Message was dropped." << std::endl;
1615 case MODCHANNEL_SIGNAL_SET_STATE: {
1619 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1620 infostream << "Received wrong channel state " << state
1621 << ", ignoring." << std::endl;
1625 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1626 infostream << "Server sets mod channel `" << channel
1627 << "` in read-only mode." << std::endl;
1632 warningstream << "Received unhandled mod channel signal ID "
1633 << signal << ", ignoring." << std::endl;
1635 valid_signal = false;
1639 // If signal is valid, forward it to client side mods
1641 m_script->on_modchannel_signal(channel, signal);