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.
22 #include "util/base64.h"
23 #include "chatmessage.h"
24 #include "clientmedia.h"
27 #include "mapsector.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);
246 void Client::handleCommand_BlockData(NetworkPacket* pkt)
248 // Ignore too small packet
249 if (pkt->getSize() < 6)
255 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
256 std::istringstream istr(datastring, std::ios_base::binary);
262 sector = m_env.getMap().emergeSector(p2d);
264 assert(sector->getPos() == p2d);
266 block = sector->getBlockNoCreateNoEx(p.Y);
269 Update an existing block
271 block->deSerialize(istr, m_server_ser_ver, false);
272 block->deSerializeNetworkSpecific(istr);
278 block = new MapBlock(&m_env.getMap(), p, this);
279 block->deSerialize(istr, m_server_ser_ver, false);
280 block->deSerializeNetworkSpecific(istr);
281 sector->insertBlock(block);
285 ServerMap::saveBlock(block, m_localdb);
289 Add it to mesh update queue and set it to be acknowledged after update.
291 addUpdateMeshTaskWithEdge(p, true);
294 void Client::handleCommand_Inventory(NetworkPacket* pkt)
296 if (pkt->getSize() < 1)
299 std::string datastring(pkt->getString(0), pkt->getSize());
300 std::istringstream is(datastring, std::ios_base::binary);
302 LocalPlayer *player = m_env.getLocalPlayer();
303 assert(player != NULL);
305 player->inventory.deSerialize(is);
307 m_inventory_updated = true;
309 delete m_inventory_from_server;
310 m_inventory_from_server = new Inventory(player->inventory);
311 m_inventory_from_server_age = 0.0;
314 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
316 if (pkt->getSize() < 2)
323 time_of_day = time_of_day % 24000;
324 float time_speed = 0;
326 if (pkt->getSize() >= 2 + 4) {
330 // Old message; try to approximate speed of time by ourselves
331 float time_of_day_f = (float)time_of_day / 24000.0f;
332 float tod_diff_f = 0;
334 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
335 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
337 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
339 m_last_time_of_day_f = time_of_day_f;
340 float time_diff = m_time_of_day_update_timer;
341 m_time_of_day_update_timer = 0;
343 if (m_time_of_day_set) {
344 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
345 infostream << "Client: Measured time_of_day speed (old format): "
346 << time_speed << " tod_diff_f=" << tod_diff_f
347 << " time_diff=" << time_diff << std::endl;
351 // Update environment
352 m_env.setTimeOfDay(time_of_day);
353 m_env.setTimeOfDaySpeed(time_speed);
354 m_time_of_day_set = true;
356 u32 dr = m_env.getDayNightRatio();
357 infostream << "Client: time_of_day=" << time_of_day
358 << " time_speed=" << time_speed
359 << " dr=" << dr << std::endl;
362 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
367 u16 sendername length
373 ChatMessage *chatMessage = new ChatMessage();
374 u8 version, message_type;
375 *pkt >> version >> message_type;
377 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
382 *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
384 chatMessage->type = (ChatMessageType) message_type;
386 // @TODO send this to CSM using ChatMessage object
387 if (!moddingEnabled() || !m_script->on_receiving_message(
388 wide_to_utf8(chatMessage->message))) {
389 pushToChatQueue(chatMessage);
391 // Message was consumed by CSM and should not handled by client, destroying
396 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
399 u16 count of removed objects
400 for all removed objects {
403 u16 count of added objects
404 for all added objects {
407 u32 initialization data length
408 string initialization data
414 u16 removed_count, added_count, id;
416 // Read removed objects
417 *pkt >> removed_count;
419 for (u16 i = 0; i < removed_count; i++) {
421 m_env.removeActiveObject(id);
424 // Read added objects
427 for (u16 i = 0; i < added_count; i++) {
429 m_env.addActiveObject(id, type, pkt->readLongString());
431 } catch (PacketError &e) {
432 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
433 << ". The packet is unreliable, ignoring" << std::endl;
437 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
447 std::string datastring(pkt->getString(0), pkt->getSize());
448 std::istringstream is(datastring, std::ios_base::binary);
452 u16 id = readU16(is);
456 std::string message = deSerializeString(is);
458 // Pass on to the environment
459 m_env.processActiveObjectMessage(id, message);
461 } catch (SerializationError &e) {
462 errorstream << "Client::handleCommand_ActiveObjectMessages: "
463 << "caught SerializationError: " << e.what() << std::endl;
467 void Client::handleCommand_Movement(NetworkPacket* pkt)
469 LocalPlayer *player = m_env.getLocalPlayer();
470 assert(player != NULL);
472 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
474 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
475 >> lf >> lfs >> ls >> g;
477 player->movement_acceleration_default = mad * BS;
478 player->movement_acceleration_air = maa * BS;
479 player->movement_acceleration_fast = maf * BS;
480 player->movement_speed_walk = msw * BS;
481 player->movement_speed_crouch = mscr * BS;
482 player->movement_speed_fast = msf * BS;
483 player->movement_speed_climb = mscl * BS;
484 player->movement_speed_jump = msj * BS;
485 player->movement_liquid_fluidity = lf * BS;
486 player->movement_liquid_fluidity_smooth = lfs * BS;
487 player->movement_liquid_sink = ls * BS;
488 player->movement_gravity = g * BS;
491 void Client::handleCommand_HP(NetworkPacket* pkt)
494 LocalPlayer *player = m_env.getLocalPlayer();
495 assert(player != NULL);
497 u16 oldhp = player->hp;
504 if (moddingEnabled()) {
505 m_script->on_hp_modification(hp);
509 // Add to ClientEvent queue
510 ClientEvent *event = new ClientEvent();
511 event->type = CE_PLAYER_DAMAGE;
512 event->player_damage.amount = oldhp - hp;
513 m_client_event_queue.push(event);
517 void Client::handleCommand_Breath(NetworkPacket* pkt)
519 LocalPlayer *player = m_env.getLocalPlayer();
520 assert(player != NULL);
526 player->setBreath(breath);
529 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
531 LocalPlayer *player = m_env.getLocalPlayer();
532 assert(player != NULL);
537 *pkt >> pos >> pitch >> yaw;
539 player->setPosition(pos);
541 infostream << "Client got TOCLIENT_MOVE_PLAYER"
542 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
543 << " pitch=" << pitch
548 Add to ClientEvent queue.
549 This has to be sent to the main program because otherwise
550 it would just force the pitch and yaw values to whatever
551 the camera points to.
553 ClientEvent *event = new ClientEvent();
554 event->type = CE_PLAYER_FORCE_MOVE;
555 event->player_force_move.pitch = pitch;
556 event->player_force_move.yaw = yaw;
557 m_client_event_queue.push(event);
560 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
562 bool set_camera_point_target;
563 v3f camera_point_target;
565 *pkt >> set_camera_point_target;
566 *pkt >> camera_point_target;
568 ClientEvent *event = new ClientEvent();
569 event->type = CE_DEATHSCREEN;
570 event->deathscreen.set_camera_point_target = set_camera_point_target;
571 event->deathscreen.camera_point_target_x = camera_point_target.X;
572 event->deathscreen.camera_point_target_y = camera_point_target.Y;
573 event->deathscreen.camera_point_target_z = camera_point_target.Z;
574 m_client_event_queue.push(event);
577 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
583 infostream << "Client: Received media announcement: packet size: "
584 << pkt->getSize() << std::endl;
586 if (m_media_downloader == NULL ||
587 m_media_downloader->isStarted()) {
588 const char *problem = m_media_downloader ?
589 "we already saw another announcement" :
590 "all media has been received already";
591 errorstream << "Client: Received media announcement but "
593 << " files=" << num_files
594 << " size=" << pkt->getSize() << std::endl;
598 // Mesh update thread must be stopped while
599 // updating content definitions
600 sanity_check(!m_mesh_update_thread.isRunning());
602 for (u16 i = 0; i < num_files; i++) {
603 std::string name, sha1_base64;
605 *pkt >> name >> sha1_base64;
607 std::string sha1_raw = base64_decode(sha1_base64);
608 m_media_downloader->addFile(name, sha1_raw);
617 while(!sf.at_end()) {
618 std::string baseurl = trim(sf.next(","));
619 if (!baseurl.empty())
620 m_media_downloader->addRemoteServer(baseurl);
623 catch(SerializationError& e) {
624 // not supported by server or turned off
627 m_media_downloader->step(this);
630 void Client::handleCommand_Media(NetworkPacket* pkt)
634 u16 total number of file bunches
635 u16 index of this bunch
636 u32 number of files in this bunch
648 *pkt >> num_bunches >> bunch_i >> num_files;
650 infostream << "Client: Received files: bunch " << bunch_i << "/"
651 << num_bunches << " files=" << num_files
652 << " size=" << pkt->getSize() << std::endl;
657 if (!m_media_downloader || !m_media_downloader->isStarted()) {
658 const char *problem = m_media_downloader ?
659 "media has not been requested" :
660 "all media has been received already";
661 errorstream << "Client: Received media but "
663 << " bunch " << bunch_i << "/" << num_bunches
664 << " files=" << num_files
665 << " size=" << pkt->getSize() << std::endl;
669 // Mesh update thread must be stopped while
670 // updating content definitions
671 sanity_check(!m_mesh_update_thread.isRunning());
673 for (u32 i=0; i < num_files; i++) {
678 std::string data = pkt->readLongString();
680 m_media_downloader->conventionalTransferDone(
685 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
687 infostream << "Client: Received node definitions: packet size: "
688 << pkt->getSize() << std::endl;
690 // Mesh update thread must be stopped while
691 // updating content definitions
692 sanity_check(!m_mesh_update_thread.isRunning());
694 // Decompress node definitions
695 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
696 std::ostringstream tmp_os;
697 decompressZlib(tmp_is, tmp_os);
699 // Deserialize node definitions
700 std::istringstream tmp_is2(tmp_os.str());
701 m_nodedef->deSerialize(tmp_is2);
702 m_nodedef_received = true;
705 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
707 infostream << "Client: Received item definitions: packet size: "
708 << pkt->getSize() << std::endl;
710 // Mesh update thread must be stopped while
711 // updating content definitions
712 sanity_check(!m_mesh_update_thread.isRunning());
714 // Decompress item definitions
715 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
716 std::ostringstream tmp_os;
717 decompressZlib(tmp_is, tmp_os);
719 // Deserialize node definitions
720 std::istringstream tmp_is2(tmp_os.str());
721 m_itemdef->deSerialize(tmp_is2);
722 m_itemdef_received = true;
725 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
733 [11 + len] (f32 * 3) pos
734 [23 + len] u16 object_id
744 u8 type; // 0=local, 1=positional, 2=object
751 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
756 } catch (PacketError &e) {};
762 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
764 case 1: // positional
765 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
769 ClientActiveObject *cao = m_env.getActiveObject(object_id);
771 pos = cao->getPosition();
772 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
773 // TODO: Set up sound to move with object
780 if (client_id != -1) {
781 m_sounds_server_to_client[server_id] = client_id;
782 m_sounds_client_to_server[client_id] = server_id;
784 m_sounds_to_objects[client_id] = object_id;
788 void Client::handleCommand_StopSound(NetworkPacket* pkt)
794 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
795 if (i != m_sounds_server_to_client.end()) {
796 int client_id = i->second;
797 m_sound->stopSound(client_id);
801 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
807 *pkt >> sound_id >> step >> gain;
809 std::unordered_map<s32, int>::const_iterator i =
810 m_sounds_server_to_client.find(sound_id);
812 if (i != m_sounds_server_to_client.end())
813 m_sound->fadeSound(i->second, step, gain);
816 void Client::handleCommand_Privileges(NetworkPacket* pkt)
818 m_privileges.clear();
819 infostream << "Client: Privileges updated: ";
822 *pkt >> num_privileges;
824 for (u16 i = 0; i < num_privileges; i++) {
829 m_privileges.insert(priv);
830 infostream << priv << " ";
832 infostream << std::endl;
835 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
837 LocalPlayer *player = m_env.getLocalPlayer();
838 assert(player != NULL);
840 // Store formspec in LocalPlayer
841 player->inventory_formspec = pkt->readLongString();
844 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
846 std::string datastring(pkt->getString(0), pkt->getSize());
847 std::istringstream is(datastring, std::ios_base::binary);
849 std::string name = deSerializeString(is);
851 infostream << "Client: Detached inventory update: \"" << name
852 << "\"" << std::endl;
854 Inventory *inv = NULL;
855 if (m_detached_inventories.count(name) > 0)
856 inv = m_detached_inventories[name];
858 inv = new Inventory(m_itemdef);
859 m_detached_inventories[name] = inv;
861 inv->deSerialize(is);
864 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
866 std::string formspec = pkt->readLongString();
867 std::string formname;
871 ClientEvent *event = new ClientEvent();
872 event->type = CE_SHOW_FORMSPEC;
873 // pointer is required as event is a struct only!
874 // adding a std:string to a struct isn't possible
875 event->show_formspec.formspec = new std::string(formspec);
876 event->show_formspec.formname = new std::string(formname);
877 m_client_event_queue.push(event);
880 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
882 std::string datastring(pkt->getString(0), pkt->getSize());
883 std::istringstream is(datastring, std::ios_base::binary);
885 v3f pos = readV3F1000(is);
886 v3f vel = readV3F1000(is);
887 v3f acc = readV3F1000(is);
888 float expirationtime = readF1000(is);
889 float size = readF1000(is);
890 bool collisiondetection = readU8(is);
891 std::string texture = deSerializeLongString(is);
892 bool vertical = false;
893 bool collision_removal = false;
894 TileAnimationParams animation;
895 animation.type = TAT_NONE;
898 vertical = readU8(is);
899 collision_removal = readU8(is);
900 animation.deSerialize(is, m_proto_ver);
904 ClientEvent *event = new ClientEvent();
905 event->type = CE_SPAWN_PARTICLE;
906 event->spawn_particle.pos = new v3f (pos);
907 event->spawn_particle.vel = new v3f (vel);
908 event->spawn_particle.acc = new v3f (acc);
909 event->spawn_particle.expirationtime = expirationtime;
910 event->spawn_particle.size = size;
911 event->spawn_particle.collisiondetection = collisiondetection;
912 event->spawn_particle.collision_removal = collision_removal;
913 event->spawn_particle.vertical = vertical;
914 event->spawn_particle.texture = new std::string(texture);
915 event->spawn_particle.animation = animation;
916 event->spawn_particle.glow = glow;
918 m_client_event_queue.push(event);
921 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
935 bool collisiondetection;
938 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
939 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
940 >> maxsize >> collisiondetection;
942 std::string texture = pkt->readLongString();
946 bool vertical = false;
947 bool collision_removal = false;
948 TileAnimationParams animation;
949 animation.type = TAT_NONE;
954 *pkt >> collision_removal;
957 // This is horrible but required (why are there two ways to deserialize pkts?)
958 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
959 std::istringstream is(datastring, std::ios_base::binary);
960 animation.deSerialize(is, m_proto_ver);
964 u32 client_id = m_particle_manager.getSpawnerId();
965 m_particles_server_to_client[server_id] = client_id;
967 ClientEvent *event = new ClientEvent();
968 event->type = CE_ADD_PARTICLESPAWNER;
969 event->add_particlespawner.amount = amount;
970 event->add_particlespawner.spawntime = spawntime;
971 event->add_particlespawner.minpos = new v3f (minpos);
972 event->add_particlespawner.maxpos = new v3f (maxpos);
973 event->add_particlespawner.minvel = new v3f (minvel);
974 event->add_particlespawner.maxvel = new v3f (maxvel);
975 event->add_particlespawner.minacc = new v3f (minacc);
976 event->add_particlespawner.maxacc = new v3f (maxacc);
977 event->add_particlespawner.minexptime = minexptime;
978 event->add_particlespawner.maxexptime = maxexptime;
979 event->add_particlespawner.minsize = minsize;
980 event->add_particlespawner.maxsize = maxsize;
981 event->add_particlespawner.collisiondetection = collisiondetection;
982 event->add_particlespawner.collision_removal = collision_removal;
983 event->add_particlespawner.attached_id = attached_id;
984 event->add_particlespawner.vertical = vertical;
985 event->add_particlespawner.texture = new std::string(texture);
986 event->add_particlespawner.id = client_id;
987 event->add_particlespawner.animation = animation;
988 event->add_particlespawner.glow = glow;
990 m_client_event_queue.push(event);
994 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1000 auto i = m_particles_server_to_client.find(server_id);
1001 if (i != m_particles_server_to_client.end())
1002 client_id = i->second;
1006 ClientEvent *event = new ClientEvent();
1007 event->type = CE_DELETE_PARTICLESPAWNER;
1008 event->delete_particlespawner.id = client_id;
1010 m_client_event_queue.push(event);
1013 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1015 std::string datastring(pkt->getString(0), pkt->getSize());
1016 std::istringstream is(datastring, std::ios_base::binary);
1032 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1033 >> dir >> align >> offset;
1037 catch(SerializationError &e) {};
1041 } catch(SerializationError &e) {};
1043 ClientEvent *event = new ClientEvent();
1044 event->type = CE_HUDADD;
1045 event->hudadd.server_id = server_id;
1046 event->hudadd.type = type;
1047 event->hudadd.pos = new v2f(pos);
1048 event->hudadd.name = new std::string(name);
1049 event->hudadd.scale = new v2f(scale);
1050 event->hudadd.text = new std::string(text);
1051 event->hudadd.number = number;
1052 event->hudadd.item = item;
1053 event->hudadd.dir = dir;
1054 event->hudadd.align = new v2f(align);
1055 event->hudadd.offset = new v2f(offset);
1056 event->hudadd.world_pos = new v3f(world_pos);
1057 event->hudadd.size = new v2s32(size);
1058 m_client_event_queue.push(event);
1061 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1067 auto i = m_hud_server_to_client.find(server_id);
1068 if (i != m_hud_server_to_client.end()) {
1069 int client_id = i->second;
1070 m_hud_server_to_client.erase(i);
1072 ClientEvent *event = new ClientEvent();
1073 event->type = CE_HUDRM;
1074 event->hudrm.id = client_id;
1075 m_client_event_queue.push(event);
1079 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1089 *pkt >> server_id >> stat;
1091 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1092 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1094 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1096 else if (stat == HUD_STAT_WORLD_POS)
1098 else if (stat == HUD_STAT_SIZE )
1103 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1104 if (i != m_hud_server_to_client.end()) {
1105 ClientEvent *event = new ClientEvent();
1106 event->type = CE_HUDCHANGE;
1107 event->hudchange.id = i->second;
1108 event->hudchange.stat = (HudElementStat)stat;
1109 event->hudchange.v2fdata = new v2f(v2fdata);
1110 event->hudchange.v3fdata = new v3f(v3fdata);
1111 event->hudchange.sdata = new std::string(sdata);
1112 event->hudchange.data = intdata;
1113 event->hudchange.v2s32data = new v2s32(v2s32data);
1114 m_client_event_queue.push(event);
1118 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1122 *pkt >> flags >> mask;
1124 LocalPlayer *player = m_env.getLocalPlayer();
1125 assert(player != NULL);
1127 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1128 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1130 player->hud_flags &= ~mask;
1131 player->hud_flags |= flags;
1133 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1134 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1136 // Hide minimap if it has been disabled by the server
1137 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1138 // defers a minimap update, therefore only call it if really
1139 // needed, by checking that minimap was visible before
1140 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1142 // Switch to surface mode if radar disabled by server
1143 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1144 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1147 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1149 u16 param; std::string value;
1151 *pkt >> param >> value;
1153 LocalPlayer *player = m_env.getLocalPlayer();
1154 assert(player != NULL);
1156 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1157 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1158 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1159 player->hud_hotbar_itemcount = hotbar_itemcount;
1161 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1162 // If value not empty verify image exists in texture source
1163 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1164 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1165 << value << "')" << std::endl;
1168 player->hotbar_image = value;
1170 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1171 // If value not empty verify image exists in texture source
1172 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1173 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1174 << value << "')" << std::endl;
1177 player->hotbar_selected_image = value;
1181 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1183 std::string datastring(pkt->getString(0), pkt->getSize());
1184 std::istringstream is(datastring, std::ios_base::binary);
1186 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1187 std::string *type = new std::string(deSerializeString(is));
1188 u16 count = readU16(is);
1189 std::vector<std::string> *params = new std::vector<std::string>;
1191 for (size_t i = 0; i < count; i++)
1192 params->push_back(deSerializeString(is));
1196 clouds = readU8(is);
1199 ClientEvent *event = new ClientEvent();
1200 event->type = CE_SET_SKY;
1201 event->set_sky.bgcolor = bgcolor;
1202 event->set_sky.type = type;
1203 event->set_sky.params = params;
1204 event->set_sky.clouds = clouds;
1205 m_client_event_queue.push(event);
1208 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1211 video::SColor color_bright;
1212 video::SColor color_ambient;
1217 *pkt >> density >> color_bright >> color_ambient
1218 >> height >> thickness >> speed;
1220 ClientEvent *event = new ClientEvent();
1221 event->type = CE_CLOUD_PARAMS;
1222 event->cloud_params.density = density;
1223 // use the underlying u32 representation, because we can't
1224 // use struct members with constructors here, and this way
1225 // we avoid using new() and delete() for no good reason
1226 event->cloud_params.color_bright = color_bright.color;
1227 event->cloud_params.color_ambient = color_ambient.color;
1228 event->cloud_params.height = height;
1229 event->cloud_params.thickness = thickness;
1230 // same here: deconstruct to skip constructor
1231 event->cloud_params.speed_x = speed.X;
1232 event->cloud_params.speed_y = speed.Y;
1233 m_client_event_queue.push(event);
1236 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1239 u16 day_night_ratio_u;
1241 *pkt >> do_override >> day_night_ratio_u;
1243 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1245 ClientEvent *event = new ClientEvent();
1246 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1247 event->override_day_night_ratio.do_override = do_override;
1248 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1249 m_client_event_queue.push(event);
1252 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1254 LocalPlayer *player = m_env.getLocalPlayer();
1255 assert(player != NULL);
1257 *pkt >> player->local_animations[0];
1258 *pkt >> player->local_animations[1];
1259 *pkt >> player->local_animations[2];
1260 *pkt >> player->local_animations[3];
1261 *pkt >> player->local_animation_speed;
1264 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1266 LocalPlayer *player = m_env.getLocalPlayer();
1267 assert(player != NULL);
1269 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1272 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1276 *pkt >> type >> num_players;
1277 PlayerListModifer notice_type = (PlayerListModifer) type;
1279 for (u16 i = 0; i < num_players; i++) {
1282 switch (notice_type) {
1283 case PLAYER_LIST_INIT:
1284 case PLAYER_LIST_ADD:
1285 m_env.addPlayerName(name);
1287 case PLAYER_LIST_REMOVE:
1288 m_env.removePlayerName(name);
1294 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1296 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1297 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1298 errorstream << "Client: Received SRP S_B login message,"
1299 << " but wasn't supposed to (chosen_mech="
1300 << m_chosen_auth_mech << ")." << std::endl;
1306 SRPUser *usr = (SRPUser *) m_auth_data;
1311 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1313 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1314 (const unsigned char *) B.c_str(), B.size(),
1315 (unsigned char **) &bytes_M, &len_M);
1318 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1322 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1323 resp_pkt << std::string(bytes_M, len_M);
1327 void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
1329 *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;
1331 // Now we have flavours, load mods if it's enabled
1332 // Note: this should be moved after mods receptions from server instead
1340 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1342 std::string channel_name, sender, channel_msg;
1343 *pkt >> channel_name >> sender >> channel_msg;
1345 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1346 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1347 << channel_msg << std::endl;
1349 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1350 verbosestream << "Server sent us messages on unregistered channel "
1351 << channel_name << ", ignoring." << std::endl;
1355 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1358 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1361 ModChannelSignal signal;
1362 std::string channel;
1364 *pkt >> signal_tmp >> channel;
1366 signal = (ModChannelSignal)signal_tmp;
1368 bool valid_signal = true;
1369 // @TODO: send Signal to Lua API
1371 case MODCHANNEL_SIGNAL_JOIN_OK:
1372 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1373 infostream << "Server ack our mod channel join on channel `" << channel
1374 << "`, joining." << std::endl;
1376 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1377 // Unable to join, remove channel
1378 m_modchannel_mgr->leaveChannel(channel, 0);
1379 infostream << "Server refused our mod channel join on channel `" << channel
1380 << "`" << std::endl;
1382 case MODCHANNEL_SIGNAL_LEAVE_OK:
1384 infostream << "Server ack our mod channel leave on channel " << channel
1385 << "`, leaving." << std::endl;
1388 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1389 infostream << "Server refused our mod channel leave on channel `" << channel
1390 << "`" << std::endl;
1392 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1394 // Generally unused, but ensure we don't do an implementation error
1395 infostream << "Server tells us we sent a message on channel `" << channel
1396 << "` but we are not registered. Message was dropped." << std::endl;
1399 case MODCHANNEL_SIGNAL_SET_STATE: {
1403 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1404 infostream << "Received wrong channel state " << state
1405 << ", ignoring." << std::endl;
1409 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1410 infostream << "Server sets mod channel `" << channel
1411 << "` in read-only mode." << std::endl;
1416 warningstream << "Received unhandled mod channel signal ID "
1417 << signal << ", ignoring." << std::endl;
1419 valid_signal = false;
1423 // If signal is valid, forward it to client side mods
1425 m_script->on_modchannel_signal(channel, signal);