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/scripting_client.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 (!moddingEnabled() || !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 if (moddingEnabled()) {
530 m_script->on_hp_modification(hp);
534 // Add to ClientEvent queue
536 event.type = CE_PLAYER_DAMAGE;
537 event.player_damage.amount = oldhp - hp;
538 m_client_event_queue.push(event);
542 void Client::handleCommand_Breath(NetworkPacket* pkt)
544 LocalPlayer *player = m_env.getLocalPlayer();
545 assert(player != NULL);
551 player->setBreath(breath);
554 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
556 LocalPlayer *player = m_env.getLocalPlayer();
557 assert(player != NULL);
562 *pkt >> pos >> pitch >> yaw;
564 player->setPosition(pos);
566 infostream << "Client got TOCLIENT_MOVE_PLAYER"
567 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
568 << " pitch=" << pitch
573 Add to ClientEvent queue.
574 This has to be sent to the main program because otherwise
575 it would just force the pitch and yaw values to whatever
576 the camera points to.
579 event.type = CE_PLAYER_FORCE_MOVE;
580 event.player_force_move.pitch = pitch;
581 event.player_force_move.yaw = yaw;
582 m_client_event_queue.push(event);
584 // Ignore damage for a few seconds, so that the player doesn't
585 // get damage from falling on ground
586 m_ignore_damage_timer = 3.0;
589 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
591 bool set_camera_point_target;
592 v3f camera_point_target;
594 *pkt >> set_camera_point_target;
595 *pkt >> camera_point_target;
598 event.type = CE_DEATHSCREEN;
599 event.deathscreen.set_camera_point_target = set_camera_point_target;
600 event.deathscreen.camera_point_target_x = camera_point_target.X;
601 event.deathscreen.camera_point_target_y = camera_point_target.Y;
602 event.deathscreen.camera_point_target_z = camera_point_target.Z;
603 m_client_event_queue.push(event);
606 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
612 infostream << "Client: Received media announcement: packet size: "
613 << pkt->getSize() << std::endl;
615 if (m_media_downloader == NULL ||
616 m_media_downloader->isStarted()) {
617 const char *problem = m_media_downloader ?
618 "we already saw another announcement" :
619 "all media has been received already";
620 errorstream << "Client: Received media announcement but "
622 << " files=" << num_files
623 << " size=" << pkt->getSize() << std::endl;
627 // Mesh update thread must be stopped while
628 // updating content definitions
629 sanity_check(!m_mesh_update_thread.isRunning());
631 for (u16 i = 0; i < num_files; i++) {
632 std::string name, sha1_base64;
634 *pkt >> name >> sha1_base64;
636 std::string sha1_raw = base64_decode(sha1_base64);
637 m_media_downloader->addFile(name, sha1_raw);
646 while(!sf.at_end()) {
647 std::string baseurl = trim(sf.next(","));
649 m_media_downloader->addRemoteServer(baseurl);
652 catch(SerializationError& e) {
653 // not supported by server or turned off
656 m_media_downloader->step(this);
659 void Client::handleCommand_Media(NetworkPacket* pkt)
663 u16 total number of file bunches
664 u16 index of this bunch
665 u32 number of files in this bunch
677 *pkt >> num_bunches >> bunch_i >> num_files;
679 infostream << "Client: Received files: bunch " << bunch_i << "/"
680 << num_bunches << " files=" << num_files
681 << " size=" << pkt->getSize() << std::endl;
686 if (m_media_downloader == NULL ||
687 !m_media_downloader->isStarted()) {
688 const char *problem = m_media_downloader ?
689 "media has not been requested" :
690 "all media has been received already";
691 errorstream << "Client: Received media but "
693 << " bunch " << bunch_i << "/" << num_bunches
694 << " files=" << num_files
695 << " size=" << pkt->getSize() << std::endl;
699 // Mesh update thread must be stopped while
700 // updating content definitions
701 sanity_check(!m_mesh_update_thread.isRunning());
703 for (u32 i=0; i < num_files; i++) {
708 std::string data = pkt->readLongString();
710 m_media_downloader->conventionalTransferDone(
715 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
717 infostream << "Client: Received node definitions: packet size: "
718 << pkt->getSize() << std::endl;
720 // Mesh update thread must be stopped while
721 // updating content definitions
722 sanity_check(!m_mesh_update_thread.isRunning());
724 // Decompress node definitions
725 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
726 std::ostringstream tmp_os;
727 decompressZlib(tmp_is, tmp_os);
729 // Deserialize node definitions
730 std::istringstream tmp_is2(tmp_os.str());
731 m_nodedef->deSerialize(tmp_is2);
732 m_nodedef_received = true;
735 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
737 infostream << "Client: Received item definitions: packet size: "
738 << pkt->getSize() << std::endl;
740 // Mesh update thread must be stopped while
741 // updating content definitions
742 sanity_check(!m_mesh_update_thread.isRunning());
744 // Decompress item definitions
745 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
746 std::ostringstream tmp_os;
747 decompressZlib(tmp_is, tmp_os);
749 // Deserialize node definitions
750 std::istringstream tmp_is2(tmp_os.str());
751 m_itemdef->deSerialize(tmp_is2);
752 m_itemdef_received = true;
755 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
763 [11 + len] (f32 * 3) pos
764 [23 + len] u16 object_id
774 u8 type; // 0=local, 1=positional, 2=object
781 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
786 } catch (PacketError &e) {};
792 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
794 case 1: // positional
795 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
799 ClientActiveObject *cao = m_env.getActiveObject(object_id);
801 pos = cao->getPosition();
802 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
803 // TODO: Set up sound to move with object
810 if (client_id != -1) {
811 m_sounds_server_to_client[server_id] = client_id;
812 m_sounds_client_to_server[client_id] = server_id;
814 m_sounds_to_objects[client_id] = object_id;
818 void Client::handleCommand_StopSound(NetworkPacket* pkt)
824 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
825 if (i != m_sounds_server_to_client.end()) {
826 int client_id = i->second;
827 m_sound->stopSound(client_id);
831 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
837 *pkt >> sound_id >> step >> gain;
839 std::unordered_map<s32, int>::const_iterator i =
840 m_sounds_server_to_client.find(sound_id);
842 if (i != m_sounds_server_to_client.end())
843 m_sound->fadeSound(i->second, step, gain);
846 void Client::handleCommand_Privileges(NetworkPacket* pkt)
848 m_privileges.clear();
849 infostream << "Client: Privileges updated: ";
852 *pkt >> num_privileges;
854 for (u16 i = 0; i < num_privileges; i++) {
859 m_privileges.insert(priv);
860 infostream << priv << " ";
862 infostream << std::endl;
865 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
867 LocalPlayer *player = m_env.getLocalPlayer();
868 assert(player != NULL);
870 // Store formspec in LocalPlayer
871 player->inventory_formspec = pkt->readLongString();
874 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
876 std::string datastring(pkt->getString(0), pkt->getSize());
877 std::istringstream is(datastring, std::ios_base::binary);
879 std::string name = deSerializeString(is);
881 infostream << "Client: Detached inventory update: \"" << name
882 << "\"" << std::endl;
884 Inventory *inv = NULL;
885 if (m_detached_inventories.count(name) > 0)
886 inv = m_detached_inventories[name];
888 inv = new Inventory(m_itemdef);
889 m_detached_inventories[name] = inv;
891 inv->deSerialize(is);
894 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
896 std::string formspec = pkt->readLongString();
897 std::string formname;
902 event.type = CE_SHOW_FORMSPEC;
903 // pointer is required as event is a struct only!
904 // adding a std:string to a struct isn't possible
905 event.show_formspec.formspec = new std::string(formspec);
906 event.show_formspec.formname = new std::string(formname);
907 m_client_event_queue.push(event);
910 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
912 std::string datastring(pkt->getString(0), pkt->getSize());
913 std::istringstream is(datastring, std::ios_base::binary);
915 v3f pos = readV3F1000(is);
916 v3f vel = readV3F1000(is);
917 v3f acc = readV3F1000(is);
918 float expirationtime = readF1000(is);
919 float size = readF1000(is);
920 bool collisiondetection = readU8(is);
921 std::string texture = deSerializeLongString(is);
922 bool vertical = false;
923 bool collision_removal = false;
924 struct TileAnimationParams animation;
925 animation.type = TAT_NONE;
928 vertical = readU8(is);
929 collision_removal = readU8(is);
930 animation.deSerialize(is, m_proto_ver);
935 event.type = CE_SPAWN_PARTICLE;
936 event.spawn_particle.pos = new v3f (pos);
937 event.spawn_particle.vel = new v3f (vel);
938 event.spawn_particle.acc = new v3f (acc);
939 event.spawn_particle.expirationtime = expirationtime;
940 event.spawn_particle.size = size;
941 event.spawn_particle.collisiondetection = collisiondetection;
942 event.spawn_particle.collision_removal = collision_removal;
943 event.spawn_particle.vertical = vertical;
944 event.spawn_particle.texture = new std::string(texture);
945 event.spawn_particle.animation = animation;
946 event.spawn_particle.glow = glow;
948 m_client_event_queue.push(event);
951 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
965 bool collisiondetection;
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;
978 struct TileAnimationParams animation;
979 animation.type = TAT_NONE;
984 *pkt >> collision_removal;
987 // This is horrible but required (why are there two ways to deserialize pkts?)
988 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
989 std::istringstream is(datastring, std::ios_base::binary);
990 animation.deSerialize(is, m_proto_ver);
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.animation = animation;
1015 event.add_particlespawner.glow = glow;
1017 m_client_event_queue.push(event);
1021 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1026 // Modification set 13/03/15, 1 year of compat for protocol v24
1027 if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
1036 event.type = CE_DELETE_PARTICLESPAWNER;
1037 event.delete_particlespawner.id =
1038 (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1040 m_client_event_queue.push(event);
1043 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1045 std::string datastring(pkt->getString(0), pkt->getSize());
1046 std::istringstream is(datastring, std::ios_base::binary);
1062 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1063 >> dir >> align >> offset;
1067 catch(SerializationError &e) {};
1071 } catch(SerializationError &e) {};
1074 event.type = CE_HUDADD;
1075 event.hudadd.id = id;
1076 event.hudadd.type = type;
1077 event.hudadd.pos = new v2f(pos);
1078 event.hudadd.name = new std::string(name);
1079 event.hudadd.scale = new v2f(scale);
1080 event.hudadd.text = new std::string(text);
1081 event.hudadd.number = number;
1082 event.hudadd.item = item;
1083 event.hudadd.dir = dir;
1084 event.hudadd.align = new v2f(align);
1085 event.hudadd.offset = new v2f(offset);
1086 event.hudadd.world_pos = new v3f(world_pos);
1087 event.hudadd.size = new v2s32(size);
1088 m_client_event_queue.push(event);
1091 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1098 event.type = CE_HUDRM;
1099 event.hudrm.id = id;
1100 m_client_event_queue.push(event);
1103 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1115 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1116 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1118 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1120 else if (stat == HUD_STAT_WORLD_POS)
1122 else if (stat == HUD_STAT_SIZE )
1128 event.type = CE_HUDCHANGE;
1129 event.hudchange.id = id;
1130 event.hudchange.stat = (HudElementStat)stat;
1131 event.hudchange.v2fdata = new v2f(v2fdata);
1132 event.hudchange.v3fdata = new v3f(v3fdata);
1133 event.hudchange.sdata = new std::string(sdata);
1134 event.hudchange.data = intdata;
1135 event.hudchange.v2s32data = new v2s32(v2s32data);
1136 m_client_event_queue.push(event);
1139 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1143 *pkt >> flags >> mask;
1145 LocalPlayer *player = m_env.getLocalPlayer();
1146 assert(player != NULL);
1148 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1150 player->hud_flags &= ~mask;
1151 player->hud_flags |= flags;
1153 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1155 // Hide minimap if it has been disabled by the server
1156 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible) {
1157 // defers a minimap update, therefore only call it if really
1158 // needed, by checking that minimap was visible before
1159 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1163 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1165 u16 param; std::string value;
1167 *pkt >> param >> value;
1169 LocalPlayer *player = m_env.getLocalPlayer();
1170 assert(player != NULL);
1172 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1173 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1174 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1175 player->hud_hotbar_itemcount = hotbar_itemcount;
1177 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1178 player->hotbar_image = value;
1180 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1181 player->hotbar_selected_image = value;
1185 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1187 std::string datastring(pkt->getString(0), pkt->getSize());
1188 std::istringstream is(datastring, std::ios_base::binary);
1190 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1191 std::string *type = new std::string(deSerializeString(is));
1192 u16 count = readU16(is);
1193 std::vector<std::string> *params = new std::vector<std::string>;
1195 for (size_t i = 0; i < count; i++)
1196 params->push_back(deSerializeString(is));
1200 clouds = readU8(is);
1204 event.type = CE_SET_SKY;
1205 event.set_sky.bgcolor = bgcolor;
1206 event.set_sky.type = type;
1207 event.set_sky.params = params;
1208 event.set_sky.clouds = clouds;
1209 m_client_event_queue.push(event);
1212 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1215 video::SColor color_bright;
1216 video::SColor color_ambient;
1221 *pkt >> density >> color_bright >> color_ambient
1222 >> height >> thickness >> speed;
1225 event.type = CE_CLOUD_PARAMS;
1226 event.cloud_params.density = density;
1227 // use the underlying u32 representation, because we can't
1228 // use struct members with constructors here, and this way
1229 // we avoid using new() and delete() for no good reason
1230 event.cloud_params.color_bright = color_bright.color;
1231 event.cloud_params.color_ambient = color_ambient.color;
1232 event.cloud_params.height = height;
1233 event.cloud_params.thickness = thickness;
1234 // same here: deconstruct to skip constructor
1235 event.cloud_params.speed_x = speed.X;
1236 event.cloud_params.speed_y = speed.Y;
1237 m_client_event_queue.push(event);
1240 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1243 u16 day_night_ratio_u;
1245 *pkt >> do_override >> day_night_ratio_u;
1247 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1250 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1251 event.override_day_night_ratio.do_override = do_override;
1252 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1253 m_client_event_queue.push(event);
1256 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1258 LocalPlayer *player = m_env.getLocalPlayer();
1259 assert(player != NULL);
1261 *pkt >> player->local_animations[0];
1262 *pkt >> player->local_animations[1];
1263 *pkt >> player->local_animations[2];
1264 *pkt >> player->local_animations[3];
1265 *pkt >> player->local_animation_speed;
1268 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1270 LocalPlayer *player = m_env.getLocalPlayer();
1271 assert(player != NULL);
1273 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1276 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1280 *pkt >> type >> num_players;
1281 PlayerListModifer notice_type = (PlayerListModifer) type;
1283 for (u16 i = 0; i < num_players; i++) {
1286 switch (notice_type) {
1287 case PLAYER_LIST_INIT:
1288 case PLAYER_LIST_ADD:
1289 m_env.addPlayerName(name);
1291 case PLAYER_LIST_REMOVE:
1292 m_env.removePlayerName(name);
1298 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1300 if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1301 && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1302 errorstream << "Client: Recieved SRP S_B login message,"
1303 << " but wasn't supposed to (chosen_mech="
1304 << m_chosen_auth_mech << ")." << std::endl;
1310 SRPUser *usr = (SRPUser *) m_auth_data;
1315 infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1317 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1318 (const unsigned char *) B.c_str(), B.size(),
1319 (unsigned char **) &bytes_M, &len_M);
1322 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1326 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1327 resp_pkt << std::string(bytes_M, len_M);