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.
20 #include "client/client.h"
22 #include "util/base64.h"
23 #include "chatmessage.h"
24 #include "client/clientmedia.h"
27 #include "mapsector.h"
28 #include "client/minimap.h"
29 #include "modchannels.h"
31 #include "serialization.h"
33 #include "util/strfnd.h"
34 #include "client/clientevent.h"
35 #include "client/sound.h"
36 #include "network/clientopcodes.h"
37 #include "network/connection.h"
38 #include "script/scripting_client.h"
39 #include "util/serialize.h"
41 #include "tileanimation.h"
44 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
46 infostream << "Got deprecated command "
47 << toClientCommandTable[pkt->getCommand()].name << " from peer "
48 << pkt->getPeerId() << "!" << std::endl;
51 void Client::handleCommand_Hello(NetworkPacket* pkt)
53 if (pkt->getSize() < 1)
60 std::string username_legacy; // for case insensitivity
61 *pkt >> serialization_ver >> compression_mode >> proto_ver
62 >> auth_mechs >> username_legacy;
64 // Chose an auth method we support
65 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
67 infostream << "Client: TOCLIENT_HELLO received with "
68 << "serialization_ver=" << (u32)serialization_ver
69 << ", auth_mechs=" << auth_mechs
70 << ", proto_ver=" << proto_ver
71 << ", compression_mode=" << compression_mode
72 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
74 if (!ser_ver_supported(serialization_ver)) {
75 infostream << "Client: TOCLIENT_HELLO: Server sent "
76 << "unsupported ser_fmt_ver"<< std::endl;
80 m_server_ser_ver = serialization_ver;
81 m_proto_ver = proto_ver;
83 //TODO verify that username_legacy matches sent username, only
84 // differs in casing (make both uppercase and compare)
85 // This is only neccessary though when we actually want to add casing support
87 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
88 // we recieved a TOCLIENT_HELLO while auth was already going on
89 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
90 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
91 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
92 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
93 srp_user_delete((SRPUser *) m_auth_data);
98 // Authenticate using that method, or abort if there wasn't any method found
99 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
100 if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
101 !m_simple_singleplayer_mode &&
102 !getServerAddress().isLocalhost() &&
103 g_settings->getBool("enable_register_confirmation")) {
104 promptConfirmRegistration(chosen_auth_mechanism);
106 startAuth(chosen_auth_mechanism);
109 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
110 m_access_denied = true;
111 m_access_denied_reason = "Unknown";
117 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
122 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
123 >> m_sudo_auth_methods;
125 playerpos -= v3f(0, BS / 2, 0);
127 // Set player position
128 LocalPlayer *player = m_env.getLocalPlayer();
129 assert(player != NULL);
130 player->setPosition(playerpos);
132 infostream << "Client: received map seed: " << m_map_seed << std::endl;
133 infostream << "Client: received recommended send interval "
134 << m_recommended_send_interval<<std::endl;
137 std::string lang = gettext("LANG_CODE");
138 if (lang == "LANG_CODE")
141 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
147 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
151 m_password = m_new_password;
153 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
155 // send packet to actually set the password
156 startAuth(AUTH_MECHANISM_FIRST_SRP);
159 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
161 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
163 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
164 L"Password change denied. Password NOT changed.");
165 pushToChatQueue(chatMessage);
166 // reset everything and be sad
170 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
172 // The server didn't like our password. Note, this needs
173 // to be processed even if the serialisation format has
174 // not been agreed yet, the same as TOCLIENT_INIT.
175 m_access_denied = true;
176 m_access_denied_reason = "Unknown";
178 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
179 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
180 // in some places of the server code
181 if (pkt->getSize() >= 2) {
182 std::wstring wide_reason;
184 m_access_denied_reason = wide_to_utf8(wide_reason);
189 if (pkt->getSize() < 1)
192 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
194 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
195 denyCode == SERVER_ACCESSDENIED_CRASH) {
196 *pkt >> m_access_denied_reason;
197 if (m_access_denied_reason.empty()) {
198 m_access_denied_reason = accessDeniedStrings[denyCode];
202 m_access_denied_reconnect = reconnect & 1;
203 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
204 *pkt >> m_access_denied_reason;
205 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
206 m_access_denied_reason = accessDeniedStrings[denyCode];
208 // Allow us to add new error messages to the
209 // protocol without raising the protocol version, if we want to.
210 // Until then (which may be never), this is outside
211 // of the defined protocol.
212 *pkt >> m_access_denied_reason;
213 if (m_access_denied_reason.empty()) {
214 m_access_denied_reason = "Unknown";
219 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
221 if (pkt->getSize() < 6)
229 void Client::handleCommand_AddNode(NetworkPacket* pkt)
231 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
238 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
240 bool remove_metadata = true;
241 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
242 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
243 remove_metadata = false;
246 addNode(p, n, remove_metadata);
249 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
251 if (pkt->getSize() < 1)
254 std::istringstream is(pkt->readLongString(), std::ios::binary);
255 std::stringstream sstr;
256 decompressZlib(is, sstr);
258 NodeMetadataList meta_updates_list(false);
259 meta_updates_list.deSerialize(sstr, m_itemdef, true);
261 Map &map = m_env.getMap();
262 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
263 i != meta_updates_list.end(); ++i) {
264 v3s16 pos = i->first;
266 if (map.isValidPosition(pos) &&
267 map.setNodeMetadata(pos, i->second))
268 continue; // Prevent from deleting metadata
270 // Meta couldn't be set, unused metadata
275 void Client::handleCommand_BlockData(NetworkPacket* pkt)
277 // Ignore too small packet
278 if (pkt->getSize() < 6)
284 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
285 std::istringstream istr(datastring, std::ios_base::binary);
291 sector = m_env.getMap().emergeSector(p2d);
293 assert(sector->getPos() == p2d);
295 block = sector->getBlockNoCreateNoEx(p.Y);
298 Update an existing block
300 block->deSerialize(istr, m_server_ser_ver, false);
301 block->deSerializeNetworkSpecific(istr);
307 block = new MapBlock(&m_env.getMap(), p, this);
308 block->deSerialize(istr, m_server_ser_ver, false);
309 block->deSerializeNetworkSpecific(istr);
310 sector->insertBlock(block);
314 ServerMap::saveBlock(block, m_localdb);
318 Add it to mesh update queue and set it to be acknowledged after update.
320 addUpdateMeshTaskWithEdge(p, true);
323 void Client::handleCommand_Inventory(NetworkPacket* pkt)
325 if (pkt->getSize() < 1)
328 std::string datastring(pkt->getString(0), pkt->getSize());
329 std::istringstream is(datastring, std::ios_base::binary);
331 LocalPlayer *player = m_env.getLocalPlayer();
332 assert(player != NULL);
334 player->inventory.deSerialize(is);
336 m_update_wielded_item = true;
338 delete m_inventory_from_server;
339 m_inventory_from_server = new Inventory(player->inventory);
340 m_inventory_from_server_age = 0.0;
343 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
345 if (pkt->getSize() < 2)
352 time_of_day = time_of_day % 24000;
353 float time_speed = 0;
355 if (pkt->getSize() >= 2 + 4) {
359 // Old message; try to approximate speed of time by ourselves
360 float time_of_day_f = (float)time_of_day / 24000.0f;
361 float tod_diff_f = 0;
363 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
364 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
366 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
368 m_last_time_of_day_f = time_of_day_f;
369 float time_diff = m_time_of_day_update_timer;
370 m_time_of_day_update_timer = 0;
372 if (m_time_of_day_set) {
373 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
374 infostream << "Client: Measured time_of_day speed (old format): "
375 << time_speed << " tod_diff_f=" << tod_diff_f
376 << " time_diff=" << time_diff << std::endl;
380 // Update environment
381 m_env.setTimeOfDay(time_of_day);
382 m_env.setTimeOfDaySpeed(time_speed);
383 m_time_of_day_set = true;
385 u32 dr = m_env.getDayNightRatio();
386 infostream << "Client: time_of_day=" << time_of_day
387 << " time_speed=" << time_speed
388 << " dr=" << dr << std::endl;
391 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
396 u16 sendername length
402 ChatMessage *chatMessage = new ChatMessage();
403 u8 version, message_type;
404 *pkt >> version >> message_type;
406 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
412 *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
413 chatMessage->timestamp = static_cast<std::time_t>(timestamp);
415 chatMessage->type = (ChatMessageType) message_type;
417 // @TODO send this to CSM using ChatMessage object
418 if (modsLoaded() && m_script->on_receiving_message(
419 wide_to_utf8(chatMessage->message))) {
420 // Message was consumed by CSM and should not be handled by client
423 pushToChatQueue(chatMessage);
427 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
430 u16 count of removed objects
431 for all removed objects {
434 u16 count of added objects
435 for all added objects {
438 u32 initialization data length
439 string initialization data
445 u16 removed_count, added_count, id;
447 // Read removed objects
448 *pkt >> removed_count;
450 for (u16 i = 0; i < removed_count; i++) {
452 m_env.removeActiveObject(id);
455 // Read added objects
458 for (u16 i = 0; i < added_count; i++) {
460 m_env.addActiveObject(id, type, pkt->readLongString());
462 } catch (PacketError &e) {
463 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
464 << ". The packet is unreliable, ignoring" << std::endl;
468 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
478 std::string datastring(pkt->getString(0), pkt->getSize());
479 std::istringstream is(datastring, std::ios_base::binary);
483 u16 id = readU16(is);
487 std::string message = deSerializeString(is);
489 // Pass on to the environment
490 m_env.processActiveObjectMessage(id, message);
492 } catch (SerializationError &e) {
493 errorstream << "Client::handleCommand_ActiveObjectMessages: "
494 << "caught SerializationError: " << e.what() << std::endl;
498 void Client::handleCommand_Movement(NetworkPacket* pkt)
500 LocalPlayer *player = m_env.getLocalPlayer();
501 assert(player != NULL);
503 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
505 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
506 >> lf >> lfs >> ls >> g;
508 player->movement_acceleration_default = mad * BS;
509 player->movement_acceleration_air = maa * BS;
510 player->movement_acceleration_fast = maf * BS;
511 player->movement_speed_walk = msw * BS;
512 player->movement_speed_crouch = mscr * BS;
513 player->movement_speed_fast = msf * BS;
514 player->movement_speed_climb = mscl * BS;
515 player->movement_speed_jump = msj * BS;
516 player->movement_liquid_fluidity = lf * BS;
517 player->movement_liquid_fluidity_smooth = lfs * BS;
518 player->movement_liquid_sink = ls * BS;
519 player->movement_gravity = g * BS;
522 void Client::handleCommand_HP(NetworkPacket* pkt)
525 LocalPlayer *player = m_env.getLocalPlayer();
526 assert(player != NULL);
528 u16 oldhp = player->hp;
536 m_script->on_hp_modification(hp);
539 // Add to ClientEvent queue
540 ClientEvent *event = new ClientEvent();
541 event->type = CE_PLAYER_DAMAGE;
542 event->player_damage.amount = oldhp - hp;
543 m_client_event_queue.push(event);
547 void Client::handleCommand_Breath(NetworkPacket* pkt)
549 LocalPlayer *player = m_env.getLocalPlayer();
550 assert(player != NULL);
556 player->setBreath(breath);
559 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
561 LocalPlayer *player = m_env.getLocalPlayer();
562 assert(player != NULL);
567 *pkt >> pos >> pitch >> yaw;
569 player->setPosition(pos);
571 infostream << "Client got TOCLIENT_MOVE_PLAYER"
572 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
573 << " pitch=" << pitch
578 Add to ClientEvent queue.
579 This has to be sent to the main program because otherwise
580 it would just force the pitch and yaw values to whatever
581 the camera points to.
583 ClientEvent *event = new ClientEvent();
584 event->type = CE_PLAYER_FORCE_MOVE;
585 event->player_force_move.pitch = pitch;
586 event->player_force_move.yaw = yaw;
587 m_client_event_queue.push(event);
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;
598 ClientEvent *event = new ClientEvent();
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(","));
649 if (!baseurl.empty())
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 || !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)
877 bool keep_inv = true;
878 *pkt >> name >> keep_inv;
880 infostream << "Client: Detached inventory update: \"" << name
881 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
883 const auto &inv_it = m_detached_inventories.find(name);
885 if (inv_it != m_detached_inventories.end()) {
886 delete inv_it->second;
887 m_detached_inventories.erase(inv_it);
891 Inventory *inv = nullptr;
892 if (inv_it == m_detached_inventories.end()) {
893 inv = new Inventory(m_itemdef);
894 m_detached_inventories[name] = inv;
896 inv = inv_it->second;
900 *pkt >> ignore; // this used to be the length of the following string, ignore it
902 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
903 std::istringstream is(contents, std::ios::binary);
904 inv->deSerialize(is);
907 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
909 std::string formspec = pkt->readLongString();
910 std::string formname;
914 ClientEvent *event = new ClientEvent();
915 event->type = CE_SHOW_FORMSPEC;
916 // pointer is required as event is a struct only!
917 // adding a std:string to a struct isn't possible
918 event->show_formspec.formspec = new std::string(formspec);
919 event->show_formspec.formname = new std::string(formname);
920 m_client_event_queue.push(event);
923 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
925 std::string datastring(pkt->getString(0), pkt->getSize());
926 std::istringstream is(datastring, std::ios_base::binary);
928 v3f pos = readV3F32(is);
929 v3f vel = readV3F32(is);
930 v3f acc = readV3F32(is);
931 float expirationtime = readF32(is);
932 float size = readF32(is);
933 bool collisiondetection = readU8(is);
934 std::string texture = deSerializeLongString(is);
936 bool vertical = false;
937 bool collision_removal = false;
938 TileAnimationParams animation;
939 animation.type = TAT_NONE;
941 bool object_collision = false;
943 vertical = readU8(is);
944 collision_removal = readU8(is);
945 animation.deSerialize(is, m_proto_ver);
947 object_collision = readU8(is);
950 ClientEvent *event = new ClientEvent();
951 event->type = CE_SPAWN_PARTICLE;
952 event->spawn_particle.pos = new v3f (pos);
953 event->spawn_particle.vel = new v3f (vel);
954 event->spawn_particle.acc = new v3f (acc);
955 event->spawn_particle.expirationtime = expirationtime;
956 event->spawn_particle.size = size;
957 event->spawn_particle.collisiondetection = collisiondetection;
958 event->spawn_particle.collision_removal = collision_removal;
959 event->spawn_particle.object_collision = object_collision;
960 event->spawn_particle.vertical = vertical;
961 event->spawn_particle.texture = new std::string(texture);
962 event->spawn_particle.animation = animation;
963 event->spawn_particle.glow = glow;
965 m_client_event_queue.push(event);
968 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
982 bool collisiondetection;
985 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
986 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
987 >> maxsize >> collisiondetection;
989 std::string texture = pkt->readLongString();
993 bool vertical = false;
994 bool collision_removal = false;
996 TileAnimationParams animation;
997 animation.type = TAT_NONE;
999 bool object_collision = false;
1002 *pkt >> collision_removal;
1003 *pkt >> attached_id;
1005 // This is horrible but required (why are there two ways to deserialize pkts?)
1006 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1007 std::istringstream is(datastring, std::ios_base::binary);
1008 animation.deSerialize(is, m_proto_ver);
1010 object_collision = readU8(is);
1013 auto event = new ClientEvent();
1014 event->type = CE_ADD_PARTICLESPAWNER;
1015 event->add_particlespawner.amount = amount;
1016 event->add_particlespawner.spawntime = spawntime;
1017 event->add_particlespawner.minpos = new v3f (minpos);
1018 event->add_particlespawner.maxpos = new v3f (maxpos);
1019 event->add_particlespawner.minvel = new v3f (minvel);
1020 event->add_particlespawner.maxvel = new v3f (maxvel);
1021 event->add_particlespawner.minacc = new v3f (minacc);
1022 event->add_particlespawner.maxacc = new v3f (maxacc);
1023 event->add_particlespawner.minexptime = minexptime;
1024 event->add_particlespawner.maxexptime = maxexptime;
1025 event->add_particlespawner.minsize = minsize;
1026 event->add_particlespawner.maxsize = maxsize;
1027 event->add_particlespawner.collisiondetection = collisiondetection;
1028 event->add_particlespawner.collision_removal = collision_removal;
1029 event->add_particlespawner.object_collision = object_collision;
1030 event->add_particlespawner.attached_id = attached_id;
1031 event->add_particlespawner.vertical = vertical;
1032 event->add_particlespawner.texture = new std::string(texture);
1033 event->add_particlespawner.id = server_id;
1034 event->add_particlespawner.animation = animation;
1035 event->add_particlespawner.glow = glow;
1037 m_client_event_queue.push(event);
1041 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1046 ClientEvent *event = new ClientEvent();
1047 event->type = CE_DELETE_PARTICLESPAWNER;
1048 event->delete_particlespawner.id = server_id;
1050 m_client_event_queue.push(event);
1053 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1055 std::string datastring(pkt->getString(0), pkt->getSize());
1056 std::istringstream is(datastring, std::ios_base::binary);
1072 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1073 >> dir >> align >> offset;
1077 catch(SerializationError &e) {};
1081 } catch(SerializationError &e) {};
1083 ClientEvent *event = new ClientEvent();
1084 event->type = CE_HUDADD;
1085 event->hudadd.server_id = server_id;
1086 event->hudadd.type = type;
1087 event->hudadd.pos = new v2f(pos);
1088 event->hudadd.name = new std::string(name);
1089 event->hudadd.scale = new v2f(scale);
1090 event->hudadd.text = new std::string(text);
1091 event->hudadd.number = number;
1092 event->hudadd.item = item;
1093 event->hudadd.dir = dir;
1094 event->hudadd.align = new v2f(align);
1095 event->hudadd.offset = new v2f(offset);
1096 event->hudadd.world_pos = new v3f(world_pos);
1097 event->hudadd.size = new v2s32(size);
1098 m_client_event_queue.push(event);
1101 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1107 auto i = m_hud_server_to_client.find(server_id);
1108 if (i != m_hud_server_to_client.end()) {
1109 int client_id = i->second;
1110 m_hud_server_to_client.erase(i);
1112 ClientEvent *event = new ClientEvent();
1113 event->type = CE_HUDRM;
1114 event->hudrm.id = client_id;
1115 m_client_event_queue.push(event);
1119 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1129 *pkt >> server_id >> stat;
1131 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1132 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1134 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1136 else if (stat == HUD_STAT_WORLD_POS)
1138 else if (stat == HUD_STAT_SIZE )
1143 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1144 if (i != m_hud_server_to_client.end()) {
1145 ClientEvent *event = new ClientEvent();
1146 event->type = CE_HUDCHANGE;
1147 event->hudchange.id = i->second;
1148 event->hudchange.stat = (HudElementStat)stat;
1149 event->hudchange.v2fdata = new v2f(v2fdata);
1150 event->hudchange.v3fdata = new v3f(v3fdata);
1151 event->hudchange.sdata = new std::string(sdata);
1152 event->hudchange.data = intdata;
1153 event->hudchange.v2s32data = new v2s32(v2s32data);
1154 m_client_event_queue.push(event);
1158 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1162 *pkt >> flags >> mask;
1164 LocalPlayer *player = m_env.getLocalPlayer();
1165 assert(player != NULL);
1167 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1168 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1170 player->hud_flags &= ~mask;
1171 player->hud_flags |= flags;
1173 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1174 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1176 // Hide minimap if it has been disabled by the server
1177 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1178 // defers a minimap update, therefore only call it if really
1179 // needed, by checking that minimap was visible before
1180 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1182 // Switch to surface mode if radar disabled by server
1183 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1184 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1187 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1189 u16 param; std::string value;
1191 *pkt >> param >> value;
1193 LocalPlayer *player = m_env.getLocalPlayer();
1194 assert(player != NULL);
1196 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1197 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1198 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1199 player->hud_hotbar_itemcount = hotbar_itemcount;
1201 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1202 // If value not empty verify image exists in texture source
1203 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1204 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1205 << value << "')" << std::endl;
1208 player->hotbar_image = value;
1210 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1211 // If value not empty verify image exists in texture source
1212 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1213 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1214 << value << "')" << std::endl;
1217 player->hotbar_selected_image = value;
1221 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1223 std::string datastring(pkt->getString(0), pkt->getSize());
1224 std::istringstream is(datastring, std::ios_base::binary);
1226 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1227 std::string *type = new std::string(deSerializeString(is));
1228 u16 count = readU16(is);
1229 std::vector<std::string> *params = new std::vector<std::string>;
1231 for (size_t i = 0; i < count; i++)
1232 params->push_back(deSerializeString(is));
1236 clouds = readU8(is);
1239 ClientEvent *event = new ClientEvent();
1240 event->type = CE_SET_SKY;
1241 event->set_sky.bgcolor = bgcolor;
1242 event->set_sky.type = type;
1243 event->set_sky.params = params;
1244 event->set_sky.clouds = clouds;
1245 m_client_event_queue.push(event);
1248 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1251 video::SColor color_bright;
1252 video::SColor color_ambient;
1257 *pkt >> density >> color_bright >> color_ambient
1258 >> height >> thickness >> speed;
1260 ClientEvent *event = new ClientEvent();
1261 event->type = CE_CLOUD_PARAMS;
1262 event->cloud_params.density = density;
1263 // use the underlying u32 representation, because we can't
1264 // use struct members with constructors here, and this way
1265 // we avoid using new() and delete() for no good reason
1266 event->cloud_params.color_bright = color_bright.color;
1267 event->cloud_params.color_ambient = color_ambient.color;
1268 event->cloud_params.height = height;
1269 event->cloud_params.thickness = thickness;
1270 // same here: deconstruct to skip constructor
1271 event->cloud_params.speed_x = speed.X;
1272 event->cloud_params.speed_y = speed.Y;
1273 m_client_event_queue.push(event);
1276 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1279 u16 day_night_ratio_u;
1281 *pkt >> do_override >> day_night_ratio_u;
1283 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1285 ClientEvent *event = new ClientEvent();
1286 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1287 event->override_day_night_ratio.do_override = do_override;
1288 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1289 m_client_event_queue.push(event);
1292 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1294 LocalPlayer *player = m_env.getLocalPlayer();
1295 assert(player != NULL);
1297 *pkt >> player->local_animations[0];
1298 *pkt >> player->local_animations[1];
1299 *pkt >> player->local_animations[2];
1300 *pkt >> player->local_animations[3];
1301 *pkt >> player->local_animation_speed;
1304 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1306 LocalPlayer *player = m_env.getLocalPlayer();
1307 assert(player != NULL);
1309 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1312 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1316 *pkt >> type >> num_players;
1317 PlayerListModifer notice_type = (PlayerListModifer) type;
1319 for (u16 i = 0; i < num_players; i++) {
1322 switch (notice_type) {
1323 case PLAYER_LIST_INIT:
1324 case PLAYER_LIST_ADD:
1325 m_env.addPlayerName(name);
1327 case PLAYER_LIST_REMOVE:
1328 m_env.removePlayerName(name);
1334 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1336 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1337 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1338 errorstream << "Client: Received SRP S_B login message,"
1339 << " but wasn't supposed to (chosen_mech="
1340 << m_chosen_auth_mech << ")." << std::endl;
1346 SRPUser *usr = (SRPUser *) m_auth_data;
1351 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1353 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1354 (const unsigned char *) B.c_str(), B.size(),
1355 (unsigned char **) &bytes_M, &len_M);
1358 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1362 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1363 resp_pkt << std::string(bytes_M, len_M);
1367 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1369 LocalPlayer *player = m_env.getLocalPlayer();
1370 assert(player != NULL);
1372 // Store formspec in LocalPlayer
1373 *pkt >> player->formspec_prepend;
1376 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1378 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1380 // Restrictions were received -> load mods if it's enabled
1381 // Note: this should be moved after mods receptions from server instead
1385 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1391 LocalPlayer *player = m_env.getLocalPlayer();
1392 assert(player != NULL);
1393 player->addVelocity(added_vel);
1400 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1402 std::string channel_name, sender, channel_msg;
1403 *pkt >> channel_name >> sender >> channel_msg;
1405 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1406 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1407 << channel_msg << std::endl;
1409 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1410 verbosestream << "Server sent us messages on unregistered channel "
1411 << channel_name << ", ignoring." << std::endl;
1415 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1418 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1421 ModChannelSignal signal;
1422 std::string channel;
1424 *pkt >> signal_tmp >> channel;
1426 signal = (ModChannelSignal)signal_tmp;
1428 bool valid_signal = true;
1429 // @TODO: send Signal to Lua API
1431 case MODCHANNEL_SIGNAL_JOIN_OK:
1432 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1433 infostream << "Server ack our mod channel join on channel `" << channel
1434 << "`, joining." << std::endl;
1436 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1437 // Unable to join, remove channel
1438 m_modchannel_mgr->leaveChannel(channel, 0);
1439 infostream << "Server refused our mod channel join on channel `" << channel
1440 << "`" << std::endl;
1442 case MODCHANNEL_SIGNAL_LEAVE_OK:
1444 infostream << "Server ack our mod channel leave on channel " << channel
1445 << "`, leaving." << std::endl;
1448 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1449 infostream << "Server refused our mod channel leave on channel `" << channel
1450 << "`" << std::endl;
1452 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1454 // Generally unused, but ensure we don't do an implementation error
1455 infostream << "Server tells us we sent a message on channel `" << channel
1456 << "` but we are not registered. Message was dropped." << std::endl;
1459 case MODCHANNEL_SIGNAL_SET_STATE: {
1463 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1464 infostream << "Received wrong channel state " << state
1465 << ", ignoring." << std::endl;
1469 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1470 infostream << "Server sets mod channel `" << channel
1471 << "` in read-only mode." << std::endl;
1476 warningstream << "Received unhandled mod channel signal ID "
1477 << signal << ", ignoring." << std::endl;
1479 valid_signal = false;
1483 // If signal is valid, forward it to client side mods
1485 m_script->on_modchannel_signal(channel, signal);