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)
1060 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1061 >> dir >> align >> offset;
1067 } catch(PacketError &e) {};
1069 ClientEvent *event = new ClientEvent();
1070 event->type = CE_HUDADD;
1071 event->hudadd = new ClientEventHudAdd();
1072 event->hudadd->server_id = server_id;
1073 event->hudadd->type = type;
1074 event->hudadd->pos = pos;
1075 event->hudadd->name = name;
1076 event->hudadd->scale = scale;
1077 event->hudadd->text = text;
1078 event->hudadd->number = number;
1079 event->hudadd->item = item;
1080 event->hudadd->dir = dir;
1081 event->hudadd->align = align;
1082 event->hudadd->offset = offset;
1083 event->hudadd->world_pos = world_pos;
1084 event->hudadd->size = size;
1085 event->hudadd->z_index = z_index;
1086 event->hudadd->text2 = text2;
1087 m_client_event_queue.push(event);
1090 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1096 ClientEvent *event = new ClientEvent();
1097 event->type = CE_HUDRM;
1098 event->hudrm.id = server_id;
1099 m_client_event_queue.push(event);
1102 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1112 *pkt >> server_id >> stat;
1114 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1115 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1117 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1119 else if (stat == HUD_STAT_WORLD_POS)
1121 else if (stat == HUD_STAT_SIZE )
1126 ClientEvent *event = new ClientEvent();
1127 event->type = CE_HUDCHANGE;
1128 event->hudchange = new ClientEventHudChange();
1129 event->hudchange->id = server_id;
1130 event->hudchange->stat = static_cast<HudElementStat>(stat);
1131 event->hudchange->v2fdata = v2fdata;
1132 event->hudchange->v3fdata = v3fdata;
1133 event->hudchange->sdata = sdata;
1134 event->hudchange->data = intdata;
1135 event->hudchange->v2s32data = v2s32data;
1136 m_client_event_queue.push(event);
1139 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1143 *pkt >> flags >> mask;
1145 LocalPlayer *player = m_env.getLocalPlayer();
1146 assert(player != NULL);
1148 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1149 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1151 player->hud_flags &= ~mask;
1152 player->hud_flags |= flags;
1154 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1155 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1157 // Not so satisying code to keep compatibility with old fixed mode system
1160 // Hide minimap if it has been disabled by the server
1161 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1162 // defers a minimap update, therefore only call it if really
1163 // needed, by checking that minimap was visible before
1164 m_minimap->setModeIndex(0);
1166 // If radar has been disabled, try to find a non radar mode or fall back to 0
1167 if (m_minimap && m_minimap_radar_disabled_by_server
1168 && was_minimap_radar_visible) {
1169 while (m_minimap->getModeIndex() > 0 &&
1170 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1171 m_minimap->nextMode();
1174 // End of 'not so satifying code'
1177 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1179 u16 param; std::string value;
1181 *pkt >> param >> value;
1183 LocalPlayer *player = m_env.getLocalPlayer();
1184 assert(player != NULL);
1186 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1187 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1188 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1189 player->hud_hotbar_itemcount = hotbar_itemcount;
1191 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1192 player->hotbar_image = value;
1194 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1195 player->hotbar_selected_image = value;
1199 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1201 if (m_proto_ver < 39) {
1202 // Handle Protocol 38 and below servers with old set_sky,
1203 // ensuring the classic look is kept.
1204 std::string datastring(pkt->getString(0), pkt->getSize());
1205 std::istringstream is(datastring, std::ios_base::binary);
1207 SkyboxParams skybox;
1208 skybox.bgcolor = video::SColor(readARGB8(is));
1209 skybox.type = std::string(deSerializeString16(is));
1210 u16 count = readU16(is);
1212 for (size_t i = 0; i < count; i++)
1213 skybox.textures.emplace_back(deSerializeString16(is));
1215 skybox.clouds = true;
1217 skybox.clouds = readU8(is);
1220 // Use default skybox settings:
1221 SkyboxDefaults sky_defaults;
1222 SunParams sun = sky_defaults.getSunDefaults();
1223 MoonParams moon = sky_defaults.getMoonDefaults();
1224 StarParams stars = sky_defaults.getStarDefaults();
1226 // Fix for "regular" skies, as color isn't kept:
1227 if (skybox.type == "regular") {
1228 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1229 skybox.fog_tint_type = "default";
1230 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1231 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1234 sun.visible = false;
1235 sun.sunrise_visible = false;
1236 moon.visible = false;
1237 stars.visible = false;
1240 // Skybox, sun, moon and stars ClientEvents:
1241 ClientEvent *sky_event = new ClientEvent();
1242 sky_event->type = CE_SET_SKY;
1243 sky_event->set_sky = new SkyboxParams(skybox);
1244 m_client_event_queue.push(sky_event);
1246 ClientEvent *sun_event = new ClientEvent();
1247 sun_event->type = CE_SET_SUN;
1248 sun_event->sun_params = new SunParams(sun);
1249 m_client_event_queue.push(sun_event);
1251 ClientEvent *moon_event = new ClientEvent();
1252 moon_event->type = CE_SET_MOON;
1253 moon_event->moon_params = new MoonParams(moon);
1254 m_client_event_queue.push(moon_event);
1256 ClientEvent *star_event = new ClientEvent();
1257 star_event->type = CE_SET_STARS;
1258 star_event->star_params = new StarParams(stars);
1259 m_client_event_queue.push(star_event);
1261 SkyboxParams skybox;
1263 std::string texture;
1265 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1266 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1268 if (skybox.type == "skybox") {
1269 *pkt >> texture_count;
1270 for (int i = 0; i < texture_count; i++) {
1272 skybox.textures.emplace_back(texture);
1275 else if (skybox.type == "regular") {
1276 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1277 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1278 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1279 >> skybox.sky_color.indoors;
1282 ClientEvent *event = new ClientEvent();
1283 event->type = CE_SET_SKY;
1284 event->set_sky = new SkyboxParams(skybox);
1285 m_client_event_queue.push(event);
1289 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1293 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1294 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1296 ClientEvent *event = new ClientEvent();
1297 event->type = CE_SET_SUN;
1298 event->sun_params = new SunParams(sun);
1299 m_client_event_queue.push(event);
1302 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1306 *pkt >> moon.visible >> moon.texture
1307 >> moon.tonemap >> moon.scale;
1309 ClientEvent *event = new ClientEvent();
1310 event->type = CE_SET_MOON;
1311 event->moon_params = new MoonParams(moon);
1312 m_client_event_queue.push(event);
1315 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1319 *pkt >> stars.visible >> stars.count
1320 >> stars.starcolor >> stars.scale;
1322 ClientEvent *event = new ClientEvent();
1323 event->type = CE_SET_STARS;
1324 event->star_params = new StarParams(stars);
1326 m_client_event_queue.push(event);
1329 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1332 video::SColor color_bright;
1333 video::SColor color_ambient;
1338 *pkt >> density >> color_bright >> color_ambient
1339 >> height >> thickness >> speed;
1341 ClientEvent *event = new ClientEvent();
1342 event->type = CE_CLOUD_PARAMS;
1343 event->cloud_params.density = density;
1344 // use the underlying u32 representation, because we can't
1345 // use struct members with constructors here, and this way
1346 // we avoid using new() and delete() for no good reason
1347 event->cloud_params.color_bright = color_bright.color;
1348 event->cloud_params.color_ambient = color_ambient.color;
1349 event->cloud_params.height = height;
1350 event->cloud_params.thickness = thickness;
1351 // same here: deconstruct to skip constructor
1352 event->cloud_params.speed_x = speed.X;
1353 event->cloud_params.speed_y = speed.Y;
1354 m_client_event_queue.push(event);
1357 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1360 u16 day_night_ratio_u;
1362 *pkt >> do_override >> day_night_ratio_u;
1364 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1366 ClientEvent *event = new ClientEvent();
1367 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1368 event->override_day_night_ratio.do_override = do_override;
1369 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1370 m_client_event_queue.push(event);
1373 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1375 LocalPlayer *player = m_env.getLocalPlayer();
1376 assert(player != NULL);
1378 *pkt >> player->local_animations[0];
1379 *pkt >> player->local_animations[1];
1380 *pkt >> player->local_animations[2];
1381 *pkt >> player->local_animations[3];
1382 *pkt >> player->local_animation_speed;
1385 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1387 LocalPlayer *player = m_env.getLocalPlayer();
1388 assert(player != NULL);
1390 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1393 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1397 *pkt >> type >> num_players;
1398 PlayerListModifer notice_type = (PlayerListModifer) type;
1400 for (u16 i = 0; i < num_players; i++) {
1403 switch (notice_type) {
1404 case PLAYER_LIST_INIT:
1405 case PLAYER_LIST_ADD:
1406 m_env.addPlayerName(name);
1408 case PLAYER_LIST_REMOVE:
1409 m_env.removePlayerName(name);
1415 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1417 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1418 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1419 errorstream << "Client: Received SRP S_B login message,"
1420 << " but wasn't supposed to (chosen_mech="
1421 << m_chosen_auth_mech << ")." << std::endl;
1427 SRPUser *usr = (SRPUser *) m_auth_data;
1432 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1434 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1435 (const unsigned char *) B.c_str(), B.size(),
1436 (unsigned char **) &bytes_M, &len_M);
1439 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1443 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1444 resp_pkt << std::string(bytes_M, len_M);
1448 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1450 LocalPlayer *player = m_env.getLocalPlayer();
1451 assert(player != NULL);
1453 // Store formspec in LocalPlayer
1454 *pkt >> player->formspec_prepend;
1457 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1459 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1461 // Restrictions were received -> load mods if it's enabled
1462 // Note: this should be moved after mods receptions from server instead
1466 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1472 LocalPlayer *player = m_env.getLocalPlayer();
1473 assert(player != NULL);
1474 player->addVelocity(added_vel);
1477 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1479 std::string raw_hash, filename, filedata;
1482 *pkt >> raw_hash >> filename >> cached;
1483 filedata = pkt->readLongString();
1485 if (raw_hash.size() != 20 || filedata.empty() || filename.empty() ||
1486 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1487 throw PacketError("Illegal filename, data or hash");
1490 verbosestream << "Server pushes media file \"" << filename << "\" with "
1491 << filedata.size() << " bytes of data (cached=" << cached
1492 << ")" << std::endl;
1494 if (m_media_pushed_files.count(filename) != 0) {
1495 // Silently ignore for synchronization purposes
1499 // Compute and check checksum of data
1500 std::string computed_hash;
1503 ctx.addBytes(filedata.c_str(), filedata.size());
1504 unsigned char *buf = ctx.getDigest();
1505 computed_hash.assign((char*) buf, 20);
1508 if (raw_hash != computed_hash) {
1509 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1513 // Actually load media
1514 loadMedia(filedata, filename, true);
1515 m_media_pushed_files.insert(filename);
1517 // Cache file for the next time when this client joins the same server
1519 clientMediaUpdateCache(raw_hash, filedata);
1526 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1528 std::string channel_name, sender, channel_msg;
1529 *pkt >> channel_name >> sender >> channel_msg;
1531 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1532 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1533 << channel_msg << std::endl;
1535 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1536 verbosestream << "Server sent us messages on unregistered channel "
1537 << channel_name << ", ignoring." << std::endl;
1541 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1544 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1547 ModChannelSignal signal;
1548 std::string channel;
1550 *pkt >> signal_tmp >> channel;
1552 signal = (ModChannelSignal)signal_tmp;
1554 bool valid_signal = true;
1555 // @TODO: send Signal to Lua API
1557 case MODCHANNEL_SIGNAL_JOIN_OK:
1558 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1559 infostream << "Server ack our mod channel join on channel `" << channel
1560 << "`, joining." << std::endl;
1562 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1563 // Unable to join, remove channel
1564 m_modchannel_mgr->leaveChannel(channel, 0);
1565 infostream << "Server refused our mod channel join on channel `" << channel
1566 << "`" << std::endl;
1568 case MODCHANNEL_SIGNAL_LEAVE_OK:
1570 infostream << "Server ack our mod channel leave on channel " << channel
1571 << "`, leaving." << std::endl;
1574 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1575 infostream << "Server refused our mod channel leave on channel `" << channel
1576 << "`" << std::endl;
1578 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1580 // Generally unused, but ensure we don't do an implementation error
1581 infostream << "Server tells us we sent a message on channel `" << channel
1582 << "` but we are not registered. Message was dropped." << std::endl;
1585 case MODCHANNEL_SIGNAL_SET_STATE: {
1589 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1590 infostream << "Received wrong channel state " << state
1591 << ", ignoring." << std::endl;
1595 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1596 infostream << "Server sets mod channel `" << channel
1597 << "` in read-only mode." << std::endl;
1602 warningstream << "Received unhandled mod channel signal ID "
1603 << signal << ", ignoring." << std::endl;
1605 valid_signal = false;
1609 // If signal is valid, forward it to client side mods
1611 m_script->on_modchannel_signal(channel, signal);
1614 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1617 u16 mode; // wanted current mode index after change
1619 *pkt >> count >> mode;
1622 m_minimap->clearModes();
1624 for (size_t index = 0; index < count; index++) {
1628 std::string texture;
1631 *pkt >> type >> label >> size >> texture >> scale;
1634 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1638 m_minimap->setModeIndex(mode);