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.
22 #include "util/base64.h"
23 #include "chatmessage.h"
24 #include "clientmedia.h"
27 #include "mapsector.h"
30 #include "serialization.h"
32 #include "util/strfnd.h"
33 #include "network/clientopcodes.h"
34 #include "network/connection.h"
35 #include "script/scripting_client.h"
36 #include "util/serialize.h"
38 #include "tileanimation.h"
41 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
43 infostream << "Got deprecated command "
44 << toClientCommandTable[pkt->getCommand()].name << " from peer "
45 << pkt->getPeerId() << "!" << std::endl;
48 void Client::handleCommand_Hello(NetworkPacket* pkt)
50 if (pkt->getSize() < 1)
57 std::string username_legacy; // for case insensitivity
58 *pkt >> serialization_ver >> compression_mode >> proto_ver
59 >> auth_mechs >> username_legacy;
61 // Chose an auth method we support
62 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
64 infostream << "Client: TOCLIENT_HELLO received with "
65 << "serialization_ver=" << (u32)serialization_ver
66 << ", auth_mechs=" << auth_mechs
67 << ", proto_ver=" << proto_ver
68 << ", compression_mode=" << compression_mode
69 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
71 if (!ser_ver_supported(serialization_ver)) {
72 infostream << "Client: TOCLIENT_HELLO: Server sent "
73 << "unsupported ser_fmt_ver"<< std::endl;
77 m_server_ser_ver = serialization_ver;
78 m_proto_ver = proto_ver;
80 //TODO verify that username_legacy matches sent username, only
81 // differs in casing (make both uppercase and compare)
82 // This is only neccessary though when we actually want to add casing support
84 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
85 // we recieved a TOCLIENT_HELLO while auth was already going on
86 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
87 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
88 if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP)
89 || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) {
90 srp_user_delete((SRPUser *) m_auth_data);
95 // Authenticate using that method, or abort if there wasn't any method found
96 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
97 startAuth(chosen_auth_mechanism);
99 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
100 m_access_denied = true;
101 m_access_denied_reason = "Unknown";
107 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
112 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
113 >> m_sudo_auth_methods;
115 playerpos -= v3f(0, BS / 2, 0);
117 // Set player position
118 LocalPlayer *player = m_env.getLocalPlayer();
119 assert(player != NULL);
120 player->setPosition(playerpos);
122 infostream << "Client: received map seed: " << m_map_seed << std::endl;
123 infostream << "Client: received recommended send interval "
124 << m_recommended_send_interval<<std::endl;
127 std::string lang = gettext("LANG_CODE");
128 if (lang == "LANG_CODE")
131 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
137 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
141 m_password = m_new_password;
143 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
145 // send packet to actually set the password
146 startAuth(AUTH_MECHANISM_FIRST_SRP);
149 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
151 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
153 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
154 L"Password change denied. Password NOT changed.");
155 pushToChatQueue(chatMessage);
156 // reset everything and be sad
159 void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
161 if (pkt->getSize() < 1)
165 *pkt >> server_ser_ver;
167 infostream << "Client: TOCLIENT_INIT_LEGACY received with "
168 "server_ser_ver=" << ((int)server_ser_ver & 0xff) << std::endl;
170 if (!ser_ver_supported(server_ser_ver)) {
171 infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
172 << "unsupported ser_fmt_ver"<< std::endl;
176 m_server_ser_ver = server_ser_ver;
178 // We can be totally wrong with this guess
179 // but we only need some value < 25.
182 // Get player position
183 v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
184 if (pkt->getSize() >= 1 + 6) {
185 *pkt >> playerpos_s16;
187 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
190 // Set player position
191 LocalPlayer *player = m_env.getLocalPlayer();
192 assert(player != NULL);
193 player->setPosition(playerpos_f);
195 if (pkt->getSize() >= 1 + 6 + 8) {
198 infostream << "Client: received map seed: " << m_map_seed << std::endl;
201 if (pkt->getSize() >= 1 + 6 + 8 + 4) {
202 *pkt >> m_recommended_send_interval;
203 infostream << "Client: received recommended send interval "
204 << m_recommended_send_interval<<std::endl;
208 NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
214 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
216 // The server didn't like our password. Note, this needs
217 // to be processed even if the serialisation format has
218 // not been agreed yet, the same as TOCLIENT_INIT.
219 m_access_denied = true;
220 m_access_denied_reason = "Unknown";
222 if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
223 if (pkt->getSize() < 1)
226 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
228 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
229 denyCode == SERVER_ACCESSDENIED_CRASH) {
230 *pkt >> m_access_denied_reason;
231 if (m_access_denied_reason.empty()) {
232 m_access_denied_reason = accessDeniedStrings[denyCode];
236 m_access_denied_reconnect = reconnect & 1;
237 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
238 *pkt >> m_access_denied_reason;
239 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
240 m_access_denied_reason = accessDeniedStrings[denyCode];
242 // Allow us to add new error messages to the
243 // protocol without raising the protocol version, if we want to.
244 // Until then (which may be never), this is outside
245 // of the defined protocol.
246 *pkt >> m_access_denied_reason;
247 if (m_access_denied_reason.empty()) {
248 m_access_denied_reason = "Unknown";
252 // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
253 // for compat with old clients
255 if (pkt->getSize() >= 2) {
256 std::wstring wide_reason;
258 m_access_denied_reason = wide_to_utf8(wide_reason);
263 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
265 if (pkt->getSize() < 6)
273 void Client::handleCommand_AddNode(NetworkPacket* pkt)
275 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
282 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
284 bool remove_metadata = true;
285 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
286 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
287 remove_metadata = false;
290 addNode(p, n, remove_metadata);
292 void Client::handleCommand_BlockData(NetworkPacket* pkt)
294 // Ignore too small packet
295 if (pkt->getSize() < 6)
301 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
302 std::istringstream istr(datastring, std::ios_base::binary);
308 sector = m_env.getMap().emergeSector(p2d);
310 assert(sector->getPos() == p2d);
312 block = sector->getBlockNoCreateNoEx(p.Y);
315 Update an existing block
317 block->deSerialize(istr, m_server_ser_ver, false);
318 block->deSerializeNetworkSpecific(istr);
324 block = new MapBlock(&m_env.getMap(), p, this);
325 block->deSerialize(istr, m_server_ser_ver, false);
326 block->deSerializeNetworkSpecific(istr);
327 sector->insertBlock(block);
331 ServerMap::saveBlock(block, m_localdb);
335 Add it to mesh update queue and set it to be acknowledged after update.
337 addUpdateMeshTaskWithEdge(p, true);
340 void Client::handleCommand_Inventory(NetworkPacket* pkt)
342 if (pkt->getSize() < 1)
345 std::string datastring(pkt->getString(0), pkt->getSize());
346 std::istringstream is(datastring, std::ios_base::binary);
348 LocalPlayer *player = m_env.getLocalPlayer();
349 assert(player != NULL);
351 player->inventory.deSerialize(is);
353 m_inventory_updated = true;
355 delete m_inventory_from_server;
356 m_inventory_from_server = new Inventory(player->inventory);
357 m_inventory_from_server_age = 0.0;
360 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
362 if (pkt->getSize() < 2)
369 time_of_day = time_of_day % 24000;
370 float time_speed = 0;
372 if (pkt->getSize() >= 2 + 4) {
376 // Old message; try to approximate speed of time by ourselves
377 float time_of_day_f = (float)time_of_day / 24000.0;
378 float tod_diff_f = 0;
380 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
381 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
383 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
385 m_last_time_of_day_f = time_of_day_f;
386 float time_diff = m_time_of_day_update_timer;
387 m_time_of_day_update_timer = 0;
389 if (m_time_of_day_set) {
390 time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
391 infostream << "Client: Measured time_of_day speed (old format): "
392 << time_speed << " tod_diff_f=" << tod_diff_f
393 << " time_diff=" << time_diff << std::endl;
397 // Update environment
398 m_env.setTimeOfDay(time_of_day);
399 m_env.setTimeOfDaySpeed(time_speed);
400 m_time_of_day_set = true;
402 u32 dr = m_env.getDayNightRatio();
403 infostream << "Client: time_of_day=" << time_of_day
404 << " time_speed=" << time_speed
405 << " dr=" << dr << std::endl;
408 void Client::handleCommand_ChatMessageOld(NetworkPacket *pkt)
419 std::wstring message;
420 for (u32 i = 0; i < len; i++) {
422 message += (wchar_t)read_wchar;
425 // If chat message not consummed by client lua API
426 // @TODO send this to CSM using ChatMessage object
427 if (!moddingEnabled() || !m_script->on_receiving_message(wide_to_utf8(message))) {
428 pushToChatQueue(new ChatMessage(message));
432 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
437 u16 sendername length
443 ChatMessage *chatMessage = new ChatMessage();
444 u8 version, message_type;
445 *pkt >> version >> message_type;
447 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
452 *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
454 chatMessage->type = (ChatMessageType) message_type;
456 // @TODO send this to CSM using ChatMessage object
457 if (!moddingEnabled() || !m_script->on_receiving_message(
458 wide_to_utf8(chatMessage->message))) {
459 pushToChatQueue(chatMessage);
461 // Message was consumed by CSM and should not handled by client, destroying
466 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
469 u16 count of removed objects
470 for all removed objects {
473 u16 count of added objects
474 for all added objects {
477 u32 initialization data length
478 string initialization data
484 u16 removed_count, added_count, id;
486 // Read removed objects
487 *pkt >> removed_count;
489 for (u16 i = 0; i < removed_count; i++) {
491 m_env.removeActiveObject(id);
494 // Read added objects
497 for (u16 i = 0; i < added_count; i++) {
499 m_env.addActiveObject(id, type, pkt->readLongString());
501 } catch (PacketError &e) {
502 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
503 << ". The packet is unreliable, ignoring" << std::endl;
507 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
517 std::string datastring(pkt->getString(0), pkt->getSize());
518 std::istringstream is(datastring, std::ios_base::binary);
522 u16 id = readU16(is);
526 std::string message = deSerializeString(is);
528 // Pass on to the environment
529 m_env.processActiveObjectMessage(id, message);
531 } catch (SerializationError &e) {
532 errorstream << "Client::handleCommand_ActiveObjectMessages: "
533 << "caught SerializationError: " << e.what() << std::endl;
537 void Client::handleCommand_Movement(NetworkPacket* pkt)
539 LocalPlayer *player = m_env.getLocalPlayer();
540 assert(player != NULL);
542 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
544 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
545 >> lf >> lfs >> ls >> g;
547 player->movement_acceleration_default = mad * BS;
548 player->movement_acceleration_air = maa * BS;
549 player->movement_acceleration_fast = maf * BS;
550 player->movement_speed_walk = msw * BS;
551 player->movement_speed_crouch = mscr * BS;
552 player->movement_speed_fast = msf * BS;
553 player->movement_speed_climb = mscl * BS;
554 player->movement_speed_jump = msj * BS;
555 player->movement_liquid_fluidity = lf * BS;
556 player->movement_liquid_fluidity_smooth = lfs * BS;
557 player->movement_liquid_sink = ls * BS;
558 player->movement_gravity = g * BS;
561 void Client::handleCommand_HP(NetworkPacket* pkt)
564 LocalPlayer *player = m_env.getLocalPlayer();
565 assert(player != NULL);
567 u8 oldhp = player->hp;
574 if (moddingEnabled()) {
575 m_script->on_hp_modification(hp);
579 // Add to ClientEvent queue
581 event.type = CE_PLAYER_DAMAGE;
582 event.player_damage.amount = oldhp - hp;
583 m_client_event_queue.push(event);
587 void Client::handleCommand_Breath(NetworkPacket* pkt)
589 LocalPlayer *player = m_env.getLocalPlayer();
590 assert(player != NULL);
596 player->setBreath(breath);
599 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
601 LocalPlayer *player = m_env.getLocalPlayer();
602 assert(player != NULL);
607 *pkt >> pos >> pitch >> yaw;
609 player->setPosition(pos);
611 infostream << "Client got TOCLIENT_MOVE_PLAYER"
612 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
613 << " pitch=" << pitch
618 Add to ClientEvent queue.
619 This has to be sent to the main program because otherwise
620 it would just force the pitch and yaw values to whatever
621 the camera points to.
624 event.type = CE_PLAYER_FORCE_MOVE;
625 event.player_force_move.pitch = pitch;
626 event.player_force_move.yaw = yaw;
627 m_client_event_queue.push(event);
629 // Ignore damage for a few seconds, so that the player doesn't
630 // get damage from falling on ground
631 m_ignore_damage_timer = 3.0;
634 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
636 bool set_camera_point_target;
637 v3f camera_point_target;
639 *pkt >> set_camera_point_target;
640 *pkt >> camera_point_target;
643 event.type = CE_DEATHSCREEN;
644 event.deathscreen.set_camera_point_target = set_camera_point_target;
645 event.deathscreen.camera_point_target_x = camera_point_target.X;
646 event.deathscreen.camera_point_target_y = camera_point_target.Y;
647 event.deathscreen.camera_point_target_z = camera_point_target.Z;
648 m_client_event_queue.push(event);
651 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
657 infostream << "Client: Received media announcement: packet size: "
658 << pkt->getSize() << std::endl;
660 if (m_media_downloader == NULL ||
661 m_media_downloader->isStarted()) {
662 const char *problem = m_media_downloader ?
663 "we already saw another announcement" :
664 "all media has been received already";
665 errorstream << "Client: Received media announcement but "
667 << " files=" << num_files
668 << " size=" << pkt->getSize() << std::endl;
672 // Mesh update thread must be stopped while
673 // updating content definitions
674 sanity_check(!m_mesh_update_thread.isRunning());
676 for (u16 i = 0; i < num_files; i++) {
677 std::string name, sha1_base64;
679 *pkt >> name >> sha1_base64;
681 std::string sha1_raw = base64_decode(sha1_base64);
682 m_media_downloader->addFile(name, sha1_raw);
691 while(!sf.at_end()) {
692 std::string baseurl = trim(sf.next(","));
693 if (!baseurl.empty())
694 m_media_downloader->addRemoteServer(baseurl);
697 catch(SerializationError& e) {
698 // not supported by server or turned off
701 m_media_downloader->step(this);
704 void Client::handleCommand_Media(NetworkPacket* pkt)
708 u16 total number of file bunches
709 u16 index of this bunch
710 u32 number of files in this bunch
722 *pkt >> num_bunches >> bunch_i >> num_files;
724 infostream << "Client: Received files: bunch " << bunch_i << "/"
725 << num_bunches << " files=" << num_files
726 << " size=" << pkt->getSize() << std::endl;
731 if (!m_media_downloader || !m_media_downloader->isStarted()) {
732 const char *problem = m_media_downloader ?
733 "media has not been requested" :
734 "all media has been received already";
735 errorstream << "Client: Received media but "
737 << " bunch " << bunch_i << "/" << num_bunches
738 << " files=" << num_files
739 << " size=" << pkt->getSize() << std::endl;
743 // Mesh update thread must be stopped while
744 // updating content definitions
745 sanity_check(!m_mesh_update_thread.isRunning());
747 for (u32 i=0; i < num_files; i++) {
752 std::string data = pkt->readLongString();
754 m_media_downloader->conventionalTransferDone(
759 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
761 infostream << "Client: Received node definitions: packet size: "
762 << pkt->getSize() << std::endl;
764 // Mesh update thread must be stopped while
765 // updating content definitions
766 sanity_check(!m_mesh_update_thread.isRunning());
768 // Decompress node definitions
769 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
770 std::ostringstream tmp_os;
771 decompressZlib(tmp_is, tmp_os);
773 // Deserialize node definitions
774 std::istringstream tmp_is2(tmp_os.str());
775 m_nodedef->deSerialize(tmp_is2);
776 m_nodedef_received = true;
779 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
781 infostream << "Client: Received item definitions: packet size: "
782 << pkt->getSize() << std::endl;
784 // Mesh update thread must be stopped while
785 // updating content definitions
786 sanity_check(!m_mesh_update_thread.isRunning());
788 // Decompress item definitions
789 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
790 std::ostringstream tmp_os;
791 decompressZlib(tmp_is, tmp_os);
793 // Deserialize node definitions
794 std::istringstream tmp_is2(tmp_os.str());
795 m_itemdef->deSerialize(tmp_is2);
796 m_itemdef_received = true;
799 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
807 [11 + len] (f32 * 3) pos
808 [23 + len] u16 object_id
818 u8 type; // 0=local, 1=positional, 2=object
825 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
830 } catch (PacketError &e) {};
836 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
838 case 1: // positional
839 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
843 ClientActiveObject *cao = m_env.getActiveObject(object_id);
845 pos = cao->getPosition();
846 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
847 // TODO: Set up sound to move with object
854 if (client_id != -1) {
855 m_sounds_server_to_client[server_id] = client_id;
856 m_sounds_client_to_server[client_id] = server_id;
858 m_sounds_to_objects[client_id] = object_id;
862 void Client::handleCommand_StopSound(NetworkPacket* pkt)
868 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
869 if (i != m_sounds_server_to_client.end()) {
870 int client_id = i->second;
871 m_sound->stopSound(client_id);
875 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
881 *pkt >> sound_id >> step >> gain;
883 std::unordered_map<s32, int>::const_iterator i =
884 m_sounds_server_to_client.find(sound_id);
886 if (i != m_sounds_server_to_client.end())
887 m_sound->fadeSound(i->second, step, gain);
890 void Client::handleCommand_Privileges(NetworkPacket* pkt)
892 m_privileges.clear();
893 infostream << "Client: Privileges updated: ";
896 *pkt >> num_privileges;
898 for (u16 i = 0; i < num_privileges; i++) {
903 m_privileges.insert(priv);
904 infostream << priv << " ";
906 infostream << std::endl;
909 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
911 LocalPlayer *player = m_env.getLocalPlayer();
912 assert(player != NULL);
914 // Store formspec in LocalPlayer
915 player->inventory_formspec = pkt->readLongString();
918 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
920 std::string datastring(pkt->getString(0), pkt->getSize());
921 std::istringstream is(datastring, std::ios_base::binary);
923 std::string name = deSerializeString(is);
925 infostream << "Client: Detached inventory update: \"" << name
926 << "\"" << std::endl;
928 Inventory *inv = NULL;
929 if (m_detached_inventories.count(name) > 0)
930 inv = m_detached_inventories[name];
932 inv = new Inventory(m_itemdef);
933 m_detached_inventories[name] = inv;
935 inv->deSerialize(is);
938 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
940 std::string formspec = pkt->readLongString();
941 std::string formname;
946 event.type = CE_SHOW_FORMSPEC;
947 // pointer is required as event is a struct only!
948 // adding a std:string to a struct isn't possible
949 event.show_formspec.formspec = new std::string(formspec);
950 event.show_formspec.formname = new std::string(formname);
951 m_client_event_queue.push(event);
954 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
956 std::string datastring(pkt->getString(0), pkt->getSize());
957 std::istringstream is(datastring, std::ios_base::binary);
959 v3f pos = readV3F1000(is);
960 v3f vel = readV3F1000(is);
961 v3f acc = readV3F1000(is);
962 float expirationtime = readF1000(is);
963 float size = readF1000(is);
964 bool collisiondetection = readU8(is);
965 std::string texture = deSerializeLongString(is);
966 bool vertical = false;
967 bool collision_removal = false;
968 struct TileAnimationParams animation;
969 animation.type = TAT_NONE;
972 vertical = readU8(is);
973 collision_removal = readU8(is);
974 animation.deSerialize(is, m_proto_ver);
979 event.type = CE_SPAWN_PARTICLE;
980 event.spawn_particle.pos = new v3f (pos);
981 event.spawn_particle.vel = new v3f (vel);
982 event.spawn_particle.acc = new v3f (acc);
983 event.spawn_particle.expirationtime = expirationtime;
984 event.spawn_particle.size = size;
985 event.spawn_particle.collisiondetection = collisiondetection;
986 event.spawn_particle.collision_removal = collision_removal;
987 event.spawn_particle.vertical = vertical;
988 event.spawn_particle.texture = new std::string(texture);
989 event.spawn_particle.animation = animation;
990 event.spawn_particle.glow = glow;
992 m_client_event_queue.push(event);
995 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
1009 bool collisiondetection;
1012 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
1013 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
1014 >> maxsize >> collisiondetection;
1016 std::string texture = pkt->readLongString();
1020 bool vertical = false;
1021 bool collision_removal = false;
1022 struct TileAnimationParams animation;
1023 animation.type = TAT_NONE;
1025 u16 attached_id = 0;
1028 *pkt >> collision_removal;
1029 *pkt >> attached_id;
1031 // This is horrible but required (why are there two ways to deserialize pkts?)
1032 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1033 std::istringstream is(datastring, std::ios_base::binary);
1034 animation.deSerialize(is, m_proto_ver);
1039 event.type = CE_ADD_PARTICLESPAWNER;
1040 event.add_particlespawner.amount = amount;
1041 event.add_particlespawner.spawntime = spawntime;
1042 event.add_particlespawner.minpos = new v3f (minpos);
1043 event.add_particlespawner.maxpos = new v3f (maxpos);
1044 event.add_particlespawner.minvel = new v3f (minvel);
1045 event.add_particlespawner.maxvel = new v3f (maxvel);
1046 event.add_particlespawner.minacc = new v3f (minacc);
1047 event.add_particlespawner.maxacc = new v3f (maxacc);
1048 event.add_particlespawner.minexptime = minexptime;
1049 event.add_particlespawner.maxexptime = maxexptime;
1050 event.add_particlespawner.minsize = minsize;
1051 event.add_particlespawner.maxsize = maxsize;
1052 event.add_particlespawner.collisiondetection = collisiondetection;
1053 event.add_particlespawner.collision_removal = collision_removal;
1054 event.add_particlespawner.attached_id = attached_id;
1055 event.add_particlespawner.vertical = vertical;
1056 event.add_particlespawner.texture = new std::string(texture);
1057 event.add_particlespawner.id = id;
1058 event.add_particlespawner.animation = animation;
1059 event.add_particlespawner.glow = glow;
1061 m_client_event_queue.push(event);
1065 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1070 // Modification set 13/03/15, 1 year of compat for protocol v24
1071 if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
1080 event.type = CE_DELETE_PARTICLESPAWNER;
1081 event.delete_particlespawner.id =
1082 (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1084 m_client_event_queue.push(event);
1087 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1089 std::string datastring(pkt->getString(0), pkt->getSize());
1090 std::istringstream is(datastring, std::ios_base::binary);
1106 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1107 >> dir >> align >> offset;
1111 catch(SerializationError &e) {};
1115 } catch(SerializationError &e) {};
1118 event.type = CE_HUDADD;
1119 event.hudadd.id = id;
1120 event.hudadd.type = type;
1121 event.hudadd.pos = new v2f(pos);
1122 event.hudadd.name = new std::string(name);
1123 event.hudadd.scale = new v2f(scale);
1124 event.hudadd.text = new std::string(text);
1125 event.hudadd.number = number;
1126 event.hudadd.item = item;
1127 event.hudadd.dir = dir;
1128 event.hudadd.align = new v2f(align);
1129 event.hudadd.offset = new v2f(offset);
1130 event.hudadd.world_pos = new v3f(world_pos);
1131 event.hudadd.size = new v2s32(size);
1132 m_client_event_queue.push(event);
1135 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1142 event.type = CE_HUDRM;
1143 event.hudrm.id = id;
1144 m_client_event_queue.push(event);
1147 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1159 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1160 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1162 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1164 else if (stat == HUD_STAT_WORLD_POS)
1166 else if (stat == HUD_STAT_SIZE )
1172 event.type = CE_HUDCHANGE;
1173 event.hudchange.id = id;
1174 event.hudchange.stat = (HudElementStat)stat;
1175 event.hudchange.v2fdata = new v2f(v2fdata);
1176 event.hudchange.v3fdata = new v3f(v3fdata);
1177 event.hudchange.sdata = new std::string(sdata);
1178 event.hudchange.data = intdata;
1179 event.hudchange.v2s32data = new v2s32(v2s32data);
1180 m_client_event_queue.push(event);
1183 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1187 *pkt >> flags >> mask;
1189 LocalPlayer *player = m_env.getLocalPlayer();
1190 assert(player != NULL);
1192 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1193 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1195 player->hud_flags &= ~mask;
1196 player->hud_flags |= flags;
1198 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1199 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1201 // Hide minimap if it has been disabled by the server
1202 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1203 // defers a minimap update, therefore only call it if really
1204 // needed, by checking that minimap was visible before
1205 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1207 // Switch to surface mode if radar disabled by server
1208 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1209 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1212 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1214 u16 param; std::string value;
1216 *pkt >> param >> value;
1218 LocalPlayer *player = m_env.getLocalPlayer();
1219 assert(player != NULL);
1221 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1222 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1223 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1224 player->hud_hotbar_itemcount = hotbar_itemcount;
1226 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1227 // If value not empty verify image exists in texture source
1228 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1229 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1230 << value << "')" << std::endl;
1233 player->hotbar_image = value;
1235 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1236 // If value not empty verify image exists in texture source
1237 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1238 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1239 << value << "')" << std::endl;
1242 player->hotbar_selected_image = value;
1246 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1248 std::string datastring(pkt->getString(0), pkt->getSize());
1249 std::istringstream is(datastring, std::ios_base::binary);
1251 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1252 std::string *type = new std::string(deSerializeString(is));
1253 u16 count = readU16(is);
1254 std::vector<std::string> *params = new std::vector<std::string>;
1256 for (size_t i = 0; i < count; i++)
1257 params->push_back(deSerializeString(is));
1261 clouds = readU8(is);
1265 event.type = CE_SET_SKY;
1266 event.set_sky.bgcolor = bgcolor;
1267 event.set_sky.type = type;
1268 event.set_sky.params = params;
1269 event.set_sky.clouds = clouds;
1270 m_client_event_queue.push(event);
1273 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1276 video::SColor color_bright;
1277 video::SColor color_ambient;
1282 *pkt >> density >> color_bright >> color_ambient
1283 >> height >> thickness >> speed;
1286 event.type = CE_CLOUD_PARAMS;
1287 event.cloud_params.density = density;
1288 // use the underlying u32 representation, because we can't
1289 // use struct members with constructors here, and this way
1290 // we avoid using new() and delete() for no good reason
1291 event.cloud_params.color_bright = color_bright.color;
1292 event.cloud_params.color_ambient = color_ambient.color;
1293 event.cloud_params.height = height;
1294 event.cloud_params.thickness = thickness;
1295 // same here: deconstruct to skip constructor
1296 event.cloud_params.speed_x = speed.X;
1297 event.cloud_params.speed_y = speed.Y;
1298 m_client_event_queue.push(event);
1301 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1304 u16 day_night_ratio_u;
1306 *pkt >> do_override >> day_night_ratio_u;
1308 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1311 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1312 event.override_day_night_ratio.do_override = do_override;
1313 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1314 m_client_event_queue.push(event);
1317 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1319 LocalPlayer *player = m_env.getLocalPlayer();
1320 assert(player != NULL);
1322 *pkt >> player->local_animations[0];
1323 *pkt >> player->local_animations[1];
1324 *pkt >> player->local_animations[2];
1325 *pkt >> player->local_animations[3];
1326 *pkt >> player->local_animation_speed;
1329 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1331 LocalPlayer *player = m_env.getLocalPlayer();
1332 assert(player != NULL);
1334 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1337 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1341 *pkt >> type >> num_players;
1342 PlayerListModifer notice_type = (PlayerListModifer) type;
1344 for (u16 i = 0; i < num_players; i++) {
1347 switch (notice_type) {
1348 case PLAYER_LIST_INIT:
1349 case PLAYER_LIST_ADD:
1350 m_env.addPlayerName(name);
1352 case PLAYER_LIST_REMOVE:
1353 m_env.removePlayerName(name);
1359 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1361 if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1362 && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1363 errorstream << "Client: Recieved SRP S_B login message,"
1364 << " but wasn't supposed to (chosen_mech="
1365 << m_chosen_auth_mech << ")." << std::endl;
1371 SRPUser *usr = (SRPUser *) m_auth_data;
1376 infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1378 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1379 (const unsigned char *) B.c_str(), B.size(),
1380 (unsigned char **) &bytes_M, &len_M);
1383 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1387 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1388 resp_pkt << std::string(bytes_M, len_M);
1392 void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
1394 *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;