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 promptConfirmRegistration(chosen_auth_mechanism);
104 startAuth(chosen_auth_mechanism);
107 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
108 m_access_denied = true;
109 m_access_denied_reason = "Unknown";
115 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
120 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
121 >> m_sudo_auth_methods;
123 playerpos -= v3f(0, BS / 2, 0);
125 // Set player position
126 LocalPlayer *player = m_env.getLocalPlayer();
127 assert(player != NULL);
128 player->setPosition(playerpos);
130 infostream << "Client: received map seed: " << m_map_seed << std::endl;
131 infostream << "Client: received recommended send interval "
132 << m_recommended_send_interval<<std::endl;
135 std::string lang = gettext("LANG_CODE");
136 if (lang == "LANG_CODE")
139 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
145 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
149 m_password = m_new_password;
151 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
153 // send packet to actually set the password
154 startAuth(AUTH_MECHANISM_FIRST_SRP);
157 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
159 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
161 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
162 L"Password change denied. Password NOT changed.");
163 pushToChatQueue(chatMessage);
164 // reset everything and be sad
168 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
170 // The server didn't like our password. Note, this needs
171 // to be processed even if the serialisation format has
172 // not been agreed yet, the same as TOCLIENT_INIT.
173 m_access_denied = true;
174 m_access_denied_reason = "Unknown";
176 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
177 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
178 // in some places of the server code
179 if (pkt->getSize() >= 2) {
180 std::wstring wide_reason;
182 m_access_denied_reason = wide_to_utf8(wide_reason);
187 if (pkt->getSize() < 1)
190 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
192 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
193 denyCode == SERVER_ACCESSDENIED_CRASH) {
194 *pkt >> m_access_denied_reason;
195 if (m_access_denied_reason.empty()) {
196 m_access_denied_reason = accessDeniedStrings[denyCode];
200 m_access_denied_reconnect = reconnect & 1;
201 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
202 *pkt >> m_access_denied_reason;
203 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
204 m_access_denied_reason = accessDeniedStrings[denyCode];
206 // Allow us to add new error messages to the
207 // protocol without raising the protocol version, if we want to.
208 // Until then (which may be never), this is outside
209 // of the defined protocol.
210 *pkt >> m_access_denied_reason;
211 if (m_access_denied_reason.empty()) {
212 m_access_denied_reason = "Unknown";
217 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
219 if (pkt->getSize() < 6)
227 void Client::handleCommand_AddNode(NetworkPacket* pkt)
229 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
236 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
238 bool remove_metadata = true;
239 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
240 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
241 remove_metadata = false;
244 addNode(p, n, remove_metadata);
247 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
249 if (pkt->getSize() < 1)
252 std::istringstream is(pkt->readLongString(), std::ios::binary);
253 std::stringstream sstr;
254 decompressZlib(is, sstr);
256 NodeMetadataList meta_updates_list(false);
257 meta_updates_list.deSerialize(sstr, m_itemdef, true);
259 Map &map = m_env.getMap();
260 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
261 i != meta_updates_list.end(); ++i) {
262 v3s16 pos = i->first;
264 if (map.isValidPosition(pos) &&
265 map.setNodeMetadata(pos, i->second))
266 continue; // Prevent from deleting metadata
268 // Meta couldn't be set, unused metadata
273 void Client::handleCommand_BlockData(NetworkPacket* pkt)
275 // Ignore too small packet
276 if (pkt->getSize() < 6)
282 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
283 std::istringstream istr(datastring, std::ios_base::binary);
289 sector = m_env.getMap().emergeSector(p2d);
291 assert(sector->getPos() == p2d);
293 block = sector->getBlockNoCreateNoEx(p.Y);
296 Update an existing block
298 block->deSerialize(istr, m_server_ser_ver, false);
299 block->deSerializeNetworkSpecific(istr);
305 block = new MapBlock(&m_env.getMap(), p, this);
306 block->deSerialize(istr, m_server_ser_ver, false);
307 block->deSerializeNetworkSpecific(istr);
308 sector->insertBlock(block);
312 ServerMap::saveBlock(block, m_localdb);
316 Add it to mesh update queue and set it to be acknowledged after update.
318 addUpdateMeshTaskWithEdge(p, true);
321 void Client::handleCommand_Inventory(NetworkPacket* pkt)
323 if (pkt->getSize() < 1)
326 std::string datastring(pkt->getString(0), pkt->getSize());
327 std::istringstream is(datastring, std::ios_base::binary);
329 LocalPlayer *player = m_env.getLocalPlayer();
330 assert(player != NULL);
332 player->inventory.deSerialize(is);
334 m_inventory_updated = true;
336 delete m_inventory_from_server;
337 m_inventory_from_server = new Inventory(player->inventory);
338 m_inventory_from_server_age = 0.0;
341 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
343 if (pkt->getSize() < 2)
350 time_of_day = time_of_day % 24000;
351 float time_speed = 0;
353 if (pkt->getSize() >= 2 + 4) {
357 // Old message; try to approximate speed of time by ourselves
358 float time_of_day_f = (float)time_of_day / 24000.0f;
359 float tod_diff_f = 0;
361 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
362 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
364 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
366 m_last_time_of_day_f = time_of_day_f;
367 float time_diff = m_time_of_day_update_timer;
368 m_time_of_day_update_timer = 0;
370 if (m_time_of_day_set) {
371 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
372 infostream << "Client: Measured time_of_day speed (old format): "
373 << time_speed << " tod_diff_f=" << tod_diff_f
374 << " time_diff=" << time_diff << std::endl;
378 // Update environment
379 m_env.setTimeOfDay(time_of_day);
380 m_env.setTimeOfDaySpeed(time_speed);
381 m_time_of_day_set = true;
383 u32 dr = m_env.getDayNightRatio();
384 infostream << "Client: time_of_day=" << time_of_day
385 << " time_speed=" << time_speed
386 << " dr=" << dr << std::endl;
389 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
394 u16 sendername length
400 ChatMessage *chatMessage = new ChatMessage();
401 u8 version, message_type;
402 *pkt >> version >> message_type;
404 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
409 *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
411 chatMessage->type = (ChatMessageType) message_type;
413 // @TODO send this to CSM using ChatMessage object
414 if (moddingEnabled() && m_script->on_receiving_message(
415 wide_to_utf8(chatMessage->message))) {
416 // Message was consumed by CSM and should not be handled by client
419 pushToChatQueue(chatMessage);
423 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
426 u16 count of removed objects
427 for all removed objects {
430 u16 count of added objects
431 for all added objects {
434 u32 initialization data length
435 string initialization data
441 u16 removed_count, added_count, id;
443 // Read removed objects
444 *pkt >> removed_count;
446 for (u16 i = 0; i < removed_count; i++) {
448 m_env.removeActiveObject(id);
451 // Read added objects
454 for (u16 i = 0; i < added_count; i++) {
456 m_env.addActiveObject(id, type, pkt->readLongString());
458 } catch (PacketError &e) {
459 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
460 << ". The packet is unreliable, ignoring" << std::endl;
464 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
474 std::string datastring(pkt->getString(0), pkt->getSize());
475 std::istringstream is(datastring, std::ios_base::binary);
479 u16 id = readU16(is);
483 std::string message = deSerializeString(is);
485 // Pass on to the environment
486 m_env.processActiveObjectMessage(id, message);
488 } catch (SerializationError &e) {
489 errorstream << "Client::handleCommand_ActiveObjectMessages: "
490 << "caught SerializationError: " << e.what() << std::endl;
494 void Client::handleCommand_Movement(NetworkPacket* pkt)
496 LocalPlayer *player = m_env.getLocalPlayer();
497 assert(player != NULL);
499 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
501 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
502 >> lf >> lfs >> ls >> g;
504 player->movement_acceleration_default = mad * BS;
505 player->movement_acceleration_air = maa * BS;
506 player->movement_acceleration_fast = maf * BS;
507 player->movement_speed_walk = msw * BS;
508 player->movement_speed_crouch = mscr * BS;
509 player->movement_speed_fast = msf * BS;
510 player->movement_speed_climb = mscl * BS;
511 player->movement_speed_jump = msj * BS;
512 player->movement_liquid_fluidity = lf * BS;
513 player->movement_liquid_fluidity_smooth = lfs * BS;
514 player->movement_liquid_sink = ls * BS;
515 player->movement_gravity = g * BS;
518 void Client::handleCommand_HP(NetworkPacket* pkt)
521 LocalPlayer *player = m_env.getLocalPlayer();
522 assert(player != NULL);
524 u16 oldhp = player->hp;
531 if (moddingEnabled()) {
532 m_script->on_hp_modification(hp);
536 // Add to ClientEvent queue
537 ClientEvent *event = new ClientEvent();
538 event->type = CE_PLAYER_DAMAGE;
539 event->player_damage.amount = oldhp - hp;
540 m_client_event_queue.push(event);
544 void Client::handleCommand_Breath(NetworkPacket* pkt)
546 LocalPlayer *player = m_env.getLocalPlayer();
547 assert(player != NULL);
553 player->setBreath(breath);
556 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
558 LocalPlayer *player = m_env.getLocalPlayer();
559 assert(player != NULL);
564 *pkt >> pos >> pitch >> yaw;
566 player->setPosition(pos);
568 infostream << "Client got TOCLIENT_MOVE_PLAYER"
569 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
570 << " pitch=" << pitch
575 Add to ClientEvent queue.
576 This has to be sent to the main program because otherwise
577 it would just force the pitch and yaw values to whatever
578 the camera points to.
580 ClientEvent *event = new ClientEvent();
581 event->type = CE_PLAYER_FORCE_MOVE;
582 event->player_force_move.pitch = pitch;
583 event->player_force_move.yaw = yaw;
584 m_client_event_queue.push(event);
587 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
589 bool set_camera_point_target;
590 v3f camera_point_target;
592 *pkt >> set_camera_point_target;
593 *pkt >> camera_point_target;
595 ClientEvent *event = new ClientEvent();
596 event->type = CE_DEATHSCREEN;
597 event->deathscreen.set_camera_point_target = set_camera_point_target;
598 event->deathscreen.camera_point_target_x = camera_point_target.X;
599 event->deathscreen.camera_point_target_y = camera_point_target.Y;
600 event->deathscreen.camera_point_target_z = camera_point_target.Z;
601 m_client_event_queue.push(event);
604 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
610 infostream << "Client: Received media announcement: packet size: "
611 << pkt->getSize() << std::endl;
613 if (m_media_downloader == NULL ||
614 m_media_downloader->isStarted()) {
615 const char *problem = m_media_downloader ?
616 "we already saw another announcement" :
617 "all media has been received already";
618 errorstream << "Client: Received media announcement but "
620 << " files=" << num_files
621 << " size=" << pkt->getSize() << std::endl;
625 // Mesh update thread must be stopped while
626 // updating content definitions
627 sanity_check(!m_mesh_update_thread.isRunning());
629 for (u16 i = 0; i < num_files; i++) {
630 std::string name, sha1_base64;
632 *pkt >> name >> sha1_base64;
634 std::string sha1_raw = base64_decode(sha1_base64);
635 m_media_downloader->addFile(name, sha1_raw);
644 while(!sf.at_end()) {
645 std::string baseurl = trim(sf.next(","));
646 if (!baseurl.empty())
647 m_media_downloader->addRemoteServer(baseurl);
650 catch(SerializationError& e) {
651 // not supported by server or turned off
654 m_media_downloader->step(this);
657 void Client::handleCommand_Media(NetworkPacket* pkt)
661 u16 total number of file bunches
662 u16 index of this bunch
663 u32 number of files in this bunch
675 *pkt >> num_bunches >> bunch_i >> num_files;
677 infostream << "Client: Received files: bunch " << bunch_i << "/"
678 << num_bunches << " files=" << num_files
679 << " size=" << pkt->getSize() << std::endl;
684 if (!m_media_downloader || !m_media_downloader->isStarted()) {
685 const char *problem = m_media_downloader ?
686 "media has not been requested" :
687 "all media has been received already";
688 errorstream << "Client: Received media but "
690 << " bunch " << bunch_i << "/" << num_bunches
691 << " files=" << num_files
692 << " size=" << pkt->getSize() << std::endl;
696 // Mesh update thread must be stopped while
697 // updating content definitions
698 sanity_check(!m_mesh_update_thread.isRunning());
700 for (u32 i=0; i < num_files; i++) {
705 std::string data = pkt->readLongString();
707 m_media_downloader->conventionalTransferDone(
712 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
714 infostream << "Client: Received node definitions: packet size: "
715 << pkt->getSize() << std::endl;
717 // Mesh update thread must be stopped while
718 // updating content definitions
719 sanity_check(!m_mesh_update_thread.isRunning());
721 // Decompress node definitions
722 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
723 std::ostringstream tmp_os;
724 decompressZlib(tmp_is, tmp_os);
726 // Deserialize node definitions
727 std::istringstream tmp_is2(tmp_os.str());
728 m_nodedef->deSerialize(tmp_is2);
729 m_nodedef_received = true;
732 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
734 infostream << "Client: Received item definitions: packet size: "
735 << pkt->getSize() << std::endl;
737 // Mesh update thread must be stopped while
738 // updating content definitions
739 sanity_check(!m_mesh_update_thread.isRunning());
741 // Decompress item definitions
742 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
743 std::ostringstream tmp_os;
744 decompressZlib(tmp_is, tmp_os);
746 // Deserialize node definitions
747 std::istringstream tmp_is2(tmp_os.str());
748 m_itemdef->deSerialize(tmp_is2);
749 m_itemdef_received = true;
752 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
760 [11 + len] (f32 * 3) pos
761 [23 + len] u16 object_id
771 u8 type; // 0=local, 1=positional, 2=object
778 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
783 } catch (PacketError &e) {};
789 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
791 case 1: // positional
792 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
796 ClientActiveObject *cao = m_env.getActiveObject(object_id);
798 pos = cao->getPosition();
799 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
800 // TODO: Set up sound to move with object
807 if (client_id != -1) {
808 m_sounds_server_to_client[server_id] = client_id;
809 m_sounds_client_to_server[client_id] = server_id;
811 m_sounds_to_objects[client_id] = object_id;
815 void Client::handleCommand_StopSound(NetworkPacket* pkt)
821 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
822 if (i != m_sounds_server_to_client.end()) {
823 int client_id = i->second;
824 m_sound->stopSound(client_id);
828 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
834 *pkt >> sound_id >> step >> gain;
836 std::unordered_map<s32, int>::const_iterator i =
837 m_sounds_server_to_client.find(sound_id);
839 if (i != m_sounds_server_to_client.end())
840 m_sound->fadeSound(i->second, step, gain);
843 void Client::handleCommand_Privileges(NetworkPacket* pkt)
845 m_privileges.clear();
846 infostream << "Client: Privileges updated: ";
849 *pkt >> num_privileges;
851 for (u16 i = 0; i < num_privileges; i++) {
856 m_privileges.insert(priv);
857 infostream << priv << " ";
859 infostream << std::endl;
862 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
864 LocalPlayer *player = m_env.getLocalPlayer();
865 assert(player != NULL);
867 // Store formspec in LocalPlayer
868 player->inventory_formspec = pkt->readLongString();
871 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
874 bool keep_inv = true;
875 *pkt >> name >> keep_inv;
877 infostream << "Client: Detached inventory update: \"" << name
878 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
880 const auto &inv_it = m_detached_inventories.find(name);
882 if (inv_it != m_detached_inventories.end()) {
883 delete inv_it->second;
884 m_detached_inventories.erase(inv_it);
888 Inventory *inv = nullptr;
889 if (inv_it == m_detached_inventories.end()) {
890 inv = new Inventory(m_itemdef);
891 m_detached_inventories[name] = inv;
893 inv = inv_it->second;
896 std::string contents;
898 std::istringstream is(contents, std::ios::binary);
899 inv->deSerialize(is);
902 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
904 std::string formspec = pkt->readLongString();
905 std::string formname;
909 ClientEvent *event = new ClientEvent();
910 event->type = CE_SHOW_FORMSPEC;
911 // pointer is required as event is a struct only!
912 // adding a std:string to a struct isn't possible
913 event->show_formspec.formspec = new std::string(formspec);
914 event->show_formspec.formname = new std::string(formname);
915 m_client_event_queue.push(event);
918 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
920 std::string datastring(pkt->getString(0), pkt->getSize());
921 std::istringstream is(datastring, std::ios_base::binary);
923 v3f pos = readV3F32(is);
924 v3f vel = readV3F32(is);
925 v3f acc = readV3F32(is);
926 float expirationtime = readF32(is);
927 float size = readF32(is);
928 bool collisiondetection = readU8(is);
929 std::string texture = deSerializeLongString(is);
931 bool vertical = false;
932 bool collision_removal = false;
933 TileAnimationParams animation;
934 animation.type = TAT_NONE;
936 bool object_collision = false;
938 vertical = readU8(is);
939 collision_removal = readU8(is);
940 animation.deSerialize(is, m_proto_ver);
942 object_collision = readU8(is);
945 ClientEvent *event = new ClientEvent();
946 event->type = CE_SPAWN_PARTICLE;
947 event->spawn_particle.pos = new v3f (pos);
948 event->spawn_particle.vel = new v3f (vel);
949 event->spawn_particle.acc = new v3f (acc);
950 event->spawn_particle.expirationtime = expirationtime;
951 event->spawn_particle.size = size;
952 event->spawn_particle.collisiondetection = collisiondetection;
953 event->spawn_particle.collision_removal = collision_removal;
954 event->spawn_particle.object_collision = object_collision;
955 event->spawn_particle.vertical = vertical;
956 event->spawn_particle.texture = new std::string(texture);
957 event->spawn_particle.animation = animation;
958 event->spawn_particle.glow = glow;
960 m_client_event_queue.push(event);
963 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
977 bool collisiondetection;
980 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
981 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
982 >> maxsize >> collisiondetection;
984 std::string texture = pkt->readLongString();
988 bool vertical = false;
989 bool collision_removal = false;
991 TileAnimationParams animation;
992 animation.type = TAT_NONE;
994 bool object_collision = false;
997 *pkt >> collision_removal;
1000 // This is horrible but required (why are there two ways to deserialize pkts?)
1001 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1002 std::istringstream is(datastring, std::ios_base::binary);
1003 animation.deSerialize(is, m_proto_ver);
1005 object_collision = readU8(is);
1008 u32 client_id = m_particle_manager.getSpawnerId();
1009 m_particles_server_to_client[server_id] = client_id;
1011 ClientEvent *event = new ClientEvent();
1012 event->type = CE_ADD_PARTICLESPAWNER;
1013 event->add_particlespawner.amount = amount;
1014 event->add_particlespawner.spawntime = spawntime;
1015 event->add_particlespawner.minpos = new v3f (minpos);
1016 event->add_particlespawner.maxpos = new v3f (maxpos);
1017 event->add_particlespawner.minvel = new v3f (minvel);
1018 event->add_particlespawner.maxvel = new v3f (maxvel);
1019 event->add_particlespawner.minacc = new v3f (minacc);
1020 event->add_particlespawner.maxacc = new v3f (maxacc);
1021 event->add_particlespawner.minexptime = minexptime;
1022 event->add_particlespawner.maxexptime = maxexptime;
1023 event->add_particlespawner.minsize = minsize;
1024 event->add_particlespawner.maxsize = maxsize;
1025 event->add_particlespawner.collisiondetection = collisiondetection;
1026 event->add_particlespawner.collision_removal = collision_removal;
1027 event->add_particlespawner.object_collision = object_collision;
1028 event->add_particlespawner.attached_id = attached_id;
1029 event->add_particlespawner.vertical = vertical;
1030 event->add_particlespawner.texture = new std::string(texture);
1031 event->add_particlespawner.id = client_id;
1032 event->add_particlespawner.animation = animation;
1033 event->add_particlespawner.glow = glow;
1035 m_client_event_queue.push(event);
1039 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1045 auto i = m_particles_server_to_client.find(server_id);
1046 if (i != m_particles_server_to_client.end())
1047 client_id = i->second;
1051 ClientEvent *event = new ClientEvent();
1052 event->type = CE_DELETE_PARTICLESPAWNER;
1053 event->delete_particlespawner.id = client_id;
1055 m_client_event_queue.push(event);
1058 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1060 std::string datastring(pkt->getString(0), pkt->getSize());
1061 std::istringstream is(datastring, std::ios_base::binary);
1077 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1078 >> dir >> align >> offset;
1082 catch(SerializationError &e) {};
1086 } catch(SerializationError &e) {};
1088 ClientEvent *event = new ClientEvent();
1089 event->type = CE_HUDADD;
1090 event->hudadd.server_id = server_id;
1091 event->hudadd.type = type;
1092 event->hudadd.pos = new v2f(pos);
1093 event->hudadd.name = new std::string(name);
1094 event->hudadd.scale = new v2f(scale);
1095 event->hudadd.text = new std::string(text);
1096 event->hudadd.number = number;
1097 event->hudadd.item = item;
1098 event->hudadd.dir = dir;
1099 event->hudadd.align = new v2f(align);
1100 event->hudadd.offset = new v2f(offset);
1101 event->hudadd.world_pos = new v3f(world_pos);
1102 event->hudadd.size = new v2s32(size);
1103 m_client_event_queue.push(event);
1106 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1112 auto i = m_hud_server_to_client.find(server_id);
1113 if (i != m_hud_server_to_client.end()) {
1114 int client_id = i->second;
1115 m_hud_server_to_client.erase(i);
1117 ClientEvent *event = new ClientEvent();
1118 event->type = CE_HUDRM;
1119 event->hudrm.id = client_id;
1120 m_client_event_queue.push(event);
1124 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1134 *pkt >> server_id >> stat;
1136 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1137 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1139 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1141 else if (stat == HUD_STAT_WORLD_POS)
1143 else if (stat == HUD_STAT_SIZE )
1148 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1149 if (i != m_hud_server_to_client.end()) {
1150 ClientEvent *event = new ClientEvent();
1151 event->type = CE_HUDCHANGE;
1152 event->hudchange.id = i->second;
1153 event->hudchange.stat = (HudElementStat)stat;
1154 event->hudchange.v2fdata = new v2f(v2fdata);
1155 event->hudchange.v3fdata = new v3f(v3fdata);
1156 event->hudchange.sdata = new std::string(sdata);
1157 event->hudchange.data = intdata;
1158 event->hudchange.v2s32data = new v2s32(v2s32data);
1159 m_client_event_queue.push(event);
1163 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1167 *pkt >> flags >> mask;
1169 LocalPlayer *player = m_env.getLocalPlayer();
1170 assert(player != NULL);
1172 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1173 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1175 player->hud_flags &= ~mask;
1176 player->hud_flags |= flags;
1178 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1179 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1181 // Hide minimap if it has been disabled by the server
1182 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1183 // defers a minimap update, therefore only call it if really
1184 // needed, by checking that minimap was visible before
1185 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1187 // Switch to surface mode if radar disabled by server
1188 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1189 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1192 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1194 u16 param; std::string value;
1196 *pkt >> param >> value;
1198 LocalPlayer *player = m_env.getLocalPlayer();
1199 assert(player != NULL);
1201 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1202 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1203 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1204 player->hud_hotbar_itemcount = hotbar_itemcount;
1206 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1207 // If value not empty verify image exists in texture source
1208 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1209 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1210 << value << "')" << std::endl;
1213 player->hotbar_image = value;
1215 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1216 // If value not empty verify image exists in texture source
1217 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1218 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1219 << value << "')" << std::endl;
1222 player->hotbar_selected_image = value;
1226 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1228 std::string datastring(pkt->getString(0), pkt->getSize());
1229 std::istringstream is(datastring, std::ios_base::binary);
1231 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1232 std::string *type = new std::string(deSerializeString(is));
1233 u16 count = readU16(is);
1234 std::vector<std::string> *params = new std::vector<std::string>;
1236 for (size_t i = 0; i < count; i++)
1237 params->push_back(deSerializeString(is));
1241 clouds = readU8(is);
1244 ClientEvent *event = new ClientEvent();
1245 event->type = CE_SET_SKY;
1246 event->set_sky.bgcolor = bgcolor;
1247 event->set_sky.type = type;
1248 event->set_sky.params = params;
1249 event->set_sky.clouds = clouds;
1250 m_client_event_queue.push(event);
1253 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1256 video::SColor color_bright;
1257 video::SColor color_ambient;
1262 *pkt >> density >> color_bright >> color_ambient
1263 >> height >> thickness >> speed;
1265 ClientEvent *event = new ClientEvent();
1266 event->type = CE_CLOUD_PARAMS;
1267 event->cloud_params.density = density;
1268 // use the underlying u32 representation, because we can't
1269 // use struct members with constructors here, and this way
1270 // we avoid using new() and delete() for no good reason
1271 event->cloud_params.color_bright = color_bright.color;
1272 event->cloud_params.color_ambient = color_ambient.color;
1273 event->cloud_params.height = height;
1274 event->cloud_params.thickness = thickness;
1275 // same here: deconstruct to skip constructor
1276 event->cloud_params.speed_x = speed.X;
1277 event->cloud_params.speed_y = speed.Y;
1278 m_client_event_queue.push(event);
1281 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1284 u16 day_night_ratio_u;
1286 *pkt >> do_override >> day_night_ratio_u;
1288 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1290 ClientEvent *event = new ClientEvent();
1291 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1292 event->override_day_night_ratio.do_override = do_override;
1293 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1294 m_client_event_queue.push(event);
1297 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1299 LocalPlayer *player = m_env.getLocalPlayer();
1300 assert(player != NULL);
1302 *pkt >> player->local_animations[0];
1303 *pkt >> player->local_animations[1];
1304 *pkt >> player->local_animations[2];
1305 *pkt >> player->local_animations[3];
1306 *pkt >> player->local_animation_speed;
1309 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1311 LocalPlayer *player = m_env.getLocalPlayer();
1312 assert(player != NULL);
1314 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1317 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1321 *pkt >> type >> num_players;
1322 PlayerListModifer notice_type = (PlayerListModifer) type;
1324 for (u16 i = 0; i < num_players; i++) {
1327 switch (notice_type) {
1328 case PLAYER_LIST_INIT:
1329 case PLAYER_LIST_ADD:
1330 m_env.addPlayerName(name);
1332 case PLAYER_LIST_REMOVE:
1333 m_env.removePlayerName(name);
1339 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1341 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1342 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1343 errorstream << "Client: Received SRP S_B login message,"
1344 << " but wasn't supposed to (chosen_mech="
1345 << m_chosen_auth_mech << ")." << std::endl;
1351 SRPUser *usr = (SRPUser *) m_auth_data;
1356 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1358 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1359 (const unsigned char *) B.c_str(), B.size(),
1360 (unsigned char **) &bytes_M, &len_M);
1363 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1367 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1368 resp_pkt << std::string(bytes_M, len_M);
1372 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1374 LocalPlayer *player = m_env.getLocalPlayer();
1375 assert(player != NULL);
1377 // Store formspec in LocalPlayer
1378 *pkt >> player->formspec_prepend;
1381 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1383 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1385 // Restrictions were received -> load mods if it's enabled
1386 // Note: this should be moved after mods receptions from server instead
1394 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1396 std::string channel_name, sender, channel_msg;
1397 *pkt >> channel_name >> sender >> channel_msg;
1399 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1400 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1401 << channel_msg << std::endl;
1403 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1404 verbosestream << "Server sent us messages on unregistered channel "
1405 << channel_name << ", ignoring." << std::endl;
1409 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1412 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1415 ModChannelSignal signal;
1416 std::string channel;
1418 *pkt >> signal_tmp >> channel;
1420 signal = (ModChannelSignal)signal_tmp;
1422 bool valid_signal = true;
1423 // @TODO: send Signal to Lua API
1425 case MODCHANNEL_SIGNAL_JOIN_OK:
1426 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1427 infostream << "Server ack our mod channel join on channel `" << channel
1428 << "`, joining." << std::endl;
1430 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1431 // Unable to join, remove channel
1432 m_modchannel_mgr->leaveChannel(channel, 0);
1433 infostream << "Server refused our mod channel join on channel `" << channel
1434 << "`" << std::endl;
1436 case MODCHANNEL_SIGNAL_LEAVE_OK:
1438 infostream << "Server ack our mod channel leave on channel " << channel
1439 << "`, leaving." << std::endl;
1442 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1443 infostream << "Server refused our mod channel leave on channel `" << channel
1444 << "`" << std::endl;
1446 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1448 // Generally unused, but ensure we don't do an implementation error
1449 infostream << "Server tells us we sent a message on channel `" << channel
1450 << "` but we are not registered. Message was dropped." << std::endl;
1453 case MODCHANNEL_SIGNAL_SET_STATE: {
1457 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1458 infostream << "Received wrong channel state " << state
1459 << ", ignoring." << std::endl;
1463 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1464 infostream << "Server sets mod channel `" << channel
1465 << "` in read-only mode." << std::endl;
1470 warningstream << "Received unhandled mod channel signal ID "
1471 << signal << ", ignoring." << std::endl;
1473 valid_signal = false;
1477 // If signal is valid, forward it to client side mods
1479 m_script->on_modchannel_signal(channel, signal);