3 Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "client/client.h"
22 #include "util/base64.h"
23 #include "client/camera.h"
24 #include "chatmessage.h"
25 #include "client/clientmedia.h"
28 #include "mapsector.h"
29 #include "client/minimap.h"
30 #include "modchannels.h"
32 #include "serialization.h"
34 #include "util/strfnd.h"
35 #include "client/clientevent.h"
36 #include "client/sound.h"
37 #include "network/clientopcodes.h"
38 #include "network/connection.h"
39 #include "script/scripting_client.h"
40 #include "util/serialize.h"
42 #include "util/sha1.h"
43 #include "tileanimation.h"
45 #include "skyparams.h"
48 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
50 infostream << "Got deprecated command "
51 << toClientCommandTable[pkt->getCommand()].name << " from peer "
52 << pkt->getPeerId() << "!" << std::endl;
55 void Client::handleCommand_Hello(NetworkPacket* pkt)
57 if (pkt->getSize() < 1)
64 std::string username_legacy; // for case insensitivity
65 *pkt >> serialization_ver >> compression_mode >> proto_ver
66 >> auth_mechs >> username_legacy;
68 // Chose an auth method we support
69 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
71 infostream << "Client: TOCLIENT_HELLO received with "
72 << "serialization_ver=" << (u32)serialization_ver
73 << ", auth_mechs=" << auth_mechs
74 << ", proto_ver=" << proto_ver
75 << ", compression_mode=" << compression_mode
76 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
78 if (!ser_ver_supported(serialization_ver)) {
79 infostream << "Client: TOCLIENT_HELLO: Server sent "
80 << "unsupported ser_fmt_ver"<< std::endl;
84 m_server_ser_ver = serialization_ver;
85 m_proto_ver = proto_ver;
87 //TODO verify that username_legacy matches sent username, only
88 // differs in casing (make both uppercase and compare)
89 // This is only neccessary though when we actually want to add casing support
91 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
92 // we received a TOCLIENT_HELLO while auth was already going on
93 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
94 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
95 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
96 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
97 srp_user_delete((SRPUser *) m_auth_data);
102 // Authenticate using that method, or abort if there wasn't any method found
103 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
104 if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
105 !m_simple_singleplayer_mode &&
106 !getServerAddress().isLocalhost() &&
107 g_settings->getBool("enable_register_confirmation")) {
108 promptConfirmRegistration(chosen_auth_mechanism);
110 startAuth(chosen_auth_mechanism);
113 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
114 m_access_denied = true;
115 m_access_denied_reason = "Unknown";
121 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
126 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
127 >> m_sudo_auth_methods;
129 playerpos -= v3f(0, BS / 2, 0);
131 // Set player position
132 LocalPlayer *player = m_env.getLocalPlayer();
133 assert(player != NULL);
134 player->setPosition(playerpos);
136 infostream << "Client: received map seed: " << m_map_seed << std::endl;
137 infostream << "Client: received recommended send interval "
138 << m_recommended_send_interval<<std::endl;
141 /*~ DO NOT TRANSLATE THIS LITERALLY!
142 This is a special string which needs to contain the translation's
143 language code (e.g. "de" for German). */
144 std::string lang = gettext("LANG_CODE");
145 if (lang == "LANG_CODE")
148 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
154 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
158 m_password = m_new_password;
160 verbosestream << "Client: Received TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
162 // send packet to actually set the password
163 startAuth(AUTH_MECHANISM_FIRST_SRP);
166 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
168 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
170 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
171 L"Password change denied. Password NOT changed.");
172 pushToChatQueue(chatMessage);
173 // reset everything and be sad
177 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
179 // The server didn't like our password. Note, this needs
180 // to be processed even if the serialisation format has
181 // not been agreed yet, the same as TOCLIENT_INIT.
182 m_access_denied = true;
183 m_access_denied_reason = "Unknown";
185 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
186 // Legacy code from 0.4.12 and older but is still used
187 // in some places of the server code
188 if (pkt->getSize() >= 2) {
189 std::wstring wide_reason;
191 m_access_denied_reason = wide_to_utf8(wide_reason);
196 if (pkt->getSize() < 1)
202 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
203 denyCode == SERVER_ACCESSDENIED_CRASH) {
204 *pkt >> m_access_denied_reason;
205 if (m_access_denied_reason.empty())
206 m_access_denied_reason = accessDeniedStrings[denyCode];
209 m_access_denied_reconnect = reconnect & 1;
210 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
211 *pkt >> m_access_denied_reason;
212 } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) {
213 m_access_denied_reason = accessDeniedStrings[denyCode];
214 m_access_denied_reconnect = true;
215 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
216 m_access_denied_reason = accessDeniedStrings[denyCode];
218 // Allow us to add new error messages to the
219 // protocol without raising the protocol version, if we want to.
220 // Until then (which may be never), this is outside
221 // of the defined protocol.
222 *pkt >> m_access_denied_reason;
223 if (m_access_denied_reason.empty())
224 m_access_denied_reason = "Unknown";
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(std::ios::binary | std::ios::in | std::ios::out);
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);
678 while (!sf.at_end()) {
679 std::string baseurl = trim(sf.next(","));
680 if (!baseurl.empty()) {
681 m_remote_media_servers.emplace_back(baseurl);
682 m_media_downloader->addRemoteServer(baseurl);
687 m_media_downloader->step(this);
690 void Client::handleCommand_Media(NetworkPacket* pkt)
694 u16 total number of file bunches
695 u16 index of this bunch
696 u32 number of files in this bunch
708 *pkt >> num_bunches >> bunch_i >> num_files;
710 infostream << "Client: Received files: bunch " << bunch_i << "/"
711 << num_bunches << " files=" << num_files
712 << " size=" << pkt->getSize() << std::endl;
717 bool init_phase = m_media_downloader && m_media_downloader->isStarted();
720 // Mesh update thread must be stopped while
721 // updating content definitions
722 sanity_check(!m_mesh_update_thread.isRunning());
725 for (u32 i = 0; i < num_files; i++) {
726 std::string name, data;
729 data = pkt->readLongString();
733 ok = m_media_downloader->conventionalTransferDone(name, data, this);
735 // Check pending dynamic transfers, one of them must be it
736 for (const auto &it : m_pending_media_downloads) {
737 if (it.second->conventionalTransferDone(name, data, this)) {
744 errorstream << "Client: Received media \"" << name
745 << "\" but no downloads pending. " << num_bunches << " bunches, "
746 << num_files << " in this one. (init_phase=" << init_phase
752 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
754 infostream << "Client: Received node definitions: packet size: "
755 << pkt->getSize() << std::endl;
757 // Mesh update thread must be stopped while
758 // updating content definitions
759 sanity_check(!m_mesh_update_thread.isRunning());
761 // Decompress node definitions
762 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
763 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
764 decompressZlib(tmp_is, tmp_os);
766 // Deserialize node definitions
767 m_nodedef->deSerialize(tmp_os);
768 m_nodedef_received = true;
771 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
773 infostream << "Client: Received item definitions: packet size: "
774 << pkt->getSize() << std::endl;
776 // Mesh update thread must be stopped while
777 // updating content definitions
778 sanity_check(!m_mesh_update_thread.isRunning());
780 // Decompress item definitions
781 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
782 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
783 decompressZlib(tmp_is, tmp_os);
785 // Deserialize node definitions
786 m_itemdef->deSerialize(tmp_os);
787 m_itemdef_received = true;
790 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
798 [11 + len] (f32 * 3) pos
799 [23 + len] u16 object_id
803 [34 + len] bool ephemeral
810 u8 type; // 0=local, 1=positional, 2=object
816 bool ephemeral = false;
818 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
824 } catch (PacketError &e) {};
830 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
832 case 1: // positional
833 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
837 ClientActiveObject *cao = m_env.getActiveObject(object_id);
839 pos = cao->getPosition();
840 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
847 if (client_id != -1) {
848 // for ephemeral sounds, server_id is not meaningful
850 m_sounds_server_to_client[server_id] = client_id;
851 m_sounds_client_to_server[client_id] = server_id;
854 m_sounds_to_objects[client_id] = object_id;
858 void Client::handleCommand_StopSound(NetworkPacket* pkt)
864 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
865 if (i != m_sounds_server_to_client.end()) {
866 int client_id = i->second;
867 m_sound->stopSound(client_id);
871 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
877 *pkt >> sound_id >> step >> gain;
879 std::unordered_map<s32, int>::const_iterator i =
880 m_sounds_server_to_client.find(sound_id);
882 if (i != m_sounds_server_to_client.end())
883 m_sound->fadeSound(i->second, step, gain);
886 void Client::handleCommand_Privileges(NetworkPacket* pkt)
888 m_privileges.clear();
889 infostream << "Client: Privileges updated: ";
892 *pkt >> num_privileges;
894 for (u16 i = 0; i < num_privileges; i++) {
899 m_privileges.insert(priv);
900 infostream << priv << " ";
902 infostream << std::endl;
905 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
907 LocalPlayer *player = m_env.getLocalPlayer();
908 assert(player != NULL);
910 // Store formspec in LocalPlayer
911 player->inventory_formspec = pkt->readLongString();
914 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
917 bool keep_inv = true;
918 *pkt >> name >> keep_inv;
920 infostream << "Client: Detached inventory update: \"" << name
921 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
923 const auto &inv_it = m_detached_inventories.find(name);
925 if (inv_it != m_detached_inventories.end()) {
926 delete inv_it->second;
927 m_detached_inventories.erase(inv_it);
931 Inventory *inv = nullptr;
932 if (inv_it == m_detached_inventories.end()) {
933 inv = new Inventory(m_itemdef);
934 m_detached_inventories[name] = inv;
936 inv = inv_it->second;
940 *pkt >> ignore; // this used to be the length of the following string, ignore it
942 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
943 std::istringstream is(contents, std::ios::binary);
944 inv->deSerialize(is);
947 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
949 std::string formspec = pkt->readLongString();
950 std::string formname;
954 ClientEvent *event = new ClientEvent();
955 event->type = CE_SHOW_FORMSPEC;
956 // pointer is required as event is a struct only!
957 // adding a std:string to a struct isn't possible
958 event->show_formspec.formspec = new std::string(formspec);
959 event->show_formspec.formname = new std::string(formname);
960 m_client_event_queue.push(event);
963 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
965 std::string datastring(pkt->getString(0), pkt->getSize());
966 std::istringstream is(datastring, std::ios_base::binary);
968 ParticleParameters p;
969 p.deSerialize(is, m_proto_ver);
971 ClientEvent *event = new ClientEvent();
972 event->type = CE_SPAWN_PARTICLE;
973 event->spawn_particle = new ParticleParameters(p);
975 m_client_event_queue.push(event);
978 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
980 std::string datastring(pkt->getString(0), pkt->getSize());
981 std::istringstream is(datastring, std::ios_base::binary);
983 ParticleSpawnerParameters p;
987 p.amount = readU16(is);
988 p.time = readF32(is);
989 p.minpos = readV3F32(is);
990 p.maxpos = readV3F32(is);
991 p.minvel = readV3F32(is);
992 p.maxvel = readV3F32(is);
993 p.minacc = readV3F32(is);
994 p.maxacc = readV3F32(is);
995 p.minexptime = readF32(is);
996 p.maxexptime = readF32(is);
997 p.minsize = readF32(is);
998 p.maxsize = readF32(is);
999 p.collisiondetection = readU8(is);
1000 p.texture = deSerializeString32(is);
1002 server_id = readU32(is);
1004 p.vertical = readU8(is);
1005 p.collision_removal = readU8(is);
1007 attached_id = readU16(is);
1009 p.animation.deSerialize(is, m_proto_ver);
1010 p.glow = readU8(is);
1011 p.object_collision = readU8(is);
1013 // This is kinda awful
1015 u16 tmp_param0 = readU16(is);
1018 p.node.param0 = tmp_param0;
1019 p.node.param2 = readU8(is);
1020 p.node_tile = readU8(is);
1023 auto event = new ClientEvent();
1024 event->type = CE_ADD_PARTICLESPAWNER;
1025 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1026 event->add_particlespawner.attached_id = attached_id;
1027 event->add_particlespawner.id = server_id;
1029 m_client_event_queue.push(event);
1033 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1038 ClientEvent *event = new ClientEvent();
1039 event->type = CE_DELETE_PARTICLESPAWNER;
1040 event->delete_particlespawner.id = server_id;
1042 m_client_event_queue.push(event);
1045 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1064 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1065 >> 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 event->hudadd->style = style;
1093 m_client_event_queue.push(event);
1096 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1102 ClientEvent *event = new ClientEvent();
1103 event->type = CE_HUDRM;
1104 event->hudrm.id = server_id;
1105 m_client_event_queue.push(event);
1108 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1118 *pkt >> server_id >> stat;
1120 // Keep in sync with:server.cpp -> SendHUDChange
1121 switch ((HudElementStat)stat) {
1123 case HUD_STAT_SCALE:
1124 case HUD_STAT_ALIGN:
1125 case HUD_STAT_OFFSET:
1130 case HUD_STAT_TEXT2:
1133 case HUD_STAT_WORLD_POS:
1144 ClientEvent *event = new ClientEvent();
1145 event->type = CE_HUDCHANGE;
1146 event->hudchange = new ClientEventHudChange();
1147 event->hudchange->id = server_id;
1148 event->hudchange->stat = static_cast<HudElementStat>(stat);
1149 event->hudchange->v2fdata = v2fdata;
1150 event->hudchange->v3fdata = v3fdata;
1151 event->hudchange->sdata = sdata;
1152 event->hudchange->data = intdata;
1153 event->hudchange->v2s32data = v2s32data;
1154 m_client_event_queue.push(event);
1157 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1161 *pkt >> flags >> mask;
1163 LocalPlayer *player = m_env.getLocalPlayer();
1164 assert(player != NULL);
1166 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1167 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1169 player->hud_flags &= ~mask;
1170 player->hud_flags |= flags;
1172 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1173 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1175 // Not so satisying code to keep compatibility with old fixed mode system
1178 // Hide minimap if it has been disabled by the server
1179 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1180 // defers a minimap update, therefore only call it if really
1181 // needed, by checking that minimap was visible before
1182 m_minimap->setModeIndex(0);
1184 // If radar has been disabled, try to find a non radar mode or fall back to 0
1185 if (m_minimap && m_minimap_radar_disabled_by_server
1186 && was_minimap_radar_visible) {
1187 while (m_minimap->getModeIndex() > 0 &&
1188 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1189 m_minimap->nextMode();
1192 // End of 'not so satifying code'
1195 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1197 u16 param; std::string value;
1199 *pkt >> param >> value;
1201 LocalPlayer *player = m_env.getLocalPlayer();
1202 assert(player != NULL);
1204 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1205 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1206 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1207 player->hud_hotbar_itemcount = hotbar_itemcount;
1209 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1210 player->hotbar_image = value;
1212 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1213 player->hotbar_selected_image = value;
1217 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1219 if (m_proto_ver < 39) {
1220 // Handle Protocol 38 and below servers with old set_sky,
1221 // ensuring the classic look is kept.
1222 std::string datastring(pkt->getString(0), pkt->getSize());
1223 std::istringstream is(datastring, std::ios_base::binary);
1225 SkyboxParams skybox;
1226 skybox.bgcolor = video::SColor(readARGB8(is));
1227 skybox.type = std::string(deSerializeString16(is));
1228 u16 count = readU16(is);
1230 for (size_t i = 0; i < count; i++)
1231 skybox.textures.emplace_back(deSerializeString16(is));
1233 skybox.clouds = true;
1235 skybox.clouds = readU8(is);
1238 // Use default skybox settings:
1239 SunParams sun = SkyboxDefaults::getSunDefaults();
1240 MoonParams moon = SkyboxDefaults::getMoonDefaults();
1241 StarParams stars = SkyboxDefaults::getStarDefaults();
1243 // Fix for "regular" skies, as color isn't kept:
1244 if (skybox.type == "regular") {
1245 skybox.sky_color = SkyboxDefaults::getSkyColorDefaults();
1246 skybox.fog_tint_type = "default";
1247 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1248 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1250 sun.visible = false;
1251 sun.sunrise_visible = false;
1252 moon.visible = false;
1253 stars.visible = false;
1256 // Skybox, sun, moon and stars ClientEvents:
1257 ClientEvent *sky_event = new ClientEvent();
1258 sky_event->type = CE_SET_SKY;
1259 sky_event->set_sky = new SkyboxParams(skybox);
1260 m_client_event_queue.push(sky_event);
1262 ClientEvent *sun_event = new ClientEvent();
1263 sun_event->type = CE_SET_SUN;
1264 sun_event->sun_params = new SunParams(sun);
1265 m_client_event_queue.push(sun_event);
1267 ClientEvent *moon_event = new ClientEvent();
1268 moon_event->type = CE_SET_MOON;
1269 moon_event->moon_params = new MoonParams(moon);
1270 m_client_event_queue.push(moon_event);
1272 ClientEvent *star_event = new ClientEvent();
1273 star_event->type = CE_SET_STARS;
1274 star_event->star_params = new StarParams(stars);
1275 m_client_event_queue.push(star_event);
1277 SkyboxParams skybox;
1279 std::string texture;
1281 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1282 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1284 if (skybox.type == "skybox") {
1285 *pkt >> texture_count;
1286 for (int i = 0; i < texture_count; i++) {
1288 skybox.textures.emplace_back(texture);
1291 else if (skybox.type == "regular") {
1292 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1293 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1294 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1295 >> skybox.sky_color.indoors;
1298 ClientEvent *event = new ClientEvent();
1299 event->type = CE_SET_SKY;
1300 event->set_sky = new SkyboxParams(skybox);
1301 m_client_event_queue.push(event);
1305 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1309 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1310 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1312 ClientEvent *event = new ClientEvent();
1313 event->type = CE_SET_SUN;
1314 event->sun_params = new SunParams(sun);
1315 m_client_event_queue.push(event);
1318 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1322 *pkt >> moon.visible >> moon.texture
1323 >> moon.tonemap >> moon.scale;
1325 ClientEvent *event = new ClientEvent();
1326 event->type = CE_SET_MOON;
1327 event->moon_params = new MoonParams(moon);
1328 m_client_event_queue.push(event);
1331 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1335 *pkt >> stars.visible >> stars.count
1336 >> stars.starcolor >> stars.scale;
1338 ClientEvent *event = new ClientEvent();
1339 event->type = CE_SET_STARS;
1340 event->star_params = new StarParams(stars);
1342 m_client_event_queue.push(event);
1345 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1348 video::SColor color_bright;
1349 video::SColor color_ambient;
1354 *pkt >> density >> color_bright >> color_ambient
1355 >> height >> thickness >> speed;
1357 ClientEvent *event = new ClientEvent();
1358 event->type = CE_CLOUD_PARAMS;
1359 event->cloud_params.density = density;
1360 // use the underlying u32 representation, because we can't
1361 // use struct members with constructors here, and this way
1362 // we avoid using new() and delete() for no good reason
1363 event->cloud_params.color_bright = color_bright.color;
1364 event->cloud_params.color_ambient = color_ambient.color;
1365 event->cloud_params.height = height;
1366 event->cloud_params.thickness = thickness;
1367 // same here: deconstruct to skip constructor
1368 event->cloud_params.speed_x = speed.X;
1369 event->cloud_params.speed_y = speed.Y;
1370 m_client_event_queue.push(event);
1373 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1376 u16 day_night_ratio_u;
1378 *pkt >> do_override >> day_night_ratio_u;
1380 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1382 ClientEvent *event = new ClientEvent();
1383 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1384 event->override_day_night_ratio.do_override = do_override;
1385 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1386 m_client_event_queue.push(event);
1389 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1391 LocalPlayer *player = m_env.getLocalPlayer();
1392 assert(player != NULL);
1394 *pkt >> player->local_animations[0];
1395 *pkt >> player->local_animations[1];
1396 *pkt >> player->local_animations[2];
1397 *pkt >> player->local_animations[3];
1398 *pkt >> player->local_animation_speed;
1400 player->last_animation = -1;
1403 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1405 LocalPlayer *player = m_env.getLocalPlayer();
1406 assert(player != NULL);
1408 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1411 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1415 *pkt >> type >> num_players;
1416 PlayerListModifer notice_type = (PlayerListModifer) type;
1418 for (u16 i = 0; i < num_players; i++) {
1421 switch (notice_type) {
1422 case PLAYER_LIST_INIT:
1423 case PLAYER_LIST_ADD:
1424 m_env.addPlayerName(name);
1426 case PLAYER_LIST_REMOVE:
1427 m_env.removePlayerName(name);
1433 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1435 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1436 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1437 errorstream << "Client: Received SRP S_B login message,"
1438 << " but wasn't supposed to (chosen_mech="
1439 << m_chosen_auth_mech << ")." << std::endl;
1445 SRPUser *usr = (SRPUser *) m_auth_data;
1450 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1452 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1453 (const unsigned char *) B.c_str(), B.size(),
1454 (unsigned char **) &bytes_M, &len_M);
1457 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1461 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1462 resp_pkt << std::string(bytes_M, len_M);
1466 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1468 LocalPlayer *player = m_env.getLocalPlayer();
1469 assert(player != NULL);
1471 // Store formspec in LocalPlayer
1472 *pkt >> player->formspec_prepend;
1475 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1477 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1479 // Restrictions were received -> load mods if it's enabled
1480 // Note: this should be moved after mods receptions from server instead
1484 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1490 LocalPlayer *player = m_env.getLocalPlayer();
1491 assert(player != NULL);
1492 player->addVelocity(added_vel);
1495 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1497 std::string raw_hash, filename, filedata;
1501 *pkt >> raw_hash >> filename >> cached;
1502 if (m_proto_ver >= 40)
1505 filedata = pkt->readLongString();
1507 if (raw_hash.size() != 20 || filename.empty() ||
1508 (m_proto_ver < 40 && filedata.empty()) ||
1509 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1510 throw PacketError("Illegal filename, data or hash");
1513 verbosestream << "Server pushes media file \"" << filename << "\" ";
1514 if (filedata.empty())
1515 verbosestream << "to be fetched ";
1517 verbosestream << "with " << filedata.size() << " bytes ";
1518 verbosestream << "(cached=" << cached << ")" << std::endl;
1520 if (m_media_pushed_files.count(filename) != 0) {
1521 // Ignore (but acknowledge). Previously this was for sync purposes,
1522 // but even in new versions media cannot be replaced at runtime.
1523 if (m_proto_ver >= 40)
1524 sendHaveMedia({ token });
1528 if (!filedata.empty()) {
1530 // Compute and check checksum of data
1531 std::string computed_hash;
1534 ctx.addBytes(filedata.c_str(), filedata.size());
1535 unsigned char *buf = ctx.getDigest();
1536 computed_hash.assign((char*) buf, 20);
1539 if (raw_hash != computed_hash) {
1540 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1544 // Actually load media
1545 loadMedia(filedata, filename, true);
1546 m_media_pushed_files.insert(filename);
1548 // Cache file for the next time when this client joins the same server
1550 clientMediaUpdateCache(raw_hash, filedata);
1554 m_media_pushed_files.insert(filename);
1556 // create a downloader for this file
1557 auto downloader(std::make_shared<SingleMediaDownloader>(cached));
1558 m_pending_media_downloads.emplace_back(token, downloader);
1559 downloader->addFile(filename, raw_hash);
1560 for (const auto &baseurl : m_remote_media_servers)
1561 downloader->addRemoteServer(baseurl);
1563 downloader->step(this);
1570 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1572 std::string channel_name, sender, channel_msg;
1573 *pkt >> channel_name >> sender >> channel_msg;
1575 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1576 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1577 << channel_msg << std::endl;
1579 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1580 verbosestream << "Server sent us messages on unregistered channel "
1581 << channel_name << ", ignoring." << std::endl;
1585 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1588 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1591 ModChannelSignal signal;
1592 std::string channel;
1594 *pkt >> signal_tmp >> channel;
1596 signal = (ModChannelSignal)signal_tmp;
1598 bool valid_signal = true;
1599 // @TODO: send Signal to Lua API
1601 case MODCHANNEL_SIGNAL_JOIN_OK:
1602 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1603 infostream << "Server ack our mod channel join on channel `" << channel
1604 << "`, joining." << std::endl;
1606 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1607 // Unable to join, remove channel
1608 m_modchannel_mgr->leaveChannel(channel, 0);
1609 infostream << "Server refused our mod channel join on channel `" << channel
1610 << "`" << std::endl;
1612 case MODCHANNEL_SIGNAL_LEAVE_OK:
1614 infostream << "Server ack our mod channel leave on channel " << channel
1615 << "`, leaving." << std::endl;
1618 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1619 infostream << "Server refused our mod channel leave on channel `" << channel
1620 << "`" << std::endl;
1622 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1624 // Generally unused, but ensure we don't do an implementation error
1625 infostream << "Server tells us we sent a message on channel `" << channel
1626 << "` but we are not registered. Message was dropped." << std::endl;
1629 case MODCHANNEL_SIGNAL_SET_STATE: {
1633 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1634 infostream << "Received wrong channel state " << state
1635 << ", ignoring." << std::endl;
1639 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1640 infostream << "Server sets mod channel `" << channel
1641 << "` in read-only mode." << std::endl;
1646 warningstream << "Received unhandled mod channel signal ID "
1647 << signal << ", ignoring." << std::endl;
1649 valid_signal = false;
1653 // If signal is valid, forward it to client side mods
1655 m_script->on_modchannel_signal(channel, signal);
1658 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1661 u16 mode; // wanted current mode index after change
1663 *pkt >> count >> mode;
1666 m_minimap->clearModes();
1668 for (size_t index = 0; index < count; index++) {
1672 std::string texture;
1675 *pkt >> type >> label >> size >> texture >> scale;
1678 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1682 m_minimap->setModeIndex(mode);
1685 void Client::handleCommand_SetLighting(NetworkPacket *pkt)
1687 Lighting& lighting = m_env.getLocalPlayer()->getLighting();
1689 if (pkt->getRemainingBytes() >= 4)
1690 *pkt >> lighting.shadow_intensity;