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->got_teleported = true;
565 player->setPosition(pos);
567 infostream << "Client got TOCLIENT_MOVE_PLAYER"
568 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
569 << " pitch=" << pitch
574 Add to ClientEvent queue.
575 This has to be sent to the main program because otherwise
576 it would just force the pitch and yaw values to whatever
577 the camera points to.
580 event.type = CE_PLAYER_FORCE_MOVE;
581 event.player_force_move.pitch = pitch;
582 event.player_force_move.yaw = yaw;
583 m_client_event_queue.push(event);
585 // Ignore damage for a few seconds, so that the player doesn't
586 // get damage from falling on ground
587 m_ignore_damage_timer = 3.0;
590 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
592 bool set_camera_point_target;
593 v3f camera_point_target;
595 *pkt >> set_camera_point_target;
596 *pkt >> camera_point_target;
599 event.type = CE_DEATHSCREEN;
600 event.deathscreen.set_camera_point_target = set_camera_point_target;
601 event.deathscreen.camera_point_target_x = camera_point_target.X;
602 event.deathscreen.camera_point_target_y = camera_point_target.Y;
603 event.deathscreen.camera_point_target_z = camera_point_target.Z;
604 m_client_event_queue.push(event);
607 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
613 infostream << "Client: Received media announcement: packet size: "
614 << pkt->getSize() << std::endl;
616 if (m_media_downloader == NULL ||
617 m_media_downloader->isStarted()) {
618 const char *problem = m_media_downloader ?
619 "we already saw another announcement" :
620 "all media has been received already";
621 errorstream << "Client: Received media announcement but "
623 << " files=" << num_files
624 << " size=" << pkt->getSize() << std::endl;
628 // Mesh update thread must be stopped while
629 // updating content definitions
630 sanity_check(!m_mesh_update_thread.isRunning());
632 for (u16 i = 0; i < num_files; i++) {
633 std::string name, sha1_base64;
635 *pkt >> name >> sha1_base64;
637 std::string sha1_raw = base64_decode(sha1_base64);
638 m_media_downloader->addFile(name, sha1_raw);
647 while(!sf.at_end()) {
648 std::string baseurl = trim(sf.next(","));
650 m_media_downloader->addRemoteServer(baseurl);
653 catch(SerializationError& e) {
654 // not supported by server or turned off
657 m_media_downloader->step(this);
660 void Client::handleCommand_Media(NetworkPacket* pkt)
664 u16 total number of file bunches
665 u16 index of this bunch
666 u32 number of files in this bunch
678 *pkt >> num_bunches >> bunch_i >> num_files;
680 infostream << "Client: Received files: bunch " << bunch_i << "/"
681 << num_bunches << " files=" << num_files
682 << " size=" << pkt->getSize() << std::endl;
687 if (m_media_downloader == NULL ||
688 !m_media_downloader->isStarted()) {
689 const char *problem = m_media_downloader ?
690 "media has not been requested" :
691 "all media has been received already";
692 errorstream << "Client: Received media but "
694 << " bunch " << bunch_i << "/" << num_bunches
695 << " files=" << num_files
696 << " size=" << pkt->getSize() << std::endl;
700 // Mesh update thread must be stopped while
701 // updating content definitions
702 sanity_check(!m_mesh_update_thread.isRunning());
704 for (u32 i=0; i < num_files; i++) {
709 std::string data = pkt->readLongString();
711 m_media_downloader->conventionalTransferDone(
716 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
718 infostream << "Client: Received node definitions: packet size: "
719 << pkt->getSize() << std::endl;
721 // Mesh update thread must be stopped while
722 // updating content definitions
723 sanity_check(!m_mesh_update_thread.isRunning());
725 // Decompress node definitions
726 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
727 std::ostringstream tmp_os;
728 decompressZlib(tmp_is, tmp_os);
730 // Deserialize node definitions
731 std::istringstream tmp_is2(tmp_os.str());
732 m_nodedef->deSerialize(tmp_is2);
733 m_nodedef_received = true;
736 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
738 infostream << "Client: Received item definitions: packet size: "
739 << pkt->getSize() << std::endl;
741 // Mesh update thread must be stopped while
742 // updating content definitions
743 sanity_check(!m_mesh_update_thread.isRunning());
745 // Decompress item definitions
746 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
747 std::ostringstream tmp_os;
748 decompressZlib(tmp_is, tmp_os);
750 // Deserialize node definitions
751 std::istringstream tmp_is2(tmp_os.str());
752 m_itemdef->deSerialize(tmp_is2);
753 m_itemdef_received = true;
756 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
761 u8 type; // 0=local, 1=positional, 2=object
766 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
772 client_id = m_sound->playSound(name, loop, gain);
774 case 1: // positional
775 client_id = m_sound->playSoundAt(name, loop, gain, pos);
779 ClientActiveObject *cao = m_env.getActiveObject(object_id);
781 pos = cao->getPosition();
782 client_id = m_sound->playSoundAt(name, loop, gain, pos);
783 // TODO: Set up sound to move with object
790 if (client_id != -1) {
791 m_sounds_server_to_client[server_id] = client_id;
792 m_sounds_client_to_server[client_id] = server_id;
794 m_sounds_to_objects[client_id] = object_id;
798 void Client::handleCommand_StopSound(NetworkPacket* pkt)
804 UNORDERED_MAP<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
805 if (i != m_sounds_server_to_client.end()) {
806 int client_id = i->second;
807 m_sound->stopSound(client_id);
811 void Client::handleCommand_Privileges(NetworkPacket* pkt)
813 m_privileges.clear();
814 infostream << "Client: Privileges updated: ";
817 *pkt >> num_privileges;
819 for (u16 i = 0; i < num_privileges; i++) {
824 m_privileges.insert(priv);
825 infostream << priv << " ";
827 infostream << std::endl;
830 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
832 LocalPlayer *player = m_env.getLocalPlayer();
833 assert(player != NULL);
835 // Store formspec in LocalPlayer
836 player->inventory_formspec = pkt->readLongString();
839 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
841 std::string datastring(pkt->getString(0), pkt->getSize());
842 std::istringstream is(datastring, std::ios_base::binary);
844 std::string name = deSerializeString(is);
846 infostream << "Client: Detached inventory update: \"" << name
847 << "\"" << std::endl;
849 Inventory *inv = NULL;
850 if (m_detached_inventories.count(name) > 0)
851 inv = m_detached_inventories[name];
853 inv = new Inventory(m_itemdef);
854 m_detached_inventories[name] = inv;
856 inv->deSerialize(is);
859 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
861 std::string formspec = pkt->readLongString();
862 std::string formname;
867 event.type = CE_SHOW_FORMSPEC;
868 // pointer is required as event is a struct only!
869 // adding a std:string to a struct isn't possible
870 event.show_formspec.formspec = new std::string(formspec);
871 event.show_formspec.formname = new std::string(formname);
872 m_client_event_queue.push(event);
875 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
877 std::string datastring(pkt->getString(0), pkt->getSize());
878 std::istringstream is(datastring, std::ios_base::binary);
880 v3f pos = readV3F1000(is);
881 v3f vel = readV3F1000(is);
882 v3f acc = readV3F1000(is);
883 float expirationtime = readF1000(is);
884 float size = readF1000(is);
885 bool collisiondetection = readU8(is);
886 std::string texture = deSerializeLongString(is);
887 bool vertical = false;
888 bool collision_removal = false;
889 struct TileAnimationParams animation;
890 animation.type = TAT_NONE;
893 vertical = readU8(is);
894 collision_removal = readU8(is);
895 animation.deSerialize(is, m_proto_ver);
900 event.type = CE_SPAWN_PARTICLE;
901 event.spawn_particle.pos = new v3f (pos);
902 event.spawn_particle.vel = new v3f (vel);
903 event.spawn_particle.acc = new v3f (acc);
904 event.spawn_particle.expirationtime = expirationtime;
905 event.spawn_particle.size = size;
906 event.spawn_particle.collisiondetection = collisiondetection;
907 event.spawn_particle.collision_removal = collision_removal;
908 event.spawn_particle.vertical = vertical;
909 event.spawn_particle.texture = new std::string(texture);
910 event.spawn_particle.animation = animation;
911 event.spawn_particle.glow = glow;
913 m_client_event_queue.push(event);
916 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
930 bool collisiondetection;
933 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
934 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
935 >> maxsize >> collisiondetection;
937 std::string texture = pkt->readLongString();
941 bool vertical = false;
942 bool collision_removal = false;
943 struct TileAnimationParams animation;
944 animation.type = TAT_NONE;
949 *pkt >> collision_removal;
952 // This is horrible but required (why are there two ways to deserialize pkts?)
953 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
954 std::istringstream is(datastring, std::ios_base::binary);
955 animation.deSerialize(is, m_proto_ver);
960 event.type = CE_ADD_PARTICLESPAWNER;
961 event.add_particlespawner.amount = amount;
962 event.add_particlespawner.spawntime = spawntime;
963 event.add_particlespawner.minpos = new v3f (minpos);
964 event.add_particlespawner.maxpos = new v3f (maxpos);
965 event.add_particlespawner.minvel = new v3f (minvel);
966 event.add_particlespawner.maxvel = new v3f (maxvel);
967 event.add_particlespawner.minacc = new v3f (minacc);
968 event.add_particlespawner.maxacc = new v3f (maxacc);
969 event.add_particlespawner.minexptime = minexptime;
970 event.add_particlespawner.maxexptime = maxexptime;
971 event.add_particlespawner.minsize = minsize;
972 event.add_particlespawner.maxsize = maxsize;
973 event.add_particlespawner.collisiondetection = collisiondetection;
974 event.add_particlespawner.collision_removal = collision_removal;
975 event.add_particlespawner.attached_id = attached_id;
976 event.add_particlespawner.vertical = vertical;
977 event.add_particlespawner.texture = new std::string(texture);
978 event.add_particlespawner.id = id;
979 event.add_particlespawner.animation = animation;
980 event.add_particlespawner.glow = glow;
982 m_client_event_queue.push(event);
986 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
991 // Modification set 13/03/15, 1 year of compat for protocol v24
992 if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
1001 event.type = CE_DELETE_PARTICLESPAWNER;
1002 event.delete_particlespawner.id =
1003 (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
1005 m_client_event_queue.push(event);
1008 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1010 std::string datastring(pkt->getString(0), pkt->getSize());
1011 std::istringstream is(datastring, std::ios_base::binary);
1027 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1028 >> dir >> align >> offset;
1032 catch(SerializationError &e) {};
1036 } catch(SerializationError &e) {};
1039 event.type = CE_HUDADD;
1040 event.hudadd.id = id;
1041 event.hudadd.type = type;
1042 event.hudadd.pos = new v2f(pos);
1043 event.hudadd.name = new std::string(name);
1044 event.hudadd.scale = new v2f(scale);
1045 event.hudadd.text = new std::string(text);
1046 event.hudadd.number = number;
1047 event.hudadd.item = item;
1048 event.hudadd.dir = dir;
1049 event.hudadd.align = new v2f(align);
1050 event.hudadd.offset = new v2f(offset);
1051 event.hudadd.world_pos = new v3f(world_pos);
1052 event.hudadd.size = new v2s32(size);
1053 m_client_event_queue.push(event);
1056 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1063 event.type = CE_HUDRM;
1064 event.hudrm.id = id;
1065 m_client_event_queue.push(event);
1068 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1080 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1081 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1083 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1085 else if (stat == HUD_STAT_WORLD_POS)
1087 else if (stat == HUD_STAT_SIZE )
1093 event.type = CE_HUDCHANGE;
1094 event.hudchange.id = id;
1095 event.hudchange.stat = (HudElementStat)stat;
1096 event.hudchange.v2fdata = new v2f(v2fdata);
1097 event.hudchange.v3fdata = new v3f(v3fdata);
1098 event.hudchange.sdata = new std::string(sdata);
1099 event.hudchange.data = intdata;
1100 event.hudchange.v2s32data = new v2s32(v2s32data);
1101 m_client_event_queue.push(event);
1104 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1108 *pkt >> flags >> mask;
1110 LocalPlayer *player = m_env.getLocalPlayer();
1111 assert(player != NULL);
1113 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1115 player->hud_flags &= ~mask;
1116 player->hud_flags |= flags;
1118 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1120 // Hide minimap if it has been disabled by the server
1121 if (m_minimap_disabled_by_server && was_minimap_visible) {
1122 // defers a minimap update, therefore only call it if really
1123 // needed, by checking that minimap was visible before
1124 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1128 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1130 u16 param; std::string value;
1132 *pkt >> param >> value;
1134 LocalPlayer *player = m_env.getLocalPlayer();
1135 assert(player != NULL);
1137 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1138 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1139 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1140 player->hud_hotbar_itemcount = hotbar_itemcount;
1142 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1143 player->hotbar_image = value;
1145 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1146 player->hotbar_selected_image = value;
1150 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1152 std::string datastring(pkt->getString(0), pkt->getSize());
1153 std::istringstream is(datastring, std::ios_base::binary);
1155 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1156 std::string *type = new std::string(deSerializeString(is));
1157 u16 count = readU16(is);
1158 std::vector<std::string> *params = new std::vector<std::string>;
1160 for (size_t i = 0; i < count; i++)
1161 params->push_back(deSerializeString(is));
1164 event.type = CE_SET_SKY;
1165 event.set_sky.bgcolor = bgcolor;
1166 event.set_sky.type = type;
1167 event.set_sky.params = params;
1168 m_client_event_queue.push(event);
1171 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1174 video::SColor color_bright;
1175 video::SColor color_ambient;
1180 *pkt >> density >> color_bright >> color_ambient
1181 >> height >> thickness >> speed;
1184 event.type = CE_CLOUD_PARAMS;
1185 event.cloud_params.density = density;
1186 // use the underlying u32 representation, because we can't
1187 // use struct members with constructors here, and this way
1188 // we avoid using new() and delete() for no good reason
1189 event.cloud_params.color_bright = color_bright.color;
1190 event.cloud_params.color_ambient = color_ambient.color;
1191 event.cloud_params.height = height;
1192 event.cloud_params.thickness = thickness;
1193 // same here: deconstruct to skip constructor
1194 event.cloud_params.speed_x = speed.X;
1195 event.cloud_params.speed_y = speed.Y;
1196 m_client_event_queue.push(event);
1199 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1202 u16 day_night_ratio_u;
1204 *pkt >> do_override >> day_night_ratio_u;
1206 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1209 event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1210 event.override_day_night_ratio.do_override = do_override;
1211 event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1212 m_client_event_queue.push(event);
1215 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1217 LocalPlayer *player = m_env.getLocalPlayer();
1218 assert(player != NULL);
1220 *pkt >> player->local_animations[0];
1221 *pkt >> player->local_animations[1];
1222 *pkt >> player->local_animations[2];
1223 *pkt >> player->local_animations[3];
1224 *pkt >> player->local_animation_speed;
1227 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1229 LocalPlayer *player = m_env.getLocalPlayer();
1230 assert(player != NULL);
1232 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1235 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1237 if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1238 && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1239 errorstream << "Client: Recieved SRP S_B login message,"
1240 << " but wasn't supposed to (chosen_mech="
1241 << m_chosen_auth_mech << ")." << std::endl;
1247 SRPUser *usr = (SRPUser *) m_auth_data;
1252 infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1254 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1255 (const unsigned char *) B.c_str(), B.size(),
1256 (unsigned char **) &bytes_M, &len_M);
1259 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1263 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1264 resp_pkt << std::string(bytes_M, len_M);