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 "script/scripting_client.h"
35 #include "util/serialize.h"
37 #include "tileanimation.h"
39 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
41 infostream << "Got deprecated command "
42 << toClientCommandTable[pkt->getCommand()].name << " from peer "
43 << pkt->getPeerId() << "!" << std::endl;
46 void Client::handleCommand_Hello(NetworkPacket* pkt)
48 if (pkt->getSize() < 1)
55 std::string username_legacy; // for case insensitivity
56 *pkt >> serialization_ver >> compression_mode >> proto_ver
57 >> auth_mechs >> username_legacy;
59 // Chose an auth method we support
60 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
62 infostream << "Client: TOCLIENT_HELLO received with "
63 << "serialization_ver=" << (u32)serialization_ver
64 << ", auth_mechs=" << auth_mechs
65 << ", proto_ver=" << proto_ver
66 << ", compression_mode=" << compression_mode
67 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
69 if (!ser_ver_supported(serialization_ver)) {
70 infostream << "Client: TOCLIENT_HELLO: Server sent "
71 << "unsupported ser_fmt_ver"<< std::endl;
75 m_server_ser_ver = serialization_ver;
76 m_proto_ver = proto_ver;
78 //TODO verify that username_legacy matches sent username, only
79 // differs in casing (make both uppercase and compare)
80 // This is only neccessary though when we actually want to add casing support
82 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
83 // we recieved a TOCLIENT_HELLO while auth was already going on
84 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
85 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
86 if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP)
87 || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) {
88 srp_user_delete((SRPUser *) m_auth_data);
93 // Authenticate using that method, or abort if there wasn't any method found
94 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
95 startAuth(chosen_auth_mechanism);
97 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
98 m_access_denied = true;
99 m_access_denied_reason = "Unknown";
105 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
110 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
111 >> m_sudo_auth_methods;
113 playerpos -= v3f(0, BS / 2, 0);
115 // Set player position
116 LocalPlayer *player = m_env.getLocalPlayer();
117 assert(player != NULL);
118 player->setPosition(playerpos);
120 infostream << "Client: received map seed: " << m_map_seed << std::endl;
121 infostream << "Client: received recommended send interval "
122 << m_recommended_send_interval<<std::endl;
125 NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
130 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
134 m_password = m_new_password;
136 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
138 // send packet to actually set the password
139 startAuth(AUTH_MECHANISM_FIRST_SRP);
142 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
144 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
146 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
147 L"Password change denied. Password NOT changed.");
148 pushToChatQueue(chatMessage);
149 // reset everything and be sad
152 void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
154 if (pkt->getSize() < 1)
158 *pkt >> server_ser_ver;
160 infostream << "Client: TOCLIENT_INIT_LEGACY received with "
161 "server_ser_ver=" << ((int)server_ser_ver & 0xff) << std::endl;
163 if (!ser_ver_supported(server_ser_ver)) {
164 infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
165 << "unsupported ser_fmt_ver"<< std::endl;
169 m_server_ser_ver = server_ser_ver;
171 // We can be totally wrong with this guess
172 // but we only need some value < 25.
175 // Get player position
176 v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
177 if (pkt->getSize() >= 1 + 6) {
178 *pkt >> playerpos_s16;
180 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
183 // Set player position
184 LocalPlayer *player = m_env.getLocalPlayer();
185 assert(player != NULL);
186 player->setPosition(playerpos_f);
188 if (pkt->getSize() >= 1 + 6 + 8) {
191 infostream << "Client: received map seed: " << m_map_seed << std::endl;
194 if (pkt->getSize() >= 1 + 6 + 8 + 4) {
195 *pkt >> m_recommended_send_interval;
196 infostream << "Client: received recommended send interval "
197 << m_recommended_send_interval<<std::endl;
201 NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
207 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
209 // The server didn't like our password. Note, this needs
210 // to be processed even if the serialisation format has
211 // not been agreed yet, the same as TOCLIENT_INIT.
212 m_access_denied = true;
213 m_access_denied_reason = "Unknown";
215 if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
216 if (pkt->getSize() < 1)
219 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
221 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
222 denyCode == SERVER_ACCESSDENIED_CRASH) {
223 *pkt >> m_access_denied_reason;
224 if (m_access_denied_reason == "") {
225 m_access_denied_reason = accessDeniedStrings[denyCode];
229 m_access_denied_reconnect = reconnect & 1;
230 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
231 *pkt >> m_access_denied_reason;
232 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
233 m_access_denied_reason = accessDeniedStrings[denyCode];
235 // Allow us to add new error messages to the
236 // protocol without raising the protocol version, if we want to.
237 // Until then (which may be never), this is outside
238 // of the defined protocol.
239 *pkt >> m_access_denied_reason;
240 if (m_access_denied_reason == "") {
241 m_access_denied_reason = "Unknown";
245 // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
246 // for compat with old clients
248 if (pkt->getSize() >= 2) {
249 std::wstring wide_reason;
251 m_access_denied_reason = wide_to_utf8(wide_reason);
256 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
258 if (pkt->getSize() < 6)
266 void Client::handleCommand_AddNode(NetworkPacket* pkt)
268 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
275 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
277 bool remove_metadata = true;
278 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
279 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
280 remove_metadata = false;
283 addNode(p, n, remove_metadata);
285 void Client::handleCommand_BlockData(NetworkPacket* pkt)
287 // Ignore too small packet
288 if (pkt->getSize() < 6)
294 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
295 std::istringstream istr(datastring, std::ios_base::binary);
301 sector = m_env.getMap().emergeSector(p2d);
303 assert(sector->getPos() == p2d);
305 block = sector->getBlockNoCreateNoEx(p.Y);
308 Update an existing block
310 block->deSerialize(istr, m_server_ser_ver, false);
311 block->deSerializeNetworkSpecific(istr);
317 block = new MapBlock(&m_env.getMap(), p, this);
318 block->deSerialize(istr, m_server_ser_ver, false);
319 block->deSerializeNetworkSpecific(istr);
320 sector->insertBlock(block);
324 ServerMap::saveBlock(block, m_localdb);
328 Add it to mesh update queue and set it to be acknowledged after update.
330 addUpdateMeshTaskWithEdge(p, true);
333 void Client::handleCommand_Inventory(NetworkPacket* pkt)
335 if (pkt->getSize() < 1)
338 std::string datastring(pkt->getString(0), pkt->getSize());
339 std::istringstream is(datastring, std::ios_base::binary);
341 LocalPlayer *player = m_env.getLocalPlayer();
342 assert(player != NULL);
344 player->inventory.deSerialize(is);
346 m_inventory_updated = true;
348 delete m_inventory_from_server;
349 m_inventory_from_server = new Inventory(player->inventory);
350 m_inventory_from_server_age = 0.0;
353 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
355 if (pkt->getSize() < 2)
362 time_of_day = time_of_day % 24000;
363 float time_speed = 0;
365 if (pkt->getSize() >= 2 + 4) {
369 // Old message; try to approximate speed of time by ourselves
370 float time_of_day_f = (float)time_of_day / 24000.0;
371 float tod_diff_f = 0;
373 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
374 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
376 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
378 m_last_time_of_day_f = time_of_day_f;
379 float time_diff = m_time_of_day_update_timer;
380 m_time_of_day_update_timer = 0;
382 if (m_time_of_day_set) {
383 time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
384 infostream << "Client: Measured time_of_day speed (old format): "
385 << time_speed << " tod_diff_f=" << tod_diff_f
386 << " time_diff=" << time_diff << std::endl;
390 // Update environment
391 m_env.setTimeOfDay(time_of_day);
392 m_env.setTimeOfDaySpeed(time_speed);
393 m_time_of_day_set = true;
395 u32 dr = m_env.getDayNightRatio();
396 infostream << "Client: time_of_day=" << time_of_day
397 << " time_speed=" << time_speed
398 << " dr=" << dr << std::endl;
401 void Client::handleCommand_ChatMessageOld(NetworkPacket *pkt)
412 std::wstring message;
413 for (u32 i = 0; i < len; i++) {
415 message += (wchar_t)read_wchar;
418 // If chat message not consummed by client lua API
419 // @TODO send this to CSM using ChatMessage object
420 if (!moddingEnabled() || !m_script->on_receiving_message(wide_to_utf8(message))) {
421 pushToChatQueue(new ChatMessage(message));
425 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
430 u16 sendername length
436 ChatMessage *chatMessage = new ChatMessage();
437 u8 version, message_type;
438 *pkt >> version >> message_type;
440 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
445 *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
447 chatMessage->type = (ChatMessageType) message_type;
449 // @TODO send this to CSM using ChatMessage object
450 if (!moddingEnabled() || !m_script->on_receiving_message(
451 wide_to_utf8(chatMessage->message))) {
452 pushToChatQueue(chatMessage);
454 // Message was consumed by CSM and should not handled by client, destroying
459 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
462 u16 count of removed objects
463 for all removed objects {
466 u16 count of added objects
467 for all added objects {
470 u32 initialization data length
471 string initialization data
477 u16 removed_count, added_count, id;
479 // Read removed objects
480 *pkt >> removed_count;
482 for (u16 i = 0; i < removed_count; i++) {
484 m_env.removeActiveObject(id);
487 // Read added objects
490 for (u16 i = 0; i < added_count; i++) {
492 m_env.addActiveObject(id, type, pkt->readLongString());
494 } catch (PacketError &e) {
495 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
496 << ". The packet is unreliable, ignoring" << std::endl;
500 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
510 std::string datastring(pkt->getString(0), pkt->getSize());
511 std::istringstream is(datastring, std::ios_base::binary);
515 u16 id = readU16(is);
519 std::string message = deSerializeString(is);
521 // Pass on to the environment
522 m_env.processActiveObjectMessage(id, message);
524 } catch (SerializationError &e) {
525 errorstream << "Client::handleCommand_ActiveObjectMessages: "
526 << "caught SerializationError: " << e.what() << std::endl;
530 void Client::handleCommand_Movement(NetworkPacket* pkt)
532 LocalPlayer *player = m_env.getLocalPlayer();
533 assert(player != NULL);
535 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
537 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
538 >> lf >> lfs >> ls >> g;
540 player->movement_acceleration_default = mad * BS;
541 player->movement_acceleration_air = maa * BS;
542 player->movement_acceleration_fast = maf * BS;
543 player->movement_speed_walk = msw * BS;
544 player->movement_speed_crouch = mscr * BS;
545 player->movement_speed_fast = msf * BS;
546 player->movement_speed_climb = mscl * BS;
547 player->movement_speed_jump = msj * BS;
548 player->movement_liquid_fluidity = lf * BS;
549 player->movement_liquid_fluidity_smooth = lfs * BS;
550 player->movement_liquid_sink = ls * BS;
551 player->movement_gravity = g * BS;
554 void Client::handleCommand_HP(NetworkPacket* pkt)
557 LocalPlayer *player = m_env.getLocalPlayer();
558 assert(player != NULL);
560 u8 oldhp = player->hp;
567 if (moddingEnabled()) {
568 m_script->on_hp_modification(hp);
572 // Add to ClientEvent queue
574 event.type = CE_PLAYER_DAMAGE;
575 event.player_damage.amount = oldhp - hp;
576 m_client_event_queue.push(event);
580 void Client::handleCommand_Breath(NetworkPacket* pkt)
582 LocalPlayer *player = m_env.getLocalPlayer();
583 assert(player != NULL);
589 player->setBreath(breath);
592 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
594 LocalPlayer *player = m_env.getLocalPlayer();
595 assert(player != NULL);
600 *pkt >> pos >> pitch >> yaw;
602 player->setPosition(pos);
604 infostream << "Client got TOCLIENT_MOVE_PLAYER"
605 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
606 << " pitch=" << pitch
611 Add to ClientEvent queue.
612 This has to be sent to the main program because otherwise
613 it would just force the pitch and yaw values to whatever
614 the camera points to.
617 event.type = CE_PLAYER_FORCE_MOVE;
618 event.player_force_move.pitch = pitch;
619 event.player_force_move.yaw = yaw;
620 m_client_event_queue.push(event);
622 // Ignore damage for a few seconds, so that the player doesn't
623 // get damage from falling on ground
624 m_ignore_damage_timer = 3.0;
627 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
629 bool set_camera_point_target;
630 v3f camera_point_target;
632 *pkt >> set_camera_point_target;
633 *pkt >> camera_point_target;
636 event.type = CE_DEATHSCREEN;
637 event.deathscreen.set_camera_point_target = set_camera_point_target;
638 event.deathscreen.camera_point_target_x = camera_point_target.X;
639 event.deathscreen.camera_point_target_y = camera_point_target.Y;
640 event.deathscreen.camera_point_target_z = camera_point_target.Z;
641 m_client_event_queue.push(event);
644 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
650 infostream << "Client: Received media announcement: packet size: "
651 << pkt->getSize() << std::endl;
653 if (m_media_downloader == NULL ||
654 m_media_downloader->isStarted()) {
655 const char *problem = m_media_downloader ?
656 "we already saw another announcement" :
657 "all media has been received already";
658 errorstream << "Client: Received media announcement but "
660 << " files=" << num_files
661 << " size=" << pkt->getSize() << std::endl;
665 // Mesh update thread must be stopped while
666 // updating content definitions
667 sanity_check(!m_mesh_update_thread.isRunning());
669 for (u16 i = 0; i < num_files; i++) {
670 std::string name, sha1_base64;
672 *pkt >> name >> sha1_base64;
674 std::string sha1_raw = base64_decode(sha1_base64);
675 m_media_downloader->addFile(name, sha1_raw);
684 while(!sf.at_end()) {
685 std::string baseurl = trim(sf.next(","));
687 m_media_downloader->addRemoteServer(baseurl);
690 catch(SerializationError& e) {
691 // not supported by server or turned off
694 m_media_downloader->step(this);
697 void Client::handleCommand_Media(NetworkPacket* pkt)
701 u16 total number of file bunches
702 u16 index of this bunch
703 u32 number of files in this bunch
715 *pkt >> num_bunches >> bunch_i >> num_files;
717 infostream << "Client: Received files: bunch " << bunch_i << "/"
718 << num_bunches << " files=" << num_files
719 << " size=" << pkt->getSize() << std::endl;
724 if (!m_media_downloader || !m_media_downloader->isStarted()) {
725 const char *problem = m_media_downloader ?
726 "media has not been requested" :
727 "all media has been received already";
728 errorstream << "Client: Received media but "
730 << " bunch " << bunch_i << "/" << num_bunches
731 << " files=" << num_files
732 << " size=" << pkt->getSize() << std::endl;
736 // Mesh update thread must be stopped while
737 // updating content definitions
738 sanity_check(!m_mesh_update_thread.isRunning());
740 for (u32 i=0; i < num_files; i++) {
745 std::string data = pkt->readLongString();
747 m_media_downloader->conventionalTransferDone(
752 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
754 infostream << "Client: Received node definitions: packet size: "
755 << pkt->getSize() << std::endl;
757 // Mesh update thread must be stopped while
758 // updating content definitions
759 sanity_check(!m_mesh_update_thread.isRunning());
761 // Decompress node definitions
762 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
763 std::ostringstream tmp_os;
764 decompressZlib(tmp_is, tmp_os);
766 // Deserialize node definitions
767 std::istringstream tmp_is2(tmp_os.str());
768 m_nodedef->deSerialize(tmp_is2);
769 m_nodedef_received = true;
772 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
774 infostream << "Client: Received item definitions: packet size: "
775 << pkt->getSize() << std::endl;
777 // Mesh update thread must be stopped while
778 // updating content definitions
779 sanity_check(!m_mesh_update_thread.isRunning());
781 // Decompress item definitions
782 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
783 std::ostringstream tmp_os;
784 decompressZlib(tmp_is, tmp_os);
786 // Deserialize node definitions
787 std::istringstream tmp_is2(tmp_os.str());
788 m_itemdef->deSerialize(tmp_is2);
789 m_itemdef_received = true;
792 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
800 [11 + len] (f32 * 3) pos
801 [23 + len] u16 object_id
811 u8 type; // 0=local, 1=positional, 2=object
818 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
823 } catch (PacketError &e) {};
829 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
831 case 1: // positional
832 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
836 ClientActiveObject *cao = m_env.getActiveObject(object_id);
838 pos = cao->getPosition();
839 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
840 // TODO: Set up sound to move with object
847 if (client_id != -1) {
848 m_sounds_server_to_client[server_id] = client_id;
849 m_sounds_client_to_server[client_id] = server_id;
851 m_sounds_to_objects[client_id] = object_id;
855 void Client::handleCommand_StopSound(NetworkPacket* pkt)
861 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
862 if (i != m_sounds_server_to_client.end()) {
863 int client_id = i->second;
864 m_sound->stopSound(client_id);
868 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
874 *pkt >> sound_id >> step >> gain;
876 std::unordered_map<s32, int>::const_iterator i =
877 m_sounds_server_to_client.find(sound_id);
879 if (i != m_sounds_server_to_client.end())
880 m_sound->fadeSound(i->second, step, gain);
883 void Client::handleCommand_Privileges(NetworkPacket* pkt)
885 m_privileges.clear();
886 infostream << "Client: Privileges updated: ";
889 *pkt >> num_privileges;
891 for (u16 i = 0; i < num_privileges; i++) {
896 m_privileges.insert(priv);
897 infostream << priv << " ";
899 infostream << std::endl;
902 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
904 LocalPlayer *player = m_env.getLocalPlayer();
905 assert(player != NULL);
907 // Store formspec in LocalPlayer
908 player->inventory_formspec = pkt->readLongString();
911 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
913 std::string datastring(pkt->getString(0), pkt->getSize());
914 std::istringstream is(datastring, std::ios_base::binary);
916 std::string name = deSerializeString(is);
918 infostream << "Client: Detached inventory update: \"" << name
919 << "\"" << std::endl;
921 Inventory *inv = NULL;
922 if (m_detached_inventories.count(name) > 0)
923 inv = m_detached_inventories[name];
925 inv = new Inventory(m_itemdef);
926 m_detached_inventories[name] = inv;
928 inv->deSerialize(is);
931 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
933 std::string formspec = pkt->readLongString();
934 std::string formname;
939 event.type = CE_SHOW_FORMSPEC;
940 // pointer is required as event is a struct only!
941 // adding a std:string to a struct isn't possible
942 event.show_formspec.formspec = new std::string(formspec);
943 event.show_formspec.formname = new std::string(formname);
944 m_client_event_queue.push(event);
947 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
949 std::string datastring(pkt->getString(0), pkt->getSize());
950 std::istringstream is(datastring, std::ios_base::binary);
952 v3f pos = readV3F1000(is);
953 v3f vel = readV3F1000(is);
954 v3f acc = readV3F1000(is);
955 float expirationtime = readF1000(is);
956 float size = readF1000(is);
957 bool collisiondetection = readU8(is);
958 std::string texture = deSerializeLongString(is);
959 bool vertical = false;
960 bool collision_removal = false;
961 struct TileAnimationParams animation;
962 animation.type = TAT_NONE;
965 vertical = readU8(is);
966 collision_removal = readU8(is);
967 animation.deSerialize(is, m_proto_ver);
972 event.type = CE_SPAWN_PARTICLE;
973 event.spawn_particle.pos = new v3f (pos);
974 event.spawn_particle.vel = new v3f (vel);
975 event.spawn_particle.acc = new v3f (acc);
976 event.spawn_particle.expirationtime = expirationtime;
977 event.spawn_particle.size = size;
978 event.spawn_particle.collisiondetection = collisiondetection;
979 event.spawn_particle.collision_removal = collision_removal;
980 event.spawn_particle.vertical = vertical;
981 event.spawn_particle.texture = new std::string(texture);
982 event.spawn_particle.animation = animation;
983 event.spawn_particle.glow = glow;
985 m_client_event_queue.push(event);
988 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
1002 bool collisiondetection;
1005 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
1006 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
1007 >> maxsize >> collisiondetection;
1009 std::string texture = pkt->readLongString();
1013 bool vertical = false;
1014 bool collision_removal = false;
1015 struct TileAnimationParams animation;
1016 animation.type = TAT_NONE;
1018 u16 attached_id = 0;
1021 *pkt >> collision_removal;
1022 *pkt >> attached_id;
1024 // This is horrible but required (why are there two ways to deserialize pkts?)
1025 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1026 std::istringstream is(datastring, std::ios_base::binary);
1027 animation.deSerialize(is, m_proto_ver);
1032 event.type = CE_ADD_PARTICLESPAWNER;
1033 event.add_particlespawner.amount = amount;
1034 event.add_particlespawner.spawntime = spawntime;
1035 event.add_particlespawner.minpos = new v3f (minpos);
1036 event.add_particlespawner.maxpos = new v3f (maxpos);
1037 event.add_particlespawner.minvel = new v3f (minvel);
1038 event.add_particlespawner.maxvel = new v3f (maxvel);
1039 event.add_particlespawner.minacc = new v3f (minacc);
1040 event.add_particlespawner.maxacc = new v3f (maxacc);
1041 event.add_particlespawner.minexptime = minexptime;
1042 event.add_particlespawner.maxexptime = maxexptime;
1043 event.add_particlespawner.minsize = minsize;
1044 event.add_particlespawner.maxsize = maxsize;
1045 event.add_particlespawner.collisiondetection = collisiondetection;
1046 event.add_particlespawner.collision_removal = collision_removal;
1047 event.add_particlespawner.attached_id = attached_id;
1048 event.add_particlespawner.vertical = vertical;
1049 event.add_particlespawner.texture = new std::string(texture);
1050 event.add_particlespawner.id = id;
1051 event.add_particlespawner.animation = animation;
1052 event.add_particlespawner.glow = glow;
1054 m_client_event_queue.push(event);
1058 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1063 // Modification set 13/03/15, 1 year of compat for protocol v24
1064 if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
1073 event.type = CE_DELETE_PARTICLESPAWNER;
1074 event.delete_particlespawner.id =
1075 (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1077 m_client_event_queue.push(event);
1080 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1082 std::string datastring(pkt->getString(0), pkt->getSize());
1083 std::istringstream is(datastring, std::ios_base::binary);
1099 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1100 >> dir >> align >> offset;
1104 catch(SerializationError &e) {};
1108 } catch(SerializationError &e) {};
1111 event.type = CE_HUDADD;
1112 event.hudadd.id = id;
1113 event.hudadd.type = type;
1114 event.hudadd.pos = new v2f(pos);
1115 event.hudadd.name = new std::string(name);
1116 event.hudadd.scale = new v2f(scale);
1117 event.hudadd.text = new std::string(text);
1118 event.hudadd.number = number;
1119 event.hudadd.item = item;
1120 event.hudadd.dir = dir;
1121 event.hudadd.align = new v2f(align);
1122 event.hudadd.offset = new v2f(offset);
1123 event.hudadd.world_pos = new v3f(world_pos);
1124 event.hudadd.size = new v2s32(size);
1125 m_client_event_queue.push(event);
1128 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1135 event.type = CE_HUDRM;
1136 event.hudrm.id = id;
1137 m_client_event_queue.push(event);
1140 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1152 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1153 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1155 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1157 else if (stat == HUD_STAT_WORLD_POS)
1159 else if (stat == HUD_STAT_SIZE )
1165 event.type = CE_HUDCHANGE;
1166 event.hudchange.id = id;
1167 event.hudchange.stat = (HudElementStat)stat;
1168 event.hudchange.v2fdata = new v2f(v2fdata);
1169 event.hudchange.v3fdata = new v3f(v3fdata);
1170 event.hudchange.sdata = new std::string(sdata);
1171 event.hudchange.data = intdata;
1172 event.hudchange.v2s32data = new v2s32(v2s32data);
1173 m_client_event_queue.push(event);
1176 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1180 *pkt >> flags >> mask;
1182 LocalPlayer *player = m_env.getLocalPlayer();
1183 assert(player != NULL);
1185 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1187 player->hud_flags &= ~mask;
1188 player->hud_flags |= flags;
1190 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1192 // Hide minimap if it has been disabled by the server
1193 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible) {
1194 // defers a minimap update, therefore only call it if really
1195 // needed, by checking that minimap was visible before
1196 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1200 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1202 u16 param; std::string value;
1204 *pkt >> param >> value;
1206 LocalPlayer *player = m_env.getLocalPlayer();
1207 assert(player != NULL);
1209 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1210 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1211 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1212 player->hud_hotbar_itemcount = hotbar_itemcount;
1214 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1215 // If value not empty verify image exists in texture source
1216 if (value != "" && !getTextureSource()->isKnownSourceImage(value)) {
1217 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1218 << value << "')" << std::endl;
1221 player->hotbar_image = value;
1223 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1224 // If value not empty verify image exists in texture source
1225 if (value != "" && !getTextureSource()->isKnownSourceImage(value)) {
1226 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1227 << value << "')" << std::endl;
1230 player->hotbar_selected_image = value;
1234 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1236 std::string datastring(pkt->getString(0), pkt->getSize());
1237 std::istringstream is(datastring, std::ios_base::binary);
1239 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1240 std::string *type = new std::string(deSerializeString(is));
1241 u16 count = readU16(is);
1242 std::vector<std::string> *params = new std::vector<std::string>;
1244 for (size_t i = 0; i < count; i++)
1245 params->push_back(deSerializeString(is));
1249 clouds = readU8(is);
1253 event.type = CE_SET_SKY;
1254 event.set_sky.bgcolor = bgcolor;
1255 event.set_sky.type = type;
1256 event.set_sky.params = params;
1257 event.set_sky.clouds = clouds;
1258 m_client_event_queue.push(event);
1261 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1264 video::SColor color_bright;
1265 video::SColor color_ambient;
1270 *pkt >> density >> color_bright >> color_ambient
1271 >> height >> thickness >> speed;
1274 event.type = CE_CLOUD_PARAMS;
1275 event.cloud_params.density = density;
1276 // use the underlying u32 representation, because we can't
1277 // use struct members with constructors here, and this way
1278 // we avoid using new() and delete() for no good reason
1279 event.cloud_params.color_bright = color_bright.color;
1280 event.cloud_params.color_ambient = color_ambient.color;
1281 event.cloud_params.height = height;
1282 event.cloud_params.thickness = thickness;
1283 // same here: deconstruct to skip constructor
1284 event.cloud_params.speed_x = speed.X;
1285 event.cloud_params.speed_y = speed.Y;
1286 m_client_event_queue.push(event);
1289 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1292 u16 day_night_ratio_u;
1294 *pkt >> do_override >> day_night_ratio_u;
1296 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1299 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1300 event.override_day_night_ratio.do_override = do_override;
1301 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1302 m_client_event_queue.push(event);
1305 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1307 LocalPlayer *player = m_env.getLocalPlayer();
1308 assert(player != NULL);
1310 *pkt >> player->local_animations[0];
1311 *pkt >> player->local_animations[1];
1312 *pkt >> player->local_animations[2];
1313 *pkt >> player->local_animations[3];
1314 *pkt >> player->local_animation_speed;
1317 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1319 LocalPlayer *player = m_env.getLocalPlayer();
1320 assert(player != NULL);
1322 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1325 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1329 *pkt >> type >> num_players;
1330 PlayerListModifer notice_type = (PlayerListModifer) type;
1332 for (u16 i = 0; i < num_players; i++) {
1335 switch (notice_type) {
1336 case PLAYER_LIST_INIT:
1337 case PLAYER_LIST_ADD:
1338 m_env.addPlayerName(name);
1340 case PLAYER_LIST_REMOVE:
1341 m_env.removePlayerName(name);
1347 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1349 if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1350 && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1351 errorstream << "Client: Recieved SRP S_B login message,"
1352 << " but wasn't supposed to (chosen_mech="
1353 << m_chosen_auth_mech << ")." << std::endl;
1359 SRPUser *usr = (SRPUser *) m_auth_data;
1364 infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1366 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1367 (const unsigned char *) B.c_str(), B.size(),
1368 (unsigned char **) &bytes_M, &len_M);
1371 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1375 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1376 resp_pkt << std::string(bytes_M, len_M);
1380 void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
1382 *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;