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 "chatmessage.h"
24 #include "client/clientmedia.h"
27 #include "mapsector.h"
28 #include "client/minimap.h"
29 #include "modchannels.h"
31 #include "serialization.h"
33 #include "util/strfnd.h"
34 #include "client/clientevent.h"
35 #include "client/sound.h"
36 #include "network/clientopcodes.h"
37 #include "network/connection.h"
38 #include "script/scripting_client.h"
39 #include "util/serialize.h"
41 #include "tileanimation.h"
43 #include "skyparams.h"
45 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
47 infostream << "Got deprecated command "
48 << toClientCommandTable[pkt->getCommand()].name << " from peer "
49 << pkt->getPeerId() << "!" << std::endl;
52 void Client::handleCommand_Hello(NetworkPacket* pkt)
54 if (pkt->getSize() < 1)
61 std::string username_legacy; // for case insensitivity
62 *pkt >> serialization_ver >> compression_mode >> proto_ver
63 >> auth_mechs >> username_legacy;
65 // Chose an auth method we support
66 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
68 infostream << "Client: TOCLIENT_HELLO received with "
69 << "serialization_ver=" << (u32)serialization_ver
70 << ", auth_mechs=" << auth_mechs
71 << ", proto_ver=" << proto_ver
72 << ", compression_mode=" << compression_mode
73 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
75 if (!ser_ver_supported(serialization_ver)) {
76 infostream << "Client: TOCLIENT_HELLO: Server sent "
77 << "unsupported ser_fmt_ver"<< std::endl;
81 m_server_ser_ver = serialization_ver;
82 m_proto_ver = proto_ver;
84 //TODO verify that username_legacy matches sent username, only
85 // differs in casing (make both uppercase and compare)
86 // This is only neccessary though when we actually want to add casing support
88 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
89 // we recieved a TOCLIENT_HELLO while auth was already going on
90 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
91 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
92 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
93 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
94 srp_user_delete((SRPUser *) m_auth_data);
99 // Authenticate using that method, or abort if there wasn't any method found
100 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
101 if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
102 !m_simple_singleplayer_mode &&
103 !getServerAddress().isLocalhost() &&
104 g_settings->getBool("enable_register_confirmation")) {
105 promptConfirmRegistration(chosen_auth_mechanism);
107 startAuth(chosen_auth_mechanism);
110 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
111 m_access_denied = true;
112 m_access_denied_reason = "Unknown";
118 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
123 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
124 >> m_sudo_auth_methods;
126 playerpos -= v3f(0, BS / 2, 0);
128 // Set player position
129 LocalPlayer *player = m_env.getLocalPlayer();
130 assert(player != NULL);
131 player->setPosition(playerpos);
133 infostream << "Client: received map seed: " << m_map_seed << std::endl;
134 infostream << "Client: received recommended send interval "
135 << m_recommended_send_interval<<std::endl;
138 /*~ DO NOT TRANSLATE THIS LITERALLY!
139 This is a special string which needs to contain the translation's
140 language code (e.g. "de" for German). */
141 std::string lang = gettext("LANG_CODE");
142 if (lang == "LANG_CODE")
145 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
151 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
155 m_password = m_new_password;
157 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
159 // send packet to actually set the password
160 startAuth(AUTH_MECHANISM_FIRST_SRP);
163 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
165 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
167 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
168 L"Password change denied. Password NOT changed.");
169 pushToChatQueue(chatMessage);
170 // reset everything and be sad
174 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
176 // The server didn't like our password. Note, this needs
177 // to be processed even if the serialisation format has
178 // not been agreed yet, the same as TOCLIENT_INIT.
179 m_access_denied = true;
180 m_access_denied_reason = "Unknown";
182 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
183 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
184 // in some places of the server code
185 if (pkt->getSize() >= 2) {
186 std::wstring wide_reason;
188 m_access_denied_reason = wide_to_utf8(wide_reason);
193 if (pkt->getSize() < 1)
196 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
198 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
199 denyCode == SERVER_ACCESSDENIED_CRASH) {
200 *pkt >> m_access_denied_reason;
201 if (m_access_denied_reason.empty()) {
202 m_access_denied_reason = accessDeniedStrings[denyCode];
206 m_access_denied_reconnect = reconnect & 1;
207 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
208 *pkt >> m_access_denied_reason;
209 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
210 m_access_denied_reason = accessDeniedStrings[denyCode];
212 // Allow us to add new error messages to the
213 // protocol without raising the protocol version, if we want to.
214 // Until then (which may be never), this is outside
215 // of the defined protocol.
216 *pkt >> m_access_denied_reason;
217 if (m_access_denied_reason.empty()) {
218 m_access_denied_reason = "Unknown";
223 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
225 if (pkt->getSize() < 6)
233 void Client::handleCommand_AddNode(NetworkPacket* pkt)
235 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
242 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
244 bool remove_metadata = true;
245 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
246 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
247 remove_metadata = false;
250 addNode(p, n, remove_metadata);
253 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
255 if (pkt->getSize() < 1)
258 std::istringstream is(pkt->readLongString(), std::ios::binary);
259 std::stringstream sstr;
260 decompressZlib(is, sstr);
262 NodeMetadataList meta_updates_list(false);
263 meta_updates_list.deSerialize(sstr, m_itemdef, true);
265 Map &map = m_env.getMap();
266 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
267 i != meta_updates_list.end(); ++i) {
268 v3s16 pos = i->first;
270 if (map.isValidPosition(pos) &&
271 map.setNodeMetadata(pos, i->second))
272 continue; // Prevent from deleting metadata
274 // Meta couldn't be set, unused metadata
279 void Client::handleCommand_BlockData(NetworkPacket* pkt)
281 // Ignore too small packet
282 if (pkt->getSize() < 6)
288 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
289 std::istringstream istr(datastring, std::ios_base::binary);
295 sector = m_env.getMap().emergeSector(p2d);
297 assert(sector->getPos() == p2d);
299 block = sector->getBlockNoCreateNoEx(p.Y);
302 Update an existing block
304 block->deSerialize(istr, m_server_ser_ver, false);
305 block->deSerializeNetworkSpecific(istr);
311 block = new MapBlock(&m_env.getMap(), p, this);
312 block->deSerialize(istr, m_server_ser_ver, false);
313 block->deSerializeNetworkSpecific(istr);
314 sector->insertBlock(block);
318 ServerMap::saveBlock(block, m_localdb);
322 Add it to mesh update queue and set it to be acknowledged after update.
324 addUpdateMeshTaskWithEdge(p, true);
327 void Client::handleCommand_Inventory(NetworkPacket* pkt)
329 if (pkt->getSize() < 1)
332 std::string datastring(pkt->getString(0), pkt->getSize());
333 std::istringstream is(datastring, std::ios_base::binary);
335 LocalPlayer *player = m_env.getLocalPlayer();
336 assert(player != NULL);
338 player->inventory.deSerialize(is);
340 m_update_wielded_item = true;
342 delete m_inventory_from_server;
343 m_inventory_from_server = new Inventory(player->inventory);
344 m_inventory_from_server_age = 0.0;
347 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
349 if (pkt->getSize() < 2)
356 time_of_day = time_of_day % 24000;
357 float time_speed = 0;
359 if (pkt->getSize() >= 2 + 4) {
363 // Old message; try to approximate speed of time by ourselves
364 float time_of_day_f = (float)time_of_day / 24000.0f;
365 float tod_diff_f = 0;
367 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
368 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
370 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
372 m_last_time_of_day_f = time_of_day_f;
373 float time_diff = m_time_of_day_update_timer;
374 m_time_of_day_update_timer = 0;
376 if (m_time_of_day_set) {
377 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
378 infostream << "Client: Measured time_of_day speed (old format): "
379 << time_speed << " tod_diff_f=" << tod_diff_f
380 << " time_diff=" << time_diff << std::endl;
384 // Update environment
385 m_env.setTimeOfDay(time_of_day);
386 m_env.setTimeOfDaySpeed(time_speed);
387 m_time_of_day_set = true;
389 u32 dr = m_env.getDayNightRatio();
390 infostream << "Client: time_of_day=" << time_of_day
391 << " time_speed=" << time_speed
392 << " dr=" << dr << std::endl;
395 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
400 u16 sendername length
406 ChatMessage *chatMessage = new ChatMessage();
407 u8 version, message_type;
408 *pkt >> version >> message_type;
410 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
416 *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
417 chatMessage->timestamp = static_cast<std::time_t>(timestamp);
419 chatMessage->type = (ChatMessageType) message_type;
421 // @TODO send this to CSM using ChatMessage object
422 if (modsLoaded() && m_script->on_receiving_message(
423 wide_to_utf8(chatMessage->message))) {
424 // Message was consumed by CSM and should not be handled by client
427 pushToChatQueue(chatMessage);
431 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
434 u16 count of removed objects
435 for all removed objects {
438 u16 count of added objects
439 for all added objects {
442 u32 initialization data length
443 string initialization data
449 u16 removed_count, added_count, id;
451 // Read removed objects
452 *pkt >> removed_count;
454 for (u16 i = 0; i < removed_count; i++) {
456 m_env.removeActiveObject(id);
459 // Read added objects
462 for (u16 i = 0; i < added_count; i++) {
464 m_env.addActiveObject(id, type, pkt->readLongString());
466 } catch (PacketError &e) {
467 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
468 << ". The packet is unreliable, ignoring" << std::endl;
471 // m_activeobjects_received is false before the first
472 // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
473 m_activeobjects_received = true;
476 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
486 std::string datastring(pkt->getString(0), pkt->getSize());
487 std::istringstream is(datastring, std::ios_base::binary);
491 u16 id = readU16(is);
495 std::string message = deSerializeString(is);
497 // Pass on to the environment
498 m_env.processActiveObjectMessage(id, message);
500 } catch (SerializationError &e) {
501 errorstream << "Client::handleCommand_ActiveObjectMessages: "
502 << "caught SerializationError: " << e.what() << std::endl;
506 void Client::handleCommand_Movement(NetworkPacket* pkt)
508 LocalPlayer *player = m_env.getLocalPlayer();
509 assert(player != NULL);
511 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
513 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
514 >> lf >> lfs >> ls >> g;
516 player->movement_acceleration_default = mad * BS;
517 player->movement_acceleration_air = maa * BS;
518 player->movement_acceleration_fast = maf * BS;
519 player->movement_speed_walk = msw * BS;
520 player->movement_speed_crouch = mscr * BS;
521 player->movement_speed_fast = msf * BS;
522 player->movement_speed_climb = mscl * BS;
523 player->movement_speed_jump = msj * BS;
524 player->movement_liquid_fluidity = lf * BS;
525 player->movement_liquid_fluidity_smooth = lfs * BS;
526 player->movement_liquid_sink = ls * BS;
527 player->movement_gravity = g * BS;
530 void Client::handleCommand_Fov(NetworkPacket *pkt)
534 *pkt >> fov >> is_multiplier;
536 LocalPlayer *player = m_env.getLocalPlayer();
537 player->setFov({ fov, is_multiplier });
540 void Client::handleCommand_HP(NetworkPacket *pkt)
542 LocalPlayer *player = m_env.getLocalPlayer();
543 assert(player != NULL);
545 u16 oldhp = player->hp;
553 m_script->on_hp_modification(hp);
556 // Add to ClientEvent queue
557 ClientEvent *event = new ClientEvent();
558 event->type = CE_PLAYER_DAMAGE;
559 event->player_damage.amount = oldhp - hp;
560 m_client_event_queue.push(event);
564 void Client::handleCommand_Breath(NetworkPacket* pkt)
566 LocalPlayer *player = m_env.getLocalPlayer();
567 assert(player != NULL);
573 player->setBreath(breath);
576 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
578 LocalPlayer *player = m_env.getLocalPlayer();
579 assert(player != NULL);
584 *pkt >> pos >> pitch >> yaw;
586 player->setPosition(pos);
588 infostream << "Client got TOCLIENT_MOVE_PLAYER"
589 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
590 << " pitch=" << pitch
595 Add to ClientEvent queue.
596 This has to be sent to the main program because otherwise
597 it would just force the pitch and yaw values to whatever
598 the camera points to.
600 ClientEvent *event = new ClientEvent();
601 event->type = CE_PLAYER_FORCE_MOVE;
602 event->player_force_move.pitch = pitch;
603 event->player_force_move.yaw = yaw;
604 m_client_event_queue.push(event);
607 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
609 bool set_camera_point_target;
610 v3f camera_point_target;
612 *pkt >> set_camera_point_target;
613 *pkt >> camera_point_target;
615 ClientEvent *event = new ClientEvent();
616 event->type = CE_DEATHSCREEN;
617 event->deathscreen.set_camera_point_target = set_camera_point_target;
618 event->deathscreen.camera_point_target_x = camera_point_target.X;
619 event->deathscreen.camera_point_target_y = camera_point_target.Y;
620 event->deathscreen.camera_point_target_z = camera_point_target.Z;
621 m_client_event_queue.push(event);
624 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
630 infostream << "Client: Received media announcement: packet size: "
631 << pkt->getSize() << std::endl;
633 if (m_media_downloader == NULL ||
634 m_media_downloader->isStarted()) {
635 const char *problem = m_media_downloader ?
636 "we already saw another announcement" :
637 "all media has been received already";
638 errorstream << "Client: Received media announcement but "
640 << " files=" << num_files
641 << " size=" << pkt->getSize() << std::endl;
645 // Mesh update thread must be stopped while
646 // updating content definitions
647 sanity_check(!m_mesh_update_thread.isRunning());
649 for (u16 i = 0; i < num_files; i++) {
650 std::string name, sha1_base64;
652 *pkt >> name >> sha1_base64;
654 std::string sha1_raw = base64_decode(sha1_base64);
655 m_media_downloader->addFile(name, sha1_raw);
664 while(!sf.at_end()) {
665 std::string baseurl = trim(sf.next(","));
666 if (!baseurl.empty())
667 m_media_downloader->addRemoteServer(baseurl);
670 catch(SerializationError& e) {
671 // not supported by server or turned off
674 m_media_downloader->step(this);
677 void Client::handleCommand_Media(NetworkPacket* pkt)
681 u16 total number of file bunches
682 u16 index of this bunch
683 u32 number of files in this bunch
695 *pkt >> num_bunches >> bunch_i >> num_files;
697 infostream << "Client: Received files: bunch " << bunch_i << "/"
698 << num_bunches << " files=" << num_files
699 << " size=" << pkt->getSize() << std::endl;
704 if (!m_media_downloader || !m_media_downloader->isStarted()) {
705 const char *problem = m_media_downloader ?
706 "media has not been requested" :
707 "all media has been received already";
708 errorstream << "Client: Received media but "
710 << " bunch " << bunch_i << "/" << num_bunches
711 << " files=" << num_files
712 << " size=" << pkt->getSize() << std::endl;
716 // Mesh update thread must be stopped while
717 // updating content definitions
718 sanity_check(!m_mesh_update_thread.isRunning());
720 for (u32 i=0; i < num_files; i++) {
725 std::string data = pkt->readLongString();
727 m_media_downloader->conventionalTransferDone(
732 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
734 infostream << "Client: Received node definitions: packet size: "
735 << pkt->getSize() << std::endl;
737 // Mesh update thread must be stopped while
738 // updating content definitions
739 sanity_check(!m_mesh_update_thread.isRunning());
741 // Decompress node definitions
742 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
743 std::ostringstream tmp_os;
744 decompressZlib(tmp_is, tmp_os);
746 // Deserialize node definitions
747 std::istringstream tmp_is2(tmp_os.str());
748 m_nodedef->deSerialize(tmp_is2);
749 m_nodedef_received = true;
752 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
754 infostream << "Client: Received item 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 item definitions
762 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
763 std::ostringstream tmp_os;
764 decompressZlib(tmp_is, tmp_os);
766 // Deserialize node definitions
767 std::istringstream tmp_is2(tmp_os.str());
768 m_itemdef->deSerialize(tmp_is2);
769 m_itemdef_received = true;
772 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
780 [11 + len] (f32 * 3) pos
781 [23 + len] u16 object_id
785 [34 + len] bool ephemeral
792 u8 type; // 0=local, 1=positional, 2=object
798 bool ephemeral = false;
800 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
806 } catch (PacketError &e) {};
812 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
814 case 1: // positional
815 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
819 ClientActiveObject *cao = m_env.getActiveObject(object_id);
821 pos = cao->getPosition();
822 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
829 if (client_id != -1) {
830 // for ephemeral sounds, server_id is not meaningful
832 m_sounds_server_to_client[server_id] = client_id;
833 m_sounds_client_to_server[client_id] = server_id;
836 m_sounds_to_objects[client_id] = object_id;
840 void Client::handleCommand_StopSound(NetworkPacket* pkt)
846 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
847 if (i != m_sounds_server_to_client.end()) {
848 int client_id = i->second;
849 m_sound->stopSound(client_id);
853 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
859 *pkt >> sound_id >> step >> gain;
861 std::unordered_map<s32, int>::const_iterator i =
862 m_sounds_server_to_client.find(sound_id);
864 if (i != m_sounds_server_to_client.end())
865 m_sound->fadeSound(i->second, step, gain);
868 void Client::handleCommand_Privileges(NetworkPacket* pkt)
870 m_privileges.clear();
871 infostream << "Client: Privileges updated: ";
874 *pkt >> num_privileges;
876 for (u16 i = 0; i < num_privileges; i++) {
881 m_privileges.insert(priv);
882 infostream << priv << " ";
884 infostream << std::endl;
887 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
889 LocalPlayer *player = m_env.getLocalPlayer();
890 assert(player != NULL);
892 // Store formspec in LocalPlayer
893 player->inventory_formspec = pkt->readLongString();
896 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
899 bool keep_inv = true;
900 *pkt >> name >> keep_inv;
902 infostream << "Client: Detached inventory update: \"" << name
903 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
905 const auto &inv_it = m_detached_inventories.find(name);
907 if (inv_it != m_detached_inventories.end()) {
908 delete inv_it->second;
909 m_detached_inventories.erase(inv_it);
913 Inventory *inv = nullptr;
914 if (inv_it == m_detached_inventories.end()) {
915 inv = new Inventory(m_itemdef);
916 m_detached_inventories[name] = inv;
918 inv = inv_it->second;
922 *pkt >> ignore; // this used to be the length of the following string, ignore it
924 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
925 std::istringstream is(contents, std::ios::binary);
926 inv->deSerialize(is);
929 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
931 std::string formspec = pkt->readLongString();
932 std::string formname;
936 ClientEvent *event = new ClientEvent();
937 event->type = CE_SHOW_FORMSPEC;
938 // pointer is required as event is a struct only!
939 // adding a std:string to a struct isn't possible
940 event->show_formspec.formspec = new std::string(formspec);
941 event->show_formspec.formname = new std::string(formname);
942 m_client_event_queue.push(event);
945 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
947 std::string datastring(pkt->getString(0), pkt->getSize());
948 std::istringstream is(datastring, std::ios_base::binary);
950 v3f pos = readV3F32(is);
951 v3f vel = readV3F32(is);
952 v3f acc = readV3F32(is);
953 float expirationtime = readF32(is);
954 float size = readF32(is);
955 bool collisiondetection = readU8(is);
956 std::string texture = deSerializeLongString(is);
958 bool vertical = false;
959 bool collision_removal = false;
960 TileAnimationParams animation;
961 animation.type = TAT_NONE;
963 bool object_collision = false;
965 vertical = readU8(is);
966 collision_removal = readU8(is);
967 animation.deSerialize(is, m_proto_ver);
969 object_collision = readU8(is);
972 ClientEvent *event = new ClientEvent();
973 event->type = CE_SPAWN_PARTICLE;
974 event->spawn_particle.pos = new v3f (pos);
975 event->spawn_particle.vel = new v3f (vel);
976 event->spawn_particle.acc = new v3f (acc);
977 event->spawn_particle.expirationtime = expirationtime;
978 event->spawn_particle.size = size;
979 event->spawn_particle.collisiondetection = collisiondetection;
980 event->spawn_particle.collision_removal = collision_removal;
981 event->spawn_particle.object_collision = object_collision;
982 event->spawn_particle.vertical = vertical;
983 event->spawn_particle.texture = new std::string(texture);
984 event->spawn_particle.animation = animation;
985 event->spawn_particle.glow = glow;
987 m_client_event_queue.push(event);
990 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
1004 bool collisiondetection;
1007 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
1008 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
1009 >> maxsize >> collisiondetection;
1011 std::string texture = pkt->readLongString();
1015 bool vertical = false;
1016 bool collision_removal = false;
1017 u16 attached_id = 0;
1018 TileAnimationParams animation;
1019 animation.type = TAT_NONE;
1021 bool object_collision = false;
1024 *pkt >> collision_removal;
1025 *pkt >> attached_id;
1027 // This is horrible but required (why are there two ways to deserialize pkts?)
1028 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1029 std::istringstream is(datastring, std::ios_base::binary);
1030 animation.deSerialize(is, m_proto_ver);
1032 object_collision = readU8(is);
1035 auto event = new ClientEvent();
1036 event->type = CE_ADD_PARTICLESPAWNER;
1037 event->add_particlespawner.amount = amount;
1038 event->add_particlespawner.spawntime = spawntime;
1039 event->add_particlespawner.minpos = new v3f (minpos);
1040 event->add_particlespawner.maxpos = new v3f (maxpos);
1041 event->add_particlespawner.minvel = new v3f (minvel);
1042 event->add_particlespawner.maxvel = new v3f (maxvel);
1043 event->add_particlespawner.minacc = new v3f (minacc);
1044 event->add_particlespawner.maxacc = new v3f (maxacc);
1045 event->add_particlespawner.minexptime = minexptime;
1046 event->add_particlespawner.maxexptime = maxexptime;
1047 event->add_particlespawner.minsize = minsize;
1048 event->add_particlespawner.maxsize = maxsize;
1049 event->add_particlespawner.collisiondetection = collisiondetection;
1050 event->add_particlespawner.collision_removal = collision_removal;
1051 event->add_particlespawner.object_collision = object_collision;
1052 event->add_particlespawner.attached_id = attached_id;
1053 event->add_particlespawner.vertical = vertical;
1054 event->add_particlespawner.texture = new std::string(texture);
1055 event->add_particlespawner.id = server_id;
1056 event->add_particlespawner.animation = animation;
1057 event->add_particlespawner.glow = glow;
1059 m_client_event_queue.push(event);
1063 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1068 ClientEvent *event = new ClientEvent();
1069 event->type = CE_DELETE_PARTICLESPAWNER;
1070 event->delete_particlespawner.id = server_id;
1072 m_client_event_queue.push(event);
1075 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1077 std::string datastring(pkt->getString(0), pkt->getSize());
1078 std::istringstream is(datastring, std::ios_base::binary);
1095 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1096 >> dir >> align >> offset;
1100 catch(SerializationError &e) {};
1104 } catch(SerializationError &e) {};
1109 catch(PacketError &e) {}
1111 ClientEvent *event = new ClientEvent();
1112 event->type = CE_HUDADD;
1113 event->hudadd.server_id = server_id;
1114 event->hudadd.type = type;
1115 event->hudadd.pos = new v2f(pos);
1116 event->hudadd.name = new std::string(name);
1117 event->hudadd.scale = new v2f(scale);
1118 event->hudadd.text = new std::string(text);
1119 event->hudadd.number = number;
1120 event->hudadd.item = item;
1121 event->hudadd.dir = dir;
1122 event->hudadd.align = new v2f(align);
1123 event->hudadd.offset = new v2f(offset);
1124 event->hudadd.world_pos = new v3f(world_pos);
1125 event->hudadd.size = new v2s32(size);
1126 event->hudadd.z_index = z_index;
1127 m_client_event_queue.push(event);
1130 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1136 auto i = m_hud_server_to_client.find(server_id);
1137 if (i != m_hud_server_to_client.end()) {
1138 int client_id = i->second;
1139 m_hud_server_to_client.erase(i);
1141 ClientEvent *event = new ClientEvent();
1142 event->type = CE_HUDRM;
1143 event->hudrm.id = client_id;
1144 m_client_event_queue.push(event);
1148 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1158 *pkt >> server_id >> stat;
1160 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1161 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1163 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1165 else if (stat == HUD_STAT_WORLD_POS)
1167 else if (stat == HUD_STAT_SIZE )
1172 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1173 if (i != m_hud_server_to_client.end()) {
1174 ClientEvent *event = new ClientEvent();
1175 event->type = CE_HUDCHANGE;
1176 event->hudchange.id = i->second;
1177 event->hudchange.stat = (HudElementStat)stat;
1178 event->hudchange.v2fdata = new v2f(v2fdata);
1179 event->hudchange.v3fdata = new v3f(v3fdata);
1180 event->hudchange.sdata = new std::string(sdata);
1181 event->hudchange.data = intdata;
1182 event->hudchange.v2s32data = new v2s32(v2s32data);
1183 m_client_event_queue.push(event);
1187 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1191 *pkt >> flags >> mask;
1193 LocalPlayer *player = m_env.getLocalPlayer();
1194 assert(player != NULL);
1196 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1197 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1199 player->hud_flags &= ~mask;
1200 player->hud_flags |= flags;
1202 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1203 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1205 // Hide minimap if it has been disabled by the server
1206 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1207 // defers a minimap update, therefore only call it if really
1208 // needed, by checking that minimap was visible before
1209 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1211 // Switch to surface mode if radar disabled by server
1212 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1213 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1216 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1218 u16 param; std::string value;
1220 *pkt >> param >> value;
1222 LocalPlayer *player = m_env.getLocalPlayer();
1223 assert(player != NULL);
1225 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1226 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1227 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1228 player->hud_hotbar_itemcount = hotbar_itemcount;
1230 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1231 player->hotbar_image = value;
1233 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1234 player->hotbar_selected_image = value;
1238 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1240 if (m_proto_ver < 39) {
1241 // Handle Protocol 38 and below servers with old set_sky,
1242 // ensuring the classic look is kept.
1243 std::string datastring(pkt->getString(0), pkt->getSize());
1244 std::istringstream is(datastring, std::ios_base::binary);
1246 SkyboxParams skybox;
1247 skybox.bgcolor = video::SColor(readARGB8(is));
1248 skybox.type = std::string(deSerializeString(is));
1249 u16 count = readU16(is);
1251 for (size_t i = 0; i < count; i++)
1252 skybox.textures.emplace_back(deSerializeString(is));
1254 skybox.clouds = true;
1256 skybox.clouds = readU8(is);
1259 // Use default skybox settings:
1260 SkyboxDefaults sky_defaults;
1261 SunParams sun = sky_defaults.getSunDefaults();
1262 MoonParams moon = sky_defaults.getMoonDefaults();
1263 StarParams stars = sky_defaults.getStarDefaults();
1265 // Fix for "regular" skies, as color isn't kept:
1266 if (skybox.type == "regular") {
1267 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1268 skybox.tint_type = "default";
1269 skybox.moon_tint = video::SColor(255, 255, 255, 255);
1270 skybox.sun_tint = video::SColor(255, 255, 255, 255);
1273 sun.visible = false;
1274 sun.sunrise_visible = false;
1275 moon.visible = false;
1276 stars.visible = false;
1279 // Skybox, sun, moon and stars ClientEvents:
1280 ClientEvent *sky_event = new ClientEvent();
1281 sky_event->type = CE_SET_SKY;
1282 sky_event->set_sky = new SkyboxParams(skybox);
1283 m_client_event_queue.push(sky_event);
1285 ClientEvent *sun_event = new ClientEvent();
1286 sun_event->type = CE_SET_SUN;
1287 sun_event->sun_params = new SunParams(sun);
1288 m_client_event_queue.push(sun_event);
1290 ClientEvent *moon_event = new ClientEvent();
1291 moon_event->type = CE_SET_MOON;
1292 moon_event->moon_params = new MoonParams(moon);
1293 m_client_event_queue.push(moon_event);
1295 ClientEvent *star_event = new ClientEvent();
1296 star_event->type = CE_SET_STARS;
1297 star_event->star_params = new StarParams(stars);
1298 m_client_event_queue.push(star_event);
1300 SkyboxParams skybox;
1302 std::string texture;
1304 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1305 skybox.sun_tint >> skybox.moon_tint >> skybox.tint_type;
1307 if (skybox.type == "skybox") {
1308 *pkt >> texture_count;
1309 for (int i = 0; i < texture_count; i++) {
1311 skybox.textures.emplace_back(texture);
1314 else if (skybox.type == "regular") {
1315 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1316 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1317 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1318 >> skybox.sky_color.indoors;
1321 ClientEvent *event = new ClientEvent();
1322 event->type = CE_SET_SKY;
1323 event->set_sky = new SkyboxParams(skybox);
1324 m_client_event_queue.push(event);
1328 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1332 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1333 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1335 ClientEvent *event = new ClientEvent();
1336 event->type = CE_SET_SUN;
1337 event->sun_params = new SunParams(sun);
1338 m_client_event_queue.push(event);
1341 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1345 *pkt >> moon.visible >> moon.texture
1346 >> moon.tonemap >> moon.scale;
1348 ClientEvent *event = new ClientEvent();
1349 event->type = CE_SET_MOON;
1350 event->moon_params = new MoonParams(moon);
1351 m_client_event_queue.push(event);
1354 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1358 *pkt >> stars.visible >> stars.count
1359 >> stars.starcolor >> stars.scale;
1361 ClientEvent *event = new ClientEvent();
1362 event->type = CE_SET_STARS;
1363 event->star_params = new StarParams(stars);
1365 m_client_event_queue.push(event);
1368 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1371 video::SColor color_bright;
1372 video::SColor color_ambient;
1377 *pkt >> density >> color_bright >> color_ambient
1378 >> height >> thickness >> speed;
1380 ClientEvent *event = new ClientEvent();
1381 event->type = CE_CLOUD_PARAMS;
1382 event->cloud_params.density = density;
1383 // use the underlying u32 representation, because we can't
1384 // use struct members with constructors here, and this way
1385 // we avoid using new() and delete() for no good reason
1386 event->cloud_params.color_bright = color_bright.color;
1387 event->cloud_params.color_ambient = color_ambient.color;
1388 event->cloud_params.height = height;
1389 event->cloud_params.thickness = thickness;
1390 // same here: deconstruct to skip constructor
1391 event->cloud_params.speed_x = speed.X;
1392 event->cloud_params.speed_y = speed.Y;
1393 m_client_event_queue.push(event);
1396 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1399 u16 day_night_ratio_u;
1401 *pkt >> do_override >> day_night_ratio_u;
1403 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1405 ClientEvent *event = new ClientEvent();
1406 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1407 event->override_day_night_ratio.do_override = do_override;
1408 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1409 m_client_event_queue.push(event);
1412 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1414 LocalPlayer *player = m_env.getLocalPlayer();
1415 assert(player != NULL);
1417 *pkt >> player->local_animations[0];
1418 *pkt >> player->local_animations[1];
1419 *pkt >> player->local_animations[2];
1420 *pkt >> player->local_animations[3];
1421 *pkt >> player->local_animation_speed;
1424 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1426 LocalPlayer *player = m_env.getLocalPlayer();
1427 assert(player != NULL);
1429 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1432 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1436 *pkt >> type >> num_players;
1437 PlayerListModifer notice_type = (PlayerListModifer) type;
1439 for (u16 i = 0; i < num_players; i++) {
1442 switch (notice_type) {
1443 case PLAYER_LIST_INIT:
1444 case PLAYER_LIST_ADD:
1445 m_env.addPlayerName(name);
1447 case PLAYER_LIST_REMOVE:
1448 m_env.removePlayerName(name);
1454 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1456 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1457 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1458 errorstream << "Client: Received SRP S_B login message,"
1459 << " but wasn't supposed to (chosen_mech="
1460 << m_chosen_auth_mech << ")." << std::endl;
1466 SRPUser *usr = (SRPUser *) m_auth_data;
1471 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1473 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1474 (const unsigned char *) B.c_str(), B.size(),
1475 (unsigned char **) &bytes_M, &len_M);
1478 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1482 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1483 resp_pkt << std::string(bytes_M, len_M);
1487 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1489 LocalPlayer *player = m_env.getLocalPlayer();
1490 assert(player != NULL);
1492 // Store formspec in LocalPlayer
1493 *pkt >> player->formspec_prepend;
1496 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1498 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1500 // Restrictions were received -> load mods if it's enabled
1501 // Note: this should be moved after mods receptions from server instead
1505 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1511 LocalPlayer *player = m_env.getLocalPlayer();
1512 assert(player != NULL);
1513 player->addVelocity(added_vel);
1520 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1522 std::string channel_name, sender, channel_msg;
1523 *pkt >> channel_name >> sender >> channel_msg;
1525 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1526 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1527 << channel_msg << std::endl;
1529 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1530 verbosestream << "Server sent us messages on unregistered channel "
1531 << channel_name << ", ignoring." << std::endl;
1535 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1538 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1541 ModChannelSignal signal;
1542 std::string channel;
1544 *pkt >> signal_tmp >> channel;
1546 signal = (ModChannelSignal)signal_tmp;
1548 bool valid_signal = true;
1549 // @TODO: send Signal to Lua API
1551 case MODCHANNEL_SIGNAL_JOIN_OK:
1552 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1553 infostream << "Server ack our mod channel join on channel `" << channel
1554 << "`, joining." << std::endl;
1556 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1557 // Unable to join, remove channel
1558 m_modchannel_mgr->leaveChannel(channel, 0);
1559 infostream << "Server refused our mod channel join on channel `" << channel
1560 << "`" << std::endl;
1562 case MODCHANNEL_SIGNAL_LEAVE_OK:
1564 infostream << "Server ack our mod channel leave on channel " << channel
1565 << "`, leaving." << std::endl;
1568 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1569 infostream << "Server refused our mod channel leave on channel `" << channel
1570 << "`" << std::endl;
1572 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1574 // Generally unused, but ensure we don't do an implementation error
1575 infostream << "Server tells us we sent a message on channel `" << channel
1576 << "` but we are not registered. Message was dropped." << std::endl;
1579 case MODCHANNEL_SIGNAL_SET_STATE: {
1583 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1584 infostream << "Received wrong channel state " << state
1585 << ", ignoring." << std::endl;
1589 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1590 infostream << "Server sets mod channel `" << channel
1591 << "` in read-only mode." << std::endl;
1596 warningstream << "Received unhandled mod channel signal ID "
1597 << signal << ", ignoring." << std::endl;
1599 valid_signal = false;
1603 // If signal is valid, forward it to client side mods
1605 m_script->on_modchannel_signal(channel, signal);