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_MAX) {
212 m_access_denied_reason = accessDeniedStrings[denyCode];
214 // Allow us to add new error messages to the
215 // protocol without raising the protocol version, if we want to.
216 // Until then (which may be never), this is outside
217 // of the defined protocol.
218 *pkt >> m_access_denied_reason;
219 if (m_access_denied_reason.empty()) {
220 m_access_denied_reason = "Unknown";
225 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
227 if (pkt->getSize() < 6)
235 void Client::handleCommand_AddNode(NetworkPacket* pkt)
237 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
244 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
246 bool remove_metadata = true;
247 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
248 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
249 remove_metadata = false;
252 addNode(p, n, remove_metadata);
255 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
257 if (pkt->getSize() < 1)
260 std::istringstream is(pkt->readLongString(), std::ios::binary);
261 std::stringstream sstr;
262 decompressZlib(is, sstr);
264 NodeMetadataList meta_updates_list(false);
265 meta_updates_list.deSerialize(sstr, m_itemdef, true);
267 Map &map = m_env.getMap();
268 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
269 i != meta_updates_list.end(); ++i) {
270 v3s16 pos = i->first;
272 if (map.isValidPosition(pos) &&
273 map.setNodeMetadata(pos, i->second))
274 continue; // Prevent from deleting metadata
276 // Meta couldn't be set, unused metadata
281 void Client::handleCommand_BlockData(NetworkPacket* pkt)
283 // Ignore too small packet
284 if (pkt->getSize() < 6)
290 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
291 std::istringstream istr(datastring, std::ios_base::binary);
297 sector = m_env.getMap().emergeSector(p2d);
299 assert(sector->getPos() == p2d);
301 block = sector->getBlockNoCreateNoEx(p.Y);
304 Update an existing block
306 block->deSerialize(istr, m_server_ser_ver, false);
307 block->deSerializeNetworkSpecific(istr);
313 block = new MapBlock(&m_env.getMap(), p, this);
314 block->deSerialize(istr, m_server_ser_ver, false);
315 block->deSerializeNetworkSpecific(istr);
316 sector->insertBlock(block);
320 ServerMap::saveBlock(block, m_localdb);
324 Add it to mesh update queue and set it to be acknowledged after update.
326 addUpdateMeshTaskWithEdge(p, true);
329 void Client::handleCommand_Inventory(NetworkPacket* pkt)
331 if (pkt->getSize() < 1)
334 std::string datastring(pkt->getString(0), pkt->getSize());
335 std::istringstream is(datastring, std::ios_base::binary);
337 LocalPlayer *player = m_env.getLocalPlayer();
338 assert(player != NULL);
340 player->inventory.deSerialize(is);
342 m_update_wielded_item = true;
344 delete m_inventory_from_server;
345 m_inventory_from_server = new Inventory(player->inventory);
346 m_inventory_from_server_age = 0.0;
349 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
351 if (pkt->getSize() < 2)
358 time_of_day = time_of_day % 24000;
359 float time_speed = 0;
361 if (pkt->getSize() >= 2 + 4) {
365 // Old message; try to approximate speed of time by ourselves
366 float time_of_day_f = (float)time_of_day / 24000.0f;
367 float tod_diff_f = 0;
369 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
370 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
372 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
374 m_last_time_of_day_f = time_of_day_f;
375 float time_diff = m_time_of_day_update_timer;
376 m_time_of_day_update_timer = 0;
378 if (m_time_of_day_set) {
379 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
380 infostream << "Client: Measured time_of_day speed (old format): "
381 << time_speed << " tod_diff_f=" << tod_diff_f
382 << " time_diff=" << time_diff << std::endl;
386 // Update environment
387 m_env.setTimeOfDay(time_of_day);
388 m_env.setTimeOfDaySpeed(time_speed);
389 m_time_of_day_set = true;
391 //u32 dr = m_env.getDayNightRatio();
392 //infostream << "Client: time_of_day=" << time_of_day
393 // << " time_speed=" << time_speed
394 // << " dr=" << dr << std::endl;
397 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
402 u16 sendername length
408 ChatMessage *chatMessage = new ChatMessage();
409 u8 version, message_type;
410 *pkt >> version >> message_type;
412 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
418 *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
419 chatMessage->timestamp = static_cast<std::time_t>(timestamp);
421 chatMessage->type = (ChatMessageType) message_type;
423 // @TODO send this to CSM using ChatMessage object
424 if (modsLoaded() && m_script->on_receiving_message(
425 wide_to_utf8(chatMessage->message))) {
426 // Message was consumed by CSM and should not be handled by client
429 pushToChatQueue(chatMessage);
433 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
436 u16 count of removed objects
437 for all removed objects {
440 u16 count of added objects
441 for all added objects {
444 u32 initialization data length
445 string initialization data
451 u16 removed_count, added_count, id;
453 // Read removed objects
454 *pkt >> removed_count;
456 for (u16 i = 0; i < removed_count; i++) {
458 m_env.removeActiveObject(id);
461 // Read added objects
464 for (u16 i = 0; i < added_count; i++) {
466 m_env.addActiveObject(id, type, pkt->readLongString());
468 } catch (PacketError &e) {
469 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
470 << ". The packet is unreliable, ignoring" << std::endl;
473 // m_activeobjects_received is false before the first
474 // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
475 m_activeobjects_received = true;
478 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
488 std::string datastring(pkt->getString(0), pkt->getSize());
489 std::istringstream is(datastring, std::ios_base::binary);
493 u16 id = readU16(is);
497 std::string message = deSerializeString(is);
499 // Pass on to the environment
500 m_env.processActiveObjectMessage(id, message);
502 } catch (SerializationError &e) {
503 errorstream << "Client::handleCommand_ActiveObjectMessages: "
504 << "caught SerializationError: " << e.what() << std::endl;
508 void Client::handleCommand_Movement(NetworkPacket* pkt)
510 LocalPlayer *player = m_env.getLocalPlayer();
511 assert(player != NULL);
513 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
515 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
516 >> lf >> lfs >> ls >> g;
518 player->movement_acceleration_default = mad * BS;
519 player->movement_acceleration_air = maa * BS;
520 player->movement_acceleration_fast = maf * BS;
521 player->movement_speed_walk = msw * BS;
522 player->movement_speed_crouch = mscr * BS;
523 player->movement_speed_fast = msf * BS;
524 player->movement_speed_climb = mscl * BS;
525 player->movement_speed_jump = msj * BS;
526 player->movement_liquid_fluidity = lf * BS;
527 player->movement_liquid_fluidity_smooth = lfs * BS;
528 player->movement_liquid_sink = ls * BS;
529 player->movement_gravity = g * BS;
532 void Client::handleCommand_Fov(NetworkPacket *pkt)
535 bool is_multiplier = false;
536 f32 transition_time = 0.0f;
538 *pkt >> fov >> is_multiplier;
540 // Wrap transition_time extraction within a
541 // try-catch to preserve backwards compat
543 *pkt >> transition_time;
544 } catch (PacketError &e) {};
546 LocalPlayer *player = m_env.getLocalPlayer();
548 player->setFov({ fov, is_multiplier, transition_time });
549 m_camera->notifyFovChange();
552 void Client::handleCommand_HP(NetworkPacket *pkt)
554 LocalPlayer *player = m_env.getLocalPlayer();
555 assert(player != NULL);
557 u16 oldhp = player->hp;
565 m_script->on_hp_modification(hp);
568 // Add to ClientEvent queue
569 ClientEvent *event = new ClientEvent();
570 event->type = CE_PLAYER_DAMAGE;
571 event->player_damage.amount = oldhp - hp;
572 m_client_event_queue.push(event);
576 void Client::handleCommand_Breath(NetworkPacket* pkt)
578 LocalPlayer *player = m_env.getLocalPlayer();
579 assert(player != NULL);
585 player->setBreath(breath);
588 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
590 LocalPlayer *player = m_env.getLocalPlayer();
591 assert(player != NULL);
596 *pkt >> pos >> pitch >> yaw;
598 player->setPosition(pos);
600 infostream << "Client got TOCLIENT_MOVE_PLAYER"
601 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
602 << " pitch=" << pitch
607 Add to ClientEvent queue.
608 This has to be sent to the main program because otherwise
609 it would just force the pitch and yaw values to whatever
610 the camera points to.
612 ClientEvent *event = new ClientEvent();
613 event->type = CE_PLAYER_FORCE_MOVE;
614 event->player_force_move.pitch = pitch;
615 event->player_force_move.yaw = yaw;
616 m_client_event_queue.push(event);
619 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
621 bool set_camera_point_target;
622 v3f camera_point_target;
624 *pkt >> set_camera_point_target;
625 *pkt >> camera_point_target;
627 ClientEvent *event = new ClientEvent();
628 event->type = CE_DEATHSCREEN;
629 event->deathscreen.set_camera_point_target = set_camera_point_target;
630 event->deathscreen.camera_point_target_x = camera_point_target.X;
631 event->deathscreen.camera_point_target_y = camera_point_target.Y;
632 event->deathscreen.camera_point_target_z = camera_point_target.Z;
633 m_client_event_queue.push(event);
636 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
642 infostream << "Client: Received media announcement: packet size: "
643 << pkt->getSize() << std::endl;
645 if (m_media_downloader == NULL ||
646 m_media_downloader->isStarted()) {
647 const char *problem = m_media_downloader ?
648 "we already saw another announcement" :
649 "all media has been received already";
650 errorstream << "Client: Received media announcement but "
652 << " files=" << num_files
653 << " size=" << pkt->getSize() << std::endl;
657 // Mesh update thread must be stopped while
658 // updating content definitions
659 sanity_check(!m_mesh_update_thread.isRunning());
661 for (u16 i = 0; i < num_files; i++) {
662 std::string name, sha1_base64;
664 *pkt >> name >> sha1_base64;
666 std::string sha1_raw = base64_decode(sha1_base64);
667 m_media_downloader->addFile(name, sha1_raw);
676 while(!sf.at_end()) {
677 std::string baseurl = trim(sf.next(","));
678 if (!baseurl.empty())
679 m_media_downloader->addRemoteServer(baseurl);
682 catch(SerializationError& e) {
683 // not supported by server or turned off
686 m_media_downloader->step(this);
689 void Client::handleCommand_Media(NetworkPacket* pkt)
693 u16 total number of file bunches
694 u16 index of this bunch
695 u32 number of files in this bunch
707 *pkt >> num_bunches >> bunch_i >> num_files;
709 infostream << "Client: Received files: bunch " << bunch_i << "/"
710 << num_bunches << " files=" << num_files
711 << " size=" << pkt->getSize() << std::endl;
716 if (!m_media_downloader || !m_media_downloader->isStarted()) {
717 const char *problem = m_media_downloader ?
718 "media has not been requested" :
719 "all media has been received already";
720 errorstream << "Client: Received media but "
722 << " bunch " << bunch_i << "/" << num_bunches
723 << " files=" << num_files
724 << " size=" << pkt->getSize() << std::endl;
728 // Mesh update thread must be stopped while
729 // updating content definitions
730 sanity_check(!m_mesh_update_thread.isRunning());
732 for (u32 i=0; i < num_files; i++) {
737 std::string data = pkt->readLongString();
739 m_media_downloader->conventionalTransferDone(
744 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
746 infostream << "Client: Received node definitions: packet size: "
747 << pkt->getSize() << std::endl;
749 // Mesh update thread must be stopped while
750 // updating content definitions
751 sanity_check(!m_mesh_update_thread.isRunning());
753 // Decompress node definitions
754 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
755 std::ostringstream tmp_os;
756 decompressZlib(tmp_is, tmp_os);
758 // Deserialize node definitions
759 std::istringstream tmp_is2(tmp_os.str());
760 m_nodedef->deSerialize(tmp_is2);
761 m_nodedef_received = true;
764 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
766 infostream << "Client: Received item definitions: packet size: "
767 << pkt->getSize() << std::endl;
769 // Mesh update thread must be stopped while
770 // updating content definitions
771 sanity_check(!m_mesh_update_thread.isRunning());
773 // Decompress item definitions
774 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
775 std::ostringstream tmp_os;
776 decompressZlib(tmp_is, tmp_os);
778 // Deserialize node definitions
779 std::istringstream tmp_is2(tmp_os.str());
780 m_itemdef->deSerialize(tmp_is2);
781 m_itemdef_received = true;
784 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
792 [11 + len] (f32 * 3) pos
793 [23 + len] u16 object_id
797 [34 + len] bool ephemeral
804 u8 type; // 0=local, 1=positional, 2=object
810 bool ephemeral = false;
812 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
818 } catch (PacketError &e) {};
824 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
826 case 1: // positional
827 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
831 ClientActiveObject *cao = m_env.getActiveObject(object_id);
833 pos = cao->getPosition();
834 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
841 if (client_id != -1) {
842 // for ephemeral sounds, server_id is not meaningful
844 m_sounds_server_to_client[server_id] = client_id;
845 m_sounds_client_to_server[client_id] = server_id;
848 m_sounds_to_objects[client_id] = object_id;
852 void Client::handleCommand_StopSound(NetworkPacket* pkt)
858 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
859 if (i != m_sounds_server_to_client.end()) {
860 int client_id = i->second;
861 m_sound->stopSound(client_id);
865 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
871 *pkt >> sound_id >> step >> gain;
873 std::unordered_map<s32, int>::const_iterator i =
874 m_sounds_server_to_client.find(sound_id);
876 if (i != m_sounds_server_to_client.end())
877 m_sound->fadeSound(i->second, step, gain);
880 void Client::handleCommand_Privileges(NetworkPacket* pkt)
882 m_privileges.clear();
883 infostream << "Client: Privileges updated: ";
886 *pkt >> num_privileges;
888 for (u16 i = 0; i < num_privileges; i++) {
893 m_privileges.insert(priv);
894 infostream << priv << " ";
896 infostream << std::endl;
899 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
901 LocalPlayer *player = m_env.getLocalPlayer();
902 assert(player != NULL);
904 // Store formspec in LocalPlayer
905 player->inventory_formspec = pkt->readLongString();
908 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
911 bool keep_inv = true;
912 *pkt >> name >> keep_inv;
914 infostream << "Client: Detached inventory update: \"" << name
915 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
917 const auto &inv_it = m_detached_inventories.find(name);
919 if (inv_it != m_detached_inventories.end()) {
920 delete inv_it->second;
921 m_detached_inventories.erase(inv_it);
925 Inventory *inv = nullptr;
926 if (inv_it == m_detached_inventories.end()) {
927 inv = new Inventory(m_itemdef);
928 m_detached_inventories[name] = inv;
930 inv = inv_it->second;
934 *pkt >> ignore; // this used to be the length of the following string, ignore it
936 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
937 std::istringstream is(contents, std::ios::binary);
938 inv->deSerialize(is);
941 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
943 std::string formspec = pkt->readLongString();
944 std::string formname;
948 ClientEvent *event = new ClientEvent();
949 event->type = CE_SHOW_FORMSPEC;
950 // pointer is required as event is a struct only!
951 // adding a std:string to a struct isn't possible
952 event->show_formspec.formspec = new std::string(formspec);
953 event->show_formspec.formname = new std::string(formname);
954 m_client_event_queue.push(event);
957 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
959 std::string datastring(pkt->getString(0), pkt->getSize());
960 std::istringstream is(datastring, std::ios_base::binary);
962 ParticleParameters p;
963 p.deSerialize(is, m_proto_ver);
965 ClientEvent *event = new ClientEvent();
966 event->type = CE_SPAWN_PARTICLE;
967 event->spawn_particle = new ParticleParameters(p);
969 m_client_event_queue.push(event);
972 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
974 std::string datastring(pkt->getString(0), pkt->getSize());
975 std::istringstream is(datastring, std::ios_base::binary);
977 ParticleSpawnerParameters p;
981 p.amount = readU16(is);
982 p.time = readF32(is);
983 p.minpos = readV3F32(is);
984 p.maxpos = readV3F32(is);
985 p.minvel = readV3F32(is);
986 p.maxvel = readV3F32(is);
987 p.minacc = readV3F32(is);
988 p.maxacc = readV3F32(is);
989 p.minexptime = readF32(is);
990 p.maxexptime = readF32(is);
991 p.minsize = readF32(is);
992 p.maxsize = readF32(is);
993 p.collisiondetection = readU8(is);
994 p.texture = deSerializeLongString(is);
996 server_id = readU32(is);
998 p.vertical = readU8(is);
999 p.collision_removal = readU8(is);
1001 attached_id = readU16(is);
1003 p.animation.deSerialize(is, m_proto_ver);
1004 p.glow = readU8(is);
1005 p.object_collision = readU8(is);
1007 // This is kinda awful
1009 u16 tmp_param0 = readU16(is);
1012 p.node.param0 = tmp_param0;
1013 p.node.param2 = readU8(is);
1014 p.node_tile = readU8(is);
1017 auto event = new ClientEvent();
1018 event->type = CE_ADD_PARTICLESPAWNER;
1019 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1020 event->add_particlespawner.attached_id = attached_id;
1021 event->add_particlespawner.id = server_id;
1023 m_client_event_queue.push(event);
1027 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1032 ClientEvent *event = new ClientEvent();
1033 event->type = CE_DELETE_PARTICLESPAWNER;
1034 event->delete_particlespawner.id = server_id;
1036 m_client_event_queue.push(event);
1039 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1041 std::string datastring(pkt->getString(0), pkt->getSize());
1042 std::istringstream is(datastring, std::ios_base::binary);
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.server_id = server_id;
1072 event->hudadd.type = type;
1073 event->hudadd.pos = new v2f(pos);
1074 event->hudadd.name = new std::string(name);
1075 event->hudadd.scale = new v2f(scale);
1076 event->hudadd.text = new std::string(text);
1077 event->hudadd.number = number;
1078 event->hudadd.item = item;
1079 event->hudadd.dir = dir;
1080 event->hudadd.align = new v2f(align);
1081 event->hudadd.offset = new v2f(offset);
1082 event->hudadd.world_pos = new v3f(world_pos);
1083 event->hudadd.size = new v2s32(size);
1084 event->hudadd.z_index = z_index;
1085 event->hudadd.text2 = new std::string(text2);
1086 m_client_event_queue.push(event);
1089 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1095 auto i = m_hud_server_to_client.find(server_id);
1096 if (i != m_hud_server_to_client.end()) {
1097 int client_id = i->second;
1098 m_hud_server_to_client.erase(i);
1100 ClientEvent *event = new ClientEvent();
1101 event->type = CE_HUDRM;
1102 event->hudrm.id = client_id;
1103 m_client_event_queue.push(event);
1107 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1117 *pkt >> server_id >> stat;
1119 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1120 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1122 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1124 else if (stat == HUD_STAT_WORLD_POS)
1126 else if (stat == HUD_STAT_SIZE )
1131 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1132 if (i != m_hud_server_to_client.end()) {
1133 ClientEvent *event = new ClientEvent();
1134 event->type = CE_HUDCHANGE;
1135 event->hudchange.id = i->second;
1136 event->hudchange.stat = (HudElementStat)stat;
1137 event->hudchange.v2fdata = new v2f(v2fdata);
1138 event->hudchange.v3fdata = new v3f(v3fdata);
1139 event->hudchange.sdata = new std::string(sdata);
1140 event->hudchange.data = intdata;
1141 event->hudchange.v2s32data = new v2s32(v2s32data);
1142 m_client_event_queue.push(event);
1146 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1150 *pkt >> flags >> mask;
1152 LocalPlayer *player = m_env.getLocalPlayer();
1153 assert(player != NULL);
1155 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1156 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1158 player->hud_flags &= ~mask;
1159 player->hud_flags |= flags;
1161 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1162 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1164 // Hide minimap if it has been disabled by the server
1165 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1166 // defers a minimap update, therefore only call it if really
1167 // needed, by checking that minimap was visible before
1168 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1170 // Switch to surface mode if radar disabled by server
1171 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1172 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1175 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1177 u16 param; std::string value;
1179 *pkt >> param >> value;
1181 LocalPlayer *player = m_env.getLocalPlayer();
1182 assert(player != NULL);
1184 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1185 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1186 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1187 player->hud_hotbar_itemcount = hotbar_itemcount;
1189 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1190 player->hotbar_image = value;
1192 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1193 player->hotbar_selected_image = value;
1197 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1199 if (m_proto_ver < 39) {
1200 // Handle Protocol 38 and below servers with old set_sky,
1201 // ensuring the classic look is kept.
1202 std::string datastring(pkt->getString(0), pkt->getSize());
1203 std::istringstream is(datastring, std::ios_base::binary);
1205 SkyboxParams skybox;
1206 skybox.bgcolor = video::SColor(readARGB8(is));
1207 skybox.type = std::string(deSerializeString(is));
1208 u16 count = readU16(is);
1210 for (size_t i = 0; i < count; i++)
1211 skybox.textures.emplace_back(deSerializeString(is));
1213 skybox.clouds = true;
1215 skybox.clouds = readU8(is);
1218 // Use default skybox settings:
1219 SkyboxDefaults sky_defaults;
1220 SunParams sun = sky_defaults.getSunDefaults();
1221 MoonParams moon = sky_defaults.getMoonDefaults();
1222 StarParams stars = sky_defaults.getStarDefaults();
1224 // Fix for "regular" skies, as color isn't kept:
1225 if (skybox.type == "regular") {
1226 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1227 skybox.fog_tint_type = "default";
1228 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1229 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1232 sun.visible = false;
1233 sun.sunrise_visible = false;
1234 moon.visible = false;
1235 stars.visible = false;
1238 // Skybox, sun, moon and stars ClientEvents:
1239 ClientEvent *sky_event = new ClientEvent();
1240 sky_event->type = CE_SET_SKY;
1241 sky_event->set_sky = new SkyboxParams(skybox);
1242 m_client_event_queue.push(sky_event);
1244 ClientEvent *sun_event = new ClientEvent();
1245 sun_event->type = CE_SET_SUN;
1246 sun_event->sun_params = new SunParams(sun);
1247 m_client_event_queue.push(sun_event);
1249 ClientEvent *moon_event = new ClientEvent();
1250 moon_event->type = CE_SET_MOON;
1251 moon_event->moon_params = new MoonParams(moon);
1252 m_client_event_queue.push(moon_event);
1254 ClientEvent *star_event = new ClientEvent();
1255 star_event->type = CE_SET_STARS;
1256 star_event->star_params = new StarParams(stars);
1257 m_client_event_queue.push(star_event);
1259 SkyboxParams skybox;
1261 std::string texture;
1263 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1264 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1266 if (skybox.type == "skybox") {
1267 *pkt >> texture_count;
1268 for (int i = 0; i < texture_count; i++) {
1270 skybox.textures.emplace_back(texture);
1273 else if (skybox.type == "regular") {
1274 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1275 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1276 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1277 >> skybox.sky_color.indoors;
1280 ClientEvent *event = new ClientEvent();
1281 event->type = CE_SET_SKY;
1282 event->set_sky = new SkyboxParams(skybox);
1283 m_client_event_queue.push(event);
1287 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1291 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1292 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1294 ClientEvent *event = new ClientEvent();
1295 event->type = CE_SET_SUN;
1296 event->sun_params = new SunParams(sun);
1297 m_client_event_queue.push(event);
1300 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1304 *pkt >> moon.visible >> moon.texture
1305 >> moon.tonemap >> moon.scale;
1307 ClientEvent *event = new ClientEvent();
1308 event->type = CE_SET_MOON;
1309 event->moon_params = new MoonParams(moon);
1310 m_client_event_queue.push(event);
1313 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1317 *pkt >> stars.visible >> stars.count
1318 >> stars.starcolor >> stars.scale;
1320 ClientEvent *event = new ClientEvent();
1321 event->type = CE_SET_STARS;
1322 event->star_params = new StarParams(stars);
1324 m_client_event_queue.push(event);
1327 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1330 video::SColor color_bright;
1331 video::SColor color_ambient;
1336 *pkt >> density >> color_bright >> color_ambient
1337 >> height >> thickness >> speed;
1339 ClientEvent *event = new ClientEvent();
1340 event->type = CE_CLOUD_PARAMS;
1341 event->cloud_params.density = density;
1342 // use the underlying u32 representation, because we can't
1343 // use struct members with constructors here, and this way
1344 // we avoid using new() and delete() for no good reason
1345 event->cloud_params.color_bright = color_bright.color;
1346 event->cloud_params.color_ambient = color_ambient.color;
1347 event->cloud_params.height = height;
1348 event->cloud_params.thickness = thickness;
1349 // same here: deconstruct to skip constructor
1350 event->cloud_params.speed_x = speed.X;
1351 event->cloud_params.speed_y = speed.Y;
1352 m_client_event_queue.push(event);
1355 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1358 u16 day_night_ratio_u;
1360 *pkt >> do_override >> day_night_ratio_u;
1362 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1364 ClientEvent *event = new ClientEvent();
1365 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1366 event->override_day_night_ratio.do_override = do_override;
1367 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1368 m_client_event_queue.push(event);
1371 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1373 LocalPlayer *player = m_env.getLocalPlayer();
1374 assert(player != NULL);
1376 *pkt >> player->local_animations[0];
1377 *pkt >> player->local_animations[1];
1378 *pkt >> player->local_animations[2];
1379 *pkt >> player->local_animations[3];
1380 *pkt >> player->local_animation_speed;
1383 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1385 LocalPlayer *player = m_env.getLocalPlayer();
1386 assert(player != NULL);
1388 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1391 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1395 *pkt >> type >> num_players;
1396 PlayerListModifer notice_type = (PlayerListModifer) type;
1398 for (u16 i = 0; i < num_players; i++) {
1401 switch (notice_type) {
1402 case PLAYER_LIST_INIT:
1403 case PLAYER_LIST_ADD:
1404 m_env.addPlayerName(name);
1406 case PLAYER_LIST_REMOVE:
1407 m_env.removePlayerName(name);
1413 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1415 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1416 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1417 errorstream << "Client: Received SRP S_B login message,"
1418 << " but wasn't supposed to (chosen_mech="
1419 << m_chosen_auth_mech << ")." << std::endl;
1425 SRPUser *usr = (SRPUser *) m_auth_data;
1430 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1432 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1433 (const unsigned char *) B.c_str(), B.size(),
1434 (unsigned char **) &bytes_M, &len_M);
1437 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1441 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1442 resp_pkt << std::string(bytes_M, len_M);
1446 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1448 LocalPlayer *player = m_env.getLocalPlayer();
1449 assert(player != NULL);
1451 // Store formspec in LocalPlayer
1452 *pkt >> player->formspec_prepend;
1455 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1457 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1459 // Restrictions were received -> load mods if it's enabled
1460 // Note: this should be moved after mods receptions from server instead
1464 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1470 LocalPlayer *player = m_env.getLocalPlayer();
1471 assert(player != NULL);
1472 player->addVelocity(added_vel);
1475 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1477 std::string raw_hash, filename, filedata;
1480 *pkt >> raw_hash >> filename >> cached;
1481 filedata = pkt->readLongString();
1483 if (raw_hash.size() != 20 || filedata.empty() || filename.empty() ||
1484 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1485 throw PacketError("Illegal filename, data or hash");
1488 verbosestream << "Server pushes media file \"" << filename << "\" with "
1489 << filedata.size() << " bytes of data (cached=" << cached
1490 << ")" << std::endl;
1492 if (m_media_pushed_files.count(filename) != 0) {
1493 // Silently ignore for synchronization purposes
1497 // Compute and check checksum of data
1498 std::string computed_hash;
1501 ctx.addBytes(filedata.c_str(), filedata.size());
1502 unsigned char *buf = ctx.getDigest();
1503 computed_hash.assign((char*) buf, 20);
1506 if (raw_hash != computed_hash) {
1507 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1511 // Actually load media
1512 loadMedia(filedata, filename, true);
1513 m_media_pushed_files.insert(filename);
1515 // Cache file for the next time when this client joins the same server
1517 clientMediaUpdateCache(raw_hash, filedata);
1524 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1526 std::string channel_name, sender, channel_msg;
1527 *pkt >> channel_name >> sender >> channel_msg;
1529 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1530 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1531 << channel_msg << std::endl;
1533 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1534 verbosestream << "Server sent us messages on unregistered channel "
1535 << channel_name << ", ignoring." << std::endl;
1539 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1542 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1545 ModChannelSignal signal;
1546 std::string channel;
1548 *pkt >> signal_tmp >> channel;
1550 signal = (ModChannelSignal)signal_tmp;
1552 bool valid_signal = true;
1553 // @TODO: send Signal to Lua API
1555 case MODCHANNEL_SIGNAL_JOIN_OK:
1556 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1557 infostream << "Server ack our mod channel join on channel `" << channel
1558 << "`, joining." << std::endl;
1560 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1561 // Unable to join, remove channel
1562 m_modchannel_mgr->leaveChannel(channel, 0);
1563 infostream << "Server refused our mod channel join on channel `" << channel
1564 << "`" << std::endl;
1566 case MODCHANNEL_SIGNAL_LEAVE_OK:
1568 infostream << "Server ack our mod channel leave on channel " << channel
1569 << "`, leaving." << std::endl;
1572 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1573 infostream << "Server refused our mod channel leave on channel `" << channel
1574 << "`" << std::endl;
1576 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1578 // Generally unused, but ensure we don't do an implementation error
1579 infostream << "Server tells us we sent a message on channel `" << channel
1580 << "` but we are not registered. Message was dropped." << std::endl;
1583 case MODCHANNEL_SIGNAL_SET_STATE: {
1587 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1588 infostream << "Received wrong channel state " << state
1589 << ", ignoring." << std::endl;
1593 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1594 infostream << "Server sets mod channel `" << channel
1595 << "` in read-only mode." << std::endl;
1600 warningstream << "Received unhandled mod channel signal ID "
1601 << signal << ", ignoring." << std::endl;
1603 valid_signal = false;
1607 // If signal is valid, forward it to client side mods
1609 m_script->on_modchannel_signal(channel, signal);