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"
50 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
52 infostream << "Got deprecated command "
53 << toClientCommandTable[pkt->getCommand()].name << " from peer "
54 << pkt->getPeerId() << "!" << std::endl;
57 void Client::handleCommand_Hello(NetworkPacket* pkt)
59 if (pkt->getSize() < 1)
66 std::string username_legacy; // for case insensitivity
67 *pkt >> serialization_ver >> compression_mode >> proto_ver
68 >> auth_mechs >> username_legacy;
70 // Chose an auth method we support
71 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
73 infostream << "Client: TOCLIENT_HELLO received with "
74 << "serialization_ver=" << (u32)serialization_ver
75 << ", auth_mechs=" << auth_mechs
76 << ", proto_ver=" << proto_ver
77 << ", compression_mode=" << compression_mode
78 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
80 if (!ser_ver_supported(serialization_ver)) {
81 infostream << "Client: TOCLIENT_HELLO: Server sent "
82 << "unsupported ser_fmt_ver"<< std::endl;
86 m_server_ser_ver = serialization_ver;
87 m_proto_ver = proto_ver;
89 //TODO verify that username_legacy matches sent username, only
90 // differs in casing (make both uppercase and compare)
91 // This is only neccessary though when we actually want to add casing support
93 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
94 // we received a TOCLIENT_HELLO while auth was already going on
95 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
96 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
97 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
98 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
99 srp_user_delete((SRPUser *) m_auth_data);
104 // Authenticate using that method, or abort if there wasn't any method found
105 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
106 if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
107 !m_simple_singleplayer_mode &&
108 !getServerAddress().isLocalhost() &&
109 g_settings->getBool("enable_register_confirmation")) {
110 promptConfirmRegistration(chosen_auth_mechanism);
112 startAuth(chosen_auth_mechanism);
115 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
116 m_access_denied = true;
117 m_access_denied_reason = "Unknown";
123 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
128 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
129 >> m_sudo_auth_methods;
131 playerpos -= v3f(0, BS / 2, 0);
133 // Set player position
134 LocalPlayer *player = m_env.getLocalPlayer();
135 assert(player != NULL);
136 player->setPosition(playerpos);
138 infostream << "Client: received map seed: " << m_map_seed << std::endl;
139 infostream << "Client: received recommended send interval "
140 << m_recommended_send_interval<<std::endl;
143 /*~ DO NOT TRANSLATE THIS LITERALLY!
144 This is a special string which needs to contain the translation's
145 language code (e.g. "de" for German). */
146 std::string lang = gettext("LANG_CODE");
147 if (lang == "LANG_CODE")
150 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
156 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
160 m_password = m_new_password;
162 verbosestream << "Client: Received TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
164 // send packet to actually set the password
165 startAuth(AUTH_MECHANISM_FIRST_SRP);
168 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
170 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
172 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
173 L"Password change denied. Password NOT changed.");
174 pushToChatQueue(chatMessage);
175 // reset everything and be sad
179 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
181 // The server didn't like our password. Note, this needs
182 // to be processed even if the serialisation format has
183 // not been agreed yet, the same as TOCLIENT_INIT.
184 m_access_denied = true;
185 m_access_denied_reason = "Unknown";
187 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
188 // Legacy code from 0.4.12 and older but is still used
189 // in some places of the server code
190 if (pkt->getSize() >= 2) {
191 std::wstring wide_reason;
193 m_access_denied_reason = wide_to_utf8(wide_reason);
198 if (pkt->getSize() < 1)
204 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
205 denyCode == SERVER_ACCESSDENIED_CRASH) {
206 *pkt >> m_access_denied_reason;
207 if (m_access_denied_reason.empty())
208 m_access_denied_reason = accessDeniedStrings[denyCode];
211 m_access_denied_reconnect = reconnect & 1;
212 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
213 *pkt >> m_access_denied_reason;
214 } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) {
215 m_access_denied_reason = accessDeniedStrings[denyCode];
216 m_access_denied_reconnect = true;
217 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
218 m_access_denied_reason = accessDeniedStrings[denyCode];
220 // Allow us to add new error messages to the
221 // protocol without raising the protocol version, if we want to.
222 // Until then (which may be never), this is outside
223 // of the defined protocol.
224 *pkt >> m_access_denied_reason;
225 if (m_access_denied_reason.empty())
226 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(std::ios::binary | std::ios::in | std::ios::out);
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 = deSerializeString16(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);
692 while (!sf.at_end()) {
693 std::string baseurl = trim(sf.next(","));
694 if (!baseurl.empty()) {
695 m_remote_media_servers.emplace_back(baseurl);
696 m_media_downloader->addRemoteServer(baseurl);
701 m_media_downloader->step(this);
704 void Client::handleCommand_Media(NetworkPacket* pkt)
708 u16 total number of file bunches
709 u16 index of this bunch
710 u32 number of files in this bunch
722 *pkt >> num_bunches >> bunch_i >> num_files;
724 infostream << "Client: Received files: bunch " << bunch_i << "/"
725 << num_bunches << " files=" << num_files
726 << " size=" << pkt->getSize() << std::endl;
731 bool init_phase = m_media_downloader && m_media_downloader->isStarted();
734 // Mesh update thread must be stopped while
735 // updating content definitions
736 sanity_check(!m_mesh_update_thread.isRunning());
739 for (u32 i = 0; i < num_files; i++) {
740 std::string name, data;
743 data = pkt->readLongString();
747 ok = m_media_downloader->conventionalTransferDone(name, data, this);
749 // Check pending dynamic transfers, one of them must be it
750 for (const auto &it : m_pending_media_downloads) {
751 if (it.second->conventionalTransferDone(name, data, this)) {
758 errorstream << "Client: Received media \"" << name
759 << "\" but no downloads pending. " << num_bunches << " bunches, "
760 << num_files << " in this one. (init_phase=" << init_phase
766 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
768 infostream << "Client: Received node definitions: packet size: "
769 << pkt->getSize() << std::endl;
771 // Mesh update thread must be stopped while
772 // updating content definitions
773 sanity_check(!m_mesh_update_thread.isRunning());
775 // Decompress node definitions
776 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
777 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
778 decompressZlib(tmp_is, tmp_os);
780 // Deserialize node definitions
781 m_nodedef->deSerialize(tmp_os);
782 m_nodedef_received = true;
785 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
787 infostream << "Client: Received item definitions: packet size: "
788 << pkt->getSize() << std::endl;
790 // Mesh update thread must be stopped while
791 // updating content definitions
792 sanity_check(!m_mesh_update_thread.isRunning());
794 // Decompress item definitions
795 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
796 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
797 decompressZlib(tmp_is, tmp_os);
799 // Deserialize node definitions
800 m_itemdef->deSerialize(tmp_os);
801 m_itemdef_received = true;
804 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
812 [11 + len] (f32 * 3) pos
813 [23 + len] u16 object_id
817 [34 + len] bool ephemeral
824 u8 type; // 0=local, 1=positional, 2=object
830 bool ephemeral = false;
832 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
838 } catch (PacketError &e) {};
840 SimpleSoundSpec sound_spec(name, gain, fade, pitch);
842 if (m_mods_loaded && m_script->on_play_sound(sound_spec))
849 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
851 case 1: // positional
852 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
856 ClientActiveObject *cao = m_env.getActiveObject(object_id);
858 pos = cao->getPosition();
859 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
866 if (client_id != -1) {
867 // for ephemeral sounds, server_id is not meaningful
869 m_sounds_server_to_client[server_id] = client_id;
870 m_sounds_client_to_server[client_id] = server_id;
873 m_sounds_to_objects[client_id] = object_id;
877 void Client::handleCommand_StopSound(NetworkPacket* pkt)
883 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
884 if (i != m_sounds_server_to_client.end()) {
885 int client_id = i->second;
886 m_sound->stopSound(client_id);
890 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
896 *pkt >> sound_id >> step >> gain;
898 std::unordered_map<s32, int>::const_iterator i =
899 m_sounds_server_to_client.find(sound_id);
901 if (i != m_sounds_server_to_client.end())
902 m_sound->fadeSound(i->second, step, gain);
905 void Client::handleCommand_Privileges(NetworkPacket* pkt)
907 m_privileges.clear();
908 infostream << "Client: Privileges updated: ";
911 *pkt >> num_privileges;
913 for (u16 i = 0; i < num_privileges; i++) {
918 m_privileges.insert(priv);
919 infostream << priv << " ";
921 infostream << std::endl;
924 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
926 LocalPlayer *player = m_env.getLocalPlayer();
927 assert(player != NULL);
929 // Store formspec in LocalPlayer
930 player->inventory_formspec = pkt->readLongString();
933 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
936 bool keep_inv = true;
937 *pkt >> name >> keep_inv;
939 infostream << "Client: Detached inventory update: \"" << name
940 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
942 const auto &inv_it = m_detached_inventories.find(name);
944 if (inv_it != m_detached_inventories.end()) {
945 delete inv_it->second;
946 m_detached_inventories.erase(inv_it);
950 Inventory *inv = nullptr;
951 if (inv_it == m_detached_inventories.end()) {
952 inv = new Inventory(m_itemdef);
953 m_detached_inventories[name] = inv;
955 inv = inv_it->second;
959 *pkt >> ignore; // this used to be the length of the following string, ignore it
961 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
962 std::istringstream is(contents, std::ios::binary);
963 inv->deSerialize(is);
966 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
968 std::string formspec = pkt->readLongString();
969 std::string formname;
973 ClientEvent *event = new ClientEvent();
974 event->type = CE_SHOW_FORMSPEC;
975 // pointer is required as event is a struct only!
976 // adding a std:string to a struct isn't possible
977 event->show_formspec.formspec = new std::string(formspec);
978 event->show_formspec.formname = new std::string(formname);
979 m_client_event_queue.push(event);
982 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
984 std::string datastring(pkt->getString(0), pkt->getSize());
985 std::istringstream is(datastring, std::ios_base::binary);
987 ParticleParameters p;
988 p.deSerialize(is, m_proto_ver);
990 ClientEvent *event = new ClientEvent();
991 event->type = CE_SPAWN_PARTICLE;
992 event->spawn_particle = new ParticleParameters(p);
994 if (m_mods_loaded && m_script->on_spawn_particle(*event->spawn_particle))
997 m_client_event_queue.push(event);
1000 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
1002 std::string datastring(pkt->getString(0), pkt->getSize());
1003 std::istringstream is(datastring, std::ios_base::binary);
1005 ParticleSpawnerParameters p;
1007 u16 attached_id = 0;
1009 p.amount = readU16(is);
1010 p.time = readF32(is);
1011 p.minpos = readV3F32(is);
1012 p.maxpos = readV3F32(is);
1013 p.minvel = readV3F32(is);
1014 p.maxvel = readV3F32(is);
1015 p.minacc = readV3F32(is);
1016 p.maxacc = readV3F32(is);
1017 p.minexptime = readF32(is);
1018 p.maxexptime = readF32(is);
1019 p.minsize = readF32(is);
1020 p.maxsize = readF32(is);
1021 p.collisiondetection = readU8(is);
1022 p.texture = deSerializeString32(is);
1024 server_id = readU32(is);
1026 p.vertical = readU8(is);
1027 p.collision_removal = readU8(is);
1029 attached_id = readU16(is);
1031 p.animation.deSerialize(is, m_proto_ver);
1032 p.glow = readU8(is);
1033 p.object_collision = readU8(is);
1035 // This is kinda awful
1037 u16 tmp_param0 = readU16(is);
1040 p.node.param0 = tmp_param0;
1041 p.node.param2 = readU8(is);
1042 p.node_tile = readU8(is);
1045 auto event = new ClientEvent();
1046 event->type = CE_ADD_PARTICLESPAWNER;
1047 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1048 event->add_particlespawner.attached_id = attached_id;
1049 event->add_particlespawner.id = server_id;
1051 m_client_event_queue.push(event);
1055 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1060 ClientEvent *event = new ClientEvent();
1061 event->type = CE_DELETE_PARTICLESPAWNER;
1062 event->delete_particlespawner.id = server_id;
1064 m_client_event_queue.push(event);
1067 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1086 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1087 >> dir >> align >> offset;
1094 } catch(PacketError &e) {};
1096 ClientEvent *event = new ClientEvent();
1097 event->type = CE_HUDADD;
1098 event->hudadd = new ClientEventHudAdd();
1099 event->hudadd->server_id = server_id;
1100 event->hudadd->type = type;
1101 event->hudadd->pos = pos;
1102 event->hudadd->name = name;
1103 event->hudadd->scale = scale;
1104 event->hudadd->text = text;
1105 event->hudadd->number = number;
1106 event->hudadd->item = item;
1107 event->hudadd->dir = dir;
1108 event->hudadd->align = align;
1109 event->hudadd->offset = offset;
1110 event->hudadd->world_pos = world_pos;
1111 event->hudadd->size = size;
1112 event->hudadd->z_index = z_index;
1113 event->hudadd->text2 = text2;
1114 event->hudadd->style = style;
1115 m_client_event_queue.push(event);
1118 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1124 ClientEvent *event = new ClientEvent();
1125 event->type = CE_HUDRM;
1126 event->hudrm.id = server_id;
1127 m_client_event_queue.push(event);
1130 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1140 *pkt >> server_id >> stat;
1142 // Keep in sync with:server.cpp -> SendHUDChange
1143 switch ((HudElementStat)stat) {
1145 case HUD_STAT_SCALE:
1146 case HUD_STAT_ALIGN:
1147 case HUD_STAT_OFFSET:
1152 case HUD_STAT_TEXT2:
1155 case HUD_STAT_WORLD_POS:
1166 ClientEvent *event = new ClientEvent();
1167 event->type = CE_HUDCHANGE;
1168 event->hudchange = new ClientEventHudChange();
1169 event->hudchange->id = server_id;
1170 event->hudchange->stat = static_cast<HudElementStat>(stat);
1171 event->hudchange->v2fdata = v2fdata;
1172 event->hudchange->v3fdata = v3fdata;
1173 event->hudchange->sdata = sdata;
1174 event->hudchange->data = intdata;
1175 event->hudchange->v2s32data = v2s32data;
1176 m_client_event_queue.push(event);
1179 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1183 *pkt >> flags >> mask;
1185 LocalPlayer *player = m_env.getLocalPlayer();
1186 assert(player != NULL);
1188 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1189 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1191 player->hud_flags &= ~mask;
1192 player->hud_flags |= flags;
1194 if (g_settings->getBool("hud_flags_bypass"))
1195 player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE |
1196 HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
1197 HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE |
1198 HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1200 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1201 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1203 // Not so satisying code to keep compatibility with old fixed mode system
1206 // Hide minimap if it has been disabled by the server
1207 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1208 // defers a minimap update, therefore only call it if really
1209 // needed, by checking that minimap was visible before
1210 m_minimap->setModeIndex(0);
1212 // If radar has been disabled, try to find a non radar mode or fall back to 0
1213 if (m_minimap && m_minimap_radar_disabled_by_server
1214 && was_minimap_radar_visible) {
1215 while (m_minimap->getModeIndex() > 0 &&
1216 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1217 m_minimap->nextMode();
1220 // End of 'not so satifying code'
1223 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1225 u16 param; std::string value;
1227 *pkt >> param >> value;
1229 LocalPlayer *player = m_env.getLocalPlayer();
1230 assert(player != NULL);
1232 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1233 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1234 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1235 player->hud_hotbar_itemcount = hotbar_itemcount;
1237 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1238 player->hotbar_image = value;
1240 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1241 player->hotbar_selected_image = value;
1245 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1247 if (m_proto_ver < 39) {
1248 // Handle Protocol 38 and below servers with old set_sky,
1249 // ensuring the classic look is kept.
1250 std::string datastring(pkt->getString(0), pkt->getSize());
1251 std::istringstream is(datastring, std::ios_base::binary);
1253 SkyboxParams skybox;
1254 skybox.bgcolor = video::SColor(readARGB8(is));
1255 skybox.type = std::string(deSerializeString16(is));
1256 u16 count = readU16(is);
1258 for (size_t i = 0; i < count; i++)
1259 skybox.textures.emplace_back(deSerializeString16(is));
1261 skybox.clouds = true;
1263 skybox.clouds = readU8(is);
1266 // Use default skybox settings:
1267 SunParams sun = SkyboxDefaults::getSunDefaults();
1268 MoonParams moon = SkyboxDefaults::getMoonDefaults();
1269 StarParams stars = SkyboxDefaults::getStarDefaults();
1271 // Fix for "regular" skies, as color isn't kept:
1272 if (skybox.type == "regular") {
1273 skybox.sky_color = SkyboxDefaults::getSkyColorDefaults();
1274 skybox.fog_tint_type = "default";
1275 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1276 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1278 sun.visible = false;
1279 sun.sunrise_visible = false;
1280 moon.visible = false;
1281 stars.visible = false;
1284 // Skybox, sun, moon and stars ClientEvents:
1285 ClientEvent *sky_event = new ClientEvent();
1286 sky_event->type = CE_SET_SKY;
1287 sky_event->set_sky = new SkyboxParams(skybox);
1288 m_client_event_queue.push(sky_event);
1290 ClientEvent *sun_event = new ClientEvent();
1291 sun_event->type = CE_SET_SUN;
1292 sun_event->sun_params = new SunParams(sun);
1293 m_client_event_queue.push(sun_event);
1295 ClientEvent *moon_event = new ClientEvent();
1296 moon_event->type = CE_SET_MOON;
1297 moon_event->moon_params = new MoonParams(moon);
1298 m_client_event_queue.push(moon_event);
1300 ClientEvent *star_event = new ClientEvent();
1301 star_event->type = CE_SET_STARS;
1302 star_event->star_params = new StarParams(stars);
1303 m_client_event_queue.push(star_event);
1305 SkyboxParams skybox;
1307 std::string texture;
1309 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1310 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1312 if (skybox.type == "skybox") {
1313 *pkt >> texture_count;
1314 for (int i = 0; i < texture_count; i++) {
1316 skybox.textures.emplace_back(texture);
1319 else if (skybox.type == "regular") {
1320 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1321 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1322 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1323 >> skybox.sky_color.indoors;
1326 ClientEvent *event = new ClientEvent();
1327 event->type = CE_SET_SKY;
1328 event->set_sky = new SkyboxParams(skybox);
1329 m_client_event_queue.push(event);
1333 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1337 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1338 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1340 ClientEvent *event = new ClientEvent();
1341 event->type = CE_SET_SUN;
1342 event->sun_params = new SunParams(sun);
1343 m_client_event_queue.push(event);
1346 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1350 *pkt >> moon.visible >> moon.texture
1351 >> moon.tonemap >> moon.scale;
1353 ClientEvent *event = new ClientEvent();
1354 event->type = CE_SET_MOON;
1355 event->moon_params = new MoonParams(moon);
1356 m_client_event_queue.push(event);
1359 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1363 *pkt >> stars.visible >> stars.count
1364 >> stars.starcolor >> stars.scale;
1366 ClientEvent *event = new ClientEvent();
1367 event->type = CE_SET_STARS;
1368 event->star_params = new StarParams(stars);
1370 m_client_event_queue.push(event);
1373 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1376 video::SColor color_bright;
1377 video::SColor color_ambient;
1382 *pkt >> density >> color_bright >> color_ambient
1383 >> height >> thickness >> speed;
1385 ClientEvent *event = new ClientEvent();
1386 event->type = CE_CLOUD_PARAMS;
1387 event->cloud_params.density = density;
1388 // use the underlying u32 representation, because we can't
1389 // use struct members with constructors here, and this way
1390 // we avoid using new() and delete() for no good reason
1391 event->cloud_params.color_bright = color_bright.color;
1392 event->cloud_params.color_ambient = color_ambient.color;
1393 event->cloud_params.height = height;
1394 event->cloud_params.thickness = thickness;
1395 // same here: deconstruct to skip constructor
1396 event->cloud_params.speed_x = speed.X;
1397 event->cloud_params.speed_y = speed.Y;
1398 m_client_event_queue.push(event);
1401 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1404 u16 day_night_ratio_u;
1406 *pkt >> do_override >> day_night_ratio_u;
1408 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1410 ClientEvent *event = new ClientEvent();
1411 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1412 event->override_day_night_ratio.do_override = do_override;
1413 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1414 m_client_event_queue.push(event);
1417 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1419 LocalPlayer *player = m_env.getLocalPlayer();
1420 assert(player != NULL);
1422 *pkt >> player->local_animations[0];
1423 *pkt >> player->local_animations[1];
1424 *pkt >> player->local_animations[2];
1425 *pkt >> player->local_animations[3];
1426 *pkt >> player->local_animation_speed;
1428 player->last_animation = -1;
1431 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1433 LocalPlayer *player = m_env.getLocalPlayer();
1434 assert(player != NULL);
1436 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1439 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1443 *pkt >> type >> num_players;
1444 PlayerListModifer notice_type = (PlayerListModifer) type;
1446 for (u16 i = 0; i < num_players; i++) {
1449 switch (notice_type) {
1450 case PLAYER_LIST_INIT:
1451 case PLAYER_LIST_ADD:
1452 m_env.addPlayerName(name);
1454 case PLAYER_LIST_REMOVE:
1455 m_env.removePlayerName(name);
1461 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1463 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1464 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1465 errorstream << "Client: Received SRP S_B login message,"
1466 << " but wasn't supposed to (chosen_mech="
1467 << m_chosen_auth_mech << ")." << std::endl;
1473 SRPUser *usr = (SRPUser *) m_auth_data;
1478 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1480 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1481 (const unsigned char *) B.c_str(), B.size(),
1482 (unsigned char **) &bytes_M, &len_M);
1485 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1489 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1490 resp_pkt << std::string(bytes_M, len_M);
1494 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1496 LocalPlayer *player = m_env.getLocalPlayer();
1497 assert(player != NULL);
1499 // Store formspec in LocalPlayer
1500 *pkt >> player->formspec_prepend;
1503 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1505 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1507 // Restrictions were received -> load mods if it's enabled
1508 // Note: this should be moved after mods receptions from server instead
1512 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1514 if (g_settings->getBool("antiknockback"))
1520 LocalPlayer *player = m_env.getLocalPlayer();
1521 assert(player != NULL);
1522 player->addVelocity(added_vel);
1525 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1527 std::string raw_hash, filename, filedata;
1531 *pkt >> raw_hash >> filename >> cached;
1532 if (m_proto_ver >= 40)
1535 filedata = pkt->readLongString();
1537 if (raw_hash.size() != 20 || filename.empty() ||
1538 (m_proto_ver < 40 && filedata.empty()) ||
1539 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1540 throw PacketError("Illegal filename, data or hash");
1543 verbosestream << "Server pushes media file \"" << filename << "\" ";
1544 if (filedata.empty())
1545 verbosestream << "to be fetched ";
1547 verbosestream << "with " << filedata.size() << " bytes ";
1548 verbosestream << "(cached=" << cached << ")" << std::endl;
1550 if (m_media_pushed_files.count(filename) != 0) {
1551 // Ignore (but acknowledge). Previously this was for sync purposes,
1552 // but even in new versions media cannot be replaced at runtime.
1553 if (m_proto_ver >= 40)
1554 sendHaveMedia({ token });
1558 if (!filedata.empty()) {
1560 // Compute and check checksum of data
1561 std::string computed_hash;
1564 ctx.addBytes(filedata.c_str(), filedata.size());
1565 unsigned char *buf = ctx.getDigest();
1566 computed_hash.assign((char*) buf, 20);
1569 if (raw_hash != computed_hash) {
1570 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1574 // Actually load media
1575 loadMedia(filedata, filename, true);
1576 m_media_pushed_files.insert(filename);
1578 // Cache file for the next time when this client joins the same server
1580 clientMediaUpdateCache(raw_hash, filedata);
1584 m_media_pushed_files.insert(filename);
1586 // create a downloader for this file
1587 auto downloader(std::make_shared<SingleMediaDownloader>(cached));
1588 m_pending_media_downloads.emplace_back(token, downloader);
1589 downloader->addFile(filename, raw_hash);
1590 for (const auto &baseurl : m_remote_media_servers)
1591 downloader->addRemoteServer(baseurl);
1593 downloader->step(this);
1600 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1602 std::string channel_name, sender, channel_msg;
1603 *pkt >> channel_name >> sender >> channel_msg;
1605 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1606 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1607 << channel_msg << std::endl;
1609 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1610 verbosestream << "Server sent us messages on unregistered channel "
1611 << channel_name << ", ignoring." << std::endl;
1615 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1618 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1621 ModChannelSignal signal;
1622 std::string channel;
1624 *pkt >> signal_tmp >> channel;
1626 signal = (ModChannelSignal)signal_tmp;
1628 bool valid_signal = true;
1629 // @TODO: send Signal to Lua API
1631 case MODCHANNEL_SIGNAL_JOIN_OK:
1632 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1633 infostream << "Server ack our mod channel join on channel `" << channel
1634 << "`, joining." << std::endl;
1636 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1637 // Unable to join, remove channel
1638 m_modchannel_mgr->leaveChannel(channel, 0);
1639 infostream << "Server refused our mod channel join on channel `" << channel
1640 << "`" << std::endl;
1642 case MODCHANNEL_SIGNAL_LEAVE_OK:
1644 infostream << "Server ack our mod channel leave on channel " << channel
1645 << "`, leaving." << std::endl;
1648 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1649 infostream << "Server refused our mod channel leave on channel `" << channel
1650 << "`" << std::endl;
1652 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1654 // Generally unused, but ensure we don't do an implementation error
1655 infostream << "Server tells us we sent a message on channel `" << channel
1656 << "` but we are not registered. Message was dropped." << std::endl;
1659 case MODCHANNEL_SIGNAL_SET_STATE: {
1663 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1664 infostream << "Received wrong channel state " << state
1665 << ", ignoring." << std::endl;
1669 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1670 infostream << "Server sets mod channel `" << channel
1671 << "` in read-only mode." << std::endl;
1676 warningstream << "Received unhandled mod channel signal ID "
1677 << signal << ", ignoring." << std::endl;
1679 valid_signal = false;
1683 // If signal is valid, forward it to client side mods
1685 m_script->on_modchannel_signal(channel, signal);
1688 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1691 u16 mode; // wanted current mode index after change
1693 *pkt >> count >> mode;
1696 m_minimap->clearModes();
1698 for (size_t index = 0; index < count; index++) {
1702 std::string texture;
1705 *pkt >> type >> label >> size >> texture >> scale;
1708 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1712 m_minimap->setModeIndex(mode);
1715 void Client::handleCommand_SetLighting(NetworkPacket *pkt)
1717 Lighting& lighting = m_env.getLocalPlayer()->getLighting();
1719 if (pkt->getRemainingBytes() >= 4)
1720 *pkt >> lighting.shadow_intensity;