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 << " ";
903 infostream << std::endl;
906 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
908 LocalPlayer *player = m_env.getLocalPlayer();
909 assert(player != NULL);
911 // Store formspec in LocalPlayer
912 player->inventory_formspec = pkt->readLongString();
915 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
918 bool keep_inv = true;
919 *pkt >> name >> keep_inv;
921 infostream << "Client: Detached inventory update: \"" << name
922 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
924 const auto &inv_it = m_detached_inventories.find(name);
926 if (inv_it != m_detached_inventories.end()) {
927 delete inv_it->second;
928 m_detached_inventories.erase(inv_it);
932 Inventory *inv = nullptr;
933 if (inv_it == m_detached_inventories.end()) {
934 inv = new Inventory(m_itemdef);
935 m_detached_inventories[name] = inv;
937 inv = inv_it->second;
941 *pkt >> ignore; // this used to be the length of the following string, ignore it
943 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
944 std::istringstream is(contents, std::ios::binary);
945 inv->deSerialize(is);
948 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
950 std::string formspec = pkt->readLongString();
951 std::string formname;
955 ClientEvent *event = new ClientEvent();
956 event->type = CE_SHOW_FORMSPEC;
957 // pointer is required as event is a struct only!
958 // adding a std:string to a struct isn't possible
959 event->show_formspec.formspec = new std::string(formspec);
960 event->show_formspec.formname = new std::string(formname);
961 m_client_event_queue.push(event);
964 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
966 std::string datastring(pkt->getString(0), pkt->getSize());
967 std::istringstream is(datastring, std::ios_base::binary);
969 ParticleParameters p;
970 p.deSerialize(is, m_proto_ver);
972 ClientEvent *event = new ClientEvent();
973 event->type = CE_SPAWN_PARTICLE;
974 event->spawn_particle = new ParticleParameters(p);
976 m_client_event_queue.push(event);
979 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
981 std::string datastring(pkt->getString(0), pkt->getSize());
982 std::istringstream is(datastring, std::ios_base::binary);
984 ParticleSpawnerParameters p;
988 p.amount = readU16(is);
989 p.time = readF32(is);
990 p.minpos = readV3F32(is);
991 p.maxpos = readV3F32(is);
992 p.minvel = readV3F32(is);
993 p.maxvel = readV3F32(is);
994 p.minacc = readV3F32(is);
995 p.maxacc = readV3F32(is);
996 p.minexptime = readF32(is);
997 p.maxexptime = readF32(is);
998 p.minsize = readF32(is);
999 p.maxsize = readF32(is);
1000 p.collisiondetection = readU8(is);
1001 p.texture = deSerializeString32(is);
1003 server_id = readU32(is);
1005 p.vertical = readU8(is);
1006 p.collision_removal = readU8(is);
1008 attached_id = readU16(is);
1010 p.animation.deSerialize(is, m_proto_ver);
1011 p.glow = readU8(is);
1012 p.object_collision = readU8(is);
1014 // This is kinda awful
1016 u16 tmp_param0 = readU16(is);
1019 p.node.param0 = tmp_param0;
1020 p.node.param2 = readU8(is);
1021 p.node_tile = readU8(is);
1024 auto event = new ClientEvent();
1025 event->type = CE_ADD_PARTICLESPAWNER;
1026 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1027 event->add_particlespawner.attached_id = attached_id;
1028 event->add_particlespawner.id = server_id;
1030 m_client_event_queue.push(event);
1034 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1039 ClientEvent *event = new ClientEvent();
1040 event->type = CE_DELETE_PARTICLESPAWNER;
1041 event->delete_particlespawner.id = server_id;
1043 m_client_event_queue.push(event);
1046 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1065 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1066 >> dir >> align >> offset;
1073 } catch(PacketError &e) {};
1075 ClientEvent *event = new ClientEvent();
1076 event->type = CE_HUDADD;
1077 event->hudadd = new ClientEventHudAdd();
1078 event->hudadd->server_id = server_id;
1079 event->hudadd->type = type;
1080 event->hudadd->pos = pos;
1081 event->hudadd->name = name;
1082 event->hudadd->scale = scale;
1083 event->hudadd->text = text;
1084 event->hudadd->number = number;
1085 event->hudadd->item = item;
1086 event->hudadd->dir = dir;
1087 event->hudadd->align = align;
1088 event->hudadd->offset = offset;
1089 event->hudadd->world_pos = world_pos;
1090 event->hudadd->size = size;
1091 event->hudadd->z_index = z_index;
1092 event->hudadd->text2 = text2;
1093 event->hudadd->style = style;
1094 m_client_event_queue.push(event);
1097 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1103 ClientEvent *event = new ClientEvent();
1104 event->type = CE_HUDRM;
1105 event->hudrm.id = server_id;
1106 m_client_event_queue.push(event);
1109 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1119 *pkt >> server_id >> stat;
1121 // Keep in sync with:server.cpp -> SendHUDChange
1122 switch ((HudElementStat)stat) {
1124 case HUD_STAT_SCALE:
1125 case HUD_STAT_ALIGN:
1126 case HUD_STAT_OFFSET:
1131 case HUD_STAT_TEXT2:
1134 case HUD_STAT_WORLD_POS:
1145 ClientEvent *event = new ClientEvent();
1146 event->type = CE_HUDCHANGE;
1147 event->hudchange = new ClientEventHudChange();
1148 event->hudchange->id = server_id;
1149 event->hudchange->stat = static_cast<HudElementStat>(stat);
1150 event->hudchange->v2fdata = v2fdata;
1151 event->hudchange->v3fdata = v3fdata;
1152 event->hudchange->sdata = sdata;
1153 event->hudchange->data = intdata;
1154 event->hudchange->v2s32data = v2s32data;
1155 m_client_event_queue.push(event);
1158 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1162 *pkt >> flags >> mask;
1164 LocalPlayer *player = m_env.getLocalPlayer();
1165 assert(player != NULL);
1167 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1168 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1170 player->hud_flags &= ~mask;
1171 player->hud_flags |= flags;
1173 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1174 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1176 // Not so satisying code to keep compatibility with old fixed mode system
1179 // Hide minimap if it has been disabled by the server
1180 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1181 // defers a minimap update, therefore only call it if really
1182 // needed, by checking that minimap was visible before
1183 m_minimap->setModeIndex(0);
1185 // If radar has been disabled, try to find a non radar mode or fall back to 0
1186 if (m_minimap && m_minimap_radar_disabled_by_server
1187 && was_minimap_radar_visible) {
1188 while (m_minimap->getModeIndex() > 0 &&
1189 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1190 m_minimap->nextMode();
1193 // End of 'not so satifying code'
1196 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1198 u16 param; std::string value;
1200 *pkt >> param >> value;
1202 LocalPlayer *player = m_env.getLocalPlayer();
1203 assert(player != NULL);
1205 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1206 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1207 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1208 player->hud_hotbar_itemcount = hotbar_itemcount;
1210 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1211 player->hotbar_image = value;
1213 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1214 player->hotbar_selected_image = value;
1218 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1220 if (m_proto_ver < 39) {
1221 // Handle Protocol 38 and below servers with old set_sky,
1222 // ensuring the classic look is kept.
1223 std::string datastring(pkt->getString(0), pkt->getSize());
1224 std::istringstream is(datastring, std::ios_base::binary);
1226 SkyboxParams skybox;
1227 skybox.bgcolor = video::SColor(readARGB8(is));
1228 skybox.type = std::string(deSerializeString16(is));
1229 u16 count = readU16(is);
1231 for (size_t i = 0; i < count; i++)
1232 skybox.textures.emplace_back(deSerializeString16(is));
1234 skybox.clouds = true;
1236 skybox.clouds = readU8(is);
1239 // Use default skybox settings:
1240 SunParams sun = SkyboxDefaults::getSunDefaults();
1241 MoonParams moon = SkyboxDefaults::getMoonDefaults();
1242 StarParams stars = SkyboxDefaults::getStarDefaults();
1244 // Fix for "regular" skies, as color isn't kept:
1245 if (skybox.type == "regular") {
1246 skybox.sky_color = SkyboxDefaults::getSkyColorDefaults();
1247 skybox.fog_tint_type = "default";
1248 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1249 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1251 sun.visible = false;
1252 sun.sunrise_visible = false;
1253 moon.visible = false;
1254 stars.visible = false;
1257 // Skybox, sun, moon and stars ClientEvents:
1258 ClientEvent *sky_event = new ClientEvent();
1259 sky_event->type = CE_SET_SKY;
1260 sky_event->set_sky = new SkyboxParams(skybox);
1261 m_client_event_queue.push(sky_event);
1263 ClientEvent *sun_event = new ClientEvent();
1264 sun_event->type = CE_SET_SUN;
1265 sun_event->sun_params = new SunParams(sun);
1266 m_client_event_queue.push(sun_event);
1268 ClientEvent *moon_event = new ClientEvent();
1269 moon_event->type = CE_SET_MOON;
1270 moon_event->moon_params = new MoonParams(moon);
1271 m_client_event_queue.push(moon_event);
1273 ClientEvent *star_event = new ClientEvent();
1274 star_event->type = CE_SET_STARS;
1275 star_event->star_params = new StarParams(stars);
1276 m_client_event_queue.push(star_event);
1278 SkyboxParams skybox;
1280 std::string texture;
1282 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1283 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1285 if (skybox.type == "skybox") {
1286 *pkt >> texture_count;
1287 for (int i = 0; i < texture_count; i++) {
1289 skybox.textures.emplace_back(texture);
1292 else if (skybox.type == "regular") {
1293 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1294 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1295 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1296 >> skybox.sky_color.indoors;
1299 ClientEvent *event = new ClientEvent();
1300 event->type = CE_SET_SKY;
1301 event->set_sky = new SkyboxParams(skybox);
1302 m_client_event_queue.push(event);
1306 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1310 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1311 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1313 ClientEvent *event = new ClientEvent();
1314 event->type = CE_SET_SUN;
1315 event->sun_params = new SunParams(sun);
1316 m_client_event_queue.push(event);
1319 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1323 *pkt >> moon.visible >> moon.texture
1324 >> moon.tonemap >> moon.scale;
1326 ClientEvent *event = new ClientEvent();
1327 event->type = CE_SET_MOON;
1328 event->moon_params = new MoonParams(moon);
1329 m_client_event_queue.push(event);
1332 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1336 *pkt >> stars.visible >> stars.count
1337 >> stars.starcolor >> stars.scale;
1339 ClientEvent *event = new ClientEvent();
1340 event->type = CE_SET_STARS;
1341 event->star_params = new StarParams(stars);
1343 m_client_event_queue.push(event);
1346 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1349 video::SColor color_bright;
1350 video::SColor color_ambient;
1355 *pkt >> density >> color_bright >> color_ambient
1356 >> height >> thickness >> speed;
1358 ClientEvent *event = new ClientEvent();
1359 event->type = CE_CLOUD_PARAMS;
1360 event->cloud_params.density = density;
1361 // use the underlying u32 representation, because we can't
1362 // use struct members with constructors here, and this way
1363 // we avoid using new() and delete() for no good reason
1364 event->cloud_params.color_bright = color_bright.color;
1365 event->cloud_params.color_ambient = color_ambient.color;
1366 event->cloud_params.height = height;
1367 event->cloud_params.thickness = thickness;
1368 // same here: deconstruct to skip constructor
1369 event->cloud_params.speed_x = speed.X;
1370 event->cloud_params.speed_y = speed.Y;
1371 m_client_event_queue.push(event);
1374 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1377 u16 day_night_ratio_u;
1379 *pkt >> do_override >> day_night_ratio_u;
1381 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1383 ClientEvent *event = new ClientEvent();
1384 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1385 event->override_day_night_ratio.do_override = do_override;
1386 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1387 m_client_event_queue.push(event);
1390 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1392 LocalPlayer *player = m_env.getLocalPlayer();
1393 assert(player != NULL);
1395 *pkt >> player->local_animations[0];
1396 *pkt >> player->local_animations[1];
1397 *pkt >> player->local_animations[2];
1398 *pkt >> player->local_animations[3];
1399 *pkt >> player->local_animation_speed;
1401 player->last_animation = -1;
1404 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1406 LocalPlayer *player = m_env.getLocalPlayer();
1407 assert(player != NULL);
1409 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1412 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1416 *pkt >> type >> num_players;
1417 PlayerListModifer notice_type = (PlayerListModifer) type;
1419 for (u16 i = 0; i < num_players; i++) {
1422 switch (notice_type) {
1423 case PLAYER_LIST_INIT:
1424 case PLAYER_LIST_ADD:
1425 m_env.addPlayerName(name);
1427 case PLAYER_LIST_REMOVE:
1428 m_env.removePlayerName(name);
1434 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1436 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1437 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1438 errorstream << "Client: Received SRP S_B login message,"
1439 << " but wasn't supposed to (chosen_mech="
1440 << m_chosen_auth_mech << ")." << std::endl;
1446 SRPUser *usr = (SRPUser *) m_auth_data;
1451 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1453 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1454 (const unsigned char *) B.c_str(), B.size(),
1455 (unsigned char **) &bytes_M, &len_M);
1458 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1462 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1463 resp_pkt << std::string(bytes_M, len_M);
1467 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1469 LocalPlayer *player = m_env.getLocalPlayer();
1470 assert(player != NULL);
1472 // Store formspec in LocalPlayer
1473 *pkt >> player->formspec_prepend;
1476 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1478 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1480 // Restrictions were received -> load mods if it's enabled
1481 // Note: this should be moved after mods receptions from server instead
1485 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1491 LocalPlayer *player = m_env.getLocalPlayer();
1492 assert(player != NULL);
1493 player->addVelocity(added_vel);
1496 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1498 std::string raw_hash, filename, filedata;
1502 *pkt >> raw_hash >> filename >> cached;
1503 if (m_proto_ver >= 40)
1506 filedata = pkt->readLongString();
1508 if (raw_hash.size() != 20 || filename.empty() ||
1509 (m_proto_ver < 40 && filedata.empty()) ||
1510 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1511 throw PacketError("Illegal filename, data or hash");
1514 verbosestream << "Server pushes media file \"" << filename << "\" ";
1515 if (filedata.empty())
1516 verbosestream << "to be fetched ";
1518 verbosestream << "with " << filedata.size() << " bytes ";
1519 verbosestream << "(cached=" << cached << ")" << std::endl;
1521 if (m_media_pushed_files.count(filename) != 0) {
1522 // Ignore (but acknowledge). Previously this was for sync purposes,
1523 // but even in new versions media cannot be replaced at runtime.
1524 if (m_proto_ver >= 40)
1525 sendHaveMedia({ token });
1529 if (!filedata.empty()) {
1531 // Compute and check checksum of data
1532 std::string computed_hash;
1535 ctx.addBytes(filedata.c_str(), filedata.size());
1536 unsigned char *buf = ctx.getDigest();
1537 computed_hash.assign((char*) buf, 20);
1540 if (raw_hash != computed_hash) {
1541 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1545 // Actually load media
1546 loadMedia(filedata, filename, true);
1547 m_media_pushed_files.insert(filename);
1549 // Cache file for the next time when this client joins the same server
1551 clientMediaUpdateCache(raw_hash, filedata);
1555 m_media_pushed_files.insert(filename);
1557 // create a downloader for this file
1558 auto downloader(std::make_shared<SingleMediaDownloader>(cached));
1559 m_pending_media_downloads.emplace_back(token, downloader);
1560 downloader->addFile(filename, raw_hash);
1561 for (const auto &baseurl : m_remote_media_servers)
1562 downloader->addRemoteServer(baseurl);
1564 downloader->step(this);
1571 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1573 std::string channel_name, sender, channel_msg;
1574 *pkt >> channel_name >> sender >> channel_msg;
1576 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1577 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1578 << channel_msg << std::endl;
1580 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1581 verbosestream << "Server sent us messages on unregistered channel "
1582 << channel_name << ", ignoring." << std::endl;
1586 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1589 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1592 ModChannelSignal signal;
1593 std::string channel;
1595 *pkt >> signal_tmp >> channel;
1597 signal = (ModChannelSignal)signal_tmp;
1599 bool valid_signal = true;
1600 // @TODO: send Signal to Lua API
1602 case MODCHANNEL_SIGNAL_JOIN_OK:
1603 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1604 infostream << "Server ack our mod channel join on channel `" << channel
1605 << "`, joining." << std::endl;
1607 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1608 // Unable to join, remove channel
1609 m_modchannel_mgr->leaveChannel(channel, 0);
1610 infostream << "Server refused our mod channel join on channel `" << channel
1611 << "`" << std::endl;
1613 case MODCHANNEL_SIGNAL_LEAVE_OK:
1615 infostream << "Server ack our mod channel leave on channel " << channel
1616 << "`, leaving." << std::endl;
1619 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1620 infostream << "Server refused our mod channel leave on channel `" << channel
1621 << "`" << std::endl;
1623 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1625 // Generally unused, but ensure we don't do an implementation error
1626 infostream << "Server tells us we sent a message on channel `" << channel
1627 << "` but we are not registered. Message was dropped." << std::endl;
1630 case MODCHANNEL_SIGNAL_SET_STATE: {
1634 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1635 infostream << "Received wrong channel state " << state
1636 << ", ignoring." << std::endl;
1640 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1641 infostream << "Server sets mod channel `" << channel
1642 << "` in read-only mode." << std::endl;
1647 warningstream << "Received unhandled mod channel signal ID "
1648 << signal << ", ignoring." << std::endl;
1650 valid_signal = false;
1654 // If signal is valid, forward it to client side mods
1656 m_script->on_modchannel_signal(channel, signal);
1659 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1662 u16 mode; // wanted current mode index after change
1664 *pkt >> count >> mode;
1667 m_minimap->clearModes();
1669 for (size_t index = 0; index < count; index++) {
1673 std::string texture;
1676 *pkt >> type >> label >> size >> texture >> scale;
1679 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1683 m_minimap->setModeIndex(mode);