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"
47 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
49 infostream << "Got deprecated command "
50 << toClientCommandTable[pkt->getCommand()].name << " from peer "
51 << pkt->getPeerId() << "!" << std::endl;
54 void Client::handleCommand_Hello(NetworkPacket* pkt)
56 if (pkt->getSize() < 1)
63 std::string username_legacy; // for case insensitivity
64 *pkt >> serialization_ver >> compression_mode >> proto_ver
65 >> auth_mechs >> username_legacy;
67 // Chose an auth method we support
68 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
70 infostream << "Client: TOCLIENT_HELLO received with "
71 << "serialization_ver=" << (u32)serialization_ver
72 << ", auth_mechs=" << auth_mechs
73 << ", proto_ver=" << proto_ver
74 << ", compression_mode=" << compression_mode
75 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
77 if (!ser_ver_supported(serialization_ver)) {
78 infostream << "Client: TOCLIENT_HELLO: Server sent "
79 << "unsupported ser_fmt_ver"<< std::endl;
83 m_server_ser_ver = serialization_ver;
84 m_proto_ver = proto_ver;
86 //TODO verify that username_legacy matches sent username, only
87 // differs in casing (make both uppercase and compare)
88 // This is only neccessary though when we actually want to add casing support
90 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
91 // we recieved a TOCLIENT_HELLO while auth was already going on
92 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
93 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
94 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
95 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
96 srp_user_delete((SRPUser *) m_auth_data);
101 // Authenticate using that method, or abort if there wasn't any method found
102 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
103 if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
104 !m_simple_singleplayer_mode &&
105 !getServerAddress().isLocalhost() &&
106 g_settings->getBool("enable_register_confirmation")) {
107 promptConfirmRegistration(chosen_auth_mechanism);
109 startAuth(chosen_auth_mechanism);
112 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
113 m_access_denied = true;
114 m_access_denied_reason = "Unknown";
120 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
125 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
126 >> m_sudo_auth_methods;
128 playerpos -= v3f(0, BS / 2, 0);
130 // Set player position
131 LocalPlayer *player = m_env.getLocalPlayer();
132 assert(player != NULL);
133 player->setPosition(playerpos);
135 infostream << "Client: received map seed: " << m_map_seed << std::endl;
136 infostream << "Client: received recommended send interval "
137 << m_recommended_send_interval<<std::endl;
140 /*~ DO NOT TRANSLATE THIS LITERALLY!
141 This is a special string which needs to contain the translation's
142 language code (e.g. "de" for German). */
143 std::string lang = gettext("LANG_CODE");
144 if (lang == "LANG_CODE")
147 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
153 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
157 m_password = m_new_password;
159 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
161 // send packet to actually set the password
162 startAuth(AUTH_MECHANISM_FIRST_SRP);
165 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
167 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
169 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
170 L"Password change denied. Password NOT changed.");
171 pushToChatQueue(chatMessage);
172 // reset everything and be sad
176 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
178 // The server didn't like our password. Note, this needs
179 // to be processed even if the serialisation format has
180 // not been agreed yet, the same as TOCLIENT_INIT.
181 m_access_denied = true;
182 m_access_denied_reason = "Unknown";
184 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
185 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
186 // in some places of the server code
187 if (pkt->getSize() >= 2) {
188 std::wstring wide_reason;
190 m_access_denied_reason = wide_to_utf8(wide_reason);
195 if (pkt->getSize() < 1)
198 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
200 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
201 denyCode == SERVER_ACCESSDENIED_CRASH) {
202 *pkt >> m_access_denied_reason;
203 if (m_access_denied_reason.empty()) {
204 m_access_denied_reason = accessDeniedStrings[denyCode];
208 m_access_denied_reconnect = reconnect & 1;
209 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
210 *pkt >> m_access_denied_reason;
211 } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) {
212 m_access_denied_reason = accessDeniedStrings[denyCode];
213 m_access_denied_reconnect = true;
214 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
215 m_access_denied_reason = accessDeniedStrings[denyCode];
217 // Allow us to add new error messages to the
218 // protocol without raising the protocol version, if we want to.
219 // Until then (which may be never), this is outside
220 // of the defined protocol.
221 *pkt >> m_access_denied_reason;
222 if (m_access_denied_reason.empty()) {
223 m_access_denied_reason = "Unknown";
228 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
230 if (pkt->getSize() < 6)
238 void Client::handleCommand_AddNode(NetworkPacket* pkt)
240 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
247 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
249 bool remove_metadata = true;
250 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
251 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
252 remove_metadata = false;
255 addNode(p, n, remove_metadata);
258 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
260 if (pkt->getSize() < 1)
263 std::istringstream is(pkt->readLongString(), std::ios::binary);
264 std::stringstream sstr;
265 decompressZlib(is, sstr);
267 NodeMetadataList meta_updates_list(false);
268 meta_updates_list.deSerialize(sstr, m_itemdef, true);
270 Map &map = m_env.getMap();
271 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
272 i != meta_updates_list.end(); ++i) {
273 v3s16 pos = i->first;
275 if (map.isValidPosition(pos) &&
276 map.setNodeMetadata(pos, i->second))
277 continue; // Prevent from deleting metadata
279 // Meta couldn't be set, unused metadata
284 void Client::handleCommand_BlockData(NetworkPacket* pkt)
286 // Ignore too small packet
287 if (pkt->getSize() < 6)
293 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
294 std::istringstream istr(datastring, std::ios_base::binary);
300 sector = m_env.getMap().emergeSector(p2d);
302 assert(sector->getPos() == p2d);
304 block = sector->getBlockNoCreateNoEx(p.Y);
307 Update an existing block
309 block->deSerialize(istr, m_server_ser_ver, false);
310 block->deSerializeNetworkSpecific(istr);
316 block = new MapBlock(&m_env.getMap(), p, this);
317 block->deSerialize(istr, m_server_ser_ver, false);
318 block->deSerializeNetworkSpecific(istr);
319 sector->insertBlock(block);
323 ServerMap::saveBlock(block, m_localdb);
327 Add it to mesh update queue and set it to be acknowledged after update.
329 addUpdateMeshTaskWithEdge(p, true);
332 void Client::handleCommand_Inventory(NetworkPacket* pkt)
334 if (pkt->getSize() < 1)
337 std::string datastring(pkt->getString(0), pkt->getSize());
338 std::istringstream is(datastring, std::ios_base::binary);
340 LocalPlayer *player = m_env.getLocalPlayer();
341 assert(player != NULL);
343 player->inventory.deSerialize(is);
345 m_update_wielded_item = true;
347 delete m_inventory_from_server;
348 m_inventory_from_server = new Inventory(player->inventory);
349 m_inventory_from_server_age = 0.0;
352 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
354 if (pkt->getSize() < 2)
361 time_of_day = time_of_day % 24000;
362 float time_speed = 0;
364 if (pkt->getSize() >= 2 + 4) {
368 // Old message; try to approximate speed of time by ourselves
369 float time_of_day_f = (float)time_of_day / 24000.0f;
370 float tod_diff_f = 0;
372 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
373 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
375 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
377 m_last_time_of_day_f = time_of_day_f;
378 float time_diff = m_time_of_day_update_timer;
379 m_time_of_day_update_timer = 0;
381 if (m_time_of_day_set) {
382 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
383 infostream << "Client: Measured time_of_day speed (old format): "
384 << time_speed << " tod_diff_f=" << tod_diff_f
385 << " time_diff=" << time_diff << std::endl;
389 // Update environment
390 m_env.setTimeOfDay(time_of_day);
391 m_env.setTimeOfDaySpeed(time_speed);
392 m_time_of_day_set = true;
394 //u32 dr = m_env.getDayNightRatio();
395 //infostream << "Client: time_of_day=" << time_of_day
396 // << " time_speed=" << time_speed
397 // << " dr=" << dr << std::endl;
400 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
405 u16 sendername length
411 ChatMessage *chatMessage = new ChatMessage();
412 u8 version, message_type;
413 *pkt >> version >> message_type;
415 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
421 *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
422 chatMessage->timestamp = static_cast<std::time_t>(timestamp);
424 chatMessage->type = (ChatMessageType) message_type;
426 // @TODO send this to CSM using ChatMessage object
427 if (modsLoaded() && m_script->on_receiving_message(
428 wide_to_utf8(chatMessage->message))) {
429 // Message was consumed by CSM and should not be handled by client
432 pushToChatQueue(chatMessage);
436 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
439 u16 count of removed objects
440 for all removed objects {
443 u16 count of added objects
444 for all added objects {
447 u32 initialization data length
448 string initialization data
454 u16 removed_count, added_count, id;
456 // Read removed objects
457 *pkt >> removed_count;
459 for (u16 i = 0; i < removed_count; i++) {
461 m_env.removeActiveObject(id);
464 // Read added objects
467 for (u16 i = 0; i < added_count; i++) {
469 m_env.addActiveObject(id, type, pkt->readLongString());
471 } catch (PacketError &e) {
472 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
473 << ". The packet is unreliable, ignoring" << std::endl;
476 // m_activeobjects_received is false before the first
477 // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
478 m_activeobjects_received = true;
481 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
491 std::string datastring(pkt->getString(0), pkt->getSize());
492 std::istringstream is(datastring, std::ios_base::binary);
496 u16 id = readU16(is);
500 std::string message = deSerializeString16(is);
502 // Pass on to the environment
503 m_env.processActiveObjectMessage(id, message);
505 } catch (SerializationError &e) {
506 errorstream << "Client::handleCommand_ActiveObjectMessages: "
507 << "caught SerializationError: " << e.what() << std::endl;
511 void Client::handleCommand_Movement(NetworkPacket* pkt)
513 LocalPlayer *player = m_env.getLocalPlayer();
514 assert(player != NULL);
516 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
518 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
519 >> lf >> lfs >> ls >> g;
521 player->movement_acceleration_default = mad * BS;
522 player->movement_acceleration_air = maa * BS;
523 player->movement_acceleration_fast = maf * BS;
524 player->movement_speed_walk = msw * BS;
525 player->movement_speed_crouch = mscr * BS;
526 player->movement_speed_fast = msf * BS;
527 player->movement_speed_climb = mscl * BS;
528 player->movement_speed_jump = msj * BS;
529 player->movement_liquid_fluidity = lf * BS;
530 player->movement_liquid_fluidity_smooth = lfs * BS;
531 player->movement_liquid_sink = ls * BS;
532 player->movement_gravity = g * BS;
535 void Client::handleCommand_Fov(NetworkPacket *pkt)
538 bool is_multiplier = false;
539 f32 transition_time = 0.0f;
541 *pkt >> fov >> is_multiplier;
543 // Wrap transition_time extraction within a
544 // try-catch to preserve backwards compat
546 *pkt >> transition_time;
547 } catch (PacketError &e) {};
549 LocalPlayer *player = m_env.getLocalPlayer();
551 player->setFov({ fov, is_multiplier, transition_time });
552 m_camera->notifyFovChange();
555 void Client::handleCommand_HP(NetworkPacket *pkt)
557 LocalPlayer *player = m_env.getLocalPlayer();
558 assert(player != NULL);
560 u16 oldhp = player->hp;
568 m_script->on_hp_modification(hp);
571 // Add to ClientEvent queue
572 ClientEvent *event = new ClientEvent();
573 event->type = CE_PLAYER_DAMAGE;
574 event->player_damage.amount = oldhp - hp;
575 m_client_event_queue.push(event);
579 void Client::handleCommand_Breath(NetworkPacket* pkt)
581 LocalPlayer *player = m_env.getLocalPlayer();
582 assert(player != NULL);
588 player->setBreath(breath);
591 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
593 LocalPlayer *player = m_env.getLocalPlayer();
594 assert(player != NULL);
599 *pkt >> pos >> pitch >> yaw;
601 player->setPosition(pos);
603 infostream << "Client got TOCLIENT_MOVE_PLAYER"
604 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
605 << " pitch=" << pitch
610 Add to ClientEvent queue.
611 This has to be sent to the main program because otherwise
612 it would just force the pitch and yaw values to whatever
613 the camera points to.
615 ClientEvent *event = new ClientEvent();
616 event->type = CE_PLAYER_FORCE_MOVE;
617 event->player_force_move.pitch = pitch;
618 event->player_force_move.yaw = yaw;
619 m_client_event_queue.push(event);
622 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
624 bool set_camera_point_target;
625 v3f camera_point_target;
627 *pkt >> set_camera_point_target;
628 *pkt >> camera_point_target;
630 ClientEvent *event = new ClientEvent();
631 event->type = CE_DEATHSCREEN;
632 event->deathscreen.set_camera_point_target = set_camera_point_target;
633 event->deathscreen.camera_point_target_x = camera_point_target.X;
634 event->deathscreen.camera_point_target_y = camera_point_target.Y;
635 event->deathscreen.camera_point_target_z = camera_point_target.Z;
636 m_client_event_queue.push(event);
639 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
645 infostream << "Client: Received media announcement: packet size: "
646 << pkt->getSize() << std::endl;
648 if (m_media_downloader == NULL ||
649 m_media_downloader->isStarted()) {
650 const char *problem = m_media_downloader ?
651 "we already saw another announcement" :
652 "all media has been received already";
653 errorstream << "Client: Received media announcement but "
655 << " files=" << num_files
656 << " size=" << pkt->getSize() << std::endl;
660 // Mesh update thread must be stopped while
661 // updating content definitions
662 sanity_check(!m_mesh_update_thread.isRunning());
664 for (u16 i = 0; i < num_files; i++) {
665 std::string name, sha1_base64;
667 *pkt >> name >> sha1_base64;
669 std::string sha1_raw = base64_decode(sha1_base64);
670 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_media_downloader->addRemoteServer(baseurl);
685 catch(SerializationError& e) {
686 // not supported by server or turned off
689 m_media_downloader->step(this);
692 void Client::handleCommand_Media(NetworkPacket* pkt)
696 u16 total number of file bunches
697 u16 index of this bunch
698 u32 number of files in this bunch
710 *pkt >> num_bunches >> bunch_i >> num_files;
712 infostream << "Client: Received files: bunch " << bunch_i << "/"
713 << num_bunches << " files=" << num_files
714 << " size=" << pkt->getSize() << std::endl;
719 if (!m_media_downloader || !m_media_downloader->isStarted()) {
720 const char *problem = m_media_downloader ?
721 "media has not been requested" :
722 "all media has been received already";
723 errorstream << "Client: Received media but "
725 << " bunch " << bunch_i << "/" << num_bunches
726 << " files=" << num_files
727 << " size=" << pkt->getSize() << std::endl;
731 // Mesh update thread must be stopped while
732 // updating content definitions
733 sanity_check(!m_mesh_update_thread.isRunning());
735 for (u32 i=0; i < num_files; i++) {
740 std::string data = pkt->readLongString();
742 m_media_downloader->conventionalTransferDone(
747 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
749 infostream << "Client: Received node definitions: packet size: "
750 << pkt->getSize() << std::endl;
752 // Mesh update thread must be stopped while
753 // updating content definitions
754 sanity_check(!m_mesh_update_thread.isRunning());
756 // Decompress node definitions
757 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
758 std::ostringstream tmp_os;
759 decompressZlib(tmp_is, tmp_os);
761 // Deserialize node definitions
762 std::istringstream tmp_is2(tmp_os.str());
763 m_nodedef->deSerialize(tmp_is2);
764 m_nodedef_received = true;
767 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
769 infostream << "Client: Received item definitions: packet size: "
770 << pkt->getSize() << std::endl;
772 // Mesh update thread must be stopped while
773 // updating content definitions
774 sanity_check(!m_mesh_update_thread.isRunning());
776 // Decompress item definitions
777 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
778 std::ostringstream tmp_os;
779 decompressZlib(tmp_is, tmp_os);
781 // Deserialize node definitions
782 std::istringstream tmp_is2(tmp_os.str());
783 m_itemdef->deSerialize(tmp_is2);
784 m_itemdef_received = true;
787 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
795 [11 + len] (f32 * 3) pos
796 [23 + len] u16 object_id
800 [34 + len] bool ephemeral
807 u8 type; // 0=local, 1=positional, 2=object
813 bool ephemeral = false;
815 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
821 } catch (PacketError &e) {};
827 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
829 case 1: // positional
830 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
834 ClientActiveObject *cao = m_env.getActiveObject(object_id);
836 pos = cao->getPosition();
837 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
844 if (client_id != -1) {
845 // for ephemeral sounds, server_id is not meaningful
847 m_sounds_server_to_client[server_id] = client_id;
848 m_sounds_client_to_server[client_id] = server_id;
851 m_sounds_to_objects[client_id] = object_id;
855 void Client::handleCommand_StopSound(NetworkPacket* pkt)
861 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
862 if (i != m_sounds_server_to_client.end()) {
863 int client_id = i->second;
864 m_sound->stopSound(client_id);
868 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
874 *pkt >> sound_id >> step >> gain;
876 std::unordered_map<s32, int>::const_iterator i =
877 m_sounds_server_to_client.find(sound_id);
879 if (i != m_sounds_server_to_client.end())
880 m_sound->fadeSound(i->second, step, gain);
883 void Client::handleCommand_Privileges(NetworkPacket* pkt)
885 m_privileges.clear();
886 infostream << "Client: Privileges updated: ";
889 *pkt >> num_privileges;
891 for (u16 i = 0; i < num_privileges; i++) {
896 m_privileges.insert(priv);
897 infostream << priv << " ";
900 // Enable basic_debug on server versions before it was added
901 if (m_proto_ver < 40)
902 m_privileges.insert("basic_debug");
904 infostream << std::endl;
907 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
909 LocalPlayer *player = m_env.getLocalPlayer();
910 assert(player != NULL);
912 // Store formspec in LocalPlayer
913 player->inventory_formspec = pkt->readLongString();
916 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
919 bool keep_inv = true;
920 *pkt >> name >> keep_inv;
922 infostream << "Client: Detached inventory update: \"" << name
923 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
925 const auto &inv_it = m_detached_inventories.find(name);
927 if (inv_it != m_detached_inventories.end()) {
928 delete inv_it->second;
929 m_detached_inventories.erase(inv_it);
933 Inventory *inv = nullptr;
934 if (inv_it == m_detached_inventories.end()) {
935 inv = new Inventory(m_itemdef);
936 m_detached_inventories[name] = inv;
938 inv = inv_it->second;
942 *pkt >> ignore; // this used to be the length of the following string, ignore it
944 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
945 std::istringstream is(contents, std::ios::binary);
946 inv->deSerialize(is);
949 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
951 std::string formspec = pkt->readLongString();
952 std::string formname;
956 ClientEvent *event = new ClientEvent();
957 event->type = CE_SHOW_FORMSPEC;
958 // pointer is required as event is a struct only!
959 // adding a std:string to a struct isn't possible
960 event->show_formspec.formspec = new std::string(formspec);
961 event->show_formspec.formname = new std::string(formname);
962 m_client_event_queue.push(event);
965 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
967 std::string datastring(pkt->getString(0), pkt->getSize());
968 std::istringstream is(datastring, std::ios_base::binary);
970 ParticleParameters p;
971 p.deSerialize(is, m_proto_ver);
973 ClientEvent *event = new ClientEvent();
974 event->type = CE_SPAWN_PARTICLE;
975 event->spawn_particle = new ParticleParameters(p);
977 m_client_event_queue.push(event);
980 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
982 std::string datastring(pkt->getString(0), pkt->getSize());
983 std::istringstream is(datastring, std::ios_base::binary);
985 ParticleSpawnerParameters p;
989 p.amount = readU16(is);
990 p.time = readF32(is);
991 p.minpos = readV3F32(is);
992 p.maxpos = readV3F32(is);
993 p.minvel = readV3F32(is);
994 p.maxvel = readV3F32(is);
995 p.minacc = readV3F32(is);
996 p.maxacc = readV3F32(is);
997 p.minexptime = readF32(is);
998 p.maxexptime = readF32(is);
999 p.minsize = readF32(is);
1000 p.maxsize = readF32(is);
1001 p.collisiondetection = readU8(is);
1002 p.texture = deSerializeString32(is);
1004 server_id = readU32(is);
1006 p.vertical = readU8(is);
1007 p.collision_removal = readU8(is);
1009 attached_id = readU16(is);
1011 p.animation.deSerialize(is, m_proto_ver);
1012 p.glow = readU8(is);
1013 p.object_collision = readU8(is);
1015 // This is kinda awful
1017 u16 tmp_param0 = readU16(is);
1020 p.node.param0 = tmp_param0;
1021 p.node.param2 = readU8(is);
1022 p.node_tile = readU8(is);
1025 auto event = new ClientEvent();
1026 event->type = CE_ADD_PARTICLESPAWNER;
1027 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1028 event->add_particlespawner.attached_id = attached_id;
1029 event->add_particlespawner.id = server_id;
1031 m_client_event_queue.push(event);
1035 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1040 ClientEvent *event = new ClientEvent();
1041 event->type = CE_DELETE_PARTICLESPAWNER;
1042 event->delete_particlespawner.id = server_id;
1044 m_client_event_queue.push(event);
1047 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1065 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1066 >> dir >> align >> offset;
1072 } catch(PacketError &e) {};
1074 ClientEvent *event = new ClientEvent();
1075 event->type = CE_HUDADD;
1076 event->hudadd = new ClientEventHudAdd();
1077 event->hudadd->server_id = server_id;
1078 event->hudadd->type = type;
1079 event->hudadd->pos = pos;
1080 event->hudadd->name = name;
1081 event->hudadd->scale = scale;
1082 event->hudadd->text = text;
1083 event->hudadd->number = number;
1084 event->hudadd->item = item;
1085 event->hudadd->dir = dir;
1086 event->hudadd->align = align;
1087 event->hudadd->offset = offset;
1088 event->hudadd->world_pos = world_pos;
1089 event->hudadd->size = size;
1090 event->hudadd->z_index = z_index;
1091 event->hudadd->text2 = text2;
1092 m_client_event_queue.push(event);
1095 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1101 ClientEvent *event = new ClientEvent();
1102 event->type = CE_HUDRM;
1103 event->hudrm.id = server_id;
1104 m_client_event_queue.push(event);
1107 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1117 *pkt >> server_id >> stat;
1119 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1120 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1122 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1124 else if (stat == HUD_STAT_WORLD_POS)
1126 else if (stat == HUD_STAT_SIZE )
1131 ClientEvent *event = new ClientEvent();
1132 event->type = CE_HUDCHANGE;
1133 event->hudchange = new ClientEventHudChange();
1134 event->hudchange->id = server_id;
1135 event->hudchange->stat = static_cast<HudElementStat>(stat);
1136 event->hudchange->v2fdata = v2fdata;
1137 event->hudchange->v3fdata = v3fdata;
1138 event->hudchange->sdata = sdata;
1139 event->hudchange->data = intdata;
1140 event->hudchange->v2s32data = v2s32data;
1141 m_client_event_queue.push(event);
1144 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1148 *pkt >> flags >> mask;
1150 LocalPlayer *player = m_env.getLocalPlayer();
1151 assert(player != NULL);
1153 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1154 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1156 player->hud_flags &= ~mask;
1157 player->hud_flags |= flags;
1159 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1160 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1162 // Not so satisying code to keep compatibility with old fixed mode system
1165 // Hide minimap if it has been disabled by the server
1166 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1167 // defers a minimap update, therefore only call it if really
1168 // needed, by checking that minimap was visible before
1169 m_minimap->setModeIndex(0);
1171 // If radar has been disabled, try to find a non radar mode or fall back to 0
1172 if (m_minimap && m_minimap_radar_disabled_by_server
1173 && was_minimap_radar_visible) {
1174 while (m_minimap->getModeIndex() > 0 &&
1175 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1176 m_minimap->nextMode();
1179 // End of 'not so satifying code'
1182 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1184 u16 param; std::string value;
1186 *pkt >> param >> value;
1188 LocalPlayer *player = m_env.getLocalPlayer();
1189 assert(player != NULL);
1191 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1192 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1193 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1194 player->hud_hotbar_itemcount = hotbar_itemcount;
1196 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1197 player->hotbar_image = value;
1199 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1200 player->hotbar_selected_image = value;
1204 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1206 if (m_proto_ver < 39) {
1207 // Handle Protocol 38 and below servers with old set_sky,
1208 // ensuring the classic look is kept.
1209 std::string datastring(pkt->getString(0), pkt->getSize());
1210 std::istringstream is(datastring, std::ios_base::binary);
1212 SkyboxParams skybox;
1213 skybox.bgcolor = video::SColor(readARGB8(is));
1214 skybox.type = std::string(deSerializeString16(is));
1215 u16 count = readU16(is);
1217 for (size_t i = 0; i < count; i++)
1218 skybox.textures.emplace_back(deSerializeString16(is));
1220 skybox.clouds = true;
1222 skybox.clouds = readU8(is);
1225 // Use default skybox settings:
1226 SkyboxDefaults sky_defaults;
1227 SunParams sun = sky_defaults.getSunDefaults();
1228 MoonParams moon = sky_defaults.getMoonDefaults();
1229 StarParams stars = sky_defaults.getStarDefaults();
1231 // Fix for "regular" skies, as color isn't kept:
1232 if (skybox.type == "regular") {
1233 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1234 skybox.fog_tint_type = "default";
1235 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1236 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1239 sun.visible = false;
1240 sun.sunrise_visible = false;
1241 moon.visible = false;
1242 stars.visible = false;
1245 // Skybox, sun, moon and stars ClientEvents:
1246 ClientEvent *sky_event = new ClientEvent();
1247 sky_event->type = CE_SET_SKY;
1248 sky_event->set_sky = new SkyboxParams(skybox);
1249 m_client_event_queue.push(sky_event);
1251 ClientEvent *sun_event = new ClientEvent();
1252 sun_event->type = CE_SET_SUN;
1253 sun_event->sun_params = new SunParams(sun);
1254 m_client_event_queue.push(sun_event);
1256 ClientEvent *moon_event = new ClientEvent();
1257 moon_event->type = CE_SET_MOON;
1258 moon_event->moon_params = new MoonParams(moon);
1259 m_client_event_queue.push(moon_event);
1261 ClientEvent *star_event = new ClientEvent();
1262 star_event->type = CE_SET_STARS;
1263 star_event->star_params = new StarParams(stars);
1264 m_client_event_queue.push(star_event);
1266 SkyboxParams skybox;
1268 std::string texture;
1270 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1271 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1273 if (skybox.type == "skybox") {
1274 *pkt >> texture_count;
1275 for (int i = 0; i < texture_count; i++) {
1277 skybox.textures.emplace_back(texture);
1280 else if (skybox.type == "regular") {
1281 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1282 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1283 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1284 >> skybox.sky_color.indoors;
1287 ClientEvent *event = new ClientEvent();
1288 event->type = CE_SET_SKY;
1289 event->set_sky = new SkyboxParams(skybox);
1290 m_client_event_queue.push(event);
1294 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1298 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1299 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1301 ClientEvent *event = new ClientEvent();
1302 event->type = CE_SET_SUN;
1303 event->sun_params = new SunParams(sun);
1304 m_client_event_queue.push(event);
1307 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1311 *pkt >> moon.visible >> moon.texture
1312 >> moon.tonemap >> moon.scale;
1314 ClientEvent *event = new ClientEvent();
1315 event->type = CE_SET_MOON;
1316 event->moon_params = new MoonParams(moon);
1317 m_client_event_queue.push(event);
1320 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1324 *pkt >> stars.visible >> stars.count
1325 >> stars.starcolor >> stars.scale;
1327 ClientEvent *event = new ClientEvent();
1328 event->type = CE_SET_STARS;
1329 event->star_params = new StarParams(stars);
1331 m_client_event_queue.push(event);
1334 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1337 video::SColor color_bright;
1338 video::SColor color_ambient;
1343 *pkt >> density >> color_bright >> color_ambient
1344 >> height >> thickness >> speed;
1346 ClientEvent *event = new ClientEvent();
1347 event->type = CE_CLOUD_PARAMS;
1348 event->cloud_params.density = density;
1349 // use the underlying u32 representation, because we can't
1350 // use struct members with constructors here, and this way
1351 // we avoid using new() and delete() for no good reason
1352 event->cloud_params.color_bright = color_bright.color;
1353 event->cloud_params.color_ambient = color_ambient.color;
1354 event->cloud_params.height = height;
1355 event->cloud_params.thickness = thickness;
1356 // same here: deconstruct to skip constructor
1357 event->cloud_params.speed_x = speed.X;
1358 event->cloud_params.speed_y = speed.Y;
1359 m_client_event_queue.push(event);
1362 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1365 u16 day_night_ratio_u;
1367 *pkt >> do_override >> day_night_ratio_u;
1369 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1371 ClientEvent *event = new ClientEvent();
1372 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1373 event->override_day_night_ratio.do_override = do_override;
1374 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1375 m_client_event_queue.push(event);
1378 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1380 LocalPlayer *player = m_env.getLocalPlayer();
1381 assert(player != NULL);
1383 *pkt >> player->local_animations[0];
1384 *pkt >> player->local_animations[1];
1385 *pkt >> player->local_animations[2];
1386 *pkt >> player->local_animations[3];
1387 *pkt >> player->local_animation_speed;
1390 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1392 LocalPlayer *player = m_env.getLocalPlayer();
1393 assert(player != NULL);
1395 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1398 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1402 *pkt >> type >> num_players;
1403 PlayerListModifer notice_type = (PlayerListModifer) type;
1405 for (u16 i = 0; i < num_players; i++) {
1408 switch (notice_type) {
1409 case PLAYER_LIST_INIT:
1410 case PLAYER_LIST_ADD:
1411 m_env.addPlayerName(name);
1413 case PLAYER_LIST_REMOVE:
1414 m_env.removePlayerName(name);
1420 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1422 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1423 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1424 errorstream << "Client: Received SRP S_B login message,"
1425 << " but wasn't supposed to (chosen_mech="
1426 << m_chosen_auth_mech << ")." << std::endl;
1432 SRPUser *usr = (SRPUser *) m_auth_data;
1437 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1439 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1440 (const unsigned char *) B.c_str(), B.size(),
1441 (unsigned char **) &bytes_M, &len_M);
1444 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1448 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1449 resp_pkt << std::string(bytes_M, len_M);
1453 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1455 LocalPlayer *player = m_env.getLocalPlayer();
1456 assert(player != NULL);
1458 // Store formspec in LocalPlayer
1459 *pkt >> player->formspec_prepend;
1462 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1464 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1466 // Restrictions were received -> load mods if it's enabled
1467 // Note: this should be moved after mods receptions from server instead
1471 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1477 LocalPlayer *player = m_env.getLocalPlayer();
1478 assert(player != NULL);
1479 player->addVelocity(added_vel);
1482 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1484 std::string raw_hash, filename, filedata;
1487 *pkt >> raw_hash >> filename >> cached;
1488 filedata = pkt->readLongString();
1490 if (raw_hash.size() != 20 || filedata.empty() || filename.empty() ||
1491 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1492 throw PacketError("Illegal filename, data or hash");
1495 verbosestream << "Server pushes media file \"" << filename << "\" with "
1496 << filedata.size() << " bytes of data (cached=" << cached
1497 << ")" << std::endl;
1499 if (m_media_pushed_files.count(filename) != 0) {
1500 // Silently ignore for synchronization purposes
1504 // Compute and check checksum of data
1505 std::string computed_hash;
1508 ctx.addBytes(filedata.c_str(), filedata.size());
1509 unsigned char *buf = ctx.getDigest();
1510 computed_hash.assign((char*) buf, 20);
1513 if (raw_hash != computed_hash) {
1514 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1518 // Actually load media
1519 loadMedia(filedata, filename, true);
1520 m_media_pushed_files.insert(filename);
1522 // Cache file for the next time when this client joins the same server
1524 clientMediaUpdateCache(raw_hash, filedata);
1531 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1533 std::string channel_name, sender, channel_msg;
1534 *pkt >> channel_name >> sender >> channel_msg;
1536 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1537 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1538 << channel_msg << std::endl;
1540 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1541 verbosestream << "Server sent us messages on unregistered channel "
1542 << channel_name << ", ignoring." << std::endl;
1546 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1549 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1552 ModChannelSignal signal;
1553 std::string channel;
1555 *pkt >> signal_tmp >> channel;
1557 signal = (ModChannelSignal)signal_tmp;
1559 bool valid_signal = true;
1560 // @TODO: send Signal to Lua API
1562 case MODCHANNEL_SIGNAL_JOIN_OK:
1563 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1564 infostream << "Server ack our mod channel join on channel `" << channel
1565 << "`, joining." << std::endl;
1567 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1568 // Unable to join, remove channel
1569 m_modchannel_mgr->leaveChannel(channel, 0);
1570 infostream << "Server refused our mod channel join on channel `" << channel
1571 << "`" << std::endl;
1573 case MODCHANNEL_SIGNAL_LEAVE_OK:
1575 infostream << "Server ack our mod channel leave on channel " << channel
1576 << "`, leaving." << std::endl;
1579 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1580 infostream << "Server refused our mod channel leave on channel `" << channel
1581 << "`" << std::endl;
1583 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1585 // Generally unused, but ensure we don't do an implementation error
1586 infostream << "Server tells us we sent a message on channel `" << channel
1587 << "` but we are not registered. Message was dropped." << std::endl;
1590 case MODCHANNEL_SIGNAL_SET_STATE: {
1594 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1595 infostream << "Received wrong channel state " << state
1596 << ", ignoring." << std::endl;
1600 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1601 infostream << "Server sets mod channel `" << channel
1602 << "` in read-only mode." << std::endl;
1607 warningstream << "Received unhandled mod channel signal ID "
1608 << signal << ", ignoring." << std::endl;
1610 valid_signal = false;
1614 // If signal is valid, forward it to client side mods
1616 m_script->on_modchannel_signal(channel, signal);
1619 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1622 u16 mode; // wanted current mode index after change
1624 *pkt >> count >> mode;
1627 m_minimap->clearModes();
1629 for (size_t index = 0; index < count; index++) {
1633 std::string texture;
1636 *pkt >> type >> label >> size >> texture >> scale;
1639 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1643 m_minimap->setModeIndex(mode);