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 "util/serialize.h"
36 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
38 infostream << "Got deprecated command "
39 << toClientCommandTable[pkt->getCommand()].name << " from peer "
40 << pkt->getPeerId() << "!" << std::endl;
43 void Client::handleCommand_Hello(NetworkPacket* pkt)
45 if (pkt->getSize() < 1)
52 std::string username_legacy; // for case insensitivity
53 *pkt >> serialization_ver >> compression_mode >> proto_ver
54 >> auth_mechs >> username_legacy;
56 // Chose an auth method we support
57 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
59 infostream << "Client: TOCLIENT_HELLO received with "
60 << "serialization_ver=" << (u32)serialization_ver
61 << ", auth_mechs=" << auth_mechs
62 << ", proto_ver=" << proto_ver
63 << ", compression_mode=" << compression_mode
64 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
66 if (!ser_ver_supported(serialization_ver)) {
67 infostream << "Client: TOCLIENT_HELLO: Server sent "
68 << "unsupported ser_fmt_ver"<< std::endl;
72 m_server_ser_ver = serialization_ver;
73 m_proto_ver = proto_ver;
75 //TODO verify that username_legacy matches sent username, only
76 // differs in casing (make both uppercase and compare)
77 // This is only neccessary though when we actually want to add casing support
79 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
80 // we recieved a TOCLIENT_HELLO while auth was already going on
81 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
82 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
83 if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP)
84 || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) {
85 srp_user_delete((SRPUser *) m_auth_data);
90 // Authenticate using that method, or abort if there wasn't any method found
91 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
92 startAuth(chosen_auth_mechanism);
94 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
95 m_access_denied = true;
96 m_access_denied_reason = "Unknown";
102 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
107 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
108 >> m_sudo_auth_methods;
110 playerpos -= v3f(0, BS / 2, 0);
112 // Set player position
113 LocalPlayer *player = m_env.getLocalPlayer();
114 assert(player != NULL);
115 player->setPosition(playerpos);
117 infostream << "Client: received map seed: " << m_map_seed << std::endl;
118 infostream << "Client: received recommended send interval "
119 << m_recommended_send_interval<<std::endl;
122 NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
127 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
131 m_password = m_new_password;
133 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
135 // send packet to actually set the password
136 startAuth(AUTH_MECHANISM_FIRST_SRP);
139 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
141 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
143 m_chat_queue.push(L"Password change denied. Password NOT changed.");
144 // reset everything and be sad
147 void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
149 if (pkt->getSize() < 1)
153 *pkt >> server_ser_ver;
155 infostream << "Client: TOCLIENT_INIT_LEGACY received with "
156 "server_ser_ver=" << ((int)server_ser_ver & 0xff) << std::endl;
158 if (!ser_ver_supported(server_ser_ver)) {
159 infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
160 << "unsupported ser_fmt_ver"<< std::endl;
164 m_server_ser_ver = server_ser_ver;
166 // We can be totally wrong with this guess
167 // but we only need some value < 25.
170 // Get player position
171 v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
172 if (pkt->getSize() >= 1 + 6) {
173 *pkt >> playerpos_s16;
175 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
178 // Set player position
179 LocalPlayer *player = m_env.getLocalPlayer();
180 assert(player != NULL);
181 player->setPosition(playerpos_f);
183 if (pkt->getSize() >= 1 + 6 + 8) {
186 infostream << "Client: received map seed: " << m_map_seed << std::endl;
189 if (pkt->getSize() >= 1 + 6 + 8 + 4) {
190 *pkt >> m_recommended_send_interval;
191 infostream << "Client: received recommended send interval "
192 << m_recommended_send_interval<<std::endl;
196 NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
202 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
204 // The server didn't like our password. Note, this needs
205 // to be processed even if the serialisation format has
206 // not been agreed yet, the same as TOCLIENT_INIT.
207 m_access_denied = true;
208 m_access_denied_reason = "Unknown";
210 if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
211 if (pkt->getSize() < 1)
214 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
216 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
217 denyCode == SERVER_ACCESSDENIED_CRASH) {
218 *pkt >> m_access_denied_reason;
219 if (m_access_denied_reason == "") {
220 m_access_denied_reason = accessDeniedStrings[denyCode];
224 m_access_denied_reconnect = reconnect & 1;
225 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
226 *pkt >> m_access_denied_reason;
227 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
228 m_access_denied_reason = accessDeniedStrings[denyCode];
230 // Allow us to add new error messages to the
231 // protocol without raising the protocol version, if we want to.
232 // Until then (which may be never), this is outside
233 // of the defined protocol.
234 *pkt >> m_access_denied_reason;
235 if (m_access_denied_reason == "") {
236 m_access_denied_reason = "Unknown";
240 // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
241 // for compat with old clients
243 if (pkt->getSize() >= 2) {
244 std::wstring wide_reason;
246 m_access_denied_reason = wide_to_utf8(wide_reason);
251 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
253 if (pkt->getSize() < 6)
261 void Client::handleCommand_AddNode(NetworkPacket* pkt)
263 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
270 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
272 bool remove_metadata = true;
273 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
274 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
275 remove_metadata = false;
278 addNode(p, n, remove_metadata);
280 void Client::handleCommand_BlockData(NetworkPacket* pkt)
282 // Ignore too small packet
283 if (pkt->getSize() < 6)
289 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
290 std::istringstream istr(datastring, std::ios_base::binary);
296 sector = m_env.getMap().emergeSector(p2d);
298 assert(sector->getPos() == p2d);
300 block = sector->getBlockNoCreateNoEx(p.Y);
303 Update an existing block
305 block->deSerialize(istr, m_server_ser_ver, false);
306 block->deSerializeNetworkSpecific(istr);
312 block = new MapBlock(&m_env.getMap(), p, this);
313 block->deSerialize(istr, m_server_ser_ver, false);
314 block->deSerializeNetworkSpecific(istr);
315 sector->insertBlock(block);
319 ServerMap::saveBlock(block, m_localdb);
323 Add it to mesh update queue and set it to be acknowledged after update.
325 addUpdateMeshTaskWithEdge(p, true);
328 void Client::handleCommand_Inventory(NetworkPacket* pkt)
330 if (pkt->getSize() < 1)
333 std::string datastring(pkt->getString(0), pkt->getSize());
334 std::istringstream is(datastring, std::ios_base::binary);
336 LocalPlayer *player = m_env.getLocalPlayer();
337 assert(player != NULL);
339 player->inventory.deSerialize(is);
341 m_inventory_updated = true;
343 delete m_inventory_from_server;
344 m_inventory_from_server = new Inventory(player->inventory);
345 m_inventory_from_server_age = 0.0;
348 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
350 if (pkt->getSize() < 2)
357 time_of_day = time_of_day % 24000;
358 float time_speed = 0;
360 if (pkt->getSize() >= 2 + 4) {
364 // Old message; try to approximate speed of time by ourselves
365 float time_of_day_f = (float)time_of_day / 24000.0;
366 float tod_diff_f = 0;
368 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
369 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
371 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
373 m_last_time_of_day_f = time_of_day_f;
374 float time_diff = m_time_of_day_update_timer;
375 m_time_of_day_update_timer = 0;
377 if (m_time_of_day_set) {
378 time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
379 infostream << "Client: Measured time_of_day speed (old format): "
380 << time_speed << " tod_diff_f=" << tod_diff_f
381 << " time_diff=" << time_diff << std::endl;
385 // Update environment
386 m_env.setTimeOfDay(time_of_day);
387 m_env.setTimeOfDaySpeed(time_speed);
388 m_time_of_day_set = true;
390 u32 dr = m_env.getDayNightRatio();
391 infostream << "Client: time_of_day=" << time_of_day
392 << " time_speed=" << time_speed
393 << " dr=" << dr << std::endl;
396 void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
407 std::wstring message;
408 for (u32 i = 0; i < len; i++) {
410 message += (wchar_t)read_wchar;
413 m_chat_queue.push(message);
416 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
419 u16 count of removed objects
420 for all removed objects {
423 u16 count of added objects
424 for all added objects {
427 u32 initialization data length
428 string initialization data
434 u16 removed_count, added_count, id;
436 // Read removed objects
437 *pkt >> removed_count;
439 for (u16 i = 0; i < removed_count; i++) {
441 m_env.removeActiveObject(id);
444 // Read added objects
447 for (u16 i = 0; i < added_count; i++) {
449 m_env.addActiveObject(id, type, pkt->readLongString());
451 } catch (PacketError &e) {
452 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
453 << ". The packet is unreliable, ignoring" << std::endl;
457 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
467 std::string datastring(pkt->getString(0), pkt->getSize());
468 std::istringstream is(datastring, std::ios_base::binary);
472 u16 id = readU16(is);
476 std::string message = deSerializeString(is);
478 // Pass on to the environment
479 m_env.processActiveObjectMessage(id, message);
481 } catch (SerializationError &e) {
482 errorstream << "Client::handleCommand_ActiveObjectMessages: "
483 << "caught SerializationError: " << e.what() << std::endl;
487 void Client::handleCommand_Movement(NetworkPacket* pkt)
489 LocalPlayer *player = m_env.getLocalPlayer();
490 assert(player != NULL);
492 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
494 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
495 >> lf >> lfs >> ls >> g;
497 player->movement_acceleration_default = mad * BS;
498 player->movement_acceleration_air = maa * BS;
499 player->movement_acceleration_fast = maf * BS;
500 player->movement_speed_walk = msw * BS;
501 player->movement_speed_crouch = mscr * BS;
502 player->movement_speed_fast = msf * BS;
503 player->movement_speed_climb = mscl * BS;
504 player->movement_speed_jump = msj * BS;
505 player->movement_liquid_fluidity = lf * BS;
506 player->movement_liquid_fluidity_smooth = lfs * BS;
507 player->movement_liquid_sink = ls * BS;
508 player->movement_gravity = g * BS;
511 void Client::handleCommand_HP(NetworkPacket* pkt)
514 LocalPlayer *player = m_env.getLocalPlayer();
515 assert(player != NULL);
517 u8 oldhp = player->hp;
525 // Add to ClientEvent queue
527 event.type = CE_PLAYER_DAMAGE;
528 event.player_damage.amount = oldhp - hp;
529 m_client_event_queue.push(event);
533 void Client::handleCommand_Breath(NetworkPacket* pkt)
535 LocalPlayer *player = m_env.getLocalPlayer();
536 assert(player != NULL);
542 player->setBreath(breath);
545 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
547 LocalPlayer *player = m_env.getLocalPlayer();
548 assert(player != NULL);
553 *pkt >> pos >> pitch >> yaw;
555 player->got_teleported = true;
556 player->setPosition(pos);
558 infostream << "Client got TOCLIENT_MOVE_PLAYER"
559 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
560 << " pitch=" << pitch
565 Add to ClientEvent queue.
566 This has to be sent to the main program because otherwise
567 it would just force the pitch and yaw values to whatever
568 the camera points to.
571 event.type = CE_PLAYER_FORCE_MOVE;
572 event.player_force_move.pitch = pitch;
573 event.player_force_move.yaw = yaw;
574 m_client_event_queue.push(event);
576 // Ignore damage for a few seconds, so that the player doesn't
577 // get damage from falling on ground
578 m_ignore_damage_timer = 3.0;
581 void Client::handleCommand_PlayerItem(NetworkPacket* pkt)
583 warningstream << "Client: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
586 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
588 bool set_camera_point_target;
589 v3f camera_point_target;
591 *pkt >> set_camera_point_target;
592 *pkt >> camera_point_target;
595 event.type = CE_DEATHSCREEN;
596 event.deathscreen.set_camera_point_target = set_camera_point_target;
597 event.deathscreen.camera_point_target_x = camera_point_target.X;
598 event.deathscreen.camera_point_target_y = camera_point_target.Y;
599 event.deathscreen.camera_point_target_z = camera_point_target.Z;
600 m_client_event_queue.push(event);
603 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
609 infostream << "Client: Received media announcement: packet size: "
610 << pkt->getSize() << std::endl;
612 if (m_media_downloader == NULL ||
613 m_media_downloader->isStarted()) {
614 const char *problem = m_media_downloader ?
615 "we already saw another announcement" :
616 "all media has been received already";
617 errorstream << "Client: Received media announcement but "
619 << " files=" << num_files
620 << " size=" << pkt->getSize() << std::endl;
624 // Mesh update thread must be stopped while
625 // updating content definitions
626 sanity_check(!m_mesh_update_thread.isRunning());
628 for (u16 i = 0; i < num_files; i++) {
629 std::string name, sha1_base64;
631 *pkt >> name >> sha1_base64;
633 std::string sha1_raw = base64_decode(sha1_base64);
634 m_media_downloader->addFile(name, sha1_raw);
643 while(!sf.at_end()) {
644 std::string baseurl = trim(sf.next(","));
646 m_media_downloader->addRemoteServer(baseurl);
649 catch(SerializationError& e) {
650 // not supported by server or turned off
653 m_media_downloader->step(this);
656 void Client::handleCommand_Media(NetworkPacket* pkt)
660 u16 total number of file bunches
661 u16 index of this bunch
662 u32 number of files in this bunch
674 *pkt >> num_bunches >> bunch_i >> num_files;
676 infostream << "Client: Received files: bunch " << bunch_i << "/"
677 << num_bunches << " files=" << num_files
678 << " size=" << pkt->getSize() << std::endl;
683 if (m_media_downloader == NULL ||
684 !m_media_downloader->isStarted()) {
685 const char *problem = m_media_downloader ?
686 "media has not been requested" :
687 "all media has been received already";
688 errorstream << "Client: Received media but "
690 << " bunch " << bunch_i << "/" << num_bunches
691 << " files=" << num_files
692 << " size=" << pkt->getSize() << std::endl;
696 // Mesh update thread must be stopped while
697 // updating content definitions
698 sanity_check(!m_mesh_update_thread.isRunning());
700 for (u32 i=0; i < num_files; i++) {
705 std::string data = pkt->readLongString();
707 m_media_downloader->conventionalTransferDone(
712 void Client::handleCommand_ToolDef(NetworkPacket* pkt)
714 warningstream << "Client: Ignoring TOCLIENT_TOOLDEF" << std::endl;
717 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
719 infostream << "Client: Received node definitions: packet size: "
720 << pkt->getSize() << std::endl;
722 // Mesh update thread must be stopped while
723 // updating content definitions
724 sanity_check(!m_mesh_update_thread.isRunning());
726 // Decompress node definitions
727 std::string datastring(pkt->getString(0), pkt->getSize());
728 std::istringstream is(datastring, std::ios_base::binary);
729 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
730 std::ostringstream tmp_os;
731 decompressZlib(tmp_is, tmp_os);
733 // Deserialize node definitions
734 std::istringstream tmp_is2(tmp_os.str());
735 m_nodedef->deSerialize(tmp_is2);
736 m_nodedef_received = true;
739 void Client::handleCommand_CraftItemDef(NetworkPacket* pkt)
741 warningstream << "Client: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
744 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
746 infostream << "Client: Received item definitions: packet size: "
747 << pkt->getSize() << std::endl;
749 // Mesh update thread must be stopped while
750 // updating content definitions
751 sanity_check(!m_mesh_update_thread.isRunning());
753 // Decompress item definitions
754 std::string datastring(pkt->getString(0), pkt->getSize());
755 std::istringstream is(datastring, std::ios_base::binary);
756 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
757 std::ostringstream tmp_os;
758 decompressZlib(tmp_is, tmp_os);
760 // Deserialize node definitions
761 std::istringstream tmp_is2(tmp_os.str());
762 m_itemdef->deSerialize(tmp_is2);
763 m_itemdef_received = true;
766 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
771 u8 type; // 0=local, 1=positional, 2=object
776 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
782 client_id = m_sound->playSound(name, loop, gain);
784 case 1: // positional
785 client_id = m_sound->playSoundAt(name, loop, gain, pos);
789 ClientActiveObject *cao = m_env.getActiveObject(object_id);
791 pos = cao->getPosition();
792 client_id = m_sound->playSoundAt(name, loop, gain, pos);
793 // TODO: Set up sound to move with object
800 if (client_id != -1) {
801 m_sounds_server_to_client[server_id] = client_id;
802 m_sounds_client_to_server[client_id] = server_id;
804 m_sounds_to_objects[client_id] = object_id;
808 void Client::handleCommand_StopSound(NetworkPacket* pkt)
814 UNORDERED_MAP<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
815 if (i != m_sounds_server_to_client.end()) {
816 int client_id = i->second;
817 m_sound->stopSound(client_id);
821 void Client::handleCommand_Privileges(NetworkPacket* pkt)
823 m_privileges.clear();
824 infostream << "Client: Privileges updated: ";
827 *pkt >> num_privileges;
829 for (u16 i = 0; i < num_privileges; i++) {
834 m_privileges.insert(priv);
835 infostream << priv << " ";
837 infostream << std::endl;
840 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
842 LocalPlayer *player = m_env.getLocalPlayer();
843 assert(player != NULL);
845 // Store formspec in LocalPlayer
846 player->inventory_formspec = pkt->readLongString();
849 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
851 std::string datastring(pkt->getString(0), pkt->getSize());
852 std::istringstream is(datastring, std::ios_base::binary);
854 std::string name = deSerializeString(is);
856 infostream << "Client: Detached inventory update: \"" << name
857 << "\"" << std::endl;
859 Inventory *inv = NULL;
860 if (m_detached_inventories.count(name) > 0)
861 inv = m_detached_inventories[name];
863 inv = new Inventory(m_itemdef);
864 m_detached_inventories[name] = inv;
866 inv->deSerialize(is);
869 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
871 std::string formspec = pkt->readLongString();
872 std::string formname;
877 event.type = CE_SHOW_FORMSPEC;
878 // pointer is required as event is a struct only!
879 // adding a std:string to a struct isn't possible
880 event.show_formspec.formspec = new std::string(formspec);
881 event.show_formspec.formname = new std::string(formname);
882 m_client_event_queue.push(event);
885 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
887 std::string datastring(pkt->getString(0), pkt->getSize());
888 std::istringstream is(datastring, std::ios_base::binary);
890 v3f pos = readV3F1000(is);
891 v3f vel = readV3F1000(is);
892 v3f acc = readV3F1000(is);
893 float expirationtime = readF1000(is);
894 float size = readF1000(is);
895 bool collisiondetection = readU8(is);
896 std::string texture = deSerializeLongString(is);
897 bool vertical = false;
898 bool collision_removal = false;
899 u32 material_type_param = 0;
900 AnimationType animation_type = AT_NONE;
901 u16 vertical_frame_num = 1;
902 u16 horizontal_frame_num = 1;
904 float frame_length = -1;
905 bool loop_animation = true;
908 vertical = readU8(is);
909 collision_removal = readU8(is);
910 material_type_param = readU32(is);
911 animation_type = (AnimationType)readU8(is);
912 vertical_frame_num = readU16(is);
913 horizontal_frame_num = readU16(is);
914 first_frame = readU16(is);
915 frame_length = readF1000(is);
916 loop_animation = readU8(is);
921 event.type = CE_SPAWN_PARTICLE;
922 event.spawn_particle.pos = new v3f (pos);
923 event.spawn_particle.vel = new v3f (vel);
924 event.spawn_particle.acc = new v3f (acc);
925 event.spawn_particle.expirationtime = expirationtime;
926 event.spawn_particle.size = size;
927 event.spawn_particle.collisiondetection = collisiondetection;
928 event.spawn_particle.collision_removal = collision_removal;
929 event.spawn_particle.vertical = vertical;
930 event.spawn_particle.texture = new std::string(texture);
931 event.spawn_particle.material_type_param = material_type_param;
932 event.spawn_particle.animation_type = animation_type;
933 event.spawn_particle.vertical_frame_num = vertical_frame_num;
934 event.spawn_particle.horizontal_frame_num = horizontal_frame_num;
935 event.spawn_particle.first_frame = first_frame;
936 event.spawn_particle.frame_length = frame_length;
937 event.spawn_particle.loop_animation = loop_animation;
938 event.spawn_particle.glow = glow;
939 m_client_event_queue.push(event);
942 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
956 bool collisiondetection;
958 u32 material_type_param = 0;
959 u8 animation_type = (u8)AT_NONE;
960 u16 vertical_frame_num = 1;
961 u16 horizontal_frame_num = 1;
962 u16 min_first_frame = 0;
963 u16 max_first_frame = 0;
964 float frame_length = -1;
965 bool loop_animation = true;
968 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
969 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
970 >> maxsize >> collisiondetection;
972 std::string texture = pkt->readLongString();
976 bool vertical = false;
977 bool collision_removal = false;
981 *pkt >> collision_removal;
983 *pkt >> material_type_param;
984 *pkt >> animation_type;
985 *pkt >> vertical_frame_num;
986 *pkt >> horizontal_frame_num;
987 *pkt >> min_first_frame;
988 *pkt >> max_first_frame;
989 *pkt >> frame_length;
990 *pkt >> loop_animation;
995 event.type = CE_ADD_PARTICLESPAWNER;
996 event.add_particlespawner.amount = amount;
997 event.add_particlespawner.spawntime = spawntime;
998 event.add_particlespawner.minpos = new v3f (minpos);
999 event.add_particlespawner.maxpos = new v3f (maxpos);
1000 event.add_particlespawner.minvel = new v3f (minvel);
1001 event.add_particlespawner.maxvel = new v3f (maxvel);
1002 event.add_particlespawner.minacc = new v3f (minacc);
1003 event.add_particlespawner.maxacc = new v3f (maxacc);
1004 event.add_particlespawner.minexptime = minexptime;
1005 event.add_particlespawner.maxexptime = maxexptime;
1006 event.add_particlespawner.minsize = minsize;
1007 event.add_particlespawner.maxsize = maxsize;
1008 event.add_particlespawner.collisiondetection = collisiondetection;
1009 event.add_particlespawner.collision_removal = collision_removal;
1010 event.add_particlespawner.attached_id = attached_id;
1011 event.add_particlespawner.vertical = vertical;
1012 event.add_particlespawner.texture = new std::string(texture);
1013 event.add_particlespawner.id = id;
1014 event.add_particlespawner.material_type_param = material_type_param;
1015 event.add_particlespawner.animation_type = (AnimationType)animation_type;
1016 event.add_particlespawner.vertical_frame_num = vertical_frame_num;
1017 event.add_particlespawner.horizontal_frame_num = horizontal_frame_num;
1018 event.add_particlespawner.min_first_frame = min_first_frame;
1019 event.add_particlespawner.max_first_frame = max_first_frame;
1020 event.add_particlespawner.frame_length = frame_length;
1021 event.add_particlespawner.loop_animation = loop_animation;
1022 event.add_particlespawner.glow = glow;
1024 m_client_event_queue.push(event);
1028 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1033 // Modification set 13/03/15, 1 year of compat for protocol v24
1034 if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
1043 event.type = CE_DELETE_PARTICLESPAWNER;
1044 event.delete_particlespawner.id =
1045 (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1047 m_client_event_queue.push(event);
1050 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1052 std::string datastring(pkt->getString(0), pkt->getSize());
1053 std::istringstream is(datastring, std::ios_base::binary);
1069 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1070 >> dir >> align >> offset;
1074 catch(SerializationError &e) {};
1078 } catch(SerializationError &e) {};
1081 event.type = CE_HUDADD;
1082 event.hudadd.id = id;
1083 event.hudadd.type = type;
1084 event.hudadd.pos = new v2f(pos);
1085 event.hudadd.name = new std::string(name);
1086 event.hudadd.scale = new v2f(scale);
1087 event.hudadd.text = new std::string(text);
1088 event.hudadd.number = number;
1089 event.hudadd.item = item;
1090 event.hudadd.dir = dir;
1091 event.hudadd.align = new v2f(align);
1092 event.hudadd.offset = new v2f(offset);
1093 event.hudadd.world_pos = new v3f(world_pos);
1094 event.hudadd.size = new v2s32(size);
1095 m_client_event_queue.push(event);
1098 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1105 event.type = CE_HUDRM;
1106 event.hudrm.id = id;
1107 m_client_event_queue.push(event);
1110 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1122 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1123 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1125 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1127 else if (stat == HUD_STAT_WORLD_POS)
1129 else if (stat == HUD_STAT_SIZE )
1135 event.type = CE_HUDCHANGE;
1136 event.hudchange.id = id;
1137 event.hudchange.stat = (HudElementStat)stat;
1138 event.hudchange.v2fdata = new v2f(v2fdata);
1139 event.hudchange.v3fdata = new v3f(v3fdata);
1140 event.hudchange.sdata = new std::string(sdata);
1141 event.hudchange.data = intdata;
1142 event.hudchange.v2s32data = new v2s32(v2s32data);
1143 m_client_event_queue.push(event);
1146 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1150 *pkt >> flags >> mask;
1152 LocalPlayer *player = m_env.getLocalPlayer();
1153 assert(player != NULL);
1155 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1157 player->hud_flags &= ~mask;
1158 player->hud_flags |= flags;
1160 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1162 // Hide minimap if it has been disabled by the server
1163 if (m_minimap_disabled_by_server && was_minimap_visible) {
1164 // defers a minimap update, therefore only call it if really
1165 // needed, by checking that minimap was visible before
1166 m_mapper->setMinimapMode(MINIMAP_MODE_OFF);
1170 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1172 u16 param; std::string value;
1174 *pkt >> param >> value;
1176 LocalPlayer *player = m_env.getLocalPlayer();
1177 assert(player != NULL);
1179 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1180 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1181 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1182 player->hud_hotbar_itemcount = hotbar_itemcount;
1184 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1185 player->hotbar_image = value;
1187 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1188 player->hotbar_selected_image = value;
1192 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1194 std::string datastring(pkt->getString(0), pkt->getSize());
1195 std::istringstream is(datastring, std::ios_base::binary);
1197 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1198 std::string *type = new std::string(deSerializeString(is));
1199 u16 count = readU16(is);
1200 std::vector<std::string> *params = new std::vector<std::string>;
1202 for (size_t i = 0; i < count; i++)
1203 params->push_back(deSerializeString(is));
1206 event.type = CE_SET_SKY;
1207 event.set_sky.bgcolor = bgcolor;
1208 event.set_sky.type = type;
1209 event.set_sky.params = params;
1210 m_client_event_queue.push(event);
1213 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1216 u16 day_night_ratio_u;
1218 *pkt >> do_override >> day_night_ratio_u;
1220 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1223 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1224 event.override_day_night_ratio.do_override = do_override;
1225 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1226 m_client_event_queue.push(event);
1229 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1231 LocalPlayer *player = m_env.getLocalPlayer();
1232 assert(player != NULL);
1234 *pkt >> player->local_animations[0];
1235 *pkt >> player->local_animations[1];
1236 *pkt >> player->local_animations[2];
1237 *pkt >> player->local_animations[3];
1238 *pkt >> player->local_animation_speed;
1241 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1243 LocalPlayer *player = m_env.getLocalPlayer();
1244 assert(player != NULL);
1246 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1249 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1251 if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1252 && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1253 errorstream << "Client: Recieved SRP S_B login message,"
1254 << " but wasn't supposed to (chosen_mech="
1255 << m_chosen_auth_mech << ")." << std::endl;
1261 SRPUser *usr = (SRPUser *) m_auth_data;
1266 infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1268 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1269 (const unsigned char *) B.c_str(), B.size(),
1270 (unsigned char **) &bytes_M, &len_M);
1273 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1277 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1278 resp_pkt << std::string(bytes_M, len_M);