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_inventory_updated = 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 (moddingEnabled() && 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;
535 if (moddingEnabled()) {
536 m_script->on_hp_modification(hp);
540 // Add to ClientEvent queue
541 ClientEvent *event = new ClientEvent();
542 event->type = CE_PLAYER_DAMAGE;
543 event->player_damage.amount = oldhp - hp;
544 m_client_event_queue.push(event);
548 void Client::handleCommand_Breath(NetworkPacket* pkt)
550 LocalPlayer *player = m_env.getLocalPlayer();
551 assert(player != NULL);
557 player->setBreath(breath);
560 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
562 LocalPlayer *player = m_env.getLocalPlayer();
563 assert(player != NULL);
568 *pkt >> pos >> pitch >> yaw;
570 player->setPosition(pos);
572 infostream << "Client got TOCLIENT_MOVE_PLAYER"
573 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
574 << " pitch=" << pitch
579 Add to ClientEvent queue.
580 This has to be sent to the main program because otherwise
581 it would just force the pitch and yaw values to whatever
582 the camera points to.
584 ClientEvent *event = new ClientEvent();
585 event->type = CE_PLAYER_FORCE_MOVE;
586 event->player_force_move.pitch = pitch;
587 event->player_force_move.yaw = yaw;
588 m_client_event_queue.push(event);
591 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
593 bool set_camera_point_target;
594 v3f camera_point_target;
596 *pkt >> set_camera_point_target;
597 *pkt >> camera_point_target;
599 ClientEvent *event = new ClientEvent();
600 event->type = CE_DEATHSCREEN;
601 event->deathscreen.set_camera_point_target = set_camera_point_target;
602 event->deathscreen.camera_point_target_x = camera_point_target.X;
603 event->deathscreen.camera_point_target_y = camera_point_target.Y;
604 event->deathscreen.camera_point_target_z = camera_point_target.Z;
605 m_client_event_queue.push(event);
608 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
614 infostream << "Client: Received media announcement: packet size: "
615 << pkt->getSize() << std::endl;
617 if (m_media_downloader == NULL ||
618 m_media_downloader->isStarted()) {
619 const char *problem = m_media_downloader ?
620 "we already saw another announcement" :
621 "all media has been received already";
622 errorstream << "Client: Received media announcement but "
624 << " files=" << num_files
625 << " size=" << pkt->getSize() << std::endl;
629 // Mesh update thread must be stopped while
630 // updating content definitions
631 sanity_check(!m_mesh_update_thread.isRunning());
633 for (u16 i = 0; i < num_files; i++) {
634 std::string name, sha1_base64;
636 *pkt >> name >> sha1_base64;
638 std::string sha1_raw = base64_decode(sha1_base64);
639 m_media_downloader->addFile(name, sha1_raw);
648 while(!sf.at_end()) {
649 std::string baseurl = trim(sf.next(","));
650 if (!baseurl.empty())
651 m_media_downloader->addRemoteServer(baseurl);
654 catch(SerializationError& e) {
655 // not supported by server or turned off
658 m_media_downloader->step(this);
661 void Client::handleCommand_Media(NetworkPacket* pkt)
665 u16 total number of file bunches
666 u16 index of this bunch
667 u32 number of files in this bunch
679 *pkt >> num_bunches >> bunch_i >> num_files;
681 infostream << "Client: Received files: bunch " << bunch_i << "/"
682 << num_bunches << " files=" << num_files
683 << " size=" << pkt->getSize() << std::endl;
688 if (!m_media_downloader || !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)
764 [11 + len] (f32 * 3) pos
765 [23 + len] u16 object_id
775 u8 type; // 0=local, 1=positional, 2=object
782 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
787 } catch (PacketError &e) {};
793 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
795 case 1: // positional
796 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
800 ClientActiveObject *cao = m_env.getActiveObject(object_id);
802 pos = cao->getPosition();
803 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
804 // TODO: Set up sound to move with object
811 if (client_id != -1) {
812 m_sounds_server_to_client[server_id] = client_id;
813 m_sounds_client_to_server[client_id] = server_id;
815 m_sounds_to_objects[client_id] = object_id;
819 void Client::handleCommand_StopSound(NetworkPacket* pkt)
825 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
826 if (i != m_sounds_server_to_client.end()) {
827 int client_id = i->second;
828 m_sound->stopSound(client_id);
832 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
838 *pkt >> sound_id >> step >> gain;
840 std::unordered_map<s32, int>::const_iterator i =
841 m_sounds_server_to_client.find(sound_id);
843 if (i != m_sounds_server_to_client.end())
844 m_sound->fadeSound(i->second, step, gain);
847 void Client::handleCommand_Privileges(NetworkPacket* pkt)
849 m_privileges.clear();
850 infostream << "Client: Privileges updated: ";
853 *pkt >> num_privileges;
855 for (u16 i = 0; i < num_privileges; i++) {
860 m_privileges.insert(priv);
861 infostream << priv << " ";
863 infostream << std::endl;
866 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
868 LocalPlayer *player = m_env.getLocalPlayer();
869 assert(player != NULL);
871 // Store formspec in LocalPlayer
872 player->inventory_formspec = pkt->readLongString();
875 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
878 bool keep_inv = true;
879 *pkt >> name >> keep_inv;
881 infostream << "Client: Detached inventory update: \"" << name
882 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
884 const auto &inv_it = m_detached_inventories.find(name);
886 if (inv_it != m_detached_inventories.end()) {
887 delete inv_it->second;
888 m_detached_inventories.erase(inv_it);
892 Inventory *inv = nullptr;
893 if (inv_it == m_detached_inventories.end()) {
894 inv = new Inventory(m_itemdef);
895 m_detached_inventories[name] = inv;
897 inv = inv_it->second;
901 *pkt >> ignore; // this used to be the length of the following string, ignore it
903 std::string contents = pkt->getRemainingString();
904 std::istringstream is(contents, std::ios::binary);
905 inv->deSerialize(is);
908 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
910 std::string formspec = pkt->readLongString();
911 std::string formname;
915 ClientEvent *event = new ClientEvent();
916 event->type = CE_SHOW_FORMSPEC;
917 // pointer is required as event is a struct only!
918 // adding a std:string to a struct isn't possible
919 event->show_formspec.formspec = new std::string(formspec);
920 event->show_formspec.formname = new std::string(formname);
921 m_client_event_queue.push(event);
924 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
926 std::string datastring(pkt->getString(0), pkt->getSize());
927 std::istringstream is(datastring, std::ios_base::binary);
929 v3f pos = readV3F32(is);
930 v3f vel = readV3F32(is);
931 v3f acc = readV3F32(is);
932 float expirationtime = readF32(is);
933 float size = readF32(is);
934 bool collisiondetection = readU8(is);
935 std::string texture = deSerializeLongString(is);
937 bool vertical = false;
938 bool collision_removal = false;
939 TileAnimationParams animation;
940 animation.type = TAT_NONE;
942 bool object_collision = false;
944 vertical = readU8(is);
945 collision_removal = readU8(is);
946 animation.deSerialize(is, m_proto_ver);
948 object_collision = readU8(is);
951 ClientEvent *event = new ClientEvent();
952 event->type = CE_SPAWN_PARTICLE;
953 event->spawn_particle.pos = new v3f (pos);
954 event->spawn_particle.vel = new v3f (vel);
955 event->spawn_particle.acc = new v3f (acc);
956 event->spawn_particle.expirationtime = expirationtime;
957 event->spawn_particle.size = size;
958 event->spawn_particle.collisiondetection = collisiondetection;
959 event->spawn_particle.collision_removal = collision_removal;
960 event->spawn_particle.object_collision = object_collision;
961 event->spawn_particle.vertical = vertical;
962 event->spawn_particle.texture = new std::string(texture);
963 event->spawn_particle.animation = animation;
964 event->spawn_particle.glow = glow;
966 m_client_event_queue.push(event);
969 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
983 bool collisiondetection;
986 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
987 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
988 >> maxsize >> collisiondetection;
990 std::string texture = pkt->readLongString();
994 bool vertical = false;
995 bool collision_removal = false;
997 TileAnimationParams animation;
998 animation.type = TAT_NONE;
1000 bool object_collision = false;
1003 *pkt >> collision_removal;
1004 *pkt >> attached_id;
1006 // This is horrible but required (why are there two ways to deserialize pkts?)
1007 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1008 std::istringstream is(datastring, std::ios_base::binary);
1009 animation.deSerialize(is, m_proto_ver);
1011 object_collision = readU8(is);
1014 auto event = new ClientEvent();
1015 event->type = CE_ADD_PARTICLESPAWNER;
1016 event->add_particlespawner.amount = amount;
1017 event->add_particlespawner.spawntime = spawntime;
1018 event->add_particlespawner.minpos = new v3f (minpos);
1019 event->add_particlespawner.maxpos = new v3f (maxpos);
1020 event->add_particlespawner.minvel = new v3f (minvel);
1021 event->add_particlespawner.maxvel = new v3f (maxvel);
1022 event->add_particlespawner.minacc = new v3f (minacc);
1023 event->add_particlespawner.maxacc = new v3f (maxacc);
1024 event->add_particlespawner.minexptime = minexptime;
1025 event->add_particlespawner.maxexptime = maxexptime;
1026 event->add_particlespawner.minsize = minsize;
1027 event->add_particlespawner.maxsize = maxsize;
1028 event->add_particlespawner.collisiondetection = collisiondetection;
1029 event->add_particlespawner.collision_removal = collision_removal;
1030 event->add_particlespawner.object_collision = object_collision;
1031 event->add_particlespawner.attached_id = attached_id;
1032 event->add_particlespawner.vertical = vertical;
1033 event->add_particlespawner.texture = new std::string(texture);
1034 event->add_particlespawner.id = server_id;
1035 event->add_particlespawner.animation = animation;
1036 event->add_particlespawner.glow = glow;
1038 m_client_event_queue.push(event);
1042 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1047 ClientEvent *event = new ClientEvent();
1048 event->type = CE_DELETE_PARTICLESPAWNER;
1049 event->delete_particlespawner.id = server_id;
1051 m_client_event_queue.push(event);
1054 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1056 std::string datastring(pkt->getString(0), pkt->getSize());
1057 std::istringstream is(datastring, std::ios_base::binary);
1073 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1074 >> dir >> align >> offset;
1078 catch(SerializationError &e) {};
1082 } catch(SerializationError &e) {};
1084 ClientEvent *event = new ClientEvent();
1085 event->type = CE_HUDADD;
1086 event->hudadd.server_id = server_id;
1087 event->hudadd.type = type;
1088 event->hudadd.pos = new v2f(pos);
1089 event->hudadd.name = new std::string(name);
1090 event->hudadd.scale = new v2f(scale);
1091 event->hudadd.text = new std::string(text);
1092 event->hudadd.number = number;
1093 event->hudadd.item = item;
1094 event->hudadd.dir = dir;
1095 event->hudadd.align = new v2f(align);
1096 event->hudadd.offset = new v2f(offset);
1097 event->hudadd.world_pos = new v3f(world_pos);
1098 event->hudadd.size = new v2s32(size);
1099 m_client_event_queue.push(event);
1102 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1108 auto i = m_hud_server_to_client.find(server_id);
1109 if (i != m_hud_server_to_client.end()) {
1110 int client_id = i->second;
1111 m_hud_server_to_client.erase(i);
1113 ClientEvent *event = new ClientEvent();
1114 event->type = CE_HUDRM;
1115 event->hudrm.id = client_id;
1116 m_client_event_queue.push(event);
1120 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1130 *pkt >> server_id >> stat;
1132 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1133 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1135 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1137 else if (stat == HUD_STAT_WORLD_POS)
1139 else if (stat == HUD_STAT_SIZE )
1144 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1145 if (i != m_hud_server_to_client.end()) {
1146 ClientEvent *event = new ClientEvent();
1147 event->type = CE_HUDCHANGE;
1148 event->hudchange.id = i->second;
1149 event->hudchange.stat = (HudElementStat)stat;
1150 event->hudchange.v2fdata = new v2f(v2fdata);
1151 event->hudchange.v3fdata = new v3f(v3fdata);
1152 event->hudchange.sdata = new std::string(sdata);
1153 event->hudchange.data = intdata;
1154 event->hudchange.v2s32data = new v2s32(v2s32data);
1155 m_client_event_queue.push(event);
1159 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1163 *pkt >> flags >> mask;
1165 LocalPlayer *player = m_env.getLocalPlayer();
1166 assert(player != NULL);
1168 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1169 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1171 player->hud_flags &= ~mask;
1172 player->hud_flags |= flags;
1174 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1175 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1177 // Hide minimap if it has been disabled by the server
1178 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1179 // defers a minimap update, therefore only call it if really
1180 // needed, by checking that minimap was visible before
1181 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1183 // Switch to surface mode if radar disabled by server
1184 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1185 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1188 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1190 u16 param; std::string value;
1192 *pkt >> param >> value;
1194 LocalPlayer *player = m_env.getLocalPlayer();
1195 assert(player != NULL);
1197 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1198 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1199 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1200 player->hud_hotbar_itemcount = hotbar_itemcount;
1202 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1203 // If value not empty verify image exists in texture source
1204 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1205 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1206 << value << "')" << std::endl;
1209 player->hotbar_image = value;
1211 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1212 // If value not empty verify image exists in texture source
1213 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1214 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1215 << value << "')" << std::endl;
1218 player->hotbar_selected_image = value;
1222 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1224 std::string datastring(pkt->getString(0), pkt->getSize());
1225 std::istringstream is(datastring, std::ios_base::binary);
1227 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1228 std::string *type = new std::string(deSerializeString(is));
1229 u16 count = readU16(is);
1230 std::vector<std::string> *params = new std::vector<std::string>;
1232 for (size_t i = 0; i < count; i++)
1233 params->push_back(deSerializeString(is));
1237 clouds = readU8(is);
1240 ClientEvent *event = new ClientEvent();
1241 event->type = CE_SET_SKY;
1242 event->set_sky.bgcolor = bgcolor;
1243 event->set_sky.type = type;
1244 event->set_sky.params = params;
1245 event->set_sky.clouds = clouds;
1246 m_client_event_queue.push(event);
1249 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1252 video::SColor color_bright;
1253 video::SColor color_ambient;
1258 *pkt >> density >> color_bright >> color_ambient
1259 >> height >> thickness >> speed;
1261 ClientEvent *event = new ClientEvent();
1262 event->type = CE_CLOUD_PARAMS;
1263 event->cloud_params.density = density;
1264 // use the underlying u32 representation, because we can't
1265 // use struct members with constructors here, and this way
1266 // we avoid using new() and delete() for no good reason
1267 event->cloud_params.color_bright = color_bright.color;
1268 event->cloud_params.color_ambient = color_ambient.color;
1269 event->cloud_params.height = height;
1270 event->cloud_params.thickness = thickness;
1271 // same here: deconstruct to skip constructor
1272 event->cloud_params.speed_x = speed.X;
1273 event->cloud_params.speed_y = speed.Y;
1274 m_client_event_queue.push(event);
1277 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1280 u16 day_night_ratio_u;
1282 *pkt >> do_override >> day_night_ratio_u;
1284 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1286 ClientEvent *event = new ClientEvent();
1287 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1288 event->override_day_night_ratio.do_override = do_override;
1289 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1290 m_client_event_queue.push(event);
1293 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1295 LocalPlayer *player = m_env.getLocalPlayer();
1296 assert(player != NULL);
1298 *pkt >> player->local_animations[0];
1299 *pkt >> player->local_animations[1];
1300 *pkt >> player->local_animations[2];
1301 *pkt >> player->local_animations[3];
1302 *pkt >> player->local_animation_speed;
1305 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1307 LocalPlayer *player = m_env.getLocalPlayer();
1308 assert(player != NULL);
1310 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1313 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1317 *pkt >> type >> num_players;
1318 PlayerListModifer notice_type = (PlayerListModifer) type;
1320 for (u16 i = 0; i < num_players; i++) {
1323 switch (notice_type) {
1324 case PLAYER_LIST_INIT:
1325 case PLAYER_LIST_ADD:
1326 m_env.addPlayerName(name);
1328 case PLAYER_LIST_REMOVE:
1329 m_env.removePlayerName(name);
1335 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1337 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1338 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1339 errorstream << "Client: Received SRP S_B login message,"
1340 << " but wasn't supposed to (chosen_mech="
1341 << m_chosen_auth_mech << ")." << std::endl;
1347 SRPUser *usr = (SRPUser *) m_auth_data;
1352 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1354 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1355 (const unsigned char *) B.c_str(), B.size(),
1356 (unsigned char **) &bytes_M, &len_M);
1359 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1363 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1364 resp_pkt << std::string(bytes_M, len_M);
1368 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1370 LocalPlayer *player = m_env.getLocalPlayer();
1371 assert(player != NULL);
1373 // Store formspec in LocalPlayer
1374 *pkt >> player->formspec_prepend;
1377 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1379 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1381 // Restrictions were received -> load mods if it's enabled
1382 // Note: this should be moved after mods receptions from server instead
1390 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1392 std::string channel_name, sender, channel_msg;
1393 *pkt >> channel_name >> sender >> channel_msg;
1395 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1396 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1397 << channel_msg << std::endl;
1399 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1400 verbosestream << "Server sent us messages on unregistered channel "
1401 << channel_name << ", ignoring." << std::endl;
1405 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1408 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1411 ModChannelSignal signal;
1412 std::string channel;
1414 *pkt >> signal_tmp >> channel;
1416 signal = (ModChannelSignal)signal_tmp;
1418 bool valid_signal = true;
1419 // @TODO: send Signal to Lua API
1421 case MODCHANNEL_SIGNAL_JOIN_OK:
1422 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1423 infostream << "Server ack our mod channel join on channel `" << channel
1424 << "`, joining." << std::endl;
1426 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1427 // Unable to join, remove channel
1428 m_modchannel_mgr->leaveChannel(channel, 0);
1429 infostream << "Server refused our mod channel join on channel `" << channel
1430 << "`" << std::endl;
1432 case MODCHANNEL_SIGNAL_LEAVE_OK:
1434 infostream << "Server ack our mod channel leave on channel " << channel
1435 << "`, leaving." << std::endl;
1438 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1439 infostream << "Server refused our mod channel leave on channel `" << channel
1440 << "`" << std::endl;
1442 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1444 // Generally unused, but ensure we don't do an implementation error
1445 infostream << "Server tells us we sent a message on channel `" << channel
1446 << "` but we are not registered. Message was dropped." << std::endl;
1449 case MODCHANNEL_SIGNAL_SET_STATE: {
1453 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1454 infostream << "Received wrong channel state " << state
1455 << ", ignoring." << std::endl;
1459 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1460 infostream << "Server sets mod channel `" << channel
1461 << "` in read-only mode." << std::endl;
1466 warningstream << "Received unhandled mod channel signal ID "
1467 << signal << ", ignoring." << std::endl;
1469 valid_signal = false;
1473 // If signal is valid, forward it to client side mods
1475 m_script->on_modchannel_signal(channel, signal);