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 "clientmedia.h"
26 #include "mapsector.h"
29 #include "serialization.h"
31 #include "util/strfnd.h"
32 #include "network/clientopcodes.h"
33 #include "script/clientscripting.h"
34 #include "util/serialize.h"
36 #include "tileanimation.h"
38 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
40 infostream << "Got deprecated command "
41 << toClientCommandTable[pkt->getCommand()].name << " from peer "
42 << pkt->getPeerId() << "!" << std::endl;
45 void Client::handleCommand_Hello(NetworkPacket* pkt)
47 if (pkt->getSize() < 1)
54 std::string username_legacy; // for case insensitivity
55 *pkt >> serialization_ver >> compression_mode >> proto_ver
56 >> auth_mechs >> username_legacy;
58 // Chose an auth method we support
59 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
61 infostream << "Client: TOCLIENT_HELLO received with "
62 << "serialization_ver=" << (u32)serialization_ver
63 << ", auth_mechs=" << auth_mechs
64 << ", proto_ver=" << proto_ver
65 << ", compression_mode=" << compression_mode
66 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
68 if (!ser_ver_supported(serialization_ver)) {
69 infostream << "Client: TOCLIENT_HELLO: Server sent "
70 << "unsupported ser_fmt_ver"<< std::endl;
74 m_server_ser_ver = serialization_ver;
75 m_proto_ver = proto_ver;
77 //TODO verify that username_legacy matches sent username, only
78 // differs in casing (make both uppercase and compare)
79 // This is only neccessary though when we actually want to add casing support
81 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
82 // we recieved a TOCLIENT_HELLO while auth was already going on
83 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
84 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
85 if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP)
86 || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) {
87 srp_user_delete((SRPUser *) m_auth_data);
92 // Authenticate using that method, or abort if there wasn't any method found
93 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
94 startAuth(chosen_auth_mechanism);
96 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
97 m_access_denied = true;
98 m_access_denied_reason = "Unknown";
104 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
109 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
110 >> m_sudo_auth_methods;
112 playerpos -= v3f(0, BS / 2, 0);
114 // Set player position
115 LocalPlayer *player = m_env.getLocalPlayer();
116 assert(player != NULL);
117 player->setPosition(playerpos);
119 infostream << "Client: received map seed: " << m_map_seed << std::endl;
120 infostream << "Client: received recommended send interval "
121 << m_recommended_send_interval<<std::endl;
124 NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
129 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
133 m_password = m_new_password;
135 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
137 // send packet to actually set the password
138 startAuth(AUTH_MECHANISM_FIRST_SRP);
141 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
143 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
145 pushToChatQueue(L"Password change denied. Password NOT changed.");
146 // reset everything and be sad
149 void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
151 if (pkt->getSize() < 1)
155 *pkt >> server_ser_ver;
157 infostream << "Client: TOCLIENT_INIT_LEGACY received with "
158 "server_ser_ver=" << ((int)server_ser_ver & 0xff) << std::endl;
160 if (!ser_ver_supported(server_ser_ver)) {
161 infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
162 << "unsupported ser_fmt_ver"<< std::endl;
166 m_server_ser_ver = server_ser_ver;
168 // We can be totally wrong with this guess
169 // but we only need some value < 25.
172 // Get player position
173 v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
174 if (pkt->getSize() >= 1 + 6) {
175 *pkt >> playerpos_s16;
177 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
180 // Set player position
181 LocalPlayer *player = m_env.getLocalPlayer();
182 assert(player != NULL);
183 player->setPosition(playerpos_f);
185 if (pkt->getSize() >= 1 + 6 + 8) {
188 infostream << "Client: received map seed: " << m_map_seed << std::endl;
191 if (pkt->getSize() >= 1 + 6 + 8 + 4) {
192 *pkt >> m_recommended_send_interval;
193 infostream << "Client: received recommended send interval "
194 << m_recommended_send_interval<<std::endl;
198 NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
204 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
206 // The server didn't like our password. Note, this needs
207 // to be processed even if the serialisation format has
208 // not been agreed yet, the same as TOCLIENT_INIT.
209 m_access_denied = true;
210 m_access_denied_reason = "Unknown";
212 if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
213 if (pkt->getSize() < 1)
216 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
218 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
219 denyCode == SERVER_ACCESSDENIED_CRASH) {
220 *pkt >> m_access_denied_reason;
221 if (m_access_denied_reason == "") {
222 m_access_denied_reason = accessDeniedStrings[denyCode];
226 m_access_denied_reconnect = reconnect & 1;
227 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
228 *pkt >> m_access_denied_reason;
229 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
230 m_access_denied_reason = accessDeniedStrings[denyCode];
232 // Allow us to add new error messages to the
233 // protocol without raising the protocol version, if we want to.
234 // Until then (which may be never), this is outside
235 // of the defined protocol.
236 *pkt >> m_access_denied_reason;
237 if (m_access_denied_reason == "") {
238 m_access_denied_reason = "Unknown";
242 // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
243 // for compat with old clients
245 if (pkt->getSize() >= 2) {
246 std::wstring wide_reason;
248 m_access_denied_reason = wide_to_utf8(wide_reason);
253 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
255 if (pkt->getSize() < 6)
263 void Client::handleCommand_AddNode(NetworkPacket* pkt)
265 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
272 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
274 bool remove_metadata = true;
275 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
276 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
277 remove_metadata = false;
280 addNode(p, n, remove_metadata);
282 void Client::handleCommand_BlockData(NetworkPacket* pkt)
284 // Ignore too small packet
285 if (pkt->getSize() < 6)
291 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
292 std::istringstream istr(datastring, std::ios_base::binary);
298 sector = m_env.getMap().emergeSector(p2d);
300 assert(sector->getPos() == p2d);
302 block = sector->getBlockNoCreateNoEx(p.Y);
305 Update an existing block
307 block->deSerialize(istr, m_server_ser_ver, false);
308 block->deSerializeNetworkSpecific(istr);
314 block = new MapBlock(&m_env.getMap(), p, this);
315 block->deSerialize(istr, m_server_ser_ver, false);
316 block->deSerializeNetworkSpecific(istr);
317 sector->insertBlock(block);
321 ServerMap::saveBlock(block, m_localdb);
325 Add it to mesh update queue and set it to be acknowledged after update.
327 addUpdateMeshTaskWithEdge(p, true);
330 void Client::handleCommand_Inventory(NetworkPacket* pkt)
332 if (pkt->getSize() < 1)
335 std::string datastring(pkt->getString(0), pkt->getSize());
336 std::istringstream is(datastring, std::ios_base::binary);
338 LocalPlayer *player = m_env.getLocalPlayer();
339 assert(player != NULL);
341 player->inventory.deSerialize(is);
343 m_inventory_updated = true;
345 delete m_inventory_from_server;
346 m_inventory_from_server = new Inventory(player->inventory);
347 m_inventory_from_server_age = 0.0;
350 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
352 if (pkt->getSize() < 2)
359 time_of_day = time_of_day % 24000;
360 float time_speed = 0;
362 if (pkt->getSize() >= 2 + 4) {
366 // Old message; try to approximate speed of time by ourselves
367 float time_of_day_f = (float)time_of_day / 24000.0;
368 float tod_diff_f = 0;
370 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
371 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
373 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
375 m_last_time_of_day_f = time_of_day_f;
376 float time_diff = m_time_of_day_update_timer;
377 m_time_of_day_update_timer = 0;
379 if (m_time_of_day_set) {
380 time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
381 infostream << "Client: Measured time_of_day speed (old format): "
382 << time_speed << " tod_diff_f=" << tod_diff_f
383 << " time_diff=" << time_diff << std::endl;
387 // Update environment
388 m_env.setTimeOfDay(time_of_day);
389 m_env.setTimeOfDaySpeed(time_speed);
390 m_time_of_day_set = true;
392 u32 dr = m_env.getDayNightRatio();
393 infostream << "Client: time_of_day=" << time_of_day
394 << " time_speed=" << time_speed
395 << " dr=" << dr << std::endl;
398 void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
409 std::wstring message;
410 for (u32 i = 0; i < len; i++) {
412 message += (wchar_t)read_wchar;
415 // If chat message not consummed by client lua API
416 if (!m_script->on_receiving_message(wide_to_utf8(message))) {
417 pushToChatQueue(message);
421 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
424 u16 count of removed objects
425 for all removed objects {
428 u16 count of added objects
429 for all added objects {
432 u32 initialization data length
433 string initialization data
439 u16 removed_count, added_count, id;
441 // Read removed objects
442 *pkt >> removed_count;
444 for (u16 i = 0; i < removed_count; i++) {
446 m_env.removeActiveObject(id);
449 // Read added objects
452 for (u16 i = 0; i < added_count; i++) {
454 m_env.addActiveObject(id, type, pkt->readLongString());
456 } catch (PacketError &e) {
457 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
458 << ". The packet is unreliable, ignoring" << std::endl;
462 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
472 std::string datastring(pkt->getString(0), pkt->getSize());
473 std::istringstream is(datastring, std::ios_base::binary);
477 u16 id = readU16(is);
481 std::string message = deSerializeString(is);
483 // Pass on to the environment
484 m_env.processActiveObjectMessage(id, message);
486 } catch (SerializationError &e) {
487 errorstream << "Client::handleCommand_ActiveObjectMessages: "
488 << "caught SerializationError: " << e.what() << std::endl;
492 void Client::handleCommand_Movement(NetworkPacket* pkt)
494 LocalPlayer *player = m_env.getLocalPlayer();
495 assert(player != NULL);
497 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
499 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
500 >> lf >> lfs >> ls >> g;
502 player->movement_acceleration_default = mad * BS;
503 player->movement_acceleration_air = maa * BS;
504 player->movement_acceleration_fast = maf * BS;
505 player->movement_speed_walk = msw * BS;
506 player->movement_speed_crouch = mscr * BS;
507 player->movement_speed_fast = msf * BS;
508 player->movement_speed_climb = mscl * BS;
509 player->movement_speed_jump = msj * BS;
510 player->movement_liquid_fluidity = lf * BS;
511 player->movement_liquid_fluidity_smooth = lfs * BS;
512 player->movement_liquid_sink = ls * BS;
513 player->movement_gravity = g * BS;
516 void Client::handleCommand_HP(NetworkPacket* pkt)
519 LocalPlayer *player = m_env.getLocalPlayer();
520 assert(player != NULL);
522 u8 oldhp = player->hp;
529 m_script->on_hp_modification(hp);
532 // Add to ClientEvent queue
534 event.type = CE_PLAYER_DAMAGE;
535 event.player_damage.amount = oldhp - hp;
536 m_client_event_queue.push(event);
540 void Client::handleCommand_Breath(NetworkPacket* pkt)
542 LocalPlayer *player = m_env.getLocalPlayer();
543 assert(player != NULL);
549 player->setBreath(breath);
552 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
554 LocalPlayer *player = m_env.getLocalPlayer();
555 assert(player != NULL);
560 *pkt >> pos >> pitch >> yaw;
562 player->got_teleported = true;
563 player->setPosition(pos);
565 infostream << "Client got TOCLIENT_MOVE_PLAYER"
566 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
567 << " pitch=" << pitch
572 Add to ClientEvent queue.
573 This has to be sent to the main program because otherwise
574 it would just force the pitch and yaw values to whatever
575 the camera points to.
578 event.type = CE_PLAYER_FORCE_MOVE;
579 event.player_force_move.pitch = pitch;
580 event.player_force_move.yaw = yaw;
581 m_client_event_queue.push(event);
583 // Ignore damage for a few seconds, so that the player doesn't
584 // get damage from falling on ground
585 m_ignore_damage_timer = 3.0;
588 void Client::handleCommand_PlayerItem(NetworkPacket* pkt)
590 warningstream << "Client: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
593 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
595 bool set_camera_point_target;
596 v3f camera_point_target;
598 *pkt >> set_camera_point_target;
599 *pkt >> camera_point_target;
602 event.type = CE_DEATHSCREEN;
603 event.deathscreen.set_camera_point_target = set_camera_point_target;
604 event.deathscreen.camera_point_target_x = camera_point_target.X;
605 event.deathscreen.camera_point_target_y = camera_point_target.Y;
606 event.deathscreen.camera_point_target_z = camera_point_target.Z;
607 m_client_event_queue.push(event);
610 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
616 infostream << "Client: Received media announcement: packet size: "
617 << pkt->getSize() << std::endl;
619 if (m_media_downloader == NULL ||
620 m_media_downloader->isStarted()) {
621 const char *problem = m_media_downloader ?
622 "we already saw another announcement" :
623 "all media has been received already";
624 errorstream << "Client: Received media announcement but "
626 << " files=" << num_files
627 << " size=" << pkt->getSize() << std::endl;
631 // Mesh update thread must be stopped while
632 // updating content definitions
633 sanity_check(!m_mesh_update_thread.isRunning());
635 for (u16 i = 0; i < num_files; i++) {
636 std::string name, sha1_base64;
638 *pkt >> name >> sha1_base64;
640 std::string sha1_raw = base64_decode(sha1_base64);
641 m_media_downloader->addFile(name, sha1_raw);
650 while(!sf.at_end()) {
651 std::string baseurl = trim(sf.next(","));
653 m_media_downloader->addRemoteServer(baseurl);
656 catch(SerializationError& e) {
657 // not supported by server or turned off
660 m_media_downloader->step(this);
663 void Client::handleCommand_Media(NetworkPacket* pkt)
667 u16 total number of file bunches
668 u16 index of this bunch
669 u32 number of files in this bunch
681 *pkt >> num_bunches >> bunch_i >> num_files;
683 infostream << "Client: Received files: bunch " << bunch_i << "/"
684 << num_bunches << " files=" << num_files
685 << " size=" << pkt->getSize() << std::endl;
690 if (m_media_downloader == NULL ||
691 !m_media_downloader->isStarted()) {
692 const char *problem = m_media_downloader ?
693 "media has not been requested" :
694 "all media has been received already";
695 errorstream << "Client: Received media but "
697 << " bunch " << bunch_i << "/" << num_bunches
698 << " files=" << num_files
699 << " size=" << pkt->getSize() << std::endl;
703 // Mesh update thread must be stopped while
704 // updating content definitions
705 sanity_check(!m_mesh_update_thread.isRunning());
707 for (u32 i=0; i < num_files; i++) {
712 std::string data = pkt->readLongString();
714 m_media_downloader->conventionalTransferDone(
719 void Client::handleCommand_ToolDef(NetworkPacket* pkt)
721 warningstream << "Client: Ignoring TOCLIENT_TOOLDEF" << std::endl;
724 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
726 infostream << "Client: Received node definitions: packet size: "
727 << pkt->getSize() << std::endl;
729 // Mesh update thread must be stopped while
730 // updating content definitions
731 sanity_check(!m_mesh_update_thread.isRunning());
733 // Decompress node definitions
734 std::string datastring(pkt->getString(0), pkt->getSize());
735 std::istringstream is(datastring, std::ios_base::binary);
736 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
737 std::ostringstream tmp_os;
738 decompressZlib(tmp_is, tmp_os);
740 // Deserialize node definitions
741 std::istringstream tmp_is2(tmp_os.str());
742 m_nodedef->deSerialize(tmp_is2);
743 m_nodedef_received = true;
746 void Client::handleCommand_CraftItemDef(NetworkPacket* pkt)
748 warningstream << "Client: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
751 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
753 infostream << "Client: Received item definitions: packet size: "
754 << pkt->getSize() << std::endl;
756 // Mesh update thread must be stopped while
757 // updating content definitions
758 sanity_check(!m_mesh_update_thread.isRunning());
760 // Decompress item definitions
761 std::string datastring(pkt->getString(0), pkt->getSize());
762 std::istringstream is(datastring, std::ios_base::binary);
763 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
764 std::ostringstream tmp_os;
765 decompressZlib(tmp_is, tmp_os);
767 // Deserialize node definitions
768 std::istringstream tmp_is2(tmp_os.str());
769 m_itemdef->deSerialize(tmp_is2);
770 m_itemdef_received = true;
773 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
778 u8 type; // 0=local, 1=positional, 2=object
783 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
789 client_id = m_sound->playSound(name, loop, gain);
791 case 1: // positional
792 client_id = m_sound->playSoundAt(name, loop, gain, pos);
796 ClientActiveObject *cao = m_env.getActiveObject(object_id);
798 pos = cao->getPosition();
799 client_id = m_sound->playSoundAt(name, loop, gain, pos);
800 // TODO: Set up sound to move with object
807 if (client_id != -1) {
808 m_sounds_server_to_client[server_id] = client_id;
809 m_sounds_client_to_server[client_id] = server_id;
811 m_sounds_to_objects[client_id] = object_id;
815 void Client::handleCommand_StopSound(NetworkPacket* pkt)
821 UNORDERED_MAP<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
822 if (i != m_sounds_server_to_client.end()) {
823 int client_id = i->second;
824 m_sound->stopSound(client_id);
828 void Client::handleCommand_Privileges(NetworkPacket* pkt)
830 m_privileges.clear();
831 infostream << "Client: Privileges updated: ";
834 *pkt >> num_privileges;
836 for (u16 i = 0; i < num_privileges; i++) {
841 m_privileges.insert(priv);
842 infostream << priv << " ";
844 infostream << std::endl;
847 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
849 LocalPlayer *player = m_env.getLocalPlayer();
850 assert(player != NULL);
852 // Store formspec in LocalPlayer
853 player->inventory_formspec = pkt->readLongString();
856 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
858 std::string datastring(pkt->getString(0), pkt->getSize());
859 std::istringstream is(datastring, std::ios_base::binary);
861 std::string name = deSerializeString(is);
863 infostream << "Client: Detached inventory update: \"" << name
864 << "\"" << std::endl;
866 Inventory *inv = NULL;
867 if (m_detached_inventories.count(name) > 0)
868 inv = m_detached_inventories[name];
870 inv = new Inventory(m_itemdef);
871 m_detached_inventories[name] = inv;
873 inv->deSerialize(is);
876 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
878 std::string formspec = pkt->readLongString();
879 std::string formname;
884 event.type = CE_SHOW_FORMSPEC;
885 // pointer is required as event is a struct only!
886 // adding a std:string to a struct isn't possible
887 event.show_formspec.formspec = new std::string(formspec);
888 event.show_formspec.formname = new std::string(formname);
889 m_client_event_queue.push(event);
892 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
894 std::string datastring(pkt->getString(0), pkt->getSize());
895 std::istringstream is(datastring, std::ios_base::binary);
897 v3f pos = readV3F1000(is);
898 v3f vel = readV3F1000(is);
899 v3f acc = readV3F1000(is);
900 float expirationtime = readF1000(is);
901 float size = readF1000(is);
902 bool collisiondetection = readU8(is);
903 std::string texture = deSerializeLongString(is);
904 bool vertical = false;
905 bool collision_removal = false;
906 struct TileAnimationParams animation;
907 animation.type = TAT_NONE;
910 vertical = readU8(is);
911 collision_removal = readU8(is);
912 animation.deSerialize(is, m_proto_ver);
917 event.type = CE_SPAWN_PARTICLE;
918 event.spawn_particle.pos = new v3f (pos);
919 event.spawn_particle.vel = new v3f (vel);
920 event.spawn_particle.acc = new v3f (acc);
921 event.spawn_particle.expirationtime = expirationtime;
922 event.spawn_particle.size = size;
923 event.spawn_particle.collisiondetection = collisiondetection;
924 event.spawn_particle.collision_removal = collision_removal;
925 event.spawn_particle.vertical = vertical;
926 event.spawn_particle.texture = new std::string(texture);
927 event.spawn_particle.animation = animation;
928 event.spawn_particle.glow = glow;
930 m_client_event_queue.push(event);
933 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
947 bool collisiondetection;
950 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
951 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
952 >> maxsize >> collisiondetection;
954 std::string texture = pkt->readLongString();
958 bool vertical = false;
959 bool collision_removal = false;
960 struct TileAnimationParams animation;
961 animation.type = TAT_NONE;
966 *pkt >> collision_removal;
969 // This is horrible but required (why are there two ways to deserialize pkts?)
970 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
971 std::istringstream is(datastring, std::ios_base::binary);
972 animation.deSerialize(is, m_proto_ver);
977 event.type = CE_ADD_PARTICLESPAWNER;
978 event.add_particlespawner.amount = amount;
979 event.add_particlespawner.spawntime = spawntime;
980 event.add_particlespawner.minpos = new v3f (minpos);
981 event.add_particlespawner.maxpos = new v3f (maxpos);
982 event.add_particlespawner.minvel = new v3f (minvel);
983 event.add_particlespawner.maxvel = new v3f (maxvel);
984 event.add_particlespawner.minacc = new v3f (minacc);
985 event.add_particlespawner.maxacc = new v3f (maxacc);
986 event.add_particlespawner.minexptime = minexptime;
987 event.add_particlespawner.maxexptime = maxexptime;
988 event.add_particlespawner.minsize = minsize;
989 event.add_particlespawner.maxsize = maxsize;
990 event.add_particlespawner.collisiondetection = collisiondetection;
991 event.add_particlespawner.collision_removal = collision_removal;
992 event.add_particlespawner.attached_id = attached_id;
993 event.add_particlespawner.vertical = vertical;
994 event.add_particlespawner.texture = new std::string(texture);
995 event.add_particlespawner.id = id;
996 event.add_particlespawner.animation = animation;
997 event.add_particlespawner.glow = glow;
999 m_client_event_queue.push(event);
1003 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1008 // Modification set 13/03/15, 1 year of compat for protocol v24
1009 if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
1018 event.type = CE_DELETE_PARTICLESPAWNER;
1019 event.delete_particlespawner.id =
1020 (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1022 m_client_event_queue.push(event);
1025 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1027 std::string datastring(pkt->getString(0), pkt->getSize());
1028 std::istringstream is(datastring, std::ios_base::binary);
1044 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1045 >> dir >> align >> offset;
1049 catch(SerializationError &e) {};
1053 } catch(SerializationError &e) {};
1056 event.type = CE_HUDADD;
1057 event.hudadd.id = id;
1058 event.hudadd.type = type;
1059 event.hudadd.pos = new v2f(pos);
1060 event.hudadd.name = new std::string(name);
1061 event.hudadd.scale = new v2f(scale);
1062 event.hudadd.text = new std::string(text);
1063 event.hudadd.number = number;
1064 event.hudadd.item = item;
1065 event.hudadd.dir = dir;
1066 event.hudadd.align = new v2f(align);
1067 event.hudadd.offset = new v2f(offset);
1068 event.hudadd.world_pos = new v3f(world_pos);
1069 event.hudadd.size = new v2s32(size);
1070 m_client_event_queue.push(event);
1073 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1080 event.type = CE_HUDRM;
1081 event.hudrm.id = id;
1082 m_client_event_queue.push(event);
1085 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1097 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1098 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1100 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1102 else if (stat == HUD_STAT_WORLD_POS)
1104 else if (stat == HUD_STAT_SIZE )
1110 event.type = CE_HUDCHANGE;
1111 event.hudchange.id = id;
1112 event.hudchange.stat = (HudElementStat)stat;
1113 event.hudchange.v2fdata = new v2f(v2fdata);
1114 event.hudchange.v3fdata = new v3f(v3fdata);
1115 event.hudchange.sdata = new std::string(sdata);
1116 event.hudchange.data = intdata;
1117 event.hudchange.v2s32data = new v2s32(v2s32data);
1118 m_client_event_queue.push(event);
1121 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1125 *pkt >> flags >> mask;
1127 LocalPlayer *player = m_env.getLocalPlayer();
1128 assert(player != NULL);
1130 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1132 player->hud_flags &= ~mask;
1133 player->hud_flags |= flags;
1135 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1137 // Hide minimap if it has been disabled by the server
1138 if (m_minimap_disabled_by_server && was_minimap_visible) {
1139 // defers a minimap update, therefore only call it if really
1140 // needed, by checking that minimap was visible before
1141 m_mapper->setMinimapMode(MINIMAP_MODE_OFF);
1145 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1147 u16 param; std::string value;
1149 *pkt >> param >> value;
1151 LocalPlayer *player = m_env.getLocalPlayer();
1152 assert(player != NULL);
1154 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1155 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1156 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1157 player->hud_hotbar_itemcount = hotbar_itemcount;
1159 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1160 player->hotbar_image = value;
1162 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1163 player->hotbar_selected_image = value;
1167 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1169 std::string datastring(pkt->getString(0), pkt->getSize());
1170 std::istringstream is(datastring, std::ios_base::binary);
1172 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1173 std::string *type = new std::string(deSerializeString(is));
1174 u16 count = readU16(is);
1175 std::vector<std::string> *params = new std::vector<std::string>;
1177 for (size_t i = 0; i < count; i++)
1178 params->push_back(deSerializeString(is));
1181 event.type = CE_SET_SKY;
1182 event.set_sky.bgcolor = bgcolor;
1183 event.set_sky.type = type;
1184 event.set_sky.params = params;
1185 m_client_event_queue.push(event);
1188 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1191 u16 day_night_ratio_u;
1193 *pkt >> do_override >> day_night_ratio_u;
1195 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1198 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1199 event.override_day_night_ratio.do_override = do_override;
1200 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1201 m_client_event_queue.push(event);
1204 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1206 LocalPlayer *player = m_env.getLocalPlayer();
1207 assert(player != NULL);
1209 *pkt >> player->local_animations[0];
1210 *pkt >> player->local_animations[1];
1211 *pkt >> player->local_animations[2];
1212 *pkt >> player->local_animations[3];
1213 *pkt >> player->local_animation_speed;
1216 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1218 LocalPlayer *player = m_env.getLocalPlayer();
1219 assert(player != NULL);
1221 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1224 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1226 if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1227 && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1228 errorstream << "Client: Recieved SRP S_B login message,"
1229 << " but wasn't supposed to (chosen_mech="
1230 << m_chosen_auth_mech << ")." << std::endl;
1236 SRPUser *usr = (SRPUser *) m_auth_data;
1241 infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1243 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1244 (const unsigned char *) B.c_str(), B.size(),
1245 (unsigned char **) &bytes_M, &len_M);
1248 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1252 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1253 resp_pkt << std::string(bytes_M, len_M);