3 Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "util/base64.h"
23 #include "chatmessage.h"
24 #include "clientmedia.h"
27 #include "mapsector.h"
30 #include "serialization.h"
32 #include "util/strfnd.h"
33 #include "network/clientopcodes.h"
34 #include "network/connection.h"
35 #include "script/scripting_client.h"
36 #include "util/serialize.h"
38 #include "tileanimation.h"
40 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
42 infostream << "Got deprecated command "
43 << toClientCommandTable[pkt->getCommand()].name << " from peer "
44 << pkt->getPeerId() << "!" << std::endl;
47 void Client::handleCommand_Hello(NetworkPacket* pkt)
49 if (pkt->getSize() < 1)
56 std::string username_legacy; // for case insensitivity
57 *pkt >> serialization_ver >> compression_mode >> proto_ver
58 >> auth_mechs >> username_legacy;
60 // Chose an auth method we support
61 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
63 infostream << "Client: TOCLIENT_HELLO received with "
64 << "serialization_ver=" << (u32)serialization_ver
65 << ", auth_mechs=" << auth_mechs
66 << ", proto_ver=" << proto_ver
67 << ", compression_mode=" << compression_mode
68 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
70 if (!ser_ver_supported(serialization_ver)) {
71 infostream << "Client: TOCLIENT_HELLO: Server sent "
72 << "unsupported ser_fmt_ver"<< std::endl;
76 m_server_ser_ver = serialization_ver;
77 m_proto_ver = proto_ver;
79 //TODO verify that username_legacy matches sent username, only
80 // differs in casing (make both uppercase and compare)
81 // This is only neccessary though when we actually want to add casing support
83 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
84 // we recieved a TOCLIENT_HELLO while auth was already going on
85 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
86 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
87 if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP)
88 || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) {
89 srp_user_delete((SRPUser *) m_auth_data);
94 // Authenticate using that method, or abort if there wasn't any method found
95 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
96 startAuth(chosen_auth_mechanism);
98 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
99 m_access_denied = true;
100 m_access_denied_reason = "Unknown";
106 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
111 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
112 >> m_sudo_auth_methods;
114 playerpos -= v3f(0, BS / 2, 0);
116 // Set player position
117 LocalPlayer *player = m_env.getLocalPlayer();
118 assert(player != NULL);
119 player->setPosition(playerpos);
121 infostream << "Client: received map seed: " << m_map_seed << std::endl;
122 infostream << "Client: received recommended send interval "
123 << m_recommended_send_interval<<std::endl;
126 NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
131 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
135 m_password = m_new_password;
137 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
139 // send packet to actually set the password
140 startAuth(AUTH_MECHANISM_FIRST_SRP);
143 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
145 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
147 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
148 L"Password change denied. Password NOT changed.");
149 pushToChatQueue(chatMessage);
150 // reset everything and be sad
153 void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
155 if (pkt->getSize() < 1)
159 *pkt >> server_ser_ver;
161 infostream << "Client: TOCLIENT_INIT_LEGACY received with "
162 "server_ser_ver=" << ((int)server_ser_ver & 0xff) << std::endl;
164 if (!ser_ver_supported(server_ser_ver)) {
165 infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
166 << "unsupported ser_fmt_ver"<< std::endl;
170 m_server_ser_ver = server_ser_ver;
172 // We can be totally wrong with this guess
173 // but we only need some value < 25.
176 // Get player position
177 v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
178 if (pkt->getSize() >= 1 + 6) {
179 *pkt >> playerpos_s16;
181 v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
184 // Set player position
185 LocalPlayer *player = m_env.getLocalPlayer();
186 assert(player != NULL);
187 player->setPosition(playerpos_f);
189 if (pkt->getSize() >= 1 + 6 + 8) {
192 infostream << "Client: received map seed: " << m_map_seed << std::endl;
195 if (pkt->getSize() >= 1 + 6 + 8 + 4) {
196 *pkt >> m_recommended_send_interval;
197 infostream << "Client: received recommended send interval "
198 << m_recommended_send_interval<<std::endl;
202 NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
208 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
210 // The server didn't like our password. Note, this needs
211 // to be processed even if the serialisation format has
212 // not been agreed yet, the same as TOCLIENT_INIT.
213 m_access_denied = true;
214 m_access_denied_reason = "Unknown";
216 if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
217 if (pkt->getSize() < 1)
220 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
222 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
223 denyCode == SERVER_ACCESSDENIED_CRASH) {
224 *pkt >> m_access_denied_reason;
225 if (m_access_denied_reason.empty()) {
226 m_access_denied_reason = accessDeniedStrings[denyCode];
230 m_access_denied_reconnect = reconnect & 1;
231 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
232 *pkt >> m_access_denied_reason;
233 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
234 m_access_denied_reason = accessDeniedStrings[denyCode];
236 // Allow us to add new error messages to the
237 // protocol without raising the protocol version, if we want to.
238 // Until then (which may be never), this is outside
239 // of the defined protocol.
240 *pkt >> m_access_denied_reason;
241 if (m_access_denied_reason.empty()) {
242 m_access_denied_reason = "Unknown";
246 // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
247 // for compat with old clients
249 if (pkt->getSize() >= 2) {
250 std::wstring wide_reason;
252 m_access_denied_reason = wide_to_utf8(wide_reason);
257 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
259 if (pkt->getSize() < 6)
267 void Client::handleCommand_AddNode(NetworkPacket* pkt)
269 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
276 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
278 bool remove_metadata = true;
279 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
280 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
281 remove_metadata = false;
284 addNode(p, n, remove_metadata);
286 void Client::handleCommand_BlockData(NetworkPacket* pkt)
288 // Ignore too small packet
289 if (pkt->getSize() < 6)
295 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
296 std::istringstream istr(datastring, std::ios_base::binary);
302 sector = m_env.getMap().emergeSector(p2d);
304 assert(sector->getPos() == p2d);
306 block = sector->getBlockNoCreateNoEx(p.Y);
309 Update an existing block
311 block->deSerialize(istr, m_server_ser_ver, false);
312 block->deSerializeNetworkSpecific(istr);
318 block = new MapBlock(&m_env.getMap(), p, this);
319 block->deSerialize(istr, m_server_ser_ver, false);
320 block->deSerializeNetworkSpecific(istr);
321 sector->insertBlock(block);
325 ServerMap::saveBlock(block, m_localdb);
329 Add it to mesh update queue and set it to be acknowledged after update.
331 addUpdateMeshTaskWithEdge(p, true);
334 void Client::handleCommand_Inventory(NetworkPacket* pkt)
336 if (pkt->getSize() < 1)
339 std::string datastring(pkt->getString(0), pkt->getSize());
340 std::istringstream is(datastring, std::ios_base::binary);
342 LocalPlayer *player = m_env.getLocalPlayer();
343 assert(player != NULL);
345 player->inventory.deSerialize(is);
347 m_inventory_updated = true;
349 delete m_inventory_from_server;
350 m_inventory_from_server = new Inventory(player->inventory);
351 m_inventory_from_server_age = 0.0;
354 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
356 if (pkt->getSize() < 2)
363 time_of_day = time_of_day % 24000;
364 float time_speed = 0;
366 if (pkt->getSize() >= 2 + 4) {
370 // Old message; try to approximate speed of time by ourselves
371 float time_of_day_f = (float)time_of_day / 24000.0;
372 float tod_diff_f = 0;
374 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
375 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
377 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
379 m_last_time_of_day_f = time_of_day_f;
380 float time_diff = m_time_of_day_update_timer;
381 m_time_of_day_update_timer = 0;
383 if (m_time_of_day_set) {
384 time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
385 infostream << "Client: Measured time_of_day speed (old format): "
386 << time_speed << " tod_diff_f=" << tod_diff_f
387 << " time_diff=" << time_diff << std::endl;
391 // Update environment
392 m_env.setTimeOfDay(time_of_day);
393 m_env.setTimeOfDaySpeed(time_speed);
394 m_time_of_day_set = true;
396 u32 dr = m_env.getDayNightRatio();
397 infostream << "Client: time_of_day=" << time_of_day
398 << " time_speed=" << time_speed
399 << " dr=" << dr << std::endl;
402 void Client::handleCommand_ChatMessageOld(NetworkPacket *pkt)
413 std::wstring message;
414 for (u32 i = 0; i < len; i++) {
416 message += (wchar_t)read_wchar;
419 // If chat message not consummed by client lua API
420 // @TODO send this to CSM using ChatMessage object
421 if (!moddingEnabled() || !m_script->on_receiving_message(wide_to_utf8(message))) {
422 pushToChatQueue(new ChatMessage(message));
426 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
431 u16 sendername length
437 ChatMessage *chatMessage = new ChatMessage();
438 u8 version, message_type;
439 *pkt >> version >> message_type;
441 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
446 *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
448 chatMessage->type = (ChatMessageType) message_type;
450 // @TODO send this to CSM using ChatMessage object
451 if (!moddingEnabled() || !m_script->on_receiving_message(
452 wide_to_utf8(chatMessage->message))) {
453 pushToChatQueue(chatMessage);
455 // Message was consumed by CSM and should not handled by client, destroying
460 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
463 u16 count of removed objects
464 for all removed objects {
467 u16 count of added objects
468 for all added objects {
471 u32 initialization data length
472 string initialization data
478 u16 removed_count, added_count, id;
480 // Read removed objects
481 *pkt >> removed_count;
483 for (u16 i = 0; i < removed_count; i++) {
485 m_env.removeActiveObject(id);
488 // Read added objects
491 for (u16 i = 0; i < added_count; i++) {
493 m_env.addActiveObject(id, type, pkt->readLongString());
495 } catch (PacketError &e) {
496 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
497 << ". The packet is unreliable, ignoring" << std::endl;
501 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
511 std::string datastring(pkt->getString(0), pkt->getSize());
512 std::istringstream is(datastring, std::ios_base::binary);
516 u16 id = readU16(is);
520 std::string message = deSerializeString(is);
522 // Pass on to the environment
523 m_env.processActiveObjectMessage(id, message);
525 } catch (SerializationError &e) {
526 errorstream << "Client::handleCommand_ActiveObjectMessages: "
527 << "caught SerializationError: " << e.what() << std::endl;
531 void Client::handleCommand_Movement(NetworkPacket* pkt)
533 LocalPlayer *player = m_env.getLocalPlayer();
534 assert(player != NULL);
536 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
538 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
539 >> lf >> lfs >> ls >> g;
541 player->movement_acceleration_default = mad * BS;
542 player->movement_acceleration_air = maa * BS;
543 player->movement_acceleration_fast = maf * BS;
544 player->movement_speed_walk = msw * BS;
545 player->movement_speed_crouch = mscr * BS;
546 player->movement_speed_fast = msf * BS;
547 player->movement_speed_climb = mscl * BS;
548 player->movement_speed_jump = msj * BS;
549 player->movement_liquid_fluidity = lf * BS;
550 player->movement_liquid_fluidity_smooth = lfs * BS;
551 player->movement_liquid_sink = ls * BS;
552 player->movement_gravity = g * BS;
555 void Client::handleCommand_HP(NetworkPacket* pkt)
558 LocalPlayer *player = m_env.getLocalPlayer();
559 assert(player != NULL);
561 u8 oldhp = player->hp;
568 if (moddingEnabled()) {
569 m_script->on_hp_modification(hp);
573 // Add to ClientEvent queue
575 event.type = CE_PLAYER_DAMAGE;
576 event.player_damage.amount = oldhp - hp;
577 m_client_event_queue.push(event);
581 void Client::handleCommand_Breath(NetworkPacket* pkt)
583 LocalPlayer *player = m_env.getLocalPlayer();
584 assert(player != NULL);
590 player->setBreath(breath);
593 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
595 LocalPlayer *player = m_env.getLocalPlayer();
596 assert(player != NULL);
601 *pkt >> pos >> pitch >> yaw;
603 player->setPosition(pos);
605 infostream << "Client got TOCLIENT_MOVE_PLAYER"
606 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
607 << " pitch=" << pitch
612 Add to ClientEvent queue.
613 This has to be sent to the main program because otherwise
614 it would just force the pitch and yaw values to whatever
615 the camera points to.
618 event.type = CE_PLAYER_FORCE_MOVE;
619 event.player_force_move.pitch = pitch;
620 event.player_force_move.yaw = yaw;
621 m_client_event_queue.push(event);
623 // Ignore damage for a few seconds, so that the player doesn't
624 // get damage from falling on ground
625 m_ignore_damage_timer = 3.0;
628 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
630 bool set_camera_point_target;
631 v3f camera_point_target;
633 *pkt >> set_camera_point_target;
634 *pkt >> camera_point_target;
637 event.type = CE_DEATHSCREEN;
638 event.deathscreen.set_camera_point_target = set_camera_point_target;
639 event.deathscreen.camera_point_target_x = camera_point_target.X;
640 event.deathscreen.camera_point_target_y = camera_point_target.Y;
641 event.deathscreen.camera_point_target_z = camera_point_target.Z;
642 m_client_event_queue.push(event);
645 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
651 infostream << "Client: Received media announcement: packet size: "
652 << pkt->getSize() << std::endl;
654 if (m_media_downloader == NULL ||
655 m_media_downloader->isStarted()) {
656 const char *problem = m_media_downloader ?
657 "we already saw another announcement" :
658 "all media has been received already";
659 errorstream << "Client: Received media announcement but "
661 << " files=" << num_files
662 << " size=" << pkt->getSize() << std::endl;
666 // Mesh update thread must be stopped while
667 // updating content definitions
668 sanity_check(!m_mesh_update_thread.isRunning());
670 for (u16 i = 0; i < num_files; i++) {
671 std::string name, sha1_base64;
673 *pkt >> name >> sha1_base64;
675 std::string sha1_raw = base64_decode(sha1_base64);
676 m_media_downloader->addFile(name, sha1_raw);
685 while(!sf.at_end()) {
686 std::string baseurl = trim(sf.next(","));
687 if (!baseurl.empty())
688 m_media_downloader->addRemoteServer(baseurl);
691 catch(SerializationError& e) {
692 // not supported by server or turned off
695 m_media_downloader->step(this);
698 void Client::handleCommand_Media(NetworkPacket* pkt)
702 u16 total number of file bunches
703 u16 index of this bunch
704 u32 number of files in this bunch
716 *pkt >> num_bunches >> bunch_i >> num_files;
718 infostream << "Client: Received files: bunch " << bunch_i << "/"
719 << num_bunches << " files=" << num_files
720 << " size=" << pkt->getSize() << std::endl;
725 if (!m_media_downloader || !m_media_downloader->isStarted()) {
726 const char *problem = m_media_downloader ?
727 "media has not been requested" :
728 "all media has been received already";
729 errorstream << "Client: Received media but "
731 << " bunch " << bunch_i << "/" << num_bunches
732 << " files=" << num_files
733 << " size=" << pkt->getSize() << std::endl;
737 // Mesh update thread must be stopped while
738 // updating content definitions
739 sanity_check(!m_mesh_update_thread.isRunning());
741 for (u32 i=0; i < num_files; i++) {
746 std::string data = pkt->readLongString();
748 m_media_downloader->conventionalTransferDone(
753 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
755 infostream << "Client: Received node definitions: packet size: "
756 << pkt->getSize() << std::endl;
758 // Mesh update thread must be stopped while
759 // updating content definitions
760 sanity_check(!m_mesh_update_thread.isRunning());
762 // Decompress node definitions
763 std::istringstream tmp_is(pkt->readLongString(), 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_nodedef->deSerialize(tmp_is2);
770 m_nodedef_received = true;
773 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
775 infostream << "Client: Received item definitions: packet size: "
776 << pkt->getSize() << std::endl;
778 // Mesh update thread must be stopped while
779 // updating content definitions
780 sanity_check(!m_mesh_update_thread.isRunning());
782 // Decompress item definitions
783 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
784 std::ostringstream tmp_os;
785 decompressZlib(tmp_is, tmp_os);
787 // Deserialize node definitions
788 std::istringstream tmp_is2(tmp_os.str());
789 m_itemdef->deSerialize(tmp_is2);
790 m_itemdef_received = true;
793 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
801 [11 + len] (f32 * 3) pos
802 [23 + len] u16 object_id
812 u8 type; // 0=local, 1=positional, 2=object
819 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
824 } catch (PacketError &e) {};
830 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
832 case 1: // positional
833 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
837 ClientActiveObject *cao = m_env.getActiveObject(object_id);
839 pos = cao->getPosition();
840 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
841 // TODO: Set up sound to move with object
848 if (client_id != -1) {
849 m_sounds_server_to_client[server_id] = client_id;
850 m_sounds_client_to_server[client_id] = server_id;
852 m_sounds_to_objects[client_id] = object_id;
856 void Client::handleCommand_StopSound(NetworkPacket* pkt)
862 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
863 if (i != m_sounds_server_to_client.end()) {
864 int client_id = i->second;
865 m_sound->stopSound(client_id);
869 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
875 *pkt >> sound_id >> step >> gain;
877 std::unordered_map<s32, int>::const_iterator i =
878 m_sounds_server_to_client.find(sound_id);
880 if (i != m_sounds_server_to_client.end())
881 m_sound->fadeSound(i->second, step, gain);
884 void Client::handleCommand_Privileges(NetworkPacket* pkt)
886 m_privileges.clear();
887 infostream << "Client: Privileges updated: ";
890 *pkt >> num_privileges;
892 for (u16 i = 0; i < num_privileges; i++) {
897 m_privileges.insert(priv);
898 infostream << priv << " ";
900 infostream << std::endl;
903 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
905 LocalPlayer *player = m_env.getLocalPlayer();
906 assert(player != NULL);
908 // Store formspec in LocalPlayer
909 player->inventory_formspec = pkt->readLongString();
912 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
914 std::string datastring(pkt->getString(0), pkt->getSize());
915 std::istringstream is(datastring, std::ios_base::binary);
917 std::string name = deSerializeString(is);
919 infostream << "Client: Detached inventory update: \"" << name
920 << "\"" << std::endl;
922 Inventory *inv = NULL;
923 if (m_detached_inventories.count(name) > 0)
924 inv = m_detached_inventories[name];
926 inv = new Inventory(m_itemdef);
927 m_detached_inventories[name] = inv;
929 inv->deSerialize(is);
932 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
934 std::string formspec = pkt->readLongString();
935 std::string formname;
940 event.type = CE_SHOW_FORMSPEC;
941 // pointer is required as event is a struct only!
942 // adding a std:string to a struct isn't possible
943 event.show_formspec.formspec = new std::string(formspec);
944 event.show_formspec.formname = new std::string(formname);
945 m_client_event_queue.push(event);
948 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
950 std::string datastring(pkt->getString(0), pkt->getSize());
951 std::istringstream is(datastring, std::ios_base::binary);
953 v3f pos = readV3F1000(is);
954 v3f vel = readV3F1000(is);
955 v3f acc = readV3F1000(is);
956 float expirationtime = readF1000(is);
957 float size = readF1000(is);
958 bool collisiondetection = readU8(is);
959 std::string texture = deSerializeLongString(is);
960 bool vertical = false;
961 bool collision_removal = false;
962 struct TileAnimationParams animation;
963 animation.type = TAT_NONE;
966 vertical = readU8(is);
967 collision_removal = readU8(is);
968 animation.deSerialize(is, m_proto_ver);
973 event.type = CE_SPAWN_PARTICLE;
974 event.spawn_particle.pos = new v3f (pos);
975 event.spawn_particle.vel = new v3f (vel);
976 event.spawn_particle.acc = new v3f (acc);
977 event.spawn_particle.expirationtime = expirationtime;
978 event.spawn_particle.size = size;
979 event.spawn_particle.collisiondetection = collisiondetection;
980 event.spawn_particle.collision_removal = collision_removal;
981 event.spawn_particle.vertical = vertical;
982 event.spawn_particle.texture = new std::string(texture);
983 event.spawn_particle.animation = animation;
984 event.spawn_particle.glow = glow;
986 m_client_event_queue.push(event);
989 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
1003 bool collisiondetection;
1006 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
1007 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
1008 >> maxsize >> collisiondetection;
1010 std::string texture = pkt->readLongString();
1014 bool vertical = false;
1015 bool collision_removal = false;
1016 struct TileAnimationParams animation;
1017 animation.type = TAT_NONE;
1019 u16 attached_id = 0;
1022 *pkt >> collision_removal;
1023 *pkt >> attached_id;
1025 // This is horrible but required (why are there two ways to deserialize pkts?)
1026 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1027 std::istringstream is(datastring, std::ios_base::binary);
1028 animation.deSerialize(is, m_proto_ver);
1033 event.type = CE_ADD_PARTICLESPAWNER;
1034 event.add_particlespawner.amount = amount;
1035 event.add_particlespawner.spawntime = spawntime;
1036 event.add_particlespawner.minpos = new v3f (minpos);
1037 event.add_particlespawner.maxpos = new v3f (maxpos);
1038 event.add_particlespawner.minvel = new v3f (minvel);
1039 event.add_particlespawner.maxvel = new v3f (maxvel);
1040 event.add_particlespawner.minacc = new v3f (minacc);
1041 event.add_particlespawner.maxacc = new v3f (maxacc);
1042 event.add_particlespawner.minexptime = minexptime;
1043 event.add_particlespawner.maxexptime = maxexptime;
1044 event.add_particlespawner.minsize = minsize;
1045 event.add_particlespawner.maxsize = maxsize;
1046 event.add_particlespawner.collisiondetection = collisiondetection;
1047 event.add_particlespawner.collision_removal = collision_removal;
1048 event.add_particlespawner.attached_id = attached_id;
1049 event.add_particlespawner.vertical = vertical;
1050 event.add_particlespawner.texture = new std::string(texture);
1051 event.add_particlespawner.id = id;
1052 event.add_particlespawner.animation = animation;
1053 event.add_particlespawner.glow = glow;
1055 m_client_event_queue.push(event);
1059 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1064 // Modification set 13/03/15, 1 year of compat for protocol v24
1065 if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
1074 event.type = CE_DELETE_PARTICLESPAWNER;
1075 event.delete_particlespawner.id =
1076 (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1078 m_client_event_queue.push(event);
1081 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1083 std::string datastring(pkt->getString(0), pkt->getSize());
1084 std::istringstream is(datastring, std::ios_base::binary);
1100 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1101 >> dir >> align >> offset;
1105 catch(SerializationError &e) {};
1109 } catch(SerializationError &e) {};
1112 event.type = CE_HUDADD;
1113 event.hudadd.id = id;
1114 event.hudadd.type = type;
1115 event.hudadd.pos = new v2f(pos);
1116 event.hudadd.name = new std::string(name);
1117 event.hudadd.scale = new v2f(scale);
1118 event.hudadd.text = new std::string(text);
1119 event.hudadd.number = number;
1120 event.hudadd.item = item;
1121 event.hudadd.dir = dir;
1122 event.hudadd.align = new v2f(align);
1123 event.hudadd.offset = new v2f(offset);
1124 event.hudadd.world_pos = new v3f(world_pos);
1125 event.hudadd.size = new v2s32(size);
1126 m_client_event_queue.push(event);
1129 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1136 event.type = CE_HUDRM;
1137 event.hudrm.id = id;
1138 m_client_event_queue.push(event);
1141 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1153 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1154 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1156 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1158 else if (stat == HUD_STAT_WORLD_POS)
1160 else if (stat == HUD_STAT_SIZE )
1166 event.type = CE_HUDCHANGE;
1167 event.hudchange.id = id;
1168 event.hudchange.stat = (HudElementStat)stat;
1169 event.hudchange.v2fdata = new v2f(v2fdata);
1170 event.hudchange.v3fdata = new v3f(v3fdata);
1171 event.hudchange.sdata = new std::string(sdata);
1172 event.hudchange.data = intdata;
1173 event.hudchange.v2s32data = new v2s32(v2s32data);
1174 m_client_event_queue.push(event);
1177 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1181 *pkt >> flags >> mask;
1183 LocalPlayer *player = m_env.getLocalPlayer();
1184 assert(player != NULL);
1186 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1187 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1189 player->hud_flags &= ~mask;
1190 player->hud_flags |= flags;
1192 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1193 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1195 // Hide minimap if it has been disabled by the server
1196 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1197 // defers a minimap update, therefore only call it if really
1198 // needed, by checking that minimap was visible before
1199 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1201 // Switch to surface mode if radar disabled by server
1202 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1203 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1206 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1208 u16 param; std::string value;
1210 *pkt >> param >> value;
1212 LocalPlayer *player = m_env.getLocalPlayer();
1213 assert(player != NULL);
1215 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1216 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1217 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1218 player->hud_hotbar_itemcount = hotbar_itemcount;
1220 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1221 // If value not empty verify image exists in texture source
1222 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1223 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1224 << value << "')" << std::endl;
1227 player->hotbar_image = value;
1229 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1230 // If value not empty verify image exists in texture source
1231 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1232 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1233 << value << "')" << std::endl;
1236 player->hotbar_selected_image = value;
1240 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1242 std::string datastring(pkt->getString(0), pkt->getSize());
1243 std::istringstream is(datastring, std::ios_base::binary);
1245 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1246 std::string *type = new std::string(deSerializeString(is));
1247 u16 count = readU16(is);
1248 std::vector<std::string> *params = new std::vector<std::string>;
1250 for (size_t i = 0; i < count; i++)
1251 params->push_back(deSerializeString(is));
1255 clouds = readU8(is);
1259 event.type = CE_SET_SKY;
1260 event.set_sky.bgcolor = bgcolor;
1261 event.set_sky.type = type;
1262 event.set_sky.params = params;
1263 event.set_sky.clouds = clouds;
1264 m_client_event_queue.push(event);
1267 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1270 video::SColor color_bright;
1271 video::SColor color_ambient;
1276 *pkt >> density >> color_bright >> color_ambient
1277 >> height >> thickness >> speed;
1280 event.type = CE_CLOUD_PARAMS;
1281 event.cloud_params.density = density;
1282 // use the underlying u32 representation, because we can't
1283 // use struct members with constructors here, and this way
1284 // we avoid using new() and delete() for no good reason
1285 event.cloud_params.color_bright = color_bright.color;
1286 event.cloud_params.color_ambient = color_ambient.color;
1287 event.cloud_params.height = height;
1288 event.cloud_params.thickness = thickness;
1289 // same here: deconstruct to skip constructor
1290 event.cloud_params.speed_x = speed.X;
1291 event.cloud_params.speed_y = speed.Y;
1292 m_client_event_queue.push(event);
1295 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1298 u16 day_night_ratio_u;
1300 *pkt >> do_override >> day_night_ratio_u;
1302 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1305 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1306 event.override_day_night_ratio.do_override = do_override;
1307 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1308 m_client_event_queue.push(event);
1311 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1313 LocalPlayer *player = m_env.getLocalPlayer();
1314 assert(player != NULL);
1316 *pkt >> player->local_animations[0];
1317 *pkt >> player->local_animations[1];
1318 *pkt >> player->local_animations[2];
1319 *pkt >> player->local_animations[3];
1320 *pkt >> player->local_animation_speed;
1323 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1325 LocalPlayer *player = m_env.getLocalPlayer();
1326 assert(player != NULL);
1328 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1331 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1335 *pkt >> type >> num_players;
1336 PlayerListModifer notice_type = (PlayerListModifer) type;
1338 for (u16 i = 0; i < num_players; i++) {
1341 switch (notice_type) {
1342 case PLAYER_LIST_INIT:
1343 case PLAYER_LIST_ADD:
1344 m_env.addPlayerName(name);
1346 case PLAYER_LIST_REMOVE:
1347 m_env.removePlayerName(name);
1353 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1355 if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1356 && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1357 errorstream << "Client: Recieved SRP S_B login message,"
1358 << " but wasn't supposed to (chosen_mech="
1359 << m_chosen_auth_mech << ")." << std::endl;
1365 SRPUser *usr = (SRPUser *) m_auth_data;
1370 infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1372 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1373 (const unsigned char *) B.c_str(), B.size(),
1374 (unsigned char **) &bytes_M, &len_M);
1377 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1381 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1382 resp_pkt << std::string(bytes_M, len_M);
1386 void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
1388 *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;