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 = 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);
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) {};
837 SimpleSoundSpec sound_spec(name, gain, fade, pitch);
839 if (m_mods_loaded && m_script->on_play_sound(sound_spec))
846 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
848 case 1: // positional
849 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
853 ClientActiveObject *cao = m_env.getActiveObject(object_id);
855 pos = cao->getPosition();
856 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
863 if (client_id != -1) {
864 // for ephemeral sounds, server_id is not meaningful
866 m_sounds_server_to_client[server_id] = client_id;
867 m_sounds_client_to_server[client_id] = server_id;
870 m_sounds_to_objects[client_id] = object_id;
874 void Client::handleCommand_StopSound(NetworkPacket* pkt)
880 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
881 if (i != m_sounds_server_to_client.end()) {
882 int client_id = i->second;
883 m_sound->stopSound(client_id);
887 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
893 *pkt >> sound_id >> step >> gain;
895 std::unordered_map<s32, int>::const_iterator i =
896 m_sounds_server_to_client.find(sound_id);
898 if (i != m_sounds_server_to_client.end())
899 m_sound->fadeSound(i->second, step, gain);
902 void Client::handleCommand_Privileges(NetworkPacket* pkt)
904 m_privileges.clear();
905 infostream << "Client: Privileges updated: ";
908 *pkt >> num_privileges;
910 for (u16 i = 0; i < num_privileges; i++) {
915 m_privileges.insert(priv);
916 infostream << priv << " ";
918 infostream << std::endl;
921 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
923 LocalPlayer *player = m_env.getLocalPlayer();
924 assert(player != NULL);
926 // Store formspec in LocalPlayer
927 player->inventory_formspec = pkt->readLongString();
930 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
933 bool keep_inv = true;
934 *pkt >> name >> keep_inv;
936 infostream << "Client: Detached inventory update: \"" << name
937 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
939 const auto &inv_it = m_detached_inventories.find(name);
941 if (inv_it != m_detached_inventories.end()) {
942 delete inv_it->second;
943 m_detached_inventories.erase(inv_it);
947 Inventory *inv = nullptr;
948 if (inv_it == m_detached_inventories.end()) {
949 inv = new Inventory(m_itemdef);
950 m_detached_inventories[name] = inv;
952 inv = inv_it->second;
956 *pkt >> ignore; // this used to be the length of the following string, ignore it
958 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
959 std::istringstream is(contents, std::ios::binary);
960 inv->deSerialize(is);
963 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
965 std::string formspec = pkt->readLongString();
966 std::string formname;
970 ClientEvent *event = new ClientEvent();
971 event->type = CE_SHOW_FORMSPEC;
972 // pointer is required as event is a struct only!
973 // adding a std:string to a struct isn't possible
974 event->show_formspec.formspec = new std::string(formspec);
975 event->show_formspec.formname = new std::string(formname);
976 m_client_event_queue.push(event);
979 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
981 std::string datastring(pkt->getString(0), pkt->getSize());
982 std::istringstream is(datastring, std::ios_base::binary);
984 ParticleParameters p;
985 p.deSerialize(is, m_proto_ver);
987 ClientEvent *event = new ClientEvent();
988 event->type = CE_SPAWN_PARTICLE;
989 event->spawn_particle = new ParticleParameters(p);
991 if (m_mods_loaded && m_script->on_spawn_particle(*event->spawn_particle))
994 m_client_event_queue.push(event);
997 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
999 std::string datastring(pkt->getString(0), pkt->getSize());
1000 std::istringstream is(datastring, std::ios_base::binary);
1002 ParticleSpawnerParameters p;
1004 u16 attached_id = 0;
1006 p.amount = readU16(is);
1007 p.time = readF32(is);
1008 p.minpos = readV3F32(is);
1009 p.maxpos = readV3F32(is);
1010 p.minvel = readV3F32(is);
1011 p.maxvel = readV3F32(is);
1012 p.minacc = readV3F32(is);
1013 p.maxacc = readV3F32(is);
1014 p.minexptime = readF32(is);
1015 p.maxexptime = readF32(is);
1016 p.minsize = readF32(is);
1017 p.maxsize = readF32(is);
1018 p.collisiondetection = readU8(is);
1019 p.texture = deSerializeString32(is);
1021 server_id = readU32(is);
1023 p.vertical = readU8(is);
1024 p.collision_removal = readU8(is);
1026 attached_id = readU16(is);
1028 p.animation.deSerialize(is, m_proto_ver);
1029 p.glow = readU8(is);
1030 p.object_collision = readU8(is);
1032 // This is kinda awful
1034 u16 tmp_param0 = readU16(is);
1037 p.node.param0 = tmp_param0;
1038 p.node.param2 = readU8(is);
1039 p.node_tile = readU8(is);
1042 auto event = new ClientEvent();
1043 event->type = CE_ADD_PARTICLESPAWNER;
1044 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1045 event->add_particlespawner.attached_id = attached_id;
1046 event->add_particlespawner.id = server_id;
1048 m_client_event_queue.push(event);
1052 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1057 ClientEvent *event = new ClientEvent();
1058 event->type = CE_DELETE_PARTICLESPAWNER;
1059 event->delete_particlespawner.id = server_id;
1061 m_client_event_queue.push(event);
1064 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1066 std::string datastring(pkt->getString(0), pkt->getSize());
1067 std::istringstream is(datastring, std::ios_base::binary);
1085 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1086 >> dir >> align >> offset;
1092 } catch(PacketError &e) {};
1094 ClientEvent *event = new ClientEvent();
1095 event->type = CE_HUDADD;
1096 event->hudadd.server_id = server_id;
1097 event->hudadd.type = type;
1098 event->hudadd.pos = new v2f(pos);
1099 event->hudadd.name = new std::string(name);
1100 event->hudadd.scale = new v2f(scale);
1101 event->hudadd.text = new std::string(text);
1102 event->hudadd.number = number;
1103 event->hudadd.item = item;
1104 event->hudadd.dir = dir;
1105 event->hudadd.align = new v2f(align);
1106 event->hudadd.offset = new v2f(offset);
1107 event->hudadd.world_pos = new v3f(world_pos);
1108 event->hudadd.size = new v2s32(size);
1109 event->hudadd.z_index = z_index;
1110 event->hudadd.text2 = new std::string(text2);
1111 m_client_event_queue.push(event);
1114 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1120 auto i = m_hud_server_to_client.find(server_id);
1121 if (i != m_hud_server_to_client.end()) {
1122 int client_id = i->second;
1123 m_hud_server_to_client.erase(i);
1125 ClientEvent *event = new ClientEvent();
1126 event->type = CE_HUDRM;
1127 event->hudrm.id = client_id;
1128 m_client_event_queue.push(event);
1132 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1142 *pkt >> server_id >> stat;
1144 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1145 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1147 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1149 else if (stat == HUD_STAT_WORLD_POS)
1151 else if (stat == HUD_STAT_SIZE )
1156 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1157 if (i != m_hud_server_to_client.end()) {
1158 ClientEvent *event = new ClientEvent();
1159 event->type = CE_HUDCHANGE;
1160 event->hudchange.id = i->second;
1161 event->hudchange.stat = (HudElementStat)stat;
1162 event->hudchange.v2fdata = new v2f(v2fdata);
1163 event->hudchange.v3fdata = new v3f(v3fdata);
1164 event->hudchange.sdata = new std::string(sdata);
1165 event->hudchange.data = intdata;
1166 event->hudchange.v2s32data = new v2s32(v2s32data);
1167 m_client_event_queue.push(event);
1171 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1175 *pkt >> flags >> mask;
1177 LocalPlayer *player = m_env.getLocalPlayer();
1178 assert(player != NULL);
1180 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1181 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1183 player->hud_flags &= ~mask;
1184 player->hud_flags |= flags;
1186 if (g_settings->getBool("hud_flags_bypass"))
1187 player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE |
1188 HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
1189 HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE |
1190 HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1192 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1193 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1195 // Not so satisying code to keep compatibility with old fixed mode system
1198 // Hide minimap if it has been disabled by the server
1199 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1200 // defers a minimap update, therefore only call it if really
1201 // needed, by checking that minimap was visible before
1202 m_minimap->setModeIndex(0);
1204 // If radar has been disabled, try to find a non radar mode or fall back to 0
1205 if (m_minimap && m_minimap_radar_disabled_by_server
1206 && was_minimap_radar_visible) {
1207 while (m_minimap->getModeIndex() > 0 &&
1208 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1209 m_minimap->nextMode();
1212 // End of 'not so satifying code'
1215 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1217 u16 param; std::string value;
1219 *pkt >> param >> value;
1221 LocalPlayer *player = m_env.getLocalPlayer();
1222 assert(player != NULL);
1224 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1225 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1226 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1227 player->hud_hotbar_itemcount = hotbar_itemcount;
1229 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1230 player->hotbar_image = value;
1232 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1233 player->hotbar_selected_image = value;
1237 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1239 if (m_proto_ver < 39) {
1240 // Handle Protocol 38 and below servers with old set_sky,
1241 // ensuring the classic look is kept.
1242 std::string datastring(pkt->getString(0), pkt->getSize());
1243 std::istringstream is(datastring, std::ios_base::binary);
1245 SkyboxParams skybox;
1246 skybox.bgcolor = video::SColor(readARGB8(is));
1247 skybox.type = std::string(deSerializeString16(is));
1248 u16 count = readU16(is);
1250 for (size_t i = 0; i < count; i++)
1251 skybox.textures.emplace_back(deSerializeString16(is));
1253 skybox.clouds = true;
1255 skybox.clouds = readU8(is);
1258 // Use default skybox settings:
1259 SkyboxDefaults sky_defaults;
1260 SunParams sun = sky_defaults.getSunDefaults();
1261 MoonParams moon = sky_defaults.getMoonDefaults();
1262 StarParams stars = sky_defaults.getStarDefaults();
1264 // Fix for "regular" skies, as color isn't kept:
1265 if (skybox.type == "regular") {
1266 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1267 skybox.fog_tint_type = "default";
1268 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1269 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1272 sun.visible = false;
1273 sun.sunrise_visible = false;
1274 moon.visible = false;
1275 stars.visible = false;
1278 // Skybox, sun, moon and stars ClientEvents:
1279 ClientEvent *sky_event = new ClientEvent();
1280 sky_event->type = CE_SET_SKY;
1281 sky_event->set_sky = new SkyboxParams(skybox);
1282 m_client_event_queue.push(sky_event);
1284 ClientEvent *sun_event = new ClientEvent();
1285 sun_event->type = CE_SET_SUN;
1286 sun_event->sun_params = new SunParams(sun);
1287 m_client_event_queue.push(sun_event);
1289 ClientEvent *moon_event = new ClientEvent();
1290 moon_event->type = CE_SET_MOON;
1291 moon_event->moon_params = new MoonParams(moon);
1292 m_client_event_queue.push(moon_event);
1294 ClientEvent *star_event = new ClientEvent();
1295 star_event->type = CE_SET_STARS;
1296 star_event->star_params = new StarParams(stars);
1297 m_client_event_queue.push(star_event);
1299 SkyboxParams skybox;
1301 std::string texture;
1303 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1304 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1306 if (skybox.type == "skybox") {
1307 *pkt >> texture_count;
1308 for (int i = 0; i < texture_count; i++) {
1310 skybox.textures.emplace_back(texture);
1313 else if (skybox.type == "regular") {
1314 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1315 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1316 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1317 >> skybox.sky_color.indoors;
1320 ClientEvent *event = new ClientEvent();
1321 event->type = CE_SET_SKY;
1322 event->set_sky = new SkyboxParams(skybox);
1323 m_client_event_queue.push(event);
1327 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1331 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1332 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1334 ClientEvent *event = new ClientEvent();
1335 event->type = CE_SET_SUN;
1336 event->sun_params = new SunParams(sun);
1337 m_client_event_queue.push(event);
1340 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1344 *pkt >> moon.visible >> moon.texture
1345 >> moon.tonemap >> moon.scale;
1347 ClientEvent *event = new ClientEvent();
1348 event->type = CE_SET_MOON;
1349 event->moon_params = new MoonParams(moon);
1350 m_client_event_queue.push(event);
1353 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1357 *pkt >> stars.visible >> stars.count
1358 >> stars.starcolor >> stars.scale;
1360 ClientEvent *event = new ClientEvent();
1361 event->type = CE_SET_STARS;
1362 event->star_params = new StarParams(stars);
1364 m_client_event_queue.push(event);
1367 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1370 video::SColor color_bright;
1371 video::SColor color_ambient;
1376 *pkt >> density >> color_bright >> color_ambient
1377 >> height >> thickness >> speed;
1379 ClientEvent *event = new ClientEvent();
1380 event->type = CE_CLOUD_PARAMS;
1381 event->cloud_params.density = density;
1382 // use the underlying u32 representation, because we can't
1383 // use struct members with constructors here, and this way
1384 // we avoid using new() and delete() for no good reason
1385 event->cloud_params.color_bright = color_bright.color;
1386 event->cloud_params.color_ambient = color_ambient.color;
1387 event->cloud_params.height = height;
1388 event->cloud_params.thickness = thickness;
1389 // same here: deconstruct to skip constructor
1390 event->cloud_params.speed_x = speed.X;
1391 event->cloud_params.speed_y = speed.Y;
1392 m_client_event_queue.push(event);
1395 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1398 u16 day_night_ratio_u;
1400 *pkt >> do_override >> day_night_ratio_u;
1402 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1404 ClientEvent *event = new ClientEvent();
1405 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1406 event->override_day_night_ratio.do_override = do_override;
1407 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1408 m_client_event_queue.push(event);
1411 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1413 LocalPlayer *player = m_env.getLocalPlayer();
1414 assert(player != NULL);
1416 *pkt >> player->local_animations[0];
1417 *pkt >> player->local_animations[1];
1418 *pkt >> player->local_animations[2];
1419 *pkt >> player->local_animations[3];
1420 *pkt >> player->local_animation_speed;
1423 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1425 LocalPlayer *player = m_env.getLocalPlayer();
1426 assert(player != NULL);
1428 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1431 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1435 *pkt >> type >> num_players;
1436 PlayerListModifer notice_type = (PlayerListModifer) type;
1438 for (u16 i = 0; i < num_players; i++) {
1441 switch (notice_type) {
1442 case PLAYER_LIST_INIT:
1443 case PLAYER_LIST_ADD:
1444 m_env.addPlayerName(name);
1446 case PLAYER_LIST_REMOVE:
1447 m_env.removePlayerName(name);
1453 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1455 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1456 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1457 errorstream << "Client: Received SRP S_B login message,"
1458 << " but wasn't supposed to (chosen_mech="
1459 << m_chosen_auth_mech << ")." << std::endl;
1465 SRPUser *usr = (SRPUser *) m_auth_data;
1470 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1472 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1473 (const unsigned char *) B.c_str(), B.size(),
1474 (unsigned char **) &bytes_M, &len_M);
1477 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1481 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1482 resp_pkt << std::string(bytes_M, len_M);
1486 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1488 LocalPlayer *player = m_env.getLocalPlayer();
1489 assert(player != NULL);
1491 // Store formspec in LocalPlayer
1492 *pkt >> player->formspec_prepend;
1495 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1497 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1499 // Restrictions were received -> load mods if it's enabled
1500 // Note: this should be moved after mods receptions from server instead
1504 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1506 if (g_settings->getBool("antiknockback"))
1512 LocalPlayer *player = m_env.getLocalPlayer();
1513 assert(player != NULL);
1514 player->addVelocity(added_vel);
1517 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1519 std::string raw_hash, filename, filedata;
1522 *pkt >> raw_hash >> filename >> cached;
1523 filedata = pkt->readLongString();
1525 if (raw_hash.size() != 20 || filedata.empty() || filename.empty() ||
1526 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1527 throw PacketError("Illegal filename, data or hash");
1530 verbosestream << "Server pushes media file \"" << filename << "\" with "
1531 << filedata.size() << " bytes of data (cached=" << cached
1532 << ")" << std::endl;
1534 if (m_media_pushed_files.count(filename) != 0) {
1535 // Silently ignore for synchronization purposes
1539 // Compute and check checksum of data
1540 std::string computed_hash;
1543 ctx.addBytes(filedata.c_str(), filedata.size());
1544 unsigned char *buf = ctx.getDigest();
1545 computed_hash.assign((char*) buf, 20);
1548 if (raw_hash != computed_hash) {
1549 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1553 // Actually load media
1554 loadMedia(filedata, filename, true);
1555 m_media_pushed_files.insert(filename);
1557 // Cache file for the next time when this client joins the same server
1559 clientMediaUpdateCache(raw_hash, filedata);
1566 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1568 std::string channel_name, sender, channel_msg;
1569 *pkt >> channel_name >> sender >> channel_msg;
1571 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1572 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1573 << channel_msg << std::endl;
1575 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1576 verbosestream << "Server sent us messages on unregistered channel "
1577 << channel_name << ", ignoring." << std::endl;
1581 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1584 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1587 ModChannelSignal signal;
1588 std::string channel;
1590 *pkt >> signal_tmp >> channel;
1592 signal = (ModChannelSignal)signal_tmp;
1594 bool valid_signal = true;
1595 // @TODO: send Signal to Lua API
1597 case MODCHANNEL_SIGNAL_JOIN_OK:
1598 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1599 infostream << "Server ack our mod channel join on channel `" << channel
1600 << "`, joining." << std::endl;
1602 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1603 // Unable to join, remove channel
1604 m_modchannel_mgr->leaveChannel(channel, 0);
1605 infostream << "Server refused our mod channel join on channel `" << channel
1606 << "`" << std::endl;
1608 case MODCHANNEL_SIGNAL_LEAVE_OK:
1610 infostream << "Server ack our mod channel leave on channel " << channel
1611 << "`, leaving." << std::endl;
1614 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1615 infostream << "Server refused our mod channel leave on channel `" << channel
1616 << "`" << std::endl;
1618 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1620 // Generally unused, but ensure we don't do an implementation error
1621 infostream << "Server tells us we sent a message on channel `" << channel
1622 << "` but we are not registered. Message was dropped." << std::endl;
1625 case MODCHANNEL_SIGNAL_SET_STATE: {
1629 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1630 infostream << "Received wrong channel state " << state
1631 << ", ignoring." << std::endl;
1635 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1636 infostream << "Server sets mod channel `" << channel
1637 << "` in read-only mode." << std::endl;
1642 warningstream << "Received unhandled mod channel signal ID "
1643 << signal << ", ignoring." << std::endl;
1645 valid_signal = false;
1649 // If signal is valid, forward it to client side mods
1651 m_script->on_modchannel_signal(channel, signal);
1654 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1657 u16 mode; // wanted current mode index after change
1659 *pkt >> count >> mode;
1662 m_minimap->clearModes();
1664 for (size_t index = 0; index < count; index++) {
1668 std::string texture;
1671 *pkt >> type >> label >> size >> texture >> scale;
1674 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1678 m_minimap->setModeIndex(mode);