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) {
411 *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
413 chatMessage->type = (ChatMessageType) message_type;
415 // @TODO send this to CSM using ChatMessage object
416 if (moddingEnabled() && m_script->on_receiving_message(
417 wide_to_utf8(chatMessage->message))) {
418 // Message was consumed by CSM and should not be handled by client
421 pushToChatQueue(chatMessage);
425 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
428 u16 count of removed objects
429 for all removed objects {
432 u16 count of added objects
433 for all added objects {
436 u32 initialization data length
437 string initialization data
443 u16 removed_count, added_count, id;
445 // Read removed objects
446 *pkt >> removed_count;
448 for (u16 i = 0; i < removed_count; i++) {
450 m_env.removeActiveObject(id);
453 // Read added objects
456 for (u16 i = 0; i < added_count; i++) {
458 m_env.addActiveObject(id, type, pkt->readLongString());
460 } catch (PacketError &e) {
461 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
462 << ". The packet is unreliable, ignoring" << std::endl;
466 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
476 std::string datastring(pkt->getString(0), pkt->getSize());
477 std::istringstream is(datastring, std::ios_base::binary);
481 u16 id = readU16(is);
485 std::string message = deSerializeString(is);
487 // Pass on to the environment
488 m_env.processActiveObjectMessage(id, message);
490 } catch (SerializationError &e) {
491 errorstream << "Client::handleCommand_ActiveObjectMessages: "
492 << "caught SerializationError: " << e.what() << std::endl;
496 void Client::handleCommand_Movement(NetworkPacket* pkt)
498 LocalPlayer *player = m_env.getLocalPlayer();
499 assert(player != NULL);
501 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
503 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
504 >> lf >> lfs >> ls >> g;
506 player->movement_acceleration_default = mad * BS;
507 player->movement_acceleration_air = maa * BS;
508 player->movement_acceleration_fast = maf * BS;
509 player->movement_speed_walk = msw * BS;
510 player->movement_speed_crouch = mscr * BS;
511 player->movement_speed_fast = msf * BS;
512 player->movement_speed_climb = mscl * BS;
513 player->movement_speed_jump = msj * BS;
514 player->movement_liquid_fluidity = lf * BS;
515 player->movement_liquid_fluidity_smooth = lfs * BS;
516 player->movement_liquid_sink = ls * BS;
517 player->movement_gravity = g * BS;
520 void Client::handleCommand_HP(NetworkPacket* pkt)
523 LocalPlayer *player = m_env.getLocalPlayer();
524 assert(player != NULL);
526 u16 oldhp = player->hp;
533 if (moddingEnabled()) {
534 m_script->on_hp_modification(hp);
538 // Add to ClientEvent queue
539 ClientEvent *event = new ClientEvent();
540 event->type = CE_PLAYER_DAMAGE;
541 event->player_damage.amount = oldhp - hp;
542 m_client_event_queue.push(event);
546 void Client::handleCommand_Breath(NetworkPacket* pkt)
548 LocalPlayer *player = m_env.getLocalPlayer();
549 assert(player != NULL);
555 player->setBreath(breath);
558 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
560 LocalPlayer *player = m_env.getLocalPlayer();
561 assert(player != NULL);
566 *pkt >> pos >> pitch >> yaw;
568 player->setPosition(pos);
570 infostream << "Client got TOCLIENT_MOVE_PLAYER"
571 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
572 << " pitch=" << pitch
577 Add to ClientEvent queue.
578 This has to be sent to the main program because otherwise
579 it would just force the pitch and yaw values to whatever
580 the camera points to.
582 ClientEvent *event = new ClientEvent();
583 event->type = CE_PLAYER_FORCE_MOVE;
584 event->player_force_move.pitch = pitch;
585 event->player_force_move.yaw = yaw;
586 m_client_event_queue.push(event);
589 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
591 bool set_camera_point_target;
592 v3f camera_point_target;
594 *pkt >> set_camera_point_target;
595 *pkt >> camera_point_target;
597 ClientEvent *event = new ClientEvent();
598 event->type = CE_DEATHSCREEN;
599 event->deathscreen.set_camera_point_target = set_camera_point_target;
600 event->deathscreen.camera_point_target_x = camera_point_target.X;
601 event->deathscreen.camera_point_target_y = camera_point_target.Y;
602 event->deathscreen.camera_point_target_z = camera_point_target.Z;
603 m_client_event_queue.push(event);
606 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
612 infostream << "Client: Received media announcement: packet size: "
613 << pkt->getSize() << std::endl;
615 if (m_media_downloader == NULL ||
616 m_media_downloader->isStarted()) {
617 const char *problem = m_media_downloader ?
618 "we already saw another announcement" :
619 "all media has been received already";
620 errorstream << "Client: Received media announcement but "
622 << " files=" << num_files
623 << " size=" << pkt->getSize() << std::endl;
627 // Mesh update thread must be stopped while
628 // updating content definitions
629 sanity_check(!m_mesh_update_thread.isRunning());
631 for (u16 i = 0; i < num_files; i++) {
632 std::string name, sha1_base64;
634 *pkt >> name >> sha1_base64;
636 std::string sha1_raw = base64_decode(sha1_base64);
637 m_media_downloader->addFile(name, sha1_raw);
646 while(!sf.at_end()) {
647 std::string baseurl = trim(sf.next(","));
648 if (!baseurl.empty())
649 m_media_downloader->addRemoteServer(baseurl);
652 catch(SerializationError& e) {
653 // not supported by server or turned off
656 m_media_downloader->step(this);
659 void Client::handleCommand_Media(NetworkPacket* pkt)
663 u16 total number of file bunches
664 u16 index of this bunch
665 u32 number of files in this bunch
677 *pkt >> num_bunches >> bunch_i >> num_files;
679 infostream << "Client: Received files: bunch " << bunch_i << "/"
680 << num_bunches << " files=" << num_files
681 << " size=" << pkt->getSize() << std::endl;
686 if (!m_media_downloader || !m_media_downloader->isStarted()) {
687 const char *problem = m_media_downloader ?
688 "media has not been requested" :
689 "all media has been received already";
690 errorstream << "Client: Received media but "
692 << " bunch " << bunch_i << "/" << num_bunches
693 << " files=" << num_files
694 << " size=" << pkt->getSize() << std::endl;
698 // Mesh update thread must be stopped while
699 // updating content definitions
700 sanity_check(!m_mesh_update_thread.isRunning());
702 for (u32 i=0; i < num_files; i++) {
707 std::string data = pkt->readLongString();
709 m_media_downloader->conventionalTransferDone(
714 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
716 infostream << "Client: Received node definitions: packet size: "
717 << pkt->getSize() << std::endl;
719 // Mesh update thread must be stopped while
720 // updating content definitions
721 sanity_check(!m_mesh_update_thread.isRunning());
723 // Decompress node definitions
724 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
725 std::ostringstream tmp_os;
726 decompressZlib(tmp_is, tmp_os);
728 // Deserialize node definitions
729 std::istringstream tmp_is2(tmp_os.str());
730 m_nodedef->deSerialize(tmp_is2);
731 m_nodedef_received = true;
734 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
736 infostream << "Client: Received item definitions: packet size: "
737 << pkt->getSize() << std::endl;
739 // Mesh update thread must be stopped while
740 // updating content definitions
741 sanity_check(!m_mesh_update_thread.isRunning());
743 // Decompress item definitions
744 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
745 std::ostringstream tmp_os;
746 decompressZlib(tmp_is, tmp_os);
748 // Deserialize node definitions
749 std::istringstream tmp_is2(tmp_os.str());
750 m_itemdef->deSerialize(tmp_is2);
751 m_itemdef_received = true;
754 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
762 [11 + len] (f32 * 3) pos
763 [23 + len] u16 object_id
773 u8 type; // 0=local, 1=positional, 2=object
780 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
785 } catch (PacketError &e) {};
791 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
793 case 1: // positional
794 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
798 ClientActiveObject *cao = m_env.getActiveObject(object_id);
800 pos = cao->getPosition();
801 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
802 // TODO: Set up sound to move with object
809 if (client_id != -1) {
810 m_sounds_server_to_client[server_id] = client_id;
811 m_sounds_client_to_server[client_id] = server_id;
813 m_sounds_to_objects[client_id] = object_id;
817 void Client::handleCommand_StopSound(NetworkPacket* pkt)
823 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
824 if (i != m_sounds_server_to_client.end()) {
825 int client_id = i->second;
826 m_sound->stopSound(client_id);
830 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
836 *pkt >> sound_id >> step >> gain;
838 std::unordered_map<s32, int>::const_iterator i =
839 m_sounds_server_to_client.find(sound_id);
841 if (i != m_sounds_server_to_client.end())
842 m_sound->fadeSound(i->second, step, gain);
845 void Client::handleCommand_Privileges(NetworkPacket* pkt)
847 m_privileges.clear();
848 infostream << "Client: Privileges updated: ";
851 *pkt >> num_privileges;
853 for (u16 i = 0; i < num_privileges; i++) {
858 m_privileges.insert(priv);
859 infostream << priv << " ";
861 infostream << std::endl;
864 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
866 LocalPlayer *player = m_env.getLocalPlayer();
867 assert(player != NULL);
869 // Store formspec in LocalPlayer
870 player->inventory_formspec = pkt->readLongString();
873 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
876 bool keep_inv = true;
877 *pkt >> name >> keep_inv;
879 infostream << "Client: Detached inventory update: \"" << name
880 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
882 const auto &inv_it = m_detached_inventories.find(name);
884 if (inv_it != m_detached_inventories.end()) {
885 delete inv_it->second;
886 m_detached_inventories.erase(inv_it);
890 Inventory *inv = nullptr;
891 if (inv_it == m_detached_inventories.end()) {
892 inv = new Inventory(m_itemdef);
893 m_detached_inventories[name] = inv;
895 inv = inv_it->second;
898 std::string contents;
900 std::istringstream is(contents, std::ios::binary);
901 inv->deSerialize(is);
904 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
906 std::string formspec = pkt->readLongString();
907 std::string formname;
911 ClientEvent *event = new ClientEvent();
912 event->type = CE_SHOW_FORMSPEC;
913 // pointer is required as event is a struct only!
914 // adding a std:string to a struct isn't possible
915 event->show_formspec.formspec = new std::string(formspec);
916 event->show_formspec.formname = new std::string(formname);
917 m_client_event_queue.push(event);
920 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
922 std::string datastring(pkt->getString(0), pkt->getSize());
923 std::istringstream is(datastring, std::ios_base::binary);
925 v3f pos = readV3F32(is);
926 v3f vel = readV3F32(is);
927 v3f acc = readV3F32(is);
928 float expirationtime = readF32(is);
929 float size = readF32(is);
930 bool collisiondetection = readU8(is);
931 std::string texture = deSerializeLongString(is);
933 bool vertical = false;
934 bool collision_removal = false;
935 TileAnimationParams animation;
936 animation.type = TAT_NONE;
938 bool object_collision = false;
940 vertical = readU8(is);
941 collision_removal = readU8(is);
942 animation.deSerialize(is, m_proto_ver);
944 object_collision = readU8(is);
947 ClientEvent *event = new ClientEvent();
948 event->type = CE_SPAWN_PARTICLE;
949 event->spawn_particle.pos = new v3f (pos);
950 event->spawn_particle.vel = new v3f (vel);
951 event->spawn_particle.acc = new v3f (acc);
952 event->spawn_particle.expirationtime = expirationtime;
953 event->spawn_particle.size = size;
954 event->spawn_particle.collisiondetection = collisiondetection;
955 event->spawn_particle.collision_removal = collision_removal;
956 event->spawn_particle.object_collision = object_collision;
957 event->spawn_particle.vertical = vertical;
958 event->spawn_particle.texture = new std::string(texture);
959 event->spawn_particle.animation = animation;
960 event->spawn_particle.glow = glow;
962 m_client_event_queue.push(event);
965 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
979 bool collisiondetection;
982 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
983 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
984 >> maxsize >> collisiondetection;
986 std::string texture = pkt->readLongString();
990 bool vertical = false;
991 bool collision_removal = false;
993 TileAnimationParams animation;
994 animation.type = TAT_NONE;
996 bool object_collision = false;
999 *pkt >> collision_removal;
1000 *pkt >> attached_id;
1002 // This is horrible but required (why are there two ways to deserialize pkts?)
1003 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1004 std::istringstream is(datastring, std::ios_base::binary);
1005 animation.deSerialize(is, m_proto_ver);
1007 object_collision = readU8(is);
1010 u32 client_id = m_particle_manager.getSpawnerId();
1011 m_particles_server_to_client[server_id] = client_id;
1013 ClientEvent *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 = client_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)
1047 auto i = m_particles_server_to_client.find(server_id);
1048 if (i != m_particles_server_to_client.end())
1049 client_id = i->second;
1053 ClientEvent *event = new ClientEvent();
1054 event->type = CE_DELETE_PARTICLESPAWNER;
1055 event->delete_particlespawner.id = client_id;
1057 m_client_event_queue.push(event);
1060 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1062 std::string datastring(pkt->getString(0), pkt->getSize());
1063 std::istringstream is(datastring, std::ios_base::binary);
1079 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1080 >> dir >> align >> offset;
1084 catch(SerializationError &e) {};
1088 } catch(SerializationError &e) {};
1090 ClientEvent *event = new ClientEvent();
1091 event->type = CE_HUDADD;
1092 event->hudadd.server_id = server_id;
1093 event->hudadd.type = type;
1094 event->hudadd.pos = new v2f(pos);
1095 event->hudadd.name = new std::string(name);
1096 event->hudadd.scale = new v2f(scale);
1097 event->hudadd.text = new std::string(text);
1098 event->hudadd.number = number;
1099 event->hudadd.item = item;
1100 event->hudadd.dir = dir;
1101 event->hudadd.align = new v2f(align);
1102 event->hudadd.offset = new v2f(offset);
1103 event->hudadd.world_pos = new v3f(world_pos);
1104 event->hudadd.size = new v2s32(size);
1105 m_client_event_queue.push(event);
1108 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1114 auto i = m_hud_server_to_client.find(server_id);
1115 if (i != m_hud_server_to_client.end()) {
1116 int client_id = i->second;
1117 m_hud_server_to_client.erase(i);
1119 ClientEvent *event = new ClientEvent();
1120 event->type = CE_HUDRM;
1121 event->hudrm.id = client_id;
1122 m_client_event_queue.push(event);
1126 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1136 *pkt >> server_id >> stat;
1138 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1139 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1141 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1143 else if (stat == HUD_STAT_WORLD_POS)
1145 else if (stat == HUD_STAT_SIZE )
1150 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1151 if (i != m_hud_server_to_client.end()) {
1152 ClientEvent *event = new ClientEvent();
1153 event->type = CE_HUDCHANGE;
1154 event->hudchange.id = i->second;
1155 event->hudchange.stat = (HudElementStat)stat;
1156 event->hudchange.v2fdata = new v2f(v2fdata);
1157 event->hudchange.v3fdata = new v3f(v3fdata);
1158 event->hudchange.sdata = new std::string(sdata);
1159 event->hudchange.data = intdata;
1160 event->hudchange.v2s32data = new v2s32(v2s32data);
1161 m_client_event_queue.push(event);
1165 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1169 *pkt >> flags >> mask;
1171 LocalPlayer *player = m_env.getLocalPlayer();
1172 assert(player != NULL);
1174 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1175 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1177 player->hud_flags &= ~mask;
1178 player->hud_flags |= flags;
1180 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1181 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1183 // Hide minimap if it has been disabled by the server
1184 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1185 // defers a minimap update, therefore only call it if really
1186 // needed, by checking that minimap was visible before
1187 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1189 // Switch to surface mode if radar disabled by server
1190 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1191 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1194 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1196 u16 param; std::string value;
1198 *pkt >> param >> value;
1200 LocalPlayer *player = m_env.getLocalPlayer();
1201 assert(player != NULL);
1203 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1204 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1205 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1206 player->hud_hotbar_itemcount = hotbar_itemcount;
1208 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1209 // If value not empty verify image exists in texture source
1210 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1211 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1212 << value << "')" << std::endl;
1215 player->hotbar_image = value;
1217 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1218 // If value not empty verify image exists in texture source
1219 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1220 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1221 << value << "')" << std::endl;
1224 player->hotbar_selected_image = value;
1228 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1230 std::string datastring(pkt->getString(0), pkt->getSize());
1231 std::istringstream is(datastring, std::ios_base::binary);
1233 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1234 std::string *type = new std::string(deSerializeString(is));
1235 u16 count = readU16(is);
1236 std::vector<std::string> *params = new std::vector<std::string>;
1238 for (size_t i = 0; i < count; i++)
1239 params->push_back(deSerializeString(is));
1243 clouds = readU8(is);
1246 ClientEvent *event = new ClientEvent();
1247 event->type = CE_SET_SKY;
1248 event->set_sky.bgcolor = bgcolor;
1249 event->set_sky.type = type;
1250 event->set_sky.params = params;
1251 event->set_sky.clouds = clouds;
1252 m_client_event_queue.push(event);
1255 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1258 video::SColor color_bright;
1259 video::SColor color_ambient;
1264 *pkt >> density >> color_bright >> color_ambient
1265 >> height >> thickness >> speed;
1267 ClientEvent *event = new ClientEvent();
1268 event->type = CE_CLOUD_PARAMS;
1269 event->cloud_params.density = density;
1270 // use the underlying u32 representation, because we can't
1271 // use struct members with constructors here, and this way
1272 // we avoid using new() and delete() for no good reason
1273 event->cloud_params.color_bright = color_bright.color;
1274 event->cloud_params.color_ambient = color_ambient.color;
1275 event->cloud_params.height = height;
1276 event->cloud_params.thickness = thickness;
1277 // same here: deconstruct to skip constructor
1278 event->cloud_params.speed_x = speed.X;
1279 event->cloud_params.speed_y = speed.Y;
1280 m_client_event_queue.push(event);
1283 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1286 u16 day_night_ratio_u;
1288 *pkt >> do_override >> day_night_ratio_u;
1290 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1292 ClientEvent *event = new ClientEvent();
1293 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1294 event->override_day_night_ratio.do_override = do_override;
1295 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1296 m_client_event_queue.push(event);
1299 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1301 LocalPlayer *player = m_env.getLocalPlayer();
1302 assert(player != NULL);
1304 *pkt >> player->local_animations[0];
1305 *pkt >> player->local_animations[1];
1306 *pkt >> player->local_animations[2];
1307 *pkt >> player->local_animations[3];
1308 *pkt >> player->local_animation_speed;
1311 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1313 LocalPlayer *player = m_env.getLocalPlayer();
1314 assert(player != NULL);
1316 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1319 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1323 *pkt >> type >> num_players;
1324 PlayerListModifer notice_type = (PlayerListModifer) type;
1326 for (u16 i = 0; i < num_players; i++) {
1329 switch (notice_type) {
1330 case PLAYER_LIST_INIT:
1331 case PLAYER_LIST_ADD:
1332 m_env.addPlayerName(name);
1334 case PLAYER_LIST_REMOVE:
1335 m_env.removePlayerName(name);
1341 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1343 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1344 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1345 errorstream << "Client: Received SRP S_B login message,"
1346 << " but wasn't supposed to (chosen_mech="
1347 << m_chosen_auth_mech << ")." << std::endl;
1353 SRPUser *usr = (SRPUser *) m_auth_data;
1358 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1360 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1361 (const unsigned char *) B.c_str(), B.size(),
1362 (unsigned char **) &bytes_M, &len_M);
1365 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1369 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1370 resp_pkt << std::string(bytes_M, len_M);
1374 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1376 LocalPlayer *player = m_env.getLocalPlayer();
1377 assert(player != NULL);
1379 // Store formspec in LocalPlayer
1380 *pkt >> player->formspec_prepend;
1383 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1385 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1387 // Restrictions were received -> load mods if it's enabled
1388 // Note: this should be moved after mods receptions from server instead
1396 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1398 std::string channel_name, sender, channel_msg;
1399 *pkt >> channel_name >> sender >> channel_msg;
1401 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1402 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1403 << channel_msg << std::endl;
1405 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1406 verbosestream << "Server sent us messages on unregistered channel "
1407 << channel_name << ", ignoring." << std::endl;
1411 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1414 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1417 ModChannelSignal signal;
1418 std::string channel;
1420 *pkt >> signal_tmp >> channel;
1422 signal = (ModChannelSignal)signal_tmp;
1424 bool valid_signal = true;
1425 // @TODO: send Signal to Lua API
1427 case MODCHANNEL_SIGNAL_JOIN_OK:
1428 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1429 infostream << "Server ack our mod channel join on channel `" << channel
1430 << "`, joining." << std::endl;
1432 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1433 // Unable to join, remove channel
1434 m_modchannel_mgr->leaveChannel(channel, 0);
1435 infostream << "Server refused our mod channel join on channel `" << channel
1436 << "`" << std::endl;
1438 case MODCHANNEL_SIGNAL_LEAVE_OK:
1440 infostream << "Server ack our mod channel leave on channel " << channel
1441 << "`, leaving." << std::endl;
1444 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1445 infostream << "Server refused our mod channel leave on channel `" << channel
1446 << "`" << std::endl;
1448 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1450 // Generally unused, but ensure we don't do an implementation error
1451 infostream << "Server tells us we sent a message on channel `" << channel
1452 << "` but we are not registered. Message was dropped." << std::endl;
1455 case MODCHANNEL_SIGNAL_SET_STATE: {
1459 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1460 infostream << "Received wrong channel state " << state
1461 << ", ignoring." << std::endl;
1465 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1466 infostream << "Server sets mod channel `" << channel
1467 << "` in read-only mode." << std::endl;
1472 warningstream << "Received unhandled mod channel signal ID "
1473 << signal << ", ignoring." << std::endl;
1475 valid_signal = false;
1479 // If signal is valid, forward it to client side mods
1481 m_script->on_modchannel_signal(channel, signal);