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.
21 #include "client/client.h"
23 #include "util/base64.h"
24 #include "client/camera.h"
25 #include "chatmessage.h"
26 #include "client/clientmedia.h"
29 #include "mapsector.h"
30 #include "client/minimap.h"
31 #include "modchannels.h"
33 #include "serialization.h"
35 #include "util/strfnd.h"
36 #include "client/clientevent.h"
37 #include "client/content_cao.h"
38 #include "client/sound.h"
39 #include "network/clientopcodes.h"
40 #include "network/connection.h"
41 #include "script/scripting_client.h"
42 #include "util/serialize.h"
44 #include "util/sha1.h"
45 #include "tileanimation.h"
47 #include "skyparams.h"
50 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
52 infostream << "Got deprecated command "
53 << toClientCommandTable[pkt->getCommand()].name << " from peer "
54 << pkt->getPeerId() << "!" << std::endl;
57 void Client::handleCommand_Hello(NetworkPacket* pkt)
59 if (pkt->getSize() < 1)
66 std::string username_legacy; // for case insensitivity
67 *pkt >> serialization_ver >> compression_mode >> proto_ver
68 >> auth_mechs >> username_legacy;
70 // Chose an auth method we support
71 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
73 infostream << "Client: TOCLIENT_HELLO received with "
74 << "serialization_ver=" << (u32)serialization_ver
75 << ", auth_mechs=" << auth_mechs
76 << ", proto_ver=" << proto_ver
77 << ", compression_mode=" << compression_mode
78 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
80 if (!ser_ver_supported(serialization_ver)) {
81 infostream << "Client: TOCLIENT_HELLO: Server sent "
82 << "unsupported ser_fmt_ver"<< std::endl;
86 m_server_ser_ver = serialization_ver;
87 m_proto_ver = proto_ver;
89 //TODO verify that username_legacy matches sent username, only
90 // differs in casing (make both uppercase and compare)
91 // This is only neccessary though when we actually want to add casing support
93 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
94 // we received a TOCLIENT_HELLO while auth was already going on
95 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
96 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
97 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
98 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
99 srp_user_delete((SRPUser *) m_auth_data);
104 // Authenticate using that method, or abort if there wasn't any method found
105 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
106 bool is_register = chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP;
107 ELoginRegister mode = is_register ? ELoginRegister::Register : ELoginRegister::Login;
108 if (m_allow_login_or_register != ELoginRegister::Any &&
109 m_allow_login_or_register != mode) {
110 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
111 m_access_denied = true;
112 if (m_allow_login_or_register == ELoginRegister::Login) {
113 m_access_denied_reason =
114 gettext("Name is not registered. To create an account on this server, click 'Register'");
116 m_access_denied_reason =
117 gettext("Name is taken. Please choose another name");
121 startAuth(chosen_auth_mechanism);
124 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
125 m_access_denied = true;
126 m_access_denied_reason = "Unknown";
132 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
137 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
138 >> m_sudo_auth_methods;
140 playerpos -= v3f(0, BS / 2, 0);
142 // Set player position
143 LocalPlayer *player = m_env.getLocalPlayer();
144 assert(player != NULL);
145 player->setPosition(playerpos);
147 infostream << "Client: received map seed: " << m_map_seed << std::endl;
148 infostream << "Client: received recommended send interval "
149 << m_recommended_send_interval<<std::endl;
152 /*~ DO NOT TRANSLATE THIS LITERALLY!
153 This is a special string which needs to contain the translation's
154 language code (e.g. "de" for German). */
155 std::string lang = gettext("LANG_CODE");
156 if (lang == "LANG_CODE")
159 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
165 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
169 m_password = m_new_password;
171 verbosestream << "Client: Received TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
173 // send packet to actually set the password
174 startAuth(AUTH_MECHANISM_FIRST_SRP);
177 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
179 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
181 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
182 L"Password change denied. Password NOT changed.");
183 pushToChatQueue(chatMessage);
184 // reset everything and be sad
188 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
190 // The server didn't like our password. Note, this needs
191 // to be processed even if the serialisation format has
192 // not been agreed yet, the same as TOCLIENT_INIT.
193 m_access_denied = true;
194 m_access_denied_reason = "Unknown";
196 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
197 // Legacy code from 0.4.12 and older but is still used
198 // in some places of the server code
199 if (pkt->getSize() >= 2) {
200 std::wstring wide_reason;
202 m_access_denied_reason = wide_to_utf8(wide_reason);
207 if (pkt->getSize() < 1)
213 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
214 denyCode == SERVER_ACCESSDENIED_CRASH) {
215 *pkt >> m_access_denied_reason;
216 if (m_access_denied_reason.empty())
217 m_access_denied_reason = accessDeniedStrings[denyCode];
220 m_access_denied_reconnect = reconnect & 1;
221 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
222 *pkt >> m_access_denied_reason;
223 } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) {
224 m_access_denied_reason = accessDeniedStrings[denyCode];
225 m_access_denied_reconnect = true;
226 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
227 m_access_denied_reason = accessDeniedStrings[denyCode];
229 // Allow us to add new error messages to the
230 // protocol without raising the protocol version, if we want to.
231 // Until then (which may be never), this is outside
232 // of the defined protocol.
233 *pkt >> m_access_denied_reason;
234 if (m_access_denied_reason.empty())
235 m_access_denied_reason = "Unknown";
239 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
241 if (pkt->getSize() < 6)
249 void Client::handleCommand_AddNode(NetworkPacket* pkt)
251 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
258 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
260 bool remove_metadata = true;
261 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
262 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
263 remove_metadata = false;
266 addNode(p, n, remove_metadata);
269 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
271 if (pkt->getSize() < 1)
274 std::istringstream is(pkt->readLongString(), std::ios::binary);
275 std::stringstream sstr(std::ios::binary | std::ios::in | std::ios::out);
276 decompressZlib(is, sstr);
278 NodeMetadataList meta_updates_list(false);
279 meta_updates_list.deSerialize(sstr, m_itemdef, true);
281 Map &map = m_env.getMap();
282 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
283 i != meta_updates_list.end(); ++i) {
284 v3s16 pos = i->first;
286 if (map.isValidPosition(pos) &&
287 map.setNodeMetadata(pos, i->second))
288 continue; // Prevent from deleting metadata
290 // Meta couldn't be set, unused metadata
295 void Client::handleCommand_BlockData(NetworkPacket* pkt)
297 // Ignore too small packet
298 if (pkt->getSize() < 6)
304 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
305 std::istringstream istr(datastring, std::ios_base::binary);
311 sector = m_env.getMap().emergeSector(p2d);
313 assert(sector->getPos() == p2d);
315 block = sector->getBlockNoCreateNoEx(p.Y);
318 Update an existing block
320 block->deSerialize(istr, m_server_ser_ver, false);
321 block->deSerializeNetworkSpecific(istr);
327 block = new MapBlock(&m_env.getMap(), p, this);
328 block->deSerialize(istr, m_server_ser_ver, false);
329 block->deSerializeNetworkSpecific(istr);
330 sector->insertBlock(block);
334 ServerMap::saveBlock(block, m_localdb);
338 Add it to mesh update queue and set it to be acknowledged after update.
340 addUpdateMeshTaskWithEdge(p, true);
343 void Client::handleCommand_Inventory(NetworkPacket* pkt)
345 if (pkt->getSize() < 1)
348 std::string datastring(pkt->getString(0), pkt->getSize());
349 std::istringstream is(datastring, std::ios_base::binary);
351 LocalPlayer *player = m_env.getLocalPlayer();
352 assert(player != NULL);
354 player->inventory.deSerialize(is);
356 m_update_wielded_item = true;
358 delete m_inventory_from_server;
359 m_inventory_from_server = new Inventory(player->inventory);
360 m_inventory_from_server_age = 0.0;
363 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
365 if (pkt->getSize() < 2)
372 time_of_day = time_of_day % 24000;
373 float time_speed = 0;
375 if (pkt->getSize() >= 2 + 4) {
379 // Old message; try to approximate speed of time by ourselves
380 float time_of_day_f = (float)time_of_day / 24000.0f;
381 float tod_diff_f = 0;
383 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
384 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
386 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
388 m_last_time_of_day_f = time_of_day_f;
389 float time_diff = m_time_of_day_update_timer;
390 m_time_of_day_update_timer = 0;
392 if (m_time_of_day_set) {
393 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
394 infostream << "Client: Measured time_of_day speed (old format): "
395 << time_speed << " tod_diff_f=" << tod_diff_f
396 << " time_diff=" << time_diff << std::endl;
400 // Update environment
401 m_env.setTimeOfDay(time_of_day);
402 m_env.setTimeOfDaySpeed(time_speed);
403 m_time_of_day_set = true;
405 //u32 dr = m_env.getDayNightRatio();
406 //infostream << "Client: time_of_day=" << time_of_day
407 // << " time_speed=" << time_speed
408 // << " dr=" << dr << std::endl;
411 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
416 u16 sendername length
422 ChatMessage *chatMessage = new ChatMessage();
423 u8 version, message_type;
424 *pkt >> version >> message_type;
426 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
432 *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
433 chatMessage->timestamp = static_cast<std::time_t>(timestamp);
435 chatMessage->type = (ChatMessageType) message_type;
437 // @TODO send this to CSM using ChatMessage object
438 if (modsLoaded() && m_script->on_receiving_message(
439 wide_to_utf8(chatMessage->message))) {
440 // Message was consumed by CSM and should not be handled by client
443 pushToChatQueue(chatMessage);
447 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
450 u16 count of removed objects
451 for all removed objects {
454 u16 count of added objects
455 for all added objects {
458 u32 initialization data length
459 string initialization data
463 LocalPlayer *player = m_env.getLocalPlayer();
464 bool try_reattach = player && player->isWaitingForReattach();
468 u16 removed_count, added_count, id;
470 // Read removed objects
471 *pkt >> removed_count;
473 for (u16 i = 0; i < removed_count; i++) {
475 m_env.removeActiveObject(id);
478 // Read added objects
481 for (u16 i = 0; i < added_count; i++) {
483 m_env.addActiveObject(id, type, pkt->readLongString());
485 player->tryReattach(id);
487 } catch (PacketError &e) {
488 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
489 << ". The packet is unreliable, ignoring" << std::endl;
492 // m_activeobjects_received is false before the first
493 // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
494 m_activeobjects_received = true;
497 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
507 std::string datastring(pkt->getString(0), pkt->getSize());
508 std::istringstream is(datastring, std::ios_base::binary);
512 u16 id = readU16(is);
516 std::string message = deSerializeString16(is);
518 // Pass on to the environment
519 m_env.processActiveObjectMessage(id, message);
521 } catch (SerializationError &e) {
522 errorstream << "Client::handleCommand_ActiveObjectMessages: "
523 << "caught SerializationError: " << e.what() << std::endl;
527 void Client::handleCommand_Movement(NetworkPacket* pkt)
529 LocalPlayer *player = m_env.getLocalPlayer();
530 assert(player != NULL);
532 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
534 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
535 >> lf >> lfs >> ls >> g;
537 player->movement_acceleration_default = mad * BS;
538 player->movement_acceleration_air = maa * BS;
539 player->movement_acceleration_fast = maf * BS;
540 player->movement_speed_walk = msw * BS;
541 player->movement_speed_crouch = mscr * BS;
542 player->movement_speed_fast = msf * BS;
543 player->movement_speed_climb = mscl * BS;
544 player->movement_speed_jump = msj * BS;
545 player->movement_liquid_fluidity = lf * BS;
546 player->movement_liquid_fluidity_smooth = lfs * BS;
547 player->movement_liquid_sink = ls * BS;
548 player->movement_gravity = g * BS;
551 void Client::handleCommand_Fov(NetworkPacket *pkt)
554 bool is_multiplier = false;
555 f32 transition_time = 0.0f;
557 *pkt >> fov >> is_multiplier;
559 // Wrap transition_time extraction within a
560 // try-catch to preserve backwards compat
562 *pkt >> transition_time;
563 } catch (PacketError &e) {};
565 LocalPlayer *player = m_env.getLocalPlayer();
567 player->setFov({ fov, is_multiplier, transition_time });
568 m_camera->notifyFovChange();
571 void Client::handleCommand_HP(NetworkPacket *pkt)
573 LocalPlayer *player = m_env.getLocalPlayer();
574 assert(player != NULL);
576 u16 oldhp = player->hp;
584 m_script->on_hp_modification(hp);
587 // Add to ClientEvent queue
588 ClientEvent *event = new ClientEvent();
589 event->type = CE_PLAYER_DAMAGE;
590 event->player_damage.amount = oldhp - hp;
591 m_client_event_queue.push(event);
595 void Client::handleCommand_Breath(NetworkPacket* pkt)
597 LocalPlayer *player = m_env.getLocalPlayer();
598 assert(player != NULL);
604 player->setBreath(breath);
607 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
609 LocalPlayer *player = m_env.getLocalPlayer();
610 assert(player != NULL);
612 if ((player->getCAO() && player->getCAO()->getParentId()) || player->isWaitingForReattach())
618 *pkt >> pos >> pitch >> yaw;
620 player->setLegitPosition(pos);
622 infostream << "Client got TOCLIENT_MOVE_PLAYER"
623 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
624 << " pitch=" << pitch
629 Add to ClientEvent queue.
630 This has to be sent to the main program because otherwise
631 it would just force the pitch and yaw values to whatever
632 the camera points to.
635 if (g_settings->getBool("no_force_rotate"))
638 ClientEvent *event = new ClientEvent();
639 event->type = CE_PLAYER_FORCE_MOVE;
640 event->player_force_move.pitch = pitch;
641 event->player_force_move.yaw = yaw;
642 m_client_event_queue.push(event);
645 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
647 bool set_camera_point_target;
648 v3f camera_point_target;
650 *pkt >> set_camera_point_target;
651 *pkt >> camera_point_target;
653 ClientEvent *event = new ClientEvent();
654 event->type = CE_DEATHSCREEN;
655 event->deathscreen.set_camera_point_target = set_camera_point_target;
656 event->deathscreen.camera_point_target_x = camera_point_target.X;
657 event->deathscreen.camera_point_target_y = camera_point_target.Y;
658 event->deathscreen.camera_point_target_z = camera_point_target.Z;
659 m_client_event_queue.push(event);
662 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
668 infostream << "Client: Received media announcement: packet size: "
669 << pkt->getSize() << std::endl;
671 if (m_media_downloader == NULL ||
672 m_media_downloader->isStarted()) {
673 const char *problem = m_media_downloader ?
674 "we already saw another announcement" :
675 "all media has been received already";
676 errorstream << "Client: Received media announcement but "
678 << " files=" << num_files
679 << " size=" << pkt->getSize() << std::endl;
683 // Mesh update thread must be stopped while
684 // updating content definitions
685 sanity_check(!m_mesh_update_thread.isRunning());
687 for (u16 i = 0; i < num_files; i++) {
688 std::string name, sha1_base64;
690 *pkt >> name >> sha1_base64;
692 std::string sha1_raw = base64_decode(sha1_base64);
693 m_media_downloader->addFile(name, sha1_raw);
701 while (!sf.at_end()) {
702 std::string baseurl = trim(sf.next(","));
703 if (!baseurl.empty()) {
704 m_remote_media_servers.emplace_back(baseurl);
705 m_media_downloader->addRemoteServer(baseurl);
710 m_media_downloader->step(this);
713 void Client::handleCommand_Media(NetworkPacket* pkt)
717 u16 total number of file bunches
718 u16 index of this bunch
719 u32 number of files in this bunch
731 *pkt >> num_bunches >> bunch_i >> num_files;
733 infostream << "Client: Received files: bunch " << bunch_i << "/"
734 << num_bunches << " files=" << num_files
735 << " size=" << pkt->getSize() << std::endl;
740 bool init_phase = m_media_downloader && m_media_downloader->isStarted();
743 // Mesh update thread must be stopped while
744 // updating content definitions
745 sanity_check(!m_mesh_update_thread.isRunning());
748 for (u32 i = 0; i < num_files; i++) {
749 std::string name, data;
752 data = pkt->readLongString();
756 ok = m_media_downloader->conventionalTransferDone(name, data, this);
758 // Check pending dynamic transfers, one of them must be it
759 for (const auto &it : m_pending_media_downloads) {
760 if (it.second->conventionalTransferDone(name, data, this)) {
767 errorstream << "Client: Received media \"" << name
768 << "\" but no downloads pending. " << num_bunches << " bunches, "
769 << num_files << " in this one. (init_phase=" << init_phase
775 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
777 infostream << "Client: Received node definitions: packet size: "
778 << pkt->getSize() << std::endl;
780 // Mesh update thread must be stopped while
781 // updating content definitions
782 sanity_check(!m_mesh_update_thread.isRunning());
784 // Decompress node definitions
785 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
786 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
787 decompressZlib(tmp_is, tmp_os);
789 // Deserialize node definitions
790 m_nodedef->deSerialize(tmp_os);
791 m_nodedef_received = true;
794 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
796 infostream << "Client: Received item definitions: packet size: "
797 << pkt->getSize() << std::endl;
799 // Mesh update thread must be stopped while
800 // updating content definitions
801 sanity_check(!m_mesh_update_thread.isRunning());
803 // Decompress item definitions
804 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
805 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
806 decompressZlib(tmp_is, tmp_os);
808 // Deserialize node definitions
809 m_itemdef->deSerialize(tmp_os);
810 m_itemdef_received = true;
813 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
821 [11 + len] (f32 * 3) pos
822 [23 + len] u16 object_id
826 [34 + len] bool ephemeral
833 u8 type; // 0=local, 1=positional, 2=object
839 bool ephemeral = false;
841 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
847 } catch (PacketError &e) {};
849 SimpleSoundSpec sound_spec(name, gain, fade, pitch);
851 if (m_mods_loaded && m_script->on_play_sound(sound_spec))
858 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
860 case 1: // positional
861 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
865 ClientActiveObject *cao = m_env.getActiveObject(object_id);
867 pos = cao->getPosition();
868 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
875 if (client_id != -1) {
876 // for ephemeral sounds, server_id is not meaningful
878 m_sounds_server_to_client[server_id] = client_id;
879 m_sounds_client_to_server[client_id] = server_id;
882 m_sounds_to_objects[client_id] = object_id;
886 void Client::handleCommand_StopSound(NetworkPacket* pkt)
892 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
893 if (i != m_sounds_server_to_client.end()) {
894 int client_id = i->second;
895 m_sound->stopSound(client_id);
899 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
905 *pkt >> sound_id >> step >> gain;
907 std::unordered_map<s32, int>::const_iterator i =
908 m_sounds_server_to_client.find(sound_id);
910 if (i != m_sounds_server_to_client.end())
911 m_sound->fadeSound(i->second, step, gain);
914 void Client::handleCommand_Privileges(NetworkPacket* pkt)
916 m_privileges.clear();
917 infostream << "Client: Privileges updated: ";
920 *pkt >> num_privileges;
922 for (u16 i = 0; i < num_privileges; i++) {
927 m_privileges.insert(priv);
928 infostream << priv << " ";
930 infostream << std::endl;
933 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
935 LocalPlayer *player = m_env.getLocalPlayer();
936 assert(player != NULL);
938 // Store formspec in LocalPlayer
939 player->inventory_formspec = pkt->readLongString();
942 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
945 bool keep_inv = true;
946 *pkt >> name >> keep_inv;
948 infostream << "Client: Detached inventory update: \"" << name
949 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
951 const auto &inv_it = m_detached_inventories.find(name);
953 if (inv_it != m_detached_inventories.end()) {
954 delete inv_it->second;
955 m_detached_inventories.erase(inv_it);
959 Inventory *inv = nullptr;
960 if (inv_it == m_detached_inventories.end()) {
961 inv = new Inventory(m_itemdef);
962 m_detached_inventories[name] = inv;
964 inv = inv_it->second;
968 *pkt >> ignore; // this used to be the length of the following string, ignore it
970 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
971 std::istringstream is(contents, std::ios::binary);
972 inv->deSerialize(is);
975 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
977 std::string formspec = pkt->readLongString();
978 std::string formname;
982 ClientEvent *event = new ClientEvent();
983 event->type = CE_SHOW_FORMSPEC;
984 // pointer is required as event is a struct only!
985 // adding a std:string to a struct isn't possible
986 event->show_formspec.formspec = new std::string(formspec);
987 event->show_formspec.formname = new std::string(formname);
988 m_client_event_queue.push(event);
991 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
993 std::string datastring(pkt->getString(0), pkt->getSize());
994 std::istringstream is(datastring, std::ios_base::binary);
996 ParticleParameters p;
997 p.deSerialize(is, m_proto_ver);
999 ClientEvent *event = new ClientEvent();
1000 event->type = CE_SPAWN_PARTICLE;
1001 event->spawn_particle = new ParticleParameters(p);
1003 if (m_mods_loaded && m_script->on_spawn_particle(*event->spawn_particle))
1006 m_client_event_queue.push(event);
1009 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
1011 std::string datastring(pkt->getString(0), pkt->getSize());
1012 std::istringstream is(datastring, std::ios_base::binary);
1014 ParticleSpawnerParameters p;
1016 u16 attached_id = 0;
1018 p.amount = readU16(is);
1019 p.time = readF32(is);
1020 p.minpos = readV3F32(is);
1021 p.maxpos = readV3F32(is);
1022 p.minvel = readV3F32(is);
1023 p.maxvel = readV3F32(is);
1024 p.minacc = readV3F32(is);
1025 p.maxacc = readV3F32(is);
1026 p.minexptime = readF32(is);
1027 p.maxexptime = readF32(is);
1028 p.minsize = readF32(is);
1029 p.maxsize = readF32(is);
1030 p.collisiondetection = readU8(is);
1031 p.texture = deSerializeString32(is);
1033 server_id = readU32(is);
1035 p.vertical = readU8(is);
1036 p.collision_removal = readU8(is);
1038 attached_id = readU16(is);
1040 p.animation.deSerialize(is, m_proto_ver);
1041 p.glow = readU8(is);
1042 p.object_collision = readU8(is);
1044 // This is kinda awful
1046 u16 tmp_param0 = readU16(is);
1049 p.node.param0 = tmp_param0;
1050 p.node.param2 = readU8(is);
1051 p.node_tile = readU8(is);
1054 auto event = new ClientEvent();
1055 event->type = CE_ADD_PARTICLESPAWNER;
1056 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1057 event->add_particlespawner.attached_id = attached_id;
1058 event->add_particlespawner.id = server_id;
1060 m_client_event_queue.push(event);
1064 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1069 ClientEvent *event = new ClientEvent();
1070 event->type = CE_DELETE_PARTICLESPAWNER;
1071 event->delete_particlespawner.id = server_id;
1073 m_client_event_queue.push(event);
1076 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1095 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1096 >> dir >> align >> offset;
1103 } catch(PacketError &e) {};
1105 ClientEvent *event = new ClientEvent();
1106 event->type = CE_HUDADD;
1107 event->hudadd = new ClientEventHudAdd();
1108 event->hudadd->server_id = server_id;
1109 event->hudadd->type = type;
1110 event->hudadd->pos = pos;
1111 event->hudadd->name = name;
1112 event->hudadd->scale = scale;
1113 event->hudadd->text = text;
1114 event->hudadd->number = number;
1115 event->hudadd->item = item;
1116 event->hudadd->dir = dir;
1117 event->hudadd->align = align;
1118 event->hudadd->offset = offset;
1119 event->hudadd->world_pos = world_pos;
1120 event->hudadd->size = size;
1121 event->hudadd->z_index = z_index;
1122 event->hudadd->text2 = text2;
1123 event->hudadd->style = style;
1124 m_client_event_queue.push(event);
1127 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1133 ClientEvent *event = new ClientEvent();
1134 event->type = CE_HUDRM;
1135 event->hudrm.id = server_id;
1136 m_client_event_queue.push(event);
1139 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1149 *pkt >> server_id >> stat;
1151 // Keep in sync with:server.cpp -> SendHUDChange
1152 switch ((HudElementStat)stat) {
1154 case HUD_STAT_SCALE:
1155 case HUD_STAT_ALIGN:
1156 case HUD_STAT_OFFSET:
1161 case HUD_STAT_TEXT2:
1164 case HUD_STAT_WORLD_POS:
1175 ClientEvent *event = new ClientEvent();
1176 event->type = CE_HUDCHANGE;
1177 event->hudchange = new ClientEventHudChange();
1178 event->hudchange->id = server_id;
1179 event->hudchange->stat = static_cast<HudElementStat>(stat);
1180 event->hudchange->v2fdata = v2fdata;
1181 event->hudchange->v3fdata = v3fdata;
1182 event->hudchange->sdata = sdata;
1183 event->hudchange->data = intdata;
1184 event->hudchange->v2s32data = v2s32data;
1185 m_client_event_queue.push(event);
1188 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1192 *pkt >> flags >> mask;
1194 LocalPlayer *player = m_env.getLocalPlayer();
1195 assert(player != NULL);
1197 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1198 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1200 player->hud_flags &= ~mask;
1201 player->hud_flags |= flags;
1203 if (g_settings->getBool("hud_flags_bypass"))
1204 player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE |
1205 HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
1206 HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE |
1207 HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1209 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1210 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1212 // Not so satisying code to keep compatibility with old fixed mode system
1215 // Hide minimap if it has been disabled by the server
1216 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1217 // defers a minimap update, therefore only call it if really
1218 // needed, by checking that minimap was visible before
1219 m_minimap->setModeIndex(0);
1221 // If radar has been disabled, try to find a non radar mode or fall back to 0
1222 if (m_minimap && m_minimap_radar_disabled_by_server
1223 && was_minimap_radar_visible) {
1224 while (m_minimap->getModeIndex() > 0 &&
1225 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1226 m_minimap->nextMode();
1229 // End of 'not so satifying code'
1232 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1234 u16 param; std::string value;
1236 *pkt >> param >> value;
1238 LocalPlayer *player = m_env.getLocalPlayer();
1239 assert(player != NULL);
1241 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1242 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1243 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1244 player->hud_hotbar_itemcount = hotbar_itemcount;
1246 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1247 player->hotbar_image = value;
1249 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1250 player->hotbar_selected_image = value;
1254 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1256 if (m_proto_ver < 39) {
1257 // Handle Protocol 38 and below servers with old set_sky,
1258 // ensuring the classic look is kept.
1259 std::string datastring(pkt->getString(0), pkt->getSize());
1260 std::istringstream is(datastring, std::ios_base::binary);
1262 SkyboxParams skybox;
1263 skybox.bgcolor = video::SColor(readARGB8(is));
1264 skybox.type = std::string(deSerializeString16(is));
1265 u16 count = readU16(is);
1267 for (size_t i = 0; i < count; i++)
1268 skybox.textures.emplace_back(deSerializeString16(is));
1270 skybox.clouds = true;
1272 skybox.clouds = readU8(is);
1275 // Use default skybox settings:
1276 SunParams sun = SkyboxDefaults::getSunDefaults();
1277 MoonParams moon = SkyboxDefaults::getMoonDefaults();
1278 StarParams stars = SkyboxDefaults::getStarDefaults();
1280 // Fix for "regular" skies, as color isn't kept:
1281 if (skybox.type == "regular") {
1282 skybox.sky_color = SkyboxDefaults::getSkyColorDefaults();
1283 skybox.fog_tint_type = "default";
1284 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1285 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1287 sun.visible = false;
1288 sun.sunrise_visible = false;
1289 moon.visible = false;
1290 stars.visible = false;
1293 // Skybox, sun, moon and stars ClientEvents:
1294 ClientEvent *sky_event = new ClientEvent();
1295 sky_event->type = CE_SET_SKY;
1296 sky_event->set_sky = new SkyboxParams(skybox);
1297 m_client_event_queue.push(sky_event);
1299 ClientEvent *sun_event = new ClientEvent();
1300 sun_event->type = CE_SET_SUN;
1301 sun_event->sun_params = new SunParams(sun);
1302 m_client_event_queue.push(sun_event);
1304 ClientEvent *moon_event = new ClientEvent();
1305 moon_event->type = CE_SET_MOON;
1306 moon_event->moon_params = new MoonParams(moon);
1307 m_client_event_queue.push(moon_event);
1309 ClientEvent *star_event = new ClientEvent();
1310 star_event->type = CE_SET_STARS;
1311 star_event->star_params = new StarParams(stars);
1312 m_client_event_queue.push(star_event);
1314 SkyboxParams skybox;
1316 std::string texture;
1318 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1319 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1321 if (skybox.type == "skybox") {
1322 *pkt >> texture_count;
1323 for (int i = 0; i < texture_count; i++) {
1325 skybox.textures.emplace_back(texture);
1328 else if (skybox.type == "regular") {
1329 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1330 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1331 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1332 >> skybox.sky_color.indoors;
1335 ClientEvent *event = new ClientEvent();
1336 event->type = CE_SET_SKY;
1337 event->set_sky = new SkyboxParams(skybox);
1338 m_client_event_queue.push(event);
1342 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1346 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1347 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1349 ClientEvent *event = new ClientEvent();
1350 event->type = CE_SET_SUN;
1351 event->sun_params = new SunParams(sun);
1352 m_client_event_queue.push(event);
1355 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1359 *pkt >> moon.visible >> moon.texture
1360 >> moon.tonemap >> moon.scale;
1362 ClientEvent *event = new ClientEvent();
1363 event->type = CE_SET_MOON;
1364 event->moon_params = new MoonParams(moon);
1365 m_client_event_queue.push(event);
1368 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1372 *pkt >> stars.visible >> stars.count
1373 >> stars.starcolor >> stars.scale;
1375 ClientEvent *event = new ClientEvent();
1376 event->type = CE_SET_STARS;
1377 event->star_params = new StarParams(stars);
1379 m_client_event_queue.push(event);
1382 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1385 video::SColor color_bright;
1386 video::SColor color_ambient;
1391 *pkt >> density >> color_bright >> color_ambient
1392 >> height >> thickness >> speed;
1394 ClientEvent *event = new ClientEvent();
1395 event->type = CE_CLOUD_PARAMS;
1396 event->cloud_params.density = density;
1397 // use the underlying u32 representation, because we can't
1398 // use struct members with constructors here, and this way
1399 // we avoid using new() and delete() for no good reason
1400 event->cloud_params.color_bright = color_bright.color;
1401 event->cloud_params.color_ambient = color_ambient.color;
1402 event->cloud_params.height = height;
1403 event->cloud_params.thickness = thickness;
1404 // same here: deconstruct to skip constructor
1405 event->cloud_params.speed_x = speed.X;
1406 event->cloud_params.speed_y = speed.Y;
1407 m_client_event_queue.push(event);
1410 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1413 u16 day_night_ratio_u;
1415 *pkt >> do_override >> day_night_ratio_u;
1417 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1419 ClientEvent *event = new ClientEvent();
1420 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1421 event->override_day_night_ratio.do_override = do_override;
1422 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1423 m_client_event_queue.push(event);
1426 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1428 LocalPlayer *player = m_env.getLocalPlayer();
1429 assert(player != NULL);
1431 *pkt >> player->local_animations[0];
1432 *pkt >> player->local_animations[1];
1433 *pkt >> player->local_animations[2];
1434 *pkt >> player->local_animations[3];
1435 *pkt >> player->local_animation_speed;
1437 player->last_animation = -1;
1440 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1442 LocalPlayer *player = m_env.getLocalPlayer();
1443 assert(player != NULL);
1445 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1448 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1452 *pkt >> type >> num_players;
1453 PlayerListModifer notice_type = (PlayerListModifer) type;
1455 for (u16 i = 0; i < num_players; i++) {
1458 switch (notice_type) {
1459 case PLAYER_LIST_INIT:
1460 case PLAYER_LIST_ADD:
1461 m_env.addPlayerName(name);
1463 case PLAYER_LIST_REMOVE:
1464 m_env.removePlayerName(name);
1470 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1472 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1473 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1474 errorstream << "Client: Received SRP S_B login message,"
1475 << " but wasn't supposed to (chosen_mech="
1476 << m_chosen_auth_mech << ")." << std::endl;
1482 SRPUser *usr = (SRPUser *) m_auth_data;
1487 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1489 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1490 (const unsigned char *) B.c_str(), B.size(),
1491 (unsigned char **) &bytes_M, &len_M);
1494 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1498 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1499 resp_pkt << std::string(bytes_M, len_M);
1503 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1505 LocalPlayer *player = m_env.getLocalPlayer();
1506 assert(player != NULL);
1508 // Store formspec in LocalPlayer
1509 *pkt >> player->formspec_prepend;
1512 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1514 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1516 // Restrictions were received -> load mods if it's enabled
1517 // Note: this should be moved after mods receptions from server instead
1521 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1523 if (g_settings->getBool("antiknockback"))
1529 LocalPlayer *player = m_env.getLocalPlayer();
1530 assert(player != NULL);
1531 player->addVelocity(added_vel);
1534 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1536 std::string raw_hash, filename, filedata;
1540 *pkt >> raw_hash >> filename >> cached;
1541 if (m_proto_ver >= 40)
1544 filedata = pkt->readLongString();
1546 if (raw_hash.size() != 20 || filename.empty() ||
1547 (m_proto_ver < 40 && filedata.empty()) ||
1548 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1549 throw PacketError("Illegal filename, data or hash");
1552 verbosestream << "Server pushes media file \"" << filename << "\" ";
1553 if (filedata.empty())
1554 verbosestream << "to be fetched ";
1556 verbosestream << "with " << filedata.size() << " bytes ";
1557 verbosestream << "(cached=" << cached << ")" << std::endl;
1559 if (m_media_pushed_files.count(filename) != 0) {
1560 // Ignore (but acknowledge). Previously this was for sync purposes,
1561 // but even in new versions media cannot be replaced at runtime.
1562 if (m_proto_ver >= 40)
1563 sendHaveMedia({ token });
1567 if (!filedata.empty()) {
1569 // Compute and check checksum of data
1570 std::string computed_hash;
1573 ctx.addBytes(filedata.c_str(), filedata.size());
1574 unsigned char *buf = ctx.getDigest();
1575 computed_hash.assign((char*) buf, 20);
1578 if (raw_hash != computed_hash) {
1579 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1583 // Actually load media
1584 loadMedia(filedata, filename, true);
1585 m_media_pushed_files.insert(filename);
1587 // Cache file for the next time when this client joins the same server
1589 clientMediaUpdateCache(raw_hash, filedata);
1593 m_media_pushed_files.insert(filename);
1595 // create a downloader for this file
1596 auto downloader(std::make_shared<SingleMediaDownloader>(cached));
1597 m_pending_media_downloads.emplace_back(token, downloader);
1598 downloader->addFile(filename, raw_hash);
1599 for (const auto &baseurl : m_remote_media_servers)
1600 downloader->addRemoteServer(baseurl);
1602 downloader->step(this);
1609 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1611 std::string channel_name, sender, channel_msg;
1612 *pkt >> channel_name >> sender >> channel_msg;
1614 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1615 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1616 << channel_msg << std::endl;
1618 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1619 verbosestream << "Server sent us messages on unregistered channel "
1620 << channel_name << ", ignoring." << std::endl;
1624 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1627 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1630 ModChannelSignal signal;
1631 std::string channel;
1633 *pkt >> signal_tmp >> channel;
1635 signal = (ModChannelSignal)signal_tmp;
1637 bool valid_signal = true;
1638 // @TODO: send Signal to Lua API
1640 case MODCHANNEL_SIGNAL_JOIN_OK:
1641 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1642 infostream << "Server ack our mod channel join on channel `" << channel
1643 << "`, joining." << std::endl;
1645 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1646 // Unable to join, remove channel
1647 m_modchannel_mgr->leaveChannel(channel, 0);
1648 infostream << "Server refused our mod channel join on channel `" << channel
1649 << "`" << std::endl;
1651 case MODCHANNEL_SIGNAL_LEAVE_OK:
1653 infostream << "Server ack our mod channel leave on channel " << channel
1654 << "`, leaving." << std::endl;
1657 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1658 infostream << "Server refused our mod channel leave on channel `" << channel
1659 << "`" << std::endl;
1661 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1663 // Generally unused, but ensure we don't do an implementation error
1664 infostream << "Server tells us we sent a message on channel `" << channel
1665 << "` but we are not registered. Message was dropped." << std::endl;
1668 case MODCHANNEL_SIGNAL_SET_STATE: {
1672 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1673 infostream << "Received wrong channel state " << state
1674 << ", ignoring." << std::endl;
1678 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1679 infostream << "Server sets mod channel `" << channel
1680 << "` in read-only mode." << std::endl;
1685 warningstream << "Received unhandled mod channel signal ID "
1686 << signal << ", ignoring." << std::endl;
1688 valid_signal = false;
1692 // If signal is valid, forward it to client side mods
1694 m_script->on_modchannel_signal(channel, signal);
1697 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1700 u16 mode; // wanted current mode index after change
1702 *pkt >> count >> mode;
1705 m_minimap->clearModes();
1707 for (size_t index = 0; index < count; index++) {
1711 std::string texture;
1714 *pkt >> type >> label >> size >> texture >> scale;
1717 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1721 m_minimap->setModeIndex(mode);
1724 void Client::handleCommand_SetLighting(NetworkPacket *pkt)
1726 Lighting& lighting = m_env.getLocalPlayer()->getLighting();
1728 if (pkt->getRemainingBytes() >= 4)
1729 *pkt >> lighting.shadow_intensity;