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 Player *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 Player *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 Player *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 Player *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 Player *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 Player *player = m_env.getLocalPlayer();
536 assert(player != NULL);
542 player->setBreath(breath);
545 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
547 Player *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);
637 std::vector<std::string> remote_media;
644 while(!sf.at_end()) {
645 std::string baseurl = trim(sf.next(","));
647 m_media_downloader->addRemoteServer(baseurl);
650 catch(SerializationError& e) {
651 // not supported by server or turned off
654 m_media_downloader->step(this);
657 void Client::handleCommand_Media(NetworkPacket* pkt)
661 u16 total number of file bunches
662 u16 index of this bunch
663 u32 number of files in this bunch
675 *pkt >> num_bunches >> bunch_i >> num_files;
677 infostream << "Client: Received files: bunch " << bunch_i << "/"
678 << num_bunches << " files=" << num_files
679 << " size=" << pkt->getSize() << std::endl;
684 if (m_media_downloader == NULL ||
685 !m_media_downloader->isStarted()) {
686 const char *problem = m_media_downloader ?
687 "media has not been requested" :
688 "all media has been received already";
689 errorstream << "Client: Received media but "
691 << " bunch " << bunch_i << "/" << num_bunches
692 << " files=" << num_files
693 << " size=" << pkt->getSize() << std::endl;
697 // Mesh update thread must be stopped while
698 // updating content definitions
699 sanity_check(!m_mesh_update_thread.isRunning());
701 for (u32 i=0; i < num_files; i++) {
706 std::string data = pkt->readLongString();
708 m_media_downloader->conventionalTransferDone(
713 void Client::handleCommand_ToolDef(NetworkPacket* pkt)
715 warningstream << "Client: Ignoring TOCLIENT_TOOLDEF" << std::endl;
718 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
720 infostream << "Client: Received node definitions: packet size: "
721 << pkt->getSize() << std::endl;
723 // Mesh update thread must be stopped while
724 // updating content definitions
725 sanity_check(!m_mesh_update_thread.isRunning());
727 // Decompress node definitions
728 std::string datastring(pkt->getString(0), pkt->getSize());
729 std::istringstream is(datastring, std::ios_base::binary);
730 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
731 std::ostringstream tmp_os;
732 decompressZlib(tmp_is, tmp_os);
734 // Deserialize node definitions
735 std::istringstream tmp_is2(tmp_os.str());
736 m_nodedef->deSerialize(tmp_is2);
737 m_nodedef_received = true;
740 void Client::handleCommand_CraftItemDef(NetworkPacket* pkt)
742 warningstream << "Client: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
745 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
747 infostream << "Client: Received item definitions: packet size: "
748 << pkt->getSize() << std::endl;
750 // Mesh update thread must be stopped while
751 // updating content definitions
752 sanity_check(!m_mesh_update_thread.isRunning());
754 // Decompress item definitions
755 std::string datastring(pkt->getString(0), pkt->getSize());
756 std::istringstream is(datastring, std::ios_base::binary);
757 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
758 std::ostringstream tmp_os;
759 decompressZlib(tmp_is, tmp_os);
761 // Deserialize node definitions
762 std::istringstream tmp_is2(tmp_os.str());
763 m_itemdef->deSerialize(tmp_is2);
764 m_itemdef_received = true;
767 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
772 u8 type; // 0=local, 1=positional, 2=object
777 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
783 client_id = m_sound->playSound(name, loop, gain);
785 case 1: // positional
786 client_id = m_sound->playSoundAt(name, loop, gain, pos);
790 ClientActiveObject *cao = m_env.getActiveObject(object_id);
792 pos = cao->getPosition();
793 client_id = m_sound->playSoundAt(name, loop, gain, pos);
794 // TODO: Set up sound to move with object
801 if (client_id != -1) {
802 m_sounds_server_to_client[server_id] = client_id;
803 m_sounds_client_to_server[client_id] = server_id;
805 m_sounds_to_objects[client_id] = object_id;
809 void Client::handleCommand_StopSound(NetworkPacket* pkt)
815 std::map<s32, int>::iterator i =
816 m_sounds_server_to_client.find(server_id);
818 if (i != m_sounds_server_to_client.end()) {
819 int client_id = i->second;
820 m_sound->stopSound(client_id);
824 void Client::handleCommand_Privileges(NetworkPacket* pkt)
826 m_privileges.clear();
827 infostream << "Client: Privileges updated: ";
830 *pkt >> num_privileges;
832 for (u16 i = 0; i < num_privileges; i++) {
837 m_privileges.insert(priv);
838 infostream << priv << " ";
840 infostream << std::endl;
843 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
845 Player *player = m_env.getLocalPlayer();
846 assert(player != NULL);
848 // Store formspec in LocalPlayer
849 player->inventory_formspec = pkt->readLongString();
852 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
854 std::string datastring(pkt->getString(0), pkt->getSize());
855 std::istringstream is(datastring, std::ios_base::binary);
857 std::string name = deSerializeString(is);
859 infostream << "Client: Detached inventory update: \"" << name
860 << "\"" << std::endl;
862 Inventory *inv = NULL;
863 if (m_detached_inventories.count(name) > 0)
864 inv = m_detached_inventories[name];
866 inv = new Inventory(m_itemdef);
867 m_detached_inventories[name] = inv;
869 inv->deSerialize(is);
872 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
874 std::string formspec = pkt->readLongString();
875 std::string formname;
880 event.type = CE_SHOW_FORMSPEC;
881 // pointer is required as event is a struct only!
882 // adding a std:string to a struct isn't possible
883 event.show_formspec.formspec = new std::string(formspec);
884 event.show_formspec.formname = new std::string(formname);
885 m_client_event_queue.push(event);
888 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
890 std::string datastring(pkt->getString(0), pkt->getSize());
891 std::istringstream is(datastring, std::ios_base::binary);
893 v3f pos = readV3F1000(is);
894 v3f vel = readV3F1000(is);
895 v3f acc = readV3F1000(is);
896 float expirationtime = readF1000(is);
897 float size = readF1000(is);
898 bool collisiondetection = readU8(is);
899 std::string texture = deSerializeLongString(is);
900 bool vertical = false;
902 vertical = readU8(is);
906 event.type = CE_SPAWN_PARTICLE;
907 event.spawn_particle.pos = new v3f (pos);
908 event.spawn_particle.vel = new v3f (vel);
909 event.spawn_particle.acc = new v3f (acc);
910 event.spawn_particle.expirationtime = expirationtime;
911 event.spawn_particle.size = size;
912 event.spawn_particle.collisiondetection = collisiondetection;
913 event.spawn_particle.vertical = vertical;
914 event.spawn_particle.texture = new std::string(texture);
916 m_client_event_queue.push(event);
919 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
933 bool collisiondetection;
936 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
937 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
938 >> maxsize >> collisiondetection;
940 std::string texture = pkt->readLongString();
944 bool vertical = false;
950 event.type = CE_ADD_PARTICLESPAWNER;
951 event.add_particlespawner.amount = amount;
952 event.add_particlespawner.spawntime = spawntime;
953 event.add_particlespawner.minpos = new v3f (minpos);
954 event.add_particlespawner.maxpos = new v3f (maxpos);
955 event.add_particlespawner.minvel = new v3f (minvel);
956 event.add_particlespawner.maxvel = new v3f (maxvel);
957 event.add_particlespawner.minacc = new v3f (minacc);
958 event.add_particlespawner.maxacc = new v3f (maxacc);
959 event.add_particlespawner.minexptime = minexptime;
960 event.add_particlespawner.maxexptime = maxexptime;
961 event.add_particlespawner.minsize = minsize;
962 event.add_particlespawner.maxsize = maxsize;
963 event.add_particlespawner.collisiondetection = collisiondetection;
964 event.add_particlespawner.vertical = vertical;
965 event.add_particlespawner.texture = new std::string(texture);
966 event.add_particlespawner.id = id;
968 m_client_event_queue.push(event);
972 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
977 // Modification set 13/03/15, 1 year of compat for protocol v24
978 if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
987 event.type = CE_DELETE_PARTICLESPAWNER;
988 event.delete_particlespawner.id =
989 (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
991 m_client_event_queue.push(event);
994 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
996 std::string datastring(pkt->getString(0), pkt->getSize());
997 std::istringstream is(datastring, std::ios_base::binary);
1013 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1014 >> dir >> align >> offset;
1018 catch(SerializationError &e) {};
1022 } catch(SerializationError &e) {};
1025 event.type = CE_HUDADD;
1026 event.hudadd.id = id;
1027 event.hudadd.type = type;
1028 event.hudadd.pos = new v2f(pos);
1029 event.hudadd.name = new std::string(name);
1030 event.hudadd.scale = new v2f(scale);
1031 event.hudadd.text = new std::string(text);
1032 event.hudadd.number = number;
1033 event.hudadd.item = item;
1034 event.hudadd.dir = dir;
1035 event.hudadd.align = new v2f(align);
1036 event.hudadd.offset = new v2f(offset);
1037 event.hudadd.world_pos = new v3f(world_pos);
1038 event.hudadd.size = new v2s32(size);
1039 m_client_event_queue.push(event);
1042 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1049 event.type = CE_HUDRM;
1050 event.hudrm.id = id;
1051 m_client_event_queue.push(event);
1054 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1066 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1067 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1069 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1071 else if (stat == HUD_STAT_WORLD_POS)
1073 else if (stat == HUD_STAT_SIZE )
1079 event.type = CE_HUDCHANGE;
1080 event.hudchange.id = id;
1081 event.hudchange.stat = (HudElementStat)stat;
1082 event.hudchange.v2fdata = new v2f(v2fdata);
1083 event.hudchange.v3fdata = new v3f(v3fdata);
1084 event.hudchange.sdata = new std::string(sdata);
1085 event.hudchange.data = intdata;
1086 event.hudchange.v2s32data = new v2s32(v2s32data);
1087 m_client_event_queue.push(event);
1090 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1094 *pkt >> flags >> mask;
1096 Player *player = m_env.getLocalPlayer();
1097 assert(player != NULL);
1099 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1101 player->hud_flags &= ~mask;
1102 player->hud_flags |= flags;
1104 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1106 // Hide minimap if it has been disabled by the server
1107 if (m_minimap_disabled_by_server && was_minimap_visible) {
1108 // defers a minimap update, therefore only call it if really
1109 // needed, by checking that minimap was visible before
1110 m_mapper->setMinimapMode(MINIMAP_MODE_OFF);
1114 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1116 u16 param; std::string value;
1118 *pkt >> param >> value;
1120 Player *player = m_env.getLocalPlayer();
1121 assert(player != NULL);
1123 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1124 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1125 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1126 player->hud_hotbar_itemcount = hotbar_itemcount;
1128 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1129 ((LocalPlayer *) player)->hotbar_image = value;
1131 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1132 ((LocalPlayer *) player)->hotbar_selected_image = value;
1136 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1138 std::string datastring(pkt->getString(0), pkt->getSize());
1139 std::istringstream is(datastring, std::ios_base::binary);
1141 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1142 std::string *type = new std::string(deSerializeString(is));
1143 u16 count = readU16(is);
1144 std::vector<std::string> *params = new std::vector<std::string>;
1146 for (size_t i = 0; i < count; i++)
1147 params->push_back(deSerializeString(is));
1150 event.type = CE_SET_SKY;
1151 event.set_sky.bgcolor = bgcolor;
1152 event.set_sky.type = type;
1153 event.set_sky.params = params;
1154 m_client_event_queue.push(event);
1157 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1160 u16 day_night_ratio_u;
1162 *pkt >> do_override >> day_night_ratio_u;
1164 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1167 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1168 event.override_day_night_ratio.do_override = do_override;
1169 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1170 m_client_event_queue.push(event);
1173 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1175 LocalPlayer *player = m_env.getLocalPlayer();
1176 assert(player != NULL);
1178 *pkt >> player->local_animations[0];
1179 *pkt >> player->local_animations[1];
1180 *pkt >> player->local_animations[2];
1181 *pkt >> player->local_animations[3];
1182 *pkt >> player->local_animation_speed;
1185 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1187 LocalPlayer *player = m_env.getLocalPlayer();
1188 assert(player != NULL);
1190 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1193 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1195 if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1196 && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1197 errorstream << "Client: Recieved SRP S_B login message,"
1198 << " but wasn't supposed to (chosen_mech="
1199 << m_chosen_auth_mech << ")." << std::endl;
1205 SRPUser *usr = (SRPUser *) m_auth_data;
1210 infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1212 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1213 (const unsigned char *) B.c_str(), B.size(),
1214 (unsigned char **) &bytes_M, &len_M);
1217 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1221 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1222 resp_pkt << std::string(bytes_M, len_M);