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) {};
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 (g_settings->getBool("log_particles")) {
992 std::cout << p.pos.X << " " << p.pos.Y << " " << p.pos.Z << std::endl;
995 m_client_event_queue.push(event);
998 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
1000 std::string datastring(pkt->getString(0), pkt->getSize());
1001 std::istringstream is(datastring, std::ios_base::binary);
1003 ParticleSpawnerParameters p;
1005 u16 attached_id = 0;
1007 p.amount = readU16(is);
1008 p.time = readF32(is);
1009 p.minpos = readV3F32(is);
1010 p.maxpos = readV3F32(is);
1011 p.minvel = readV3F32(is);
1012 p.maxvel = readV3F32(is);
1013 p.minacc = readV3F32(is);
1014 p.maxacc = readV3F32(is);
1015 p.minexptime = readF32(is);
1016 p.maxexptime = readF32(is);
1017 p.minsize = readF32(is);
1018 p.maxsize = readF32(is);
1019 p.collisiondetection = readU8(is);
1020 p.texture = deSerializeLongString(is);
1022 server_id = readU32(is);
1024 p.vertical = readU8(is);
1025 p.collision_removal = readU8(is);
1027 attached_id = readU16(is);
1029 p.animation.deSerialize(is, m_proto_ver);
1030 p.glow = readU8(is);
1031 p.object_collision = readU8(is);
1033 // This is kinda awful
1035 u16 tmp_param0 = readU16(is);
1038 p.node.param0 = tmp_param0;
1039 p.node.param2 = readU8(is);
1040 p.node_tile = readU8(is);
1043 auto event = new ClientEvent();
1044 event->type = CE_ADD_PARTICLESPAWNER;
1045 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1046 event->add_particlespawner.attached_id = attached_id;
1047 event->add_particlespawner.id = server_id;
1049 m_client_event_queue.push(event);
1053 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1058 ClientEvent *event = new ClientEvent();
1059 event->type = CE_DELETE_PARTICLESPAWNER;
1060 event->delete_particlespawner.id = server_id;
1062 m_client_event_queue.push(event);
1065 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1067 std::string datastring(pkt->getString(0), pkt->getSize());
1068 std::istringstream is(datastring, std::ios_base::binary);
1086 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1087 >> dir >> align >> offset;
1093 } catch(PacketError &e) {};
1095 ClientEvent *event = new ClientEvent();
1096 event->type = CE_HUDADD;
1097 event->hudadd.server_id = server_id;
1098 event->hudadd.type = type;
1099 event->hudadd.pos = new v2f(pos);
1100 event->hudadd.name = new std::string(name);
1101 event->hudadd.scale = new v2f(scale);
1102 event->hudadd.text = new std::string(text);
1103 event->hudadd.number = number;
1104 event->hudadd.item = item;
1105 event->hudadd.dir = dir;
1106 event->hudadd.align = new v2f(align);
1107 event->hudadd.offset = new v2f(offset);
1108 event->hudadd.world_pos = new v3f(world_pos);
1109 event->hudadd.size = new v2s32(size);
1110 event->hudadd.z_index = z_index;
1111 event->hudadd.text2 = new std::string(text2);
1112 m_client_event_queue.push(event);
1115 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1121 auto i = m_hud_server_to_client.find(server_id);
1122 if (i != m_hud_server_to_client.end()) {
1123 int client_id = i->second;
1124 m_hud_server_to_client.erase(i);
1126 ClientEvent *event = new ClientEvent();
1127 event->type = CE_HUDRM;
1128 event->hudrm.id = client_id;
1129 m_client_event_queue.push(event);
1133 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1143 *pkt >> server_id >> stat;
1145 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1146 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1148 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1150 else if (stat == HUD_STAT_WORLD_POS)
1152 else if (stat == HUD_STAT_SIZE )
1157 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1158 if (i != m_hud_server_to_client.end()) {
1159 ClientEvent *event = new ClientEvent();
1160 event->type = CE_HUDCHANGE;
1161 event->hudchange.id = i->second;
1162 event->hudchange.stat = (HudElementStat)stat;
1163 event->hudchange.v2fdata = new v2f(v2fdata);
1164 event->hudchange.v3fdata = new v3f(v3fdata);
1165 event->hudchange.sdata = new std::string(sdata);
1166 event->hudchange.data = intdata;
1167 event->hudchange.v2s32data = new v2s32(v2s32data);
1168 m_client_event_queue.push(event);
1172 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1176 *pkt >> flags >> mask;
1178 LocalPlayer *player = m_env.getLocalPlayer();
1179 assert(player != NULL);
1181 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1182 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1184 player->hud_flags &= ~mask;
1185 player->hud_flags |= flags;
1187 if (g_settings->getBool("hud_flags_bypass"))
1188 player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE |
1189 HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
1190 HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE |
1191 HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1194 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1195 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1197 // Hide minimap if it has been disabled by the server
1198 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1199 // defers a minimap update, therefore only call it if really
1200 // needed, by checking that minimap was visible before
1201 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1203 // Switch to surface mode if radar disabled by server
1204 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1205 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1210 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1212 u16 param; std::string value;
1214 *pkt >> param >> value;
1216 LocalPlayer *player = m_env.getLocalPlayer();
1217 assert(player != NULL);
1219 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1220 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1221 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1222 player->hud_hotbar_itemcount = hotbar_itemcount;
1224 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1225 player->hotbar_image = value;
1227 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1228 player->hotbar_selected_image = value;
1232 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1234 if (m_proto_ver < 39) {
1235 // Handle Protocol 38 and below servers with old set_sky,
1236 // ensuring the classic look is kept.
1237 std::string datastring(pkt->getString(0), pkt->getSize());
1238 std::istringstream is(datastring, std::ios_base::binary);
1240 SkyboxParams skybox;
1241 skybox.bgcolor = video::SColor(readARGB8(is));
1242 skybox.type = std::string(deSerializeString(is));
1243 u16 count = readU16(is);
1245 for (size_t i = 0; i < count; i++)
1246 skybox.textures.emplace_back(deSerializeString(is));
1248 skybox.clouds = true;
1250 skybox.clouds = readU8(is);
1253 // Use default skybox settings:
1254 SkyboxDefaults sky_defaults;
1255 SunParams sun = sky_defaults.getSunDefaults();
1256 MoonParams moon = sky_defaults.getMoonDefaults();
1257 StarParams stars = sky_defaults.getStarDefaults();
1259 // Fix for "regular" skies, as color isn't kept:
1260 if (skybox.type == "regular") {
1261 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1262 skybox.fog_tint_type = "default";
1263 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1264 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1267 sun.visible = false;
1268 sun.sunrise_visible = false;
1269 moon.visible = false;
1270 stars.visible = false;
1273 // Skybox, sun, moon and stars ClientEvents:
1274 ClientEvent *sky_event = new ClientEvent();
1275 sky_event->type = CE_SET_SKY;
1276 sky_event->set_sky = new SkyboxParams(skybox);
1277 m_client_event_queue.push(sky_event);
1279 ClientEvent *sun_event = new ClientEvent();
1280 sun_event->type = CE_SET_SUN;
1281 sun_event->sun_params = new SunParams(sun);
1282 m_client_event_queue.push(sun_event);
1284 ClientEvent *moon_event = new ClientEvent();
1285 moon_event->type = CE_SET_MOON;
1286 moon_event->moon_params = new MoonParams(moon);
1287 m_client_event_queue.push(moon_event);
1289 ClientEvent *star_event = new ClientEvent();
1290 star_event->type = CE_SET_STARS;
1291 star_event->star_params = new StarParams(stars);
1292 m_client_event_queue.push(star_event);
1294 SkyboxParams skybox;
1296 std::string texture;
1298 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1299 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1301 if (skybox.type == "skybox") {
1302 *pkt >> texture_count;
1303 for (int i = 0; i < texture_count; i++) {
1305 skybox.textures.emplace_back(texture);
1308 else if (skybox.type == "regular") {
1309 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1310 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1311 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1312 >> skybox.sky_color.indoors;
1315 ClientEvent *event = new ClientEvent();
1316 event->type = CE_SET_SKY;
1317 event->set_sky = new SkyboxParams(skybox);
1318 m_client_event_queue.push(event);
1322 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1326 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1327 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1329 ClientEvent *event = new ClientEvent();
1330 event->type = CE_SET_SUN;
1331 event->sun_params = new SunParams(sun);
1332 m_client_event_queue.push(event);
1335 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1339 *pkt >> moon.visible >> moon.texture
1340 >> moon.tonemap >> moon.scale;
1342 ClientEvent *event = new ClientEvent();
1343 event->type = CE_SET_MOON;
1344 event->moon_params = new MoonParams(moon);
1345 m_client_event_queue.push(event);
1348 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1352 *pkt >> stars.visible >> stars.count
1353 >> stars.starcolor >> stars.scale;
1355 ClientEvent *event = new ClientEvent();
1356 event->type = CE_SET_STARS;
1357 event->star_params = new StarParams(stars);
1359 m_client_event_queue.push(event);
1362 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1365 video::SColor color_bright;
1366 video::SColor color_ambient;
1371 *pkt >> density >> color_bright >> color_ambient
1372 >> height >> thickness >> speed;
1374 ClientEvent *event = new ClientEvent();
1375 event->type = CE_CLOUD_PARAMS;
1376 event->cloud_params.density = density;
1377 // use the underlying u32 representation, because we can't
1378 // use struct members with constructors here, and this way
1379 // we avoid using new() and delete() for no good reason
1380 event->cloud_params.color_bright = color_bright.color;
1381 event->cloud_params.color_ambient = color_ambient.color;
1382 event->cloud_params.height = height;
1383 event->cloud_params.thickness = thickness;
1384 // same here: deconstruct to skip constructor
1385 event->cloud_params.speed_x = speed.X;
1386 event->cloud_params.speed_y = speed.Y;
1387 m_client_event_queue.push(event);
1390 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1393 u16 day_night_ratio_u;
1395 *pkt >> do_override >> day_night_ratio_u;
1397 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1399 ClientEvent *event = new ClientEvent();
1400 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1401 event->override_day_night_ratio.do_override = do_override;
1402 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1403 m_client_event_queue.push(event);
1406 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1408 LocalPlayer *player = m_env.getLocalPlayer();
1409 assert(player != NULL);
1411 *pkt >> player->local_animations[0];
1412 *pkt >> player->local_animations[1];
1413 *pkt >> player->local_animations[2];
1414 *pkt >> player->local_animations[3];
1415 *pkt >> player->local_animation_speed;
1418 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1420 LocalPlayer *player = m_env.getLocalPlayer();
1421 assert(player != NULL);
1423 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1426 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1430 *pkt >> type >> num_players;
1431 PlayerListModifer notice_type = (PlayerListModifer) type;
1433 for (u16 i = 0; i < num_players; i++) {
1436 switch (notice_type) {
1437 case PLAYER_LIST_INIT:
1438 case PLAYER_LIST_ADD:
1439 m_env.addPlayerName(name);
1441 case PLAYER_LIST_REMOVE:
1442 m_env.removePlayerName(name);
1448 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1450 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1451 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1452 errorstream << "Client: Received SRP S_B login message,"
1453 << " but wasn't supposed to (chosen_mech="
1454 << m_chosen_auth_mech << ")." << std::endl;
1460 SRPUser *usr = (SRPUser *) m_auth_data;
1465 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1467 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1468 (const unsigned char *) B.c_str(), B.size(),
1469 (unsigned char **) &bytes_M, &len_M);
1472 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1476 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1477 resp_pkt << std::string(bytes_M, len_M);
1481 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1483 LocalPlayer *player = m_env.getLocalPlayer();
1484 assert(player != NULL);
1486 // Store formspec in LocalPlayer
1487 *pkt >> player->formspec_prepend;
1490 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1492 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1494 // Restrictions were received -> load mods if it's enabled
1495 // Note: this should be moved after mods receptions from server instead
1499 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1501 if (g_settings->getBool("antiknockback"))
1507 LocalPlayer *player = m_env.getLocalPlayer();
1508 assert(player != NULL);
1509 player->addVelocity(added_vel);
1512 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1514 std::string raw_hash, filename, filedata;
1517 *pkt >> raw_hash >> filename >> cached;
1518 filedata = pkt->readLongString();
1520 if (raw_hash.size() != 20 || filedata.empty() || filename.empty() ||
1521 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1522 throw PacketError("Illegal filename, data or hash");
1525 verbosestream << "Server pushes media file \"" << filename << "\" with "
1526 << filedata.size() << " bytes of data (cached=" << cached
1527 << ")" << std::endl;
1529 if (m_media_pushed_files.count(filename) != 0) {
1530 // Silently ignore for synchronization purposes
1534 // Compute and check checksum of data
1535 std::string computed_hash;
1538 ctx.addBytes(filedata.c_str(), filedata.size());
1539 unsigned char *buf = ctx.getDigest();
1540 computed_hash.assign((char*) buf, 20);
1543 if (raw_hash != computed_hash) {
1544 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1548 // Actually load media
1549 loadMedia(filedata, filename, true);
1550 m_media_pushed_files.insert(filename);
1552 // Cache file for the next time when this client joins the same server
1554 clientMediaUpdateCache(raw_hash, filedata);
1561 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1563 std::string channel_name, sender, channel_msg;
1564 *pkt >> channel_name >> sender >> channel_msg;
1566 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1567 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1568 << channel_msg << std::endl;
1570 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1571 verbosestream << "Server sent us messages on unregistered channel "
1572 << channel_name << ", ignoring." << std::endl;
1576 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1579 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1582 ModChannelSignal signal;
1583 std::string channel;
1585 *pkt >> signal_tmp >> channel;
1587 signal = (ModChannelSignal)signal_tmp;
1589 bool valid_signal = true;
1590 // @TODO: send Signal to Lua API
1592 case MODCHANNEL_SIGNAL_JOIN_OK:
1593 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1594 infostream << "Server ack our mod channel join on channel `" << channel
1595 << "`, joining." << std::endl;
1597 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1598 // Unable to join, remove channel
1599 m_modchannel_mgr->leaveChannel(channel, 0);
1600 infostream << "Server refused our mod channel join on channel `" << channel
1601 << "`" << std::endl;
1603 case MODCHANNEL_SIGNAL_LEAVE_OK:
1605 infostream << "Server ack our mod channel leave on channel " << channel
1606 << "`, leaving." << std::endl;
1609 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1610 infostream << "Server refused our mod channel leave on channel `" << channel
1611 << "`" << std::endl;
1613 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1615 // Generally unused, but ensure we don't do an implementation error
1616 infostream << "Server tells us we sent a message on channel `" << channel
1617 << "` but we are not registered. Message was dropped." << std::endl;
1620 case MODCHANNEL_SIGNAL_SET_STATE: {
1624 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1625 infostream << "Received wrong channel state " << state
1626 << ", ignoring." << std::endl;
1630 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1631 infostream << "Server sets mod channel `" << channel
1632 << "` in read-only mode." << std::endl;
1637 warningstream << "Received unhandled mod channel signal ID "
1638 << signal << ", ignoring." << std::endl;
1640 valid_signal = false;
1644 // If signal is valid, forward it to client side mods
1646 m_script->on_modchannel_signal(channel, signal);