3 Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "client/client.h"
22 #include "util/base64.h"
23 #include "client/camera.h"
24 #include "chatmessage.h"
25 #include "client/clientmedia.h"
28 #include "mapsector.h"
29 #include "client/minimap.h"
30 #include "modchannels.h"
32 #include "serialization.h"
34 #include "util/strfnd.h"
35 #include "client/clientevent.h"
36 #include "client/sound.h"
37 #include "network/clientopcodes.h"
38 #include "network/connection.h"
39 #include "script/scripting_client.h"
40 #include "util/serialize.h"
42 #include "util/sha1.h"
43 #include "tileanimation.h"
45 #include "skyparams.h"
48 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
50 infostream << "Got deprecated command "
51 << toClientCommandTable[pkt->getCommand()].name << " from peer "
52 << pkt->getPeerId() << "!" << std::endl;
55 void Client::handleCommand_Hello(NetworkPacket* pkt)
57 if (pkt->getSize() < 1)
64 std::string username_legacy; // for case insensitivity
65 *pkt >> serialization_ver >> compression_mode >> proto_ver
66 >> auth_mechs >> username_legacy;
68 // Chose an auth method we support
69 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
71 infostream << "Client: TOCLIENT_HELLO received with "
72 << "serialization_ver=" << (u32)serialization_ver
73 << ", auth_mechs=" << auth_mechs
74 << ", proto_ver=" << proto_ver
75 << ", compression_mode=" << compression_mode
76 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
78 if (!ser_ver_supported(serialization_ver)) {
79 infostream << "Client: TOCLIENT_HELLO: Server sent "
80 << "unsupported ser_fmt_ver"<< std::endl;
84 m_server_ser_ver = serialization_ver;
85 m_proto_ver = proto_ver;
87 //TODO verify that username_legacy matches sent username, only
88 // differs in casing (make both uppercase and compare)
89 // This is only neccessary though when we actually want to add casing support
91 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
92 // we received a TOCLIENT_HELLO while auth was already going on
93 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
94 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
95 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
96 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
97 srp_user_delete((SRPUser *) m_auth_data);
102 // Authenticate using that method, or abort if there wasn't any method found
103 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
104 if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
105 !m_simple_singleplayer_mode &&
106 !getServerAddress().isLocalhost() &&
107 g_settings->getBool("enable_register_confirmation")) {
108 promptConfirmRegistration(chosen_auth_mechanism);
110 startAuth(chosen_auth_mechanism);
113 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
114 m_access_denied = true;
115 m_access_denied_reason = "Unknown";
121 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
126 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
127 >> m_sudo_auth_methods;
129 playerpos -= v3f(0, BS / 2, 0);
131 // Set player position
132 LocalPlayer *player = m_env.getLocalPlayer();
133 assert(player != NULL);
134 player->setPosition(playerpos);
136 infostream << "Client: received map seed: " << m_map_seed << std::endl;
137 infostream << "Client: received recommended send interval "
138 << m_recommended_send_interval<<std::endl;
141 /*~ DO NOT TRANSLATE THIS LITERALLY!
142 This is a special string which needs to contain the translation's
143 language code (e.g. "de" for German). */
144 std::string lang = gettext("LANG_CODE");
145 if (lang == "LANG_CODE")
148 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
154 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
158 m_password = m_new_password;
160 verbosestream << "Client: Received TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
162 // send packet to actually set the password
163 startAuth(AUTH_MECHANISM_FIRST_SRP);
166 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
168 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
170 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
171 L"Password change denied. Password NOT changed.");
172 pushToChatQueue(chatMessage);
173 // reset everything and be sad
177 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
179 // The server didn't like our password. Note, this needs
180 // to be processed even if the serialisation format has
181 // not been agreed yet, the same as TOCLIENT_INIT.
182 m_access_denied = true;
183 m_access_denied_reason = "Unknown";
185 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
186 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
187 // in some places of the server code
188 if (pkt->getSize() >= 2) {
189 std::wstring wide_reason;
191 m_access_denied_reason = wide_to_utf8(wide_reason);
196 if (pkt->getSize() < 1)
199 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
201 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
202 denyCode == SERVER_ACCESSDENIED_CRASH) {
203 *pkt >> m_access_denied_reason;
204 if (m_access_denied_reason.empty()) {
205 m_access_denied_reason = accessDeniedStrings[denyCode];
209 m_access_denied_reconnect = reconnect & 1;
210 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
211 *pkt >> m_access_denied_reason;
212 } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) {
213 m_access_denied_reason = accessDeniedStrings[denyCode];
214 m_access_denied_reconnect = true;
215 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
216 m_access_denied_reason = accessDeniedStrings[denyCode];
218 // Allow us to add new error messages to the
219 // protocol without raising the protocol version, if we want to.
220 // Until then (which may be never), this is outside
221 // of the defined protocol.
222 *pkt >> m_access_denied_reason;
223 if (m_access_denied_reason.empty()) {
224 m_access_denied_reason = "Unknown";
229 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
231 if (pkt->getSize() < 6)
239 void Client::handleCommand_AddNode(NetworkPacket* pkt)
241 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
248 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
250 bool remove_metadata = true;
251 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
252 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
253 remove_metadata = false;
256 addNode(p, n, remove_metadata);
259 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
261 if (pkt->getSize() < 1)
264 std::istringstream is(pkt->readLongString(), std::ios::binary);
265 std::stringstream sstr(std::ios::binary | std::ios::in | std::ios::out);
266 decompressZlib(is, sstr);
268 NodeMetadataList meta_updates_list(false);
269 meta_updates_list.deSerialize(sstr, m_itemdef, true);
271 Map &map = m_env.getMap();
272 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
273 i != meta_updates_list.end(); ++i) {
274 v3s16 pos = i->first;
276 if (map.isValidPosition(pos) &&
277 map.setNodeMetadata(pos, i->second))
278 continue; // Prevent from deleting metadata
280 // Meta couldn't be set, unused metadata
285 void Client::handleCommand_BlockData(NetworkPacket* pkt)
287 // Ignore too small packet
288 if (pkt->getSize() < 6)
294 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
295 std::istringstream istr(datastring, std::ios_base::binary);
301 sector = m_env.getMap().emergeSector(p2d);
303 assert(sector->getPos() == p2d);
305 block = sector->getBlockNoCreateNoEx(p.Y);
308 Update an existing block
310 block->deSerialize(istr, m_server_ser_ver, false);
311 block->deSerializeNetworkSpecific(istr);
317 block = new MapBlock(&m_env.getMap(), p, this);
318 block->deSerialize(istr, m_server_ser_ver, false);
319 block->deSerializeNetworkSpecific(istr);
320 sector->insertBlock(block);
324 ServerMap::saveBlock(block, m_localdb);
328 Add it to mesh update queue and set it to be acknowledged after update.
330 addUpdateMeshTaskWithEdge(p, true);
333 void Client::handleCommand_Inventory(NetworkPacket* pkt)
335 if (pkt->getSize() < 1)
338 std::string datastring(pkt->getString(0), pkt->getSize());
339 std::istringstream is(datastring, std::ios_base::binary);
341 LocalPlayer *player = m_env.getLocalPlayer();
342 assert(player != NULL);
344 player->inventory.deSerialize(is);
346 m_update_wielded_item = true;
348 delete m_inventory_from_server;
349 m_inventory_from_server = new Inventory(player->inventory);
350 m_inventory_from_server_age = 0.0;
353 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
355 if (pkt->getSize() < 2)
362 time_of_day = time_of_day % 24000;
363 float time_speed = 0;
365 if (pkt->getSize() >= 2 + 4) {
369 // Old message; try to approximate speed of time by ourselves
370 float time_of_day_f = (float)time_of_day / 24000.0f;
371 float tod_diff_f = 0;
373 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
374 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
376 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
378 m_last_time_of_day_f = time_of_day_f;
379 float time_diff = m_time_of_day_update_timer;
380 m_time_of_day_update_timer = 0;
382 if (m_time_of_day_set) {
383 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
384 infostream << "Client: Measured time_of_day speed (old format): "
385 << time_speed << " tod_diff_f=" << tod_diff_f
386 << " time_diff=" << time_diff << std::endl;
390 // Update environment
391 m_env.setTimeOfDay(time_of_day);
392 m_env.setTimeOfDaySpeed(time_speed);
393 m_time_of_day_set = true;
395 //u32 dr = m_env.getDayNightRatio();
396 //infostream << "Client: time_of_day=" << time_of_day
397 // << " time_speed=" << time_speed
398 // << " dr=" << dr << std::endl;
401 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
406 u16 sendername length
412 ChatMessage *chatMessage = new ChatMessage();
413 u8 version, message_type;
414 *pkt >> version >> message_type;
416 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
422 *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
423 chatMessage->timestamp = static_cast<std::time_t>(timestamp);
425 chatMessage->type = (ChatMessageType) message_type;
427 // @TODO send this to CSM using ChatMessage object
428 if (modsLoaded() && m_script->on_receiving_message(
429 wide_to_utf8(chatMessage->message))) {
430 // Message was consumed by CSM and should not be handled by client
433 pushToChatQueue(chatMessage);
437 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
440 u16 count of removed objects
441 for all removed objects {
444 u16 count of added objects
445 for all added objects {
448 u32 initialization data length
449 string initialization data
455 u16 removed_count, added_count, id;
457 // Read removed objects
458 *pkt >> removed_count;
460 for (u16 i = 0; i < removed_count; i++) {
462 m_env.removeActiveObject(id);
465 // Read added objects
468 for (u16 i = 0; i < added_count; i++) {
470 m_env.addActiveObject(id, type, pkt->readLongString());
472 } catch (PacketError &e) {
473 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
474 << ". The packet is unreliable, ignoring" << std::endl;
477 // m_activeobjects_received is false before the first
478 // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
479 m_activeobjects_received = true;
482 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
492 std::string datastring(pkt->getString(0), pkt->getSize());
493 std::istringstream is(datastring, std::ios_base::binary);
497 u16 id = readU16(is);
501 std::string message = deSerializeString16(is);
503 // Pass on to the environment
504 m_env.processActiveObjectMessage(id, message);
506 } catch (SerializationError &e) {
507 errorstream << "Client::handleCommand_ActiveObjectMessages: "
508 << "caught SerializationError: " << e.what() << std::endl;
512 void Client::handleCommand_Movement(NetworkPacket* pkt)
514 LocalPlayer *player = m_env.getLocalPlayer();
515 assert(player != NULL);
517 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
519 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
520 >> lf >> lfs >> ls >> g;
522 player->movement_acceleration_default = mad * BS;
523 player->movement_acceleration_air = maa * BS;
524 player->movement_acceleration_fast = maf * BS;
525 player->movement_speed_walk = msw * BS;
526 player->movement_speed_crouch = mscr * BS;
527 player->movement_speed_fast = msf * BS;
528 player->movement_speed_climb = mscl * BS;
529 player->movement_speed_jump = msj * BS;
530 player->movement_liquid_fluidity = lf * BS;
531 player->movement_liquid_fluidity_smooth = lfs * BS;
532 player->movement_liquid_sink = ls * BS;
533 player->movement_gravity = g * BS;
536 void Client::handleCommand_Fov(NetworkPacket *pkt)
539 bool is_multiplier = false;
540 f32 transition_time = 0.0f;
542 *pkt >> fov >> is_multiplier;
544 // Wrap transition_time extraction within a
545 // try-catch to preserve backwards compat
547 *pkt >> transition_time;
548 } catch (PacketError &e) {};
550 LocalPlayer *player = m_env.getLocalPlayer();
552 player->setFov({ fov, is_multiplier, transition_time });
553 m_camera->notifyFovChange();
556 void Client::handleCommand_HP(NetworkPacket *pkt)
558 LocalPlayer *player = m_env.getLocalPlayer();
559 assert(player != NULL);
561 u16 oldhp = player->hp;
569 m_script->on_hp_modification(hp);
572 // Add to ClientEvent queue
573 ClientEvent *event = new ClientEvent();
574 event->type = CE_PLAYER_DAMAGE;
575 event->player_damage.amount = oldhp - hp;
576 m_client_event_queue.push(event);
580 void Client::handleCommand_Breath(NetworkPacket* pkt)
582 LocalPlayer *player = m_env.getLocalPlayer();
583 assert(player != NULL);
589 player->setBreath(breath);
592 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
594 LocalPlayer *player = m_env.getLocalPlayer();
595 assert(player != NULL);
600 *pkt >> pos >> pitch >> yaw;
602 player->setPosition(pos);
604 infostream << "Client got TOCLIENT_MOVE_PLAYER"
605 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
606 << " pitch=" << pitch
611 Add to ClientEvent queue.
612 This has to be sent to the main program because otherwise
613 it would just force the pitch and yaw values to whatever
614 the camera points to.
616 ClientEvent *event = new ClientEvent();
617 event->type = CE_PLAYER_FORCE_MOVE;
618 event->player_force_move.pitch = pitch;
619 event->player_force_move.yaw = yaw;
620 m_client_event_queue.push(event);
623 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
625 bool set_camera_point_target;
626 v3f camera_point_target;
628 *pkt >> set_camera_point_target;
629 *pkt >> camera_point_target;
631 ClientEvent *event = new ClientEvent();
632 event->type = CE_DEATHSCREEN;
633 event->deathscreen.set_camera_point_target = set_camera_point_target;
634 event->deathscreen.camera_point_target_x = camera_point_target.X;
635 event->deathscreen.camera_point_target_y = camera_point_target.Y;
636 event->deathscreen.camera_point_target_z = camera_point_target.Z;
637 m_client_event_queue.push(event);
640 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
646 infostream << "Client: Received media announcement: packet size: "
647 << pkt->getSize() << std::endl;
649 if (m_media_downloader == NULL ||
650 m_media_downloader->isStarted()) {
651 const char *problem = m_media_downloader ?
652 "we already saw another announcement" :
653 "all media has been received already";
654 errorstream << "Client: Received media announcement but "
656 << " files=" << num_files
657 << " size=" << pkt->getSize() << std::endl;
661 // Mesh update thread must be stopped while
662 // updating content definitions
663 sanity_check(!m_mesh_update_thread.isRunning());
665 for (u16 i = 0; i < num_files; i++) {
666 std::string name, sha1_base64;
668 *pkt >> name >> sha1_base64;
670 std::string sha1_raw = base64_decode(sha1_base64);
671 m_media_downloader->addFile(name, sha1_raw);
679 while (!sf.at_end()) {
680 std::string baseurl = trim(sf.next(","));
681 if (!baseurl.empty()) {
682 m_remote_media_servers.emplace_back(baseurl);
683 m_media_downloader->addRemoteServer(baseurl);
688 m_media_downloader->step(this);
691 void Client::handleCommand_Media(NetworkPacket* pkt)
695 u16 total number of file bunches
696 u16 index of this bunch
697 u32 number of files in this bunch
709 *pkt >> num_bunches >> bunch_i >> num_files;
711 infostream << "Client: Received files: bunch " << bunch_i << "/"
712 << num_bunches << " files=" << num_files
713 << " size=" << pkt->getSize() << std::endl;
718 bool init_phase = m_media_downloader && m_media_downloader->isStarted();
721 // Mesh update thread must be stopped while
722 // updating content definitions
723 sanity_check(!m_mesh_update_thread.isRunning());
726 for (u32 i = 0; i < num_files; i++) {
727 std::string name, data;
730 data = pkt->readLongString();
734 ok = m_media_downloader->conventionalTransferDone(name, data, this);
736 // Check pending dynamic transfers, one of them must be it
737 for (const auto &it : m_pending_media_downloads) {
738 if (it.second->conventionalTransferDone(name, data, this)) {
745 errorstream << "Client: Received media \"" << name
746 << "\" but no downloads pending. " << num_bunches << " bunches, "
747 << num_files << " in this one. (init_phase=" << init_phase
753 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
755 infostream << "Client: Received node definitions: packet size: "
756 << pkt->getSize() << std::endl;
758 // Mesh update thread must be stopped while
759 // updating content definitions
760 sanity_check(!m_mesh_update_thread.isRunning());
762 // Decompress node definitions
763 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
764 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
765 decompressZlib(tmp_is, tmp_os);
767 // Deserialize node definitions
768 m_nodedef->deSerialize(tmp_os);
769 m_nodedef_received = true;
772 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
774 infostream << "Client: Received item definitions: packet size: "
775 << pkt->getSize() << std::endl;
777 // Mesh update thread must be stopped while
778 // updating content definitions
779 sanity_check(!m_mesh_update_thread.isRunning());
781 // Decompress item definitions
782 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
783 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
784 decompressZlib(tmp_is, tmp_os);
786 // Deserialize node definitions
787 m_itemdef->deSerialize(tmp_os);
788 m_itemdef_received = true;
791 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
799 [11 + len] (f32 * 3) pos
800 [23 + len] u16 object_id
804 [34 + len] bool ephemeral
811 u8 type; // 0=local, 1=positional, 2=object
817 bool ephemeral = false;
819 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
825 } catch (PacketError &e) {};
831 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
833 case 1: // positional
834 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
838 ClientActiveObject *cao = m_env.getActiveObject(object_id);
840 pos = cao->getPosition();
841 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
848 if (client_id != -1) {
849 // for ephemeral sounds, server_id is not meaningful
851 m_sounds_server_to_client[server_id] = client_id;
852 m_sounds_client_to_server[client_id] = server_id;
855 m_sounds_to_objects[client_id] = object_id;
859 void Client::handleCommand_StopSound(NetworkPacket* pkt)
865 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
866 if (i != m_sounds_server_to_client.end()) {
867 int client_id = i->second;
868 m_sound->stopSound(client_id);
872 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
878 *pkt >> sound_id >> step >> gain;
880 std::unordered_map<s32, int>::const_iterator i =
881 m_sounds_server_to_client.find(sound_id);
883 if (i != m_sounds_server_to_client.end())
884 m_sound->fadeSound(i->second, step, gain);
887 void Client::handleCommand_Privileges(NetworkPacket* pkt)
889 m_privileges.clear();
890 infostream << "Client: Privileges updated: ";
893 *pkt >> num_privileges;
895 for (u16 i = 0; i < num_privileges; i++) {
900 m_privileges.insert(priv);
901 infostream << priv << " ";
904 // Enable basic_debug on server versions before it was added
905 if (m_proto_ver < 40)
906 m_privileges.insert("basic_debug");
908 infostream << std::endl;
911 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
913 LocalPlayer *player = m_env.getLocalPlayer();
914 assert(player != NULL);
916 // Store formspec in LocalPlayer
917 player->inventory_formspec = pkt->readLongString();
920 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
923 bool keep_inv = true;
924 *pkt >> name >> keep_inv;
926 infostream << "Client: Detached inventory update: \"" << name
927 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
929 const auto &inv_it = m_detached_inventories.find(name);
931 if (inv_it != m_detached_inventories.end()) {
932 delete inv_it->second;
933 m_detached_inventories.erase(inv_it);
937 Inventory *inv = nullptr;
938 if (inv_it == m_detached_inventories.end()) {
939 inv = new Inventory(m_itemdef);
940 m_detached_inventories[name] = inv;
942 inv = inv_it->second;
946 *pkt >> ignore; // this used to be the length of the following string, ignore it
948 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
949 std::istringstream is(contents, std::ios::binary);
950 inv->deSerialize(is);
953 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
955 std::string formspec = pkt->readLongString();
956 std::string formname;
960 ClientEvent *event = new ClientEvent();
961 event->type = CE_SHOW_FORMSPEC;
962 // pointer is required as event is a struct only!
963 // adding a std:string to a struct isn't possible
964 event->show_formspec.formspec = new std::string(formspec);
965 event->show_formspec.formname = new std::string(formname);
966 m_client_event_queue.push(event);
969 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
971 std::string datastring(pkt->getString(0), pkt->getSize());
972 std::istringstream is(datastring, std::ios_base::binary);
974 ParticleParameters p;
975 p.deSerialize(is, m_proto_ver);
977 ClientEvent *event = new ClientEvent();
978 event->type = CE_SPAWN_PARTICLE;
979 event->spawn_particle = new ParticleParameters(p);
981 m_client_event_queue.push(event);
984 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
986 std::string datastring(pkt->getString(0), pkt->getSize());
987 std::istringstream is(datastring, std::ios_base::binary);
989 ParticleSpawnerParameters p;
993 p.amount = readU16(is);
994 p.time = readF32(is);
995 p.minpos = readV3F32(is);
996 p.maxpos = readV3F32(is);
997 p.minvel = readV3F32(is);
998 p.maxvel = readV3F32(is);
999 p.minacc = readV3F32(is);
1000 p.maxacc = readV3F32(is);
1001 p.minexptime = readF32(is);
1002 p.maxexptime = readF32(is);
1003 p.minsize = readF32(is);
1004 p.maxsize = readF32(is);
1005 p.collisiondetection = readU8(is);
1006 p.texture = deSerializeString32(is);
1008 server_id = readU32(is);
1010 p.vertical = readU8(is);
1011 p.collision_removal = readU8(is);
1013 attached_id = readU16(is);
1015 p.animation.deSerialize(is, m_proto_ver);
1016 p.glow = readU8(is);
1017 p.object_collision = readU8(is);
1019 // This is kinda awful
1021 u16 tmp_param0 = readU16(is);
1024 p.node.param0 = tmp_param0;
1025 p.node.param2 = readU8(is);
1026 p.node_tile = readU8(is);
1029 auto event = new ClientEvent();
1030 event->type = CE_ADD_PARTICLESPAWNER;
1031 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1032 event->add_particlespawner.attached_id = attached_id;
1033 event->add_particlespawner.id = server_id;
1035 m_client_event_queue.push(event);
1039 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1044 ClientEvent *event = new ClientEvent();
1045 event->type = CE_DELETE_PARTICLESPAWNER;
1046 event->delete_particlespawner.id = server_id;
1048 m_client_event_queue.push(event);
1051 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1070 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1071 >> dir >> align >> offset;
1078 } catch(PacketError &e) {};
1080 ClientEvent *event = new ClientEvent();
1081 event->type = CE_HUDADD;
1082 event->hudadd = new ClientEventHudAdd();
1083 event->hudadd->server_id = server_id;
1084 event->hudadd->type = type;
1085 event->hudadd->pos = pos;
1086 event->hudadd->name = name;
1087 event->hudadd->scale = scale;
1088 event->hudadd->text = text;
1089 event->hudadd->number = number;
1090 event->hudadd->item = item;
1091 event->hudadd->dir = dir;
1092 event->hudadd->align = align;
1093 event->hudadd->offset = offset;
1094 event->hudadd->world_pos = world_pos;
1095 event->hudadd->size = size;
1096 event->hudadd->z_index = z_index;
1097 event->hudadd->text2 = text2;
1098 event->hudadd->style = style;
1099 m_client_event_queue.push(event);
1102 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1108 ClientEvent *event = new ClientEvent();
1109 event->type = CE_HUDRM;
1110 event->hudrm.id = server_id;
1111 m_client_event_queue.push(event);
1114 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1124 *pkt >> server_id >> stat;
1126 // Keep in sync with:server.cpp -> SendHUDChange
1127 switch ((HudElementStat)stat) {
1129 case HUD_STAT_SCALE:
1130 case HUD_STAT_ALIGN:
1131 case HUD_STAT_OFFSET:
1136 case HUD_STAT_TEXT2:
1139 case HUD_STAT_WORLD_POS:
1150 ClientEvent *event = new ClientEvent();
1151 event->type = CE_HUDCHANGE;
1152 event->hudchange = new ClientEventHudChange();
1153 event->hudchange->id = server_id;
1154 event->hudchange->stat = static_cast<HudElementStat>(stat);
1155 event->hudchange->v2fdata = v2fdata;
1156 event->hudchange->v3fdata = v3fdata;
1157 event->hudchange->sdata = sdata;
1158 event->hudchange->data = intdata;
1159 event->hudchange->v2s32data = v2s32data;
1160 m_client_event_queue.push(event);
1163 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1167 *pkt >> flags >> mask;
1169 LocalPlayer *player = m_env.getLocalPlayer();
1170 assert(player != NULL);
1172 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1173 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1175 player->hud_flags &= ~mask;
1176 player->hud_flags |= flags;
1178 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1179 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1181 // Not so satisying code to keep compatibility with old fixed mode system
1184 // Hide minimap if it has been disabled by the server
1185 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1186 // defers a minimap update, therefore only call it if really
1187 // needed, by checking that minimap was visible before
1188 m_minimap->setModeIndex(0);
1190 // If radar has been disabled, try to find a non radar mode or fall back to 0
1191 if (m_minimap && m_minimap_radar_disabled_by_server
1192 && was_minimap_radar_visible) {
1193 while (m_minimap->getModeIndex() > 0 &&
1194 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1195 m_minimap->nextMode();
1198 // End of 'not so satifying code'
1201 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1203 u16 param; std::string value;
1205 *pkt >> param >> value;
1207 LocalPlayer *player = m_env.getLocalPlayer();
1208 assert(player != NULL);
1210 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1211 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1212 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1213 player->hud_hotbar_itemcount = hotbar_itemcount;
1215 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1216 player->hotbar_image = value;
1218 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1219 player->hotbar_selected_image = value;
1223 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1225 if (m_proto_ver < 39) {
1226 // Handle Protocol 38 and below servers with old set_sky,
1227 // ensuring the classic look is kept.
1228 std::string datastring(pkt->getString(0), pkt->getSize());
1229 std::istringstream is(datastring, std::ios_base::binary);
1231 SkyboxParams skybox;
1232 skybox.bgcolor = video::SColor(readARGB8(is));
1233 skybox.type = std::string(deSerializeString16(is));
1234 u16 count = readU16(is);
1236 for (size_t i = 0; i < count; i++)
1237 skybox.textures.emplace_back(deSerializeString16(is));
1239 skybox.clouds = true;
1241 skybox.clouds = readU8(is);
1244 // Use default skybox settings:
1245 SkyboxDefaults sky_defaults;
1246 SunParams sun = sky_defaults.getSunDefaults();
1247 MoonParams moon = sky_defaults.getMoonDefaults();
1248 StarParams stars = sky_defaults.getStarDefaults();
1250 // Fix for "regular" skies, as color isn't kept:
1251 if (skybox.type == "regular") {
1252 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1253 skybox.fog_tint_type = "default";
1254 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1255 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1258 sun.visible = false;
1259 sun.sunrise_visible = false;
1260 moon.visible = false;
1261 stars.visible = false;
1264 // Skybox, sun, moon and stars ClientEvents:
1265 ClientEvent *sky_event = new ClientEvent();
1266 sky_event->type = CE_SET_SKY;
1267 sky_event->set_sky = new SkyboxParams(skybox);
1268 m_client_event_queue.push(sky_event);
1270 ClientEvent *sun_event = new ClientEvent();
1271 sun_event->type = CE_SET_SUN;
1272 sun_event->sun_params = new SunParams(sun);
1273 m_client_event_queue.push(sun_event);
1275 ClientEvent *moon_event = new ClientEvent();
1276 moon_event->type = CE_SET_MOON;
1277 moon_event->moon_params = new MoonParams(moon);
1278 m_client_event_queue.push(moon_event);
1280 ClientEvent *star_event = new ClientEvent();
1281 star_event->type = CE_SET_STARS;
1282 star_event->star_params = new StarParams(stars);
1283 m_client_event_queue.push(star_event);
1285 SkyboxParams skybox;
1287 std::string texture;
1289 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1290 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1292 if (skybox.type == "skybox") {
1293 *pkt >> texture_count;
1294 for (int i = 0; i < texture_count; i++) {
1296 skybox.textures.emplace_back(texture);
1299 else if (skybox.type == "regular") {
1300 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1301 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1302 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1303 >> skybox.sky_color.indoors;
1306 ClientEvent *event = new ClientEvent();
1307 event->type = CE_SET_SKY;
1308 event->set_sky = new SkyboxParams(skybox);
1309 m_client_event_queue.push(event);
1313 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1317 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1318 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1320 ClientEvent *event = new ClientEvent();
1321 event->type = CE_SET_SUN;
1322 event->sun_params = new SunParams(sun);
1323 m_client_event_queue.push(event);
1326 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1330 *pkt >> moon.visible >> moon.texture
1331 >> moon.tonemap >> moon.scale;
1333 ClientEvent *event = new ClientEvent();
1334 event->type = CE_SET_MOON;
1335 event->moon_params = new MoonParams(moon);
1336 m_client_event_queue.push(event);
1339 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1343 *pkt >> stars.visible >> stars.count
1344 >> stars.starcolor >> stars.scale;
1346 ClientEvent *event = new ClientEvent();
1347 event->type = CE_SET_STARS;
1348 event->star_params = new StarParams(stars);
1350 m_client_event_queue.push(event);
1353 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1356 video::SColor color_bright;
1357 video::SColor color_ambient;
1362 *pkt >> density >> color_bright >> color_ambient
1363 >> height >> thickness >> speed;
1365 ClientEvent *event = new ClientEvent();
1366 event->type = CE_CLOUD_PARAMS;
1367 event->cloud_params.density = density;
1368 // use the underlying u32 representation, because we can't
1369 // use struct members with constructors here, and this way
1370 // we avoid using new() and delete() for no good reason
1371 event->cloud_params.color_bright = color_bright.color;
1372 event->cloud_params.color_ambient = color_ambient.color;
1373 event->cloud_params.height = height;
1374 event->cloud_params.thickness = thickness;
1375 // same here: deconstruct to skip constructor
1376 event->cloud_params.speed_x = speed.X;
1377 event->cloud_params.speed_y = speed.Y;
1378 m_client_event_queue.push(event);
1381 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1384 u16 day_night_ratio_u;
1386 *pkt >> do_override >> day_night_ratio_u;
1388 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1390 ClientEvent *event = new ClientEvent();
1391 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1392 event->override_day_night_ratio.do_override = do_override;
1393 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1394 m_client_event_queue.push(event);
1397 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1399 LocalPlayer *player = m_env.getLocalPlayer();
1400 assert(player != NULL);
1402 *pkt >> player->local_animations[0];
1403 *pkt >> player->local_animations[1];
1404 *pkt >> player->local_animations[2];
1405 *pkt >> player->local_animations[3];
1406 *pkt >> player->local_animation_speed;
1409 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1411 LocalPlayer *player = m_env.getLocalPlayer();
1412 assert(player != NULL);
1414 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1417 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1421 *pkt >> type >> num_players;
1422 PlayerListModifer notice_type = (PlayerListModifer) type;
1424 for (u16 i = 0; i < num_players; i++) {
1427 switch (notice_type) {
1428 case PLAYER_LIST_INIT:
1429 case PLAYER_LIST_ADD:
1430 m_env.addPlayerName(name);
1432 case PLAYER_LIST_REMOVE:
1433 m_env.removePlayerName(name);
1439 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1441 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1442 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1443 errorstream << "Client: Received SRP S_B login message,"
1444 << " but wasn't supposed to (chosen_mech="
1445 << m_chosen_auth_mech << ")." << std::endl;
1451 SRPUser *usr = (SRPUser *) m_auth_data;
1456 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1458 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1459 (const unsigned char *) B.c_str(), B.size(),
1460 (unsigned char **) &bytes_M, &len_M);
1463 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1467 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1468 resp_pkt << std::string(bytes_M, len_M);
1472 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1474 LocalPlayer *player = m_env.getLocalPlayer();
1475 assert(player != NULL);
1477 // Store formspec in LocalPlayer
1478 *pkt >> player->formspec_prepend;
1481 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1483 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1485 // Restrictions were received -> load mods if it's enabled
1486 // Note: this should be moved after mods receptions from server instead
1490 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1496 LocalPlayer *player = m_env.getLocalPlayer();
1497 assert(player != NULL);
1498 player->addVelocity(added_vel);
1501 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1503 std::string raw_hash, filename, filedata;
1507 *pkt >> raw_hash >> filename >> cached;
1508 if (m_proto_ver >= 40)
1511 filedata = pkt->readLongString();
1513 if (raw_hash.size() != 20 || filename.empty() ||
1514 (m_proto_ver < 40 && filedata.empty()) ||
1515 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1516 throw PacketError("Illegal filename, data or hash");
1519 verbosestream << "Server pushes media file \"" << filename << "\" ";
1520 if (filedata.empty())
1521 verbosestream << "to be fetched ";
1523 verbosestream << "with " << filedata.size() << " bytes ";
1524 verbosestream << "(cached=" << cached << ")" << std::endl;
1526 if (m_media_pushed_files.count(filename) != 0) {
1527 // Ignore (but acknowledge). Previously this was for sync purposes,
1528 // but even in new versions media cannot be replaced at runtime.
1529 if (m_proto_ver >= 40)
1530 sendHaveMedia({ token });
1534 if (!filedata.empty()) {
1536 // Compute and check checksum of data
1537 std::string computed_hash;
1540 ctx.addBytes(filedata.c_str(), filedata.size());
1541 unsigned char *buf = ctx.getDigest();
1542 computed_hash.assign((char*) buf, 20);
1545 if (raw_hash != computed_hash) {
1546 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1550 // Actually load media
1551 loadMedia(filedata, filename, true);
1552 m_media_pushed_files.insert(filename);
1554 // Cache file for the next time when this client joins the same server
1556 clientMediaUpdateCache(raw_hash, filedata);
1560 m_media_pushed_files.insert(filename);
1562 // create a downloader for this file
1563 auto downloader(std::make_shared<SingleMediaDownloader>(cached));
1564 m_pending_media_downloads.emplace_back(token, downloader);
1565 downloader->addFile(filename, raw_hash);
1566 for (const auto &baseurl : m_remote_media_servers)
1567 downloader->addRemoteServer(baseurl);
1569 downloader->step(this);
1576 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1578 std::string channel_name, sender, channel_msg;
1579 *pkt >> channel_name >> sender >> channel_msg;
1581 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1582 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1583 << channel_msg << std::endl;
1585 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1586 verbosestream << "Server sent us messages on unregistered channel "
1587 << channel_name << ", ignoring." << std::endl;
1591 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1594 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1597 ModChannelSignal signal;
1598 std::string channel;
1600 *pkt >> signal_tmp >> channel;
1602 signal = (ModChannelSignal)signal_tmp;
1604 bool valid_signal = true;
1605 // @TODO: send Signal to Lua API
1607 case MODCHANNEL_SIGNAL_JOIN_OK:
1608 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1609 infostream << "Server ack our mod channel join on channel `" << channel
1610 << "`, joining." << std::endl;
1612 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1613 // Unable to join, remove channel
1614 m_modchannel_mgr->leaveChannel(channel, 0);
1615 infostream << "Server refused our mod channel join on channel `" << channel
1616 << "`" << std::endl;
1618 case MODCHANNEL_SIGNAL_LEAVE_OK:
1620 infostream << "Server ack our mod channel leave on channel " << channel
1621 << "`, leaving." << std::endl;
1624 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1625 infostream << "Server refused our mod channel leave on channel `" << channel
1626 << "`" << std::endl;
1628 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1630 // Generally unused, but ensure we don't do an implementation error
1631 infostream << "Server tells us we sent a message on channel `" << channel
1632 << "` but we are not registered. Message was dropped." << std::endl;
1635 case MODCHANNEL_SIGNAL_SET_STATE: {
1639 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1640 infostream << "Received wrong channel state " << state
1641 << ", ignoring." << std::endl;
1645 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1646 infostream << "Server sets mod channel `" << channel
1647 << "` in read-only mode." << std::endl;
1652 warningstream << "Received unhandled mod channel signal ID "
1653 << signal << ", ignoring." << std::endl;
1655 valid_signal = false;
1659 // If signal is valid, forward it to client side mods
1661 m_script->on_modchannel_signal(channel, signal);
1664 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1667 u16 mode; // wanted current mode index after change
1669 *pkt >> count >> mode;
1672 m_minimap->clearModes();
1674 for (size_t index = 0; index < count; index++) {
1678 std::string texture;
1681 *pkt >> type >> label >> size >> texture >> scale;
1684 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1688 m_minimap->setModeIndex(mode);