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;
467 // m_activeobjects_received is false before the first
468 // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
469 m_activeobjects_received = true;
472 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
482 std::string datastring(pkt->getString(0), pkt->getSize());
483 std::istringstream is(datastring, std::ios_base::binary);
487 u16 id = readU16(is);
491 std::string message = deSerializeString(is);
493 // Pass on to the environment
494 m_env.processActiveObjectMessage(id, message);
496 } catch (SerializationError &e) {
497 errorstream << "Client::handleCommand_ActiveObjectMessages: "
498 << "caught SerializationError: " << e.what() << std::endl;
502 void Client::handleCommand_Movement(NetworkPacket* pkt)
504 LocalPlayer *player = m_env.getLocalPlayer();
505 assert(player != NULL);
507 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
509 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
510 >> lf >> lfs >> ls >> g;
512 player->movement_acceleration_default = mad * BS;
513 player->movement_acceleration_air = maa * BS;
514 player->movement_acceleration_fast = maf * BS;
515 player->movement_speed_walk = msw * BS;
516 player->movement_speed_crouch = mscr * BS;
517 player->movement_speed_fast = msf * BS;
518 player->movement_speed_climb = mscl * BS;
519 player->movement_speed_jump = msj * BS;
520 player->movement_liquid_fluidity = lf * BS;
521 player->movement_liquid_fluidity_smooth = lfs * BS;
522 player->movement_liquid_sink = ls * BS;
523 player->movement_gravity = g * BS;
526 void Client::handleCommand_Fov(NetworkPacket *pkt)
530 *pkt >> fov >> is_multiplier;
532 LocalPlayer *player = m_env.getLocalPlayer();
533 player->setFov({ fov, is_multiplier });
536 void Client::handleCommand_HP(NetworkPacket *pkt)
538 LocalPlayer *player = m_env.getLocalPlayer();
539 assert(player != NULL);
541 u16 oldhp = player->hp;
549 m_script->on_hp_modification(hp);
552 // Add to ClientEvent queue
553 ClientEvent *event = new ClientEvent();
554 event->type = CE_PLAYER_DAMAGE;
555 event->player_damage.amount = oldhp - hp;
556 m_client_event_queue.push(event);
560 void Client::handleCommand_Breath(NetworkPacket* pkt)
562 LocalPlayer *player = m_env.getLocalPlayer();
563 assert(player != NULL);
569 player->setBreath(breath);
572 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
574 LocalPlayer *player = m_env.getLocalPlayer();
575 assert(player != NULL);
580 *pkt >> pos >> pitch >> yaw;
582 player->setPosition(pos);
584 infostream << "Client got TOCLIENT_MOVE_PLAYER"
585 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
586 << " pitch=" << pitch
591 Add to ClientEvent queue.
592 This has to be sent to the main program because otherwise
593 it would just force the pitch and yaw values to whatever
594 the camera points to.
596 ClientEvent *event = new ClientEvent();
597 event->type = CE_PLAYER_FORCE_MOVE;
598 event->player_force_move.pitch = pitch;
599 event->player_force_move.yaw = yaw;
600 m_client_event_queue.push(event);
603 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
605 bool set_camera_point_target;
606 v3f camera_point_target;
608 *pkt >> set_camera_point_target;
609 *pkt >> camera_point_target;
611 ClientEvent *event = new ClientEvent();
612 event->type = CE_DEATHSCREEN;
613 event->deathscreen.set_camera_point_target = set_camera_point_target;
614 event->deathscreen.camera_point_target_x = camera_point_target.X;
615 event->deathscreen.camera_point_target_y = camera_point_target.Y;
616 event->deathscreen.camera_point_target_z = camera_point_target.Z;
617 m_client_event_queue.push(event);
620 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
626 infostream << "Client: Received media announcement: packet size: "
627 << pkt->getSize() << std::endl;
629 if (m_media_downloader == NULL ||
630 m_media_downloader->isStarted()) {
631 const char *problem = m_media_downloader ?
632 "we already saw another announcement" :
633 "all media has been received already";
634 errorstream << "Client: Received media announcement but "
636 << " files=" << num_files
637 << " size=" << pkt->getSize() << std::endl;
641 // Mesh update thread must be stopped while
642 // updating content definitions
643 sanity_check(!m_mesh_update_thread.isRunning());
645 for (u16 i = 0; i < num_files; i++) {
646 std::string name, sha1_base64;
648 *pkt >> name >> sha1_base64;
650 std::string sha1_raw = base64_decode(sha1_base64);
651 m_media_downloader->addFile(name, sha1_raw);
660 while(!sf.at_end()) {
661 std::string baseurl = trim(sf.next(","));
662 if (!baseurl.empty())
663 m_media_downloader->addRemoteServer(baseurl);
666 catch(SerializationError& e) {
667 // not supported by server or turned off
670 m_media_downloader->step(this);
673 void Client::handleCommand_Media(NetworkPacket* pkt)
677 u16 total number of file bunches
678 u16 index of this bunch
679 u32 number of files in this bunch
691 *pkt >> num_bunches >> bunch_i >> num_files;
693 infostream << "Client: Received files: bunch " << bunch_i << "/"
694 << num_bunches << " files=" << num_files
695 << " size=" << pkt->getSize() << std::endl;
700 if (!m_media_downloader || !m_media_downloader->isStarted()) {
701 const char *problem = m_media_downloader ?
702 "media has not been requested" :
703 "all media has been received already";
704 errorstream << "Client: Received media but "
706 << " bunch " << bunch_i << "/" << num_bunches
707 << " files=" << num_files
708 << " size=" << pkt->getSize() << std::endl;
712 // Mesh update thread must be stopped while
713 // updating content definitions
714 sanity_check(!m_mesh_update_thread.isRunning());
716 for (u32 i=0; i < num_files; i++) {
721 std::string data = pkt->readLongString();
723 m_media_downloader->conventionalTransferDone(
728 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
730 infostream << "Client: Received node definitions: packet size: "
731 << pkt->getSize() << std::endl;
733 // Mesh update thread must be stopped while
734 // updating content definitions
735 sanity_check(!m_mesh_update_thread.isRunning());
737 // Decompress node definitions
738 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
739 std::ostringstream tmp_os;
740 decompressZlib(tmp_is, tmp_os);
742 // Deserialize node definitions
743 std::istringstream tmp_is2(tmp_os.str());
744 m_nodedef->deSerialize(tmp_is2);
745 m_nodedef_received = true;
748 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
750 infostream << "Client: Received item definitions: packet size: "
751 << pkt->getSize() << std::endl;
753 // Mesh update thread must be stopped while
754 // updating content definitions
755 sanity_check(!m_mesh_update_thread.isRunning());
757 // Decompress item definitions
758 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
759 std::ostringstream tmp_os;
760 decompressZlib(tmp_is, tmp_os);
762 // Deserialize node definitions
763 std::istringstream tmp_is2(tmp_os.str());
764 m_itemdef->deSerialize(tmp_is2);
765 m_itemdef_received = true;
768 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
776 [11 + len] (f32 * 3) pos
777 [23 + len] u16 object_id
781 [34 + len] bool ephemeral
788 u8 type; // 0=local, 1=positional, 2=object
794 bool ephemeral = false;
796 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
802 } catch (PacketError &e) {};
808 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
810 case 1: // positional
811 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
815 ClientActiveObject *cao = m_env.getActiveObject(object_id);
817 pos = cao->getPosition();
818 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
825 if (client_id != -1) {
826 // for ephemeral sounds, server_id is not meaningful
828 m_sounds_server_to_client[server_id] = client_id;
829 m_sounds_client_to_server[client_id] = server_id;
832 m_sounds_to_objects[client_id] = object_id;
836 void Client::handleCommand_StopSound(NetworkPacket* pkt)
842 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
843 if (i != m_sounds_server_to_client.end()) {
844 int client_id = i->second;
845 m_sound->stopSound(client_id);
849 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
855 *pkt >> sound_id >> step >> gain;
857 std::unordered_map<s32, int>::const_iterator i =
858 m_sounds_server_to_client.find(sound_id);
860 if (i != m_sounds_server_to_client.end())
861 m_sound->fadeSound(i->second, step, gain);
864 void Client::handleCommand_Privileges(NetworkPacket* pkt)
866 m_privileges.clear();
867 infostream << "Client: Privileges updated: ";
870 *pkt >> num_privileges;
872 for (u16 i = 0; i < num_privileges; i++) {
877 m_privileges.insert(priv);
878 infostream << priv << " ";
880 infostream << std::endl;
883 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
885 LocalPlayer *player = m_env.getLocalPlayer();
886 assert(player != NULL);
888 // Store formspec in LocalPlayer
889 player->inventory_formspec = pkt->readLongString();
892 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
895 bool keep_inv = true;
896 *pkt >> name >> keep_inv;
898 infostream << "Client: Detached inventory update: \"" << name
899 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
901 const auto &inv_it = m_detached_inventories.find(name);
903 if (inv_it != m_detached_inventories.end()) {
904 delete inv_it->second;
905 m_detached_inventories.erase(inv_it);
909 Inventory *inv = nullptr;
910 if (inv_it == m_detached_inventories.end()) {
911 inv = new Inventory(m_itemdef);
912 m_detached_inventories[name] = inv;
914 inv = inv_it->second;
918 *pkt >> ignore; // this used to be the length of the following string, ignore it
920 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
921 std::istringstream is(contents, std::ios::binary);
922 inv->deSerialize(is);
925 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
927 std::string formspec = pkt->readLongString();
928 std::string formname;
932 ClientEvent *event = new ClientEvent();
933 event->type = CE_SHOW_FORMSPEC;
934 // pointer is required as event is a struct only!
935 // adding a std:string to a struct isn't possible
936 event->show_formspec.formspec = new std::string(formspec);
937 event->show_formspec.formname = new std::string(formname);
938 m_client_event_queue.push(event);
941 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
943 std::string datastring(pkt->getString(0), pkt->getSize());
944 std::istringstream is(datastring, std::ios_base::binary);
946 v3f pos = readV3F32(is);
947 v3f vel = readV3F32(is);
948 v3f acc = readV3F32(is);
949 float expirationtime = readF32(is);
950 float size = readF32(is);
951 bool collisiondetection = readU8(is);
952 std::string texture = deSerializeLongString(is);
954 bool vertical = false;
955 bool collision_removal = false;
956 TileAnimationParams animation;
957 animation.type = TAT_NONE;
959 bool object_collision = false;
961 vertical = readU8(is);
962 collision_removal = readU8(is);
963 animation.deSerialize(is, m_proto_ver);
965 object_collision = readU8(is);
968 ClientEvent *event = new ClientEvent();
969 event->type = CE_SPAWN_PARTICLE;
970 event->spawn_particle.pos = new v3f (pos);
971 event->spawn_particle.vel = new v3f (vel);
972 event->spawn_particle.acc = new v3f (acc);
973 event->spawn_particle.expirationtime = expirationtime;
974 event->spawn_particle.size = size;
975 event->spawn_particle.collisiondetection = collisiondetection;
976 event->spawn_particle.collision_removal = collision_removal;
977 event->spawn_particle.object_collision = object_collision;
978 event->spawn_particle.vertical = vertical;
979 event->spawn_particle.texture = new std::string(texture);
980 event->spawn_particle.animation = animation;
981 event->spawn_particle.glow = glow;
983 m_client_event_queue.push(event);
986 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
1000 bool collisiondetection;
1003 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
1004 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
1005 >> maxsize >> collisiondetection;
1007 std::string texture = pkt->readLongString();
1011 bool vertical = false;
1012 bool collision_removal = false;
1013 u16 attached_id = 0;
1014 TileAnimationParams animation;
1015 animation.type = TAT_NONE;
1017 bool object_collision = false;
1020 *pkt >> collision_removal;
1021 *pkt >> attached_id;
1023 // This is horrible but required (why are there two ways to deserialize pkts?)
1024 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1025 std::istringstream is(datastring, std::ios_base::binary);
1026 animation.deSerialize(is, m_proto_ver);
1028 object_collision = readU8(is);
1031 auto event = new ClientEvent();
1032 event->type = CE_ADD_PARTICLESPAWNER;
1033 event->add_particlespawner.amount = amount;
1034 event->add_particlespawner.spawntime = spawntime;
1035 event->add_particlespawner.minpos = new v3f (minpos);
1036 event->add_particlespawner.maxpos = new v3f (maxpos);
1037 event->add_particlespawner.minvel = new v3f (minvel);
1038 event->add_particlespawner.maxvel = new v3f (maxvel);
1039 event->add_particlespawner.minacc = new v3f (minacc);
1040 event->add_particlespawner.maxacc = new v3f (maxacc);
1041 event->add_particlespawner.minexptime = minexptime;
1042 event->add_particlespawner.maxexptime = maxexptime;
1043 event->add_particlespawner.minsize = minsize;
1044 event->add_particlespawner.maxsize = maxsize;
1045 event->add_particlespawner.collisiondetection = collisiondetection;
1046 event->add_particlespawner.collision_removal = collision_removal;
1047 event->add_particlespawner.object_collision = object_collision;
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 = server_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 ClientEvent *event = new ClientEvent();
1065 event->type = CE_DELETE_PARTICLESPAWNER;
1066 event->delete_particlespawner.id = server_id;
1068 m_client_event_queue.push(event);
1071 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1073 std::string datastring(pkt->getString(0), pkt->getSize());
1074 std::istringstream is(datastring, std::ios_base::binary);
1091 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1092 >> dir >> align >> offset;
1096 catch(SerializationError &e) {};
1100 } catch(SerializationError &e) {};
1105 catch(PacketError &e) {}
1107 ClientEvent *event = new ClientEvent();
1108 event->type = CE_HUDADD;
1109 event->hudadd.server_id = server_id;
1110 event->hudadd.type = type;
1111 event->hudadd.pos = new v2f(pos);
1112 event->hudadd.name = new std::string(name);
1113 event->hudadd.scale = new v2f(scale);
1114 event->hudadd.text = new std::string(text);
1115 event->hudadd.number = number;
1116 event->hudadd.item = item;
1117 event->hudadd.dir = dir;
1118 event->hudadd.align = new v2f(align);
1119 event->hudadd.offset = new v2f(offset);
1120 event->hudadd.world_pos = new v3f(world_pos);
1121 event->hudadd.size = new v2s32(size);
1122 event->hudadd.z_index = z_index;
1123 m_client_event_queue.push(event);
1126 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1132 auto i = m_hud_server_to_client.find(server_id);
1133 if (i != m_hud_server_to_client.end()) {
1134 int client_id = i->second;
1135 m_hud_server_to_client.erase(i);
1137 ClientEvent *event = new ClientEvent();
1138 event->type = CE_HUDRM;
1139 event->hudrm.id = client_id;
1140 m_client_event_queue.push(event);
1144 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1154 *pkt >> server_id >> stat;
1156 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1157 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1159 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1161 else if (stat == HUD_STAT_WORLD_POS)
1163 else if (stat == HUD_STAT_SIZE )
1168 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1169 if (i != m_hud_server_to_client.end()) {
1170 ClientEvent *event = new ClientEvent();
1171 event->type = CE_HUDCHANGE;
1172 event->hudchange.id = i->second;
1173 event->hudchange.stat = (HudElementStat)stat;
1174 event->hudchange.v2fdata = new v2f(v2fdata);
1175 event->hudchange.v3fdata = new v3f(v3fdata);
1176 event->hudchange.sdata = new std::string(sdata);
1177 event->hudchange.data = intdata;
1178 event->hudchange.v2s32data = new v2s32(v2s32data);
1179 m_client_event_queue.push(event);
1183 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1187 *pkt >> flags >> mask;
1189 LocalPlayer *player = m_env.getLocalPlayer();
1190 assert(player != NULL);
1192 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1193 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1195 player->hud_flags &= ~mask;
1196 player->hud_flags |= flags;
1198 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1199 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1201 // Hide minimap if it has been disabled by the server
1202 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1203 // defers a minimap update, therefore only call it if really
1204 // needed, by checking that minimap was visible before
1205 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1207 // Switch to surface mode if radar disabled by server
1208 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1209 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1212 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1214 u16 param; std::string value;
1216 *pkt >> param >> value;
1218 LocalPlayer *player = m_env.getLocalPlayer();
1219 assert(player != NULL);
1221 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1222 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1223 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1224 player->hud_hotbar_itemcount = hotbar_itemcount;
1226 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1227 // If value not empty verify image exists in texture source
1228 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1229 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1230 << value << "')" << std::endl;
1233 player->hotbar_image = value;
1235 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1236 // If value not empty verify image exists in texture source
1237 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1238 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1239 << value << "')" << std::endl;
1242 player->hotbar_selected_image = value;
1246 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1248 std::string datastring(pkt->getString(0), pkt->getSize());
1249 std::istringstream is(datastring, std::ios_base::binary);
1251 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1252 std::string *type = new std::string(deSerializeString(is));
1253 u16 count = readU16(is);
1254 std::vector<std::string> *params = new std::vector<std::string>;
1256 for (size_t i = 0; i < count; i++)
1257 params->push_back(deSerializeString(is));
1261 clouds = readU8(is);
1264 ClientEvent *event = new ClientEvent();
1265 event->type = CE_SET_SKY;
1266 event->set_sky.bgcolor = bgcolor;
1267 event->set_sky.type = type;
1268 event->set_sky.params = params;
1269 event->set_sky.clouds = clouds;
1270 m_client_event_queue.push(event);
1273 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1276 video::SColor color_bright;
1277 video::SColor color_ambient;
1282 *pkt >> density >> color_bright >> color_ambient
1283 >> height >> thickness >> speed;
1285 ClientEvent *event = new ClientEvent();
1286 event->type = CE_CLOUD_PARAMS;
1287 event->cloud_params.density = density;
1288 // use the underlying u32 representation, because we can't
1289 // use struct members with constructors here, and this way
1290 // we avoid using new() and delete() for no good reason
1291 event->cloud_params.color_bright = color_bright.color;
1292 event->cloud_params.color_ambient = color_ambient.color;
1293 event->cloud_params.height = height;
1294 event->cloud_params.thickness = thickness;
1295 // same here: deconstruct to skip constructor
1296 event->cloud_params.speed_x = speed.X;
1297 event->cloud_params.speed_y = speed.Y;
1298 m_client_event_queue.push(event);
1301 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1304 u16 day_night_ratio_u;
1306 *pkt >> do_override >> day_night_ratio_u;
1308 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1310 ClientEvent *event = new ClientEvent();
1311 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1312 event->override_day_night_ratio.do_override = do_override;
1313 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1314 m_client_event_queue.push(event);
1317 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1319 LocalPlayer *player = m_env.getLocalPlayer();
1320 assert(player != NULL);
1322 *pkt >> player->local_animations[0];
1323 *pkt >> player->local_animations[1];
1324 *pkt >> player->local_animations[2];
1325 *pkt >> player->local_animations[3];
1326 *pkt >> player->local_animation_speed;
1329 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1331 LocalPlayer *player = m_env.getLocalPlayer();
1332 assert(player != NULL);
1334 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1337 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1341 *pkt >> type >> num_players;
1342 PlayerListModifer notice_type = (PlayerListModifer) type;
1344 for (u16 i = 0; i < num_players; i++) {
1347 switch (notice_type) {
1348 case PLAYER_LIST_INIT:
1349 case PLAYER_LIST_ADD:
1350 m_env.addPlayerName(name);
1352 case PLAYER_LIST_REMOVE:
1353 m_env.removePlayerName(name);
1359 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1361 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1362 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1363 errorstream << "Client: Received SRP S_B login message,"
1364 << " but wasn't supposed to (chosen_mech="
1365 << m_chosen_auth_mech << ")." << std::endl;
1371 SRPUser *usr = (SRPUser *) m_auth_data;
1376 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1378 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1379 (const unsigned char *) B.c_str(), B.size(),
1380 (unsigned char **) &bytes_M, &len_M);
1383 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1387 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1388 resp_pkt << std::string(bytes_M, len_M);
1392 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1394 LocalPlayer *player = m_env.getLocalPlayer();
1395 assert(player != NULL);
1397 // Store formspec in LocalPlayer
1398 *pkt >> player->formspec_prepend;
1401 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1403 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1405 // Restrictions were received -> load mods if it's enabled
1406 // Note: this should be moved after mods receptions from server instead
1410 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1416 LocalPlayer *player = m_env.getLocalPlayer();
1417 assert(player != NULL);
1418 player->addVelocity(added_vel);
1425 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1427 std::string channel_name, sender, channel_msg;
1428 *pkt >> channel_name >> sender >> channel_msg;
1430 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1431 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1432 << channel_msg << std::endl;
1434 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1435 verbosestream << "Server sent us messages on unregistered channel "
1436 << channel_name << ", ignoring." << std::endl;
1440 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1443 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1446 ModChannelSignal signal;
1447 std::string channel;
1449 *pkt >> signal_tmp >> channel;
1451 signal = (ModChannelSignal)signal_tmp;
1453 bool valid_signal = true;
1454 // @TODO: send Signal to Lua API
1456 case MODCHANNEL_SIGNAL_JOIN_OK:
1457 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1458 infostream << "Server ack our mod channel join on channel `" << channel
1459 << "`, joining." << std::endl;
1461 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1462 // Unable to join, remove channel
1463 m_modchannel_mgr->leaveChannel(channel, 0);
1464 infostream << "Server refused our mod channel join on channel `" << channel
1465 << "`" << std::endl;
1467 case MODCHANNEL_SIGNAL_LEAVE_OK:
1469 infostream << "Server ack our mod channel leave on channel " << channel
1470 << "`, leaving." << std::endl;
1473 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1474 infostream << "Server refused our mod channel leave on channel `" << channel
1475 << "`" << std::endl;
1477 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1479 // Generally unused, but ensure we don't do an implementation error
1480 infostream << "Server tells us we sent a message on channel `" << channel
1481 << "` but we are not registered. Message was dropped." << std::endl;
1484 case MODCHANNEL_SIGNAL_SET_STATE: {
1488 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1489 infostream << "Received wrong channel state " << state
1490 << ", ignoring." << std::endl;
1494 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1495 infostream << "Server sets mod channel `" << channel
1496 << "` in read-only mode." << std::endl;
1501 warningstream << "Received unhandled mod channel signal ID "
1502 << signal << ", ignoring." << std::endl;
1504 valid_signal = false;
1508 // If signal is valid, forward it to client side mods
1510 m_script->on_modchannel_signal(channel, signal);