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 << " ";
899 infostream << std::endl;
902 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
904 LocalPlayer *player = m_env.getLocalPlayer();
905 assert(player != NULL);
907 // Store formspec in LocalPlayer
908 player->inventory_formspec = pkt->readLongString();
911 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
914 bool keep_inv = true;
915 *pkt >> name >> keep_inv;
917 infostream << "Client: Detached inventory update: \"" << name
918 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
920 const auto &inv_it = m_detached_inventories.find(name);
922 if (inv_it != m_detached_inventories.end()) {
923 delete inv_it->second;
924 m_detached_inventories.erase(inv_it);
928 Inventory *inv = nullptr;
929 if (inv_it == m_detached_inventories.end()) {
930 inv = new Inventory(m_itemdef);
931 m_detached_inventories[name] = inv;
933 inv = inv_it->second;
937 *pkt >> ignore; // this used to be the length of the following string, ignore it
939 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
940 std::istringstream is(contents, std::ios::binary);
941 inv->deSerialize(is);
944 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
946 std::string formspec = pkt->readLongString();
947 std::string formname;
951 ClientEvent *event = new ClientEvent();
952 event->type = CE_SHOW_FORMSPEC;
953 // pointer is required as event is a struct only!
954 // adding a std:string to a struct isn't possible
955 event->show_formspec.formspec = new std::string(formspec);
956 event->show_formspec.formname = new std::string(formname);
957 m_client_event_queue.push(event);
960 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
962 std::string datastring(pkt->getString(0), pkt->getSize());
963 std::istringstream is(datastring, std::ios_base::binary);
965 ParticleParameters p;
966 p.deSerialize(is, m_proto_ver);
968 ClientEvent *event = new ClientEvent();
969 event->type = CE_SPAWN_PARTICLE;
970 event->spawn_particle = new ParticleParameters(p);
972 m_client_event_queue.push(event);
975 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
977 std::string datastring(pkt->getString(0), pkt->getSize());
978 std::istringstream is(datastring, std::ios_base::binary);
980 ParticleSpawnerParameters p;
984 p.amount = readU16(is);
985 p.time = readF32(is);
986 p.minpos = readV3F32(is);
987 p.maxpos = readV3F32(is);
988 p.minvel = readV3F32(is);
989 p.maxvel = readV3F32(is);
990 p.minacc = readV3F32(is);
991 p.maxacc = readV3F32(is);
992 p.minexptime = readF32(is);
993 p.maxexptime = readF32(is);
994 p.minsize = readF32(is);
995 p.maxsize = readF32(is);
996 p.collisiondetection = readU8(is);
997 p.texture = deSerializeString32(is);
999 server_id = readU32(is);
1001 p.vertical = readU8(is);
1002 p.collision_removal = readU8(is);
1004 attached_id = readU16(is);
1006 p.animation.deSerialize(is, m_proto_ver);
1007 p.glow = readU8(is);
1008 p.object_collision = readU8(is);
1010 // This is kinda awful
1012 u16 tmp_param0 = readU16(is);
1015 p.node.param0 = tmp_param0;
1016 p.node.param2 = readU8(is);
1017 p.node_tile = readU8(is);
1020 auto event = new ClientEvent();
1021 event->type = CE_ADD_PARTICLESPAWNER;
1022 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1023 event->add_particlespawner.attached_id = attached_id;
1024 event->add_particlespawner.id = server_id;
1026 m_client_event_queue.push(event);
1030 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1035 ClientEvent *event = new ClientEvent();
1036 event->type = CE_DELETE_PARTICLESPAWNER;
1037 event->delete_particlespawner.id = server_id;
1039 m_client_event_queue.push(event);
1042 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1044 std::string datastring(pkt->getString(0), pkt->getSize());
1045 std::istringstream is(datastring, std::ios_base::binary);
1063 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1064 >> dir >> align >> offset;
1070 } catch(PacketError &e) {};
1072 ClientEvent *event = new ClientEvent();
1073 event->type = CE_HUDADD;
1074 event->hudadd.server_id = server_id;
1075 event->hudadd.type = type;
1076 event->hudadd.pos = new v2f(pos);
1077 event->hudadd.name = new std::string(name);
1078 event->hudadd.scale = new v2f(scale);
1079 event->hudadd.text = new std::string(text);
1080 event->hudadd.number = number;
1081 event->hudadd.item = item;
1082 event->hudadd.dir = dir;
1083 event->hudadd.align = new v2f(align);
1084 event->hudadd.offset = new v2f(offset);
1085 event->hudadd.world_pos = new v3f(world_pos);
1086 event->hudadd.size = new v2s32(size);
1087 event->hudadd.z_index = z_index;
1088 event->hudadd.text2 = new std::string(text2);
1089 m_client_event_queue.push(event);
1092 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1098 auto i = m_hud_server_to_client.find(server_id);
1099 if (i != m_hud_server_to_client.end()) {
1100 int client_id = i->second;
1101 m_hud_server_to_client.erase(i);
1103 ClientEvent *event = new ClientEvent();
1104 event->type = CE_HUDRM;
1105 event->hudrm.id = client_id;
1106 m_client_event_queue.push(event);
1110 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1120 *pkt >> server_id >> stat;
1122 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1123 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1125 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1127 else if (stat == HUD_STAT_WORLD_POS)
1129 else if (stat == HUD_STAT_SIZE )
1134 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1135 if (i != m_hud_server_to_client.end()) {
1136 ClientEvent *event = new ClientEvent();
1137 event->type = CE_HUDCHANGE;
1138 event->hudchange.id = i->second;
1139 event->hudchange.stat = (HudElementStat)stat;
1140 event->hudchange.v2fdata = new v2f(v2fdata);
1141 event->hudchange.v3fdata = new v3f(v3fdata);
1142 event->hudchange.sdata = new std::string(sdata);
1143 event->hudchange.data = intdata;
1144 event->hudchange.v2s32data = new v2s32(v2s32data);
1145 m_client_event_queue.push(event);
1149 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1153 *pkt >> flags >> mask;
1155 LocalPlayer *player = m_env.getLocalPlayer();
1156 assert(player != NULL);
1158 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1159 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1161 player->hud_flags &= ~mask;
1162 player->hud_flags |= flags;
1164 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1165 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1167 // Not so satisying code to keep compatibility with old fixed mode system
1170 // Hide minimap if it has been disabled by the server
1171 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1172 // defers a minimap update, therefore only call it if really
1173 // needed, by checking that minimap was visible before
1174 m_minimap->setModeIndex(0);
1176 // If radar has been disabled, try to find a non radar mode or fall back to 0
1177 if (m_minimap && m_minimap_radar_disabled_by_server
1178 && was_minimap_radar_visible) {
1179 while (m_minimap->getModeIndex() > 0 &&
1180 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1181 m_minimap->nextMode();
1184 // End of 'not so satifying code'
1187 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1189 u16 param; std::string value;
1191 *pkt >> param >> value;
1193 LocalPlayer *player = m_env.getLocalPlayer();
1194 assert(player != NULL);
1196 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1197 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1198 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1199 player->hud_hotbar_itemcount = hotbar_itemcount;
1201 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1202 player->hotbar_image = value;
1204 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1205 player->hotbar_selected_image = value;
1209 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1211 if (m_proto_ver < 39) {
1212 // Handle Protocol 38 and below servers with old set_sky,
1213 // ensuring the classic look is kept.
1214 std::string datastring(pkt->getString(0), pkt->getSize());
1215 std::istringstream is(datastring, std::ios_base::binary);
1217 SkyboxParams skybox;
1218 skybox.bgcolor = video::SColor(readARGB8(is));
1219 skybox.type = std::string(deSerializeString16(is));
1220 u16 count = readU16(is);
1222 for (size_t i = 0; i < count; i++)
1223 skybox.textures.emplace_back(deSerializeString16(is));
1225 skybox.clouds = true;
1227 skybox.clouds = readU8(is);
1230 // Use default skybox settings:
1231 SkyboxDefaults sky_defaults;
1232 SunParams sun = sky_defaults.getSunDefaults();
1233 MoonParams moon = sky_defaults.getMoonDefaults();
1234 StarParams stars = sky_defaults.getStarDefaults();
1236 // Fix for "regular" skies, as color isn't kept:
1237 if (skybox.type == "regular") {
1238 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1239 skybox.fog_tint_type = "default";
1240 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1241 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1244 sun.visible = false;
1245 sun.sunrise_visible = false;
1246 moon.visible = false;
1247 stars.visible = false;
1250 // Skybox, sun, moon and stars ClientEvents:
1251 ClientEvent *sky_event = new ClientEvent();
1252 sky_event->type = CE_SET_SKY;
1253 sky_event->set_sky = new SkyboxParams(skybox);
1254 m_client_event_queue.push(sky_event);
1256 ClientEvent *sun_event = new ClientEvent();
1257 sun_event->type = CE_SET_SUN;
1258 sun_event->sun_params = new SunParams(sun);
1259 m_client_event_queue.push(sun_event);
1261 ClientEvent *moon_event = new ClientEvent();
1262 moon_event->type = CE_SET_MOON;
1263 moon_event->moon_params = new MoonParams(moon);
1264 m_client_event_queue.push(moon_event);
1266 ClientEvent *star_event = new ClientEvent();
1267 star_event->type = CE_SET_STARS;
1268 star_event->star_params = new StarParams(stars);
1269 m_client_event_queue.push(star_event);
1271 SkyboxParams skybox;
1273 std::string texture;
1275 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1276 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1278 if (skybox.type == "skybox") {
1279 *pkt >> texture_count;
1280 for (int i = 0; i < texture_count; i++) {
1282 skybox.textures.emplace_back(texture);
1285 else if (skybox.type == "regular") {
1286 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1287 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1288 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1289 >> skybox.sky_color.indoors;
1292 ClientEvent *event = new ClientEvent();
1293 event->type = CE_SET_SKY;
1294 event->set_sky = new SkyboxParams(skybox);
1295 m_client_event_queue.push(event);
1299 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1303 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1304 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1306 ClientEvent *event = new ClientEvent();
1307 event->type = CE_SET_SUN;
1308 event->sun_params = new SunParams(sun);
1309 m_client_event_queue.push(event);
1312 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1316 *pkt >> moon.visible >> moon.texture
1317 >> moon.tonemap >> moon.scale;
1319 ClientEvent *event = new ClientEvent();
1320 event->type = CE_SET_MOON;
1321 event->moon_params = new MoonParams(moon);
1322 m_client_event_queue.push(event);
1325 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1329 *pkt >> stars.visible >> stars.count
1330 >> stars.starcolor >> stars.scale;
1332 ClientEvent *event = new ClientEvent();
1333 event->type = CE_SET_STARS;
1334 event->star_params = new StarParams(stars);
1336 m_client_event_queue.push(event);
1339 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1342 video::SColor color_bright;
1343 video::SColor color_ambient;
1348 *pkt >> density >> color_bright >> color_ambient
1349 >> height >> thickness >> speed;
1351 ClientEvent *event = new ClientEvent();
1352 event->type = CE_CLOUD_PARAMS;
1353 event->cloud_params.density = density;
1354 // use the underlying u32 representation, because we can't
1355 // use struct members with constructors here, and this way
1356 // we avoid using new() and delete() for no good reason
1357 event->cloud_params.color_bright = color_bright.color;
1358 event->cloud_params.color_ambient = color_ambient.color;
1359 event->cloud_params.height = height;
1360 event->cloud_params.thickness = thickness;
1361 // same here: deconstruct to skip constructor
1362 event->cloud_params.speed_x = speed.X;
1363 event->cloud_params.speed_y = speed.Y;
1364 m_client_event_queue.push(event);
1367 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1370 u16 day_night_ratio_u;
1372 *pkt >> do_override >> day_night_ratio_u;
1374 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1376 ClientEvent *event = new ClientEvent();
1377 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1378 event->override_day_night_ratio.do_override = do_override;
1379 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1380 m_client_event_queue.push(event);
1383 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1385 LocalPlayer *player = m_env.getLocalPlayer();
1386 assert(player != NULL);
1388 *pkt >> player->local_animations[0];
1389 *pkt >> player->local_animations[1];
1390 *pkt >> player->local_animations[2];
1391 *pkt >> player->local_animations[3];
1392 *pkt >> player->local_animation_speed;
1395 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1397 LocalPlayer *player = m_env.getLocalPlayer();
1398 assert(player != NULL);
1400 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1403 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1407 *pkt >> type >> num_players;
1408 PlayerListModifer notice_type = (PlayerListModifer) type;
1410 for (u16 i = 0; i < num_players; i++) {
1413 switch (notice_type) {
1414 case PLAYER_LIST_INIT:
1415 case PLAYER_LIST_ADD:
1416 m_env.addPlayerName(name);
1418 case PLAYER_LIST_REMOVE:
1419 m_env.removePlayerName(name);
1425 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1427 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1428 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1429 errorstream << "Client: Received SRP S_B login message,"
1430 << " but wasn't supposed to (chosen_mech="
1431 << m_chosen_auth_mech << ")." << std::endl;
1437 SRPUser *usr = (SRPUser *) m_auth_data;
1442 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1444 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1445 (const unsigned char *) B.c_str(), B.size(),
1446 (unsigned char **) &bytes_M, &len_M);
1449 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1453 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1454 resp_pkt << std::string(bytes_M, len_M);
1458 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1460 LocalPlayer *player = m_env.getLocalPlayer();
1461 assert(player != NULL);
1463 // Store formspec in LocalPlayer
1464 *pkt >> player->formspec_prepend;
1467 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1469 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1471 // Restrictions were received -> load mods if it's enabled
1472 // Note: this should be moved after mods receptions from server instead
1476 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1482 LocalPlayer *player = m_env.getLocalPlayer();
1483 assert(player != NULL);
1484 player->addVelocity(added_vel);
1487 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1489 std::string raw_hash, filename, filedata;
1492 *pkt >> raw_hash >> filename >> cached;
1493 filedata = pkt->readLongString();
1495 if (raw_hash.size() != 20 || filedata.empty() || filename.empty() ||
1496 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1497 throw PacketError("Illegal filename, data or hash");
1500 verbosestream << "Server pushes media file \"" << filename << "\" with "
1501 << filedata.size() << " bytes of data (cached=" << cached
1502 << ")" << std::endl;
1504 if (m_media_pushed_files.count(filename) != 0) {
1505 // Silently ignore for synchronization purposes
1509 // Compute and check checksum of data
1510 std::string computed_hash;
1513 ctx.addBytes(filedata.c_str(), filedata.size());
1514 unsigned char *buf = ctx.getDigest();
1515 computed_hash.assign((char*) buf, 20);
1518 if (raw_hash != computed_hash) {
1519 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1523 // Actually load media
1524 loadMedia(filedata, filename, true);
1525 m_media_pushed_files.insert(filename);
1527 // Cache file for the next time when this client joins the same server
1529 clientMediaUpdateCache(raw_hash, filedata);
1536 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1538 std::string channel_name, sender, channel_msg;
1539 *pkt >> channel_name >> sender >> channel_msg;
1541 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1542 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1543 << channel_msg << std::endl;
1545 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1546 verbosestream << "Server sent us messages on unregistered channel "
1547 << channel_name << ", ignoring." << std::endl;
1551 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1554 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1557 ModChannelSignal signal;
1558 std::string channel;
1560 *pkt >> signal_tmp >> channel;
1562 signal = (ModChannelSignal)signal_tmp;
1564 bool valid_signal = true;
1565 // @TODO: send Signal to Lua API
1567 case MODCHANNEL_SIGNAL_JOIN_OK:
1568 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1569 infostream << "Server ack our mod channel join on channel `" << channel
1570 << "`, joining." << std::endl;
1572 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1573 // Unable to join, remove channel
1574 m_modchannel_mgr->leaveChannel(channel, 0);
1575 infostream << "Server refused our mod channel join on channel `" << channel
1576 << "`" << std::endl;
1578 case MODCHANNEL_SIGNAL_LEAVE_OK:
1580 infostream << "Server ack our mod channel leave on channel " << channel
1581 << "`, leaving." << std::endl;
1584 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1585 infostream << "Server refused our mod channel leave on channel `" << channel
1586 << "`" << std::endl;
1588 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1590 // Generally unused, but ensure we don't do an implementation error
1591 infostream << "Server tells us we sent a message on channel `" << channel
1592 << "` but we are not registered. Message was dropped." << std::endl;
1595 case MODCHANNEL_SIGNAL_SET_STATE: {
1599 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1600 infostream << "Received wrong channel state " << state
1601 << ", ignoring." << std::endl;
1605 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1606 infostream << "Server sets mod channel `" << channel
1607 << "` in read-only mode." << std::endl;
1612 warningstream << "Received unhandled mod channel signal ID "
1613 << signal << ", ignoring." << std::endl;
1615 valid_signal = false;
1619 // If signal is valid, forward it to client side mods
1621 m_script->on_modchannel_signal(channel, signal);
1624 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1627 u16 mode; // wanted current mode index after change
1629 *pkt >> count >> mode;
1632 m_minimap->clearModes();
1634 for (size_t index = 0; index < count; index++) {
1638 std::string texture;
1641 *pkt >> type >> label >> size >> texture >> scale;
1644 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1648 m_minimap->setModeIndex(mode);