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 "network/clientopcodes.h"
36 #include "network/connection.h"
37 #include "script/scripting_client.h"
38 #include "util/serialize.h"
40 #include "tileanimation.h"
43 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
45 infostream << "Got deprecated command "
46 << toClientCommandTable[pkt->getCommand()].name << " from peer "
47 << pkt->getPeerId() << "!" << std::endl;
50 void Client::handleCommand_Hello(NetworkPacket* pkt)
52 if (pkt->getSize() < 1)
59 std::string username_legacy; // for case insensitivity
60 *pkt >> serialization_ver >> compression_mode >> proto_ver
61 >> auth_mechs >> username_legacy;
63 // Chose an auth method we support
64 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
66 infostream << "Client: TOCLIENT_HELLO received with "
67 << "serialization_ver=" << (u32)serialization_ver
68 << ", auth_mechs=" << auth_mechs
69 << ", proto_ver=" << proto_ver
70 << ", compression_mode=" << compression_mode
71 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
73 if (!ser_ver_supported(serialization_ver)) {
74 infostream << "Client: TOCLIENT_HELLO: Server sent "
75 << "unsupported ser_fmt_ver"<< std::endl;
79 m_server_ser_ver = serialization_ver;
80 m_proto_ver = proto_ver;
82 //TODO verify that username_legacy matches sent username, only
83 // differs in casing (make both uppercase and compare)
84 // This is only neccessary though when we actually want to add casing support
86 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
87 // we recieved a TOCLIENT_HELLO while auth was already going on
88 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
89 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
90 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
91 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
92 srp_user_delete((SRPUser *) m_auth_data);
97 // Authenticate using that method, or abort if there wasn't any method found
98 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
99 if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP
100 && !m_simple_singleplayer_mode) {
101 promptConfirmRegistration(chosen_auth_mechanism);
103 startAuth(chosen_auth_mechanism);
106 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
107 m_access_denied = true;
108 m_access_denied_reason = "Unknown";
114 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
119 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
120 >> m_sudo_auth_methods;
122 playerpos -= v3f(0, BS / 2, 0);
124 // Set player position
125 LocalPlayer *player = m_env.getLocalPlayer();
126 assert(player != NULL);
127 player->setPosition(playerpos);
129 infostream << "Client: received map seed: " << m_map_seed << std::endl;
130 infostream << "Client: received recommended send interval "
131 << m_recommended_send_interval<<std::endl;
134 std::string lang = gettext("LANG_CODE");
135 if (lang == "LANG_CODE")
138 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
144 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
148 m_password = m_new_password;
150 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
152 // send packet to actually set the password
153 startAuth(AUTH_MECHANISM_FIRST_SRP);
156 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
158 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
160 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
161 L"Password change denied. Password NOT changed.");
162 pushToChatQueue(chatMessage);
163 // reset everything and be sad
167 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
169 // The server didn't like our password. Note, this needs
170 // to be processed even if the serialisation format has
171 // not been agreed yet, the same as TOCLIENT_INIT.
172 m_access_denied = true;
173 m_access_denied_reason = "Unknown";
175 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
176 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
177 // in some places of the server code
178 if (pkt->getSize() >= 2) {
179 std::wstring wide_reason;
181 m_access_denied_reason = wide_to_utf8(wide_reason);
186 if (pkt->getSize() < 1)
189 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
191 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
192 denyCode == SERVER_ACCESSDENIED_CRASH) {
193 *pkt >> m_access_denied_reason;
194 if (m_access_denied_reason.empty()) {
195 m_access_denied_reason = accessDeniedStrings[denyCode];
199 m_access_denied_reconnect = reconnect & 1;
200 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
201 *pkt >> m_access_denied_reason;
202 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
203 m_access_denied_reason = accessDeniedStrings[denyCode];
205 // Allow us to add new error messages to the
206 // protocol without raising the protocol version, if we want to.
207 // Until then (which may be never), this is outside
208 // of the defined protocol.
209 *pkt >> m_access_denied_reason;
210 if (m_access_denied_reason.empty()) {
211 m_access_denied_reason = "Unknown";
216 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
218 if (pkt->getSize() < 6)
226 void Client::handleCommand_AddNode(NetworkPacket* pkt)
228 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
235 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
237 bool remove_metadata = true;
238 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
239 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
240 remove_metadata = false;
243 addNode(p, n, remove_metadata);
245 void Client::handleCommand_BlockData(NetworkPacket* pkt)
247 // Ignore too small packet
248 if (pkt->getSize() < 6)
254 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
255 std::istringstream istr(datastring, std::ios_base::binary);
261 sector = m_env.getMap().emergeSector(p2d);
263 assert(sector->getPos() == p2d);
265 block = sector->getBlockNoCreateNoEx(p.Y);
268 Update an existing block
270 block->deSerialize(istr, m_server_ser_ver, false);
271 block->deSerializeNetworkSpecific(istr);
277 block = new MapBlock(&m_env.getMap(), p, this);
278 block->deSerialize(istr, m_server_ser_ver, false);
279 block->deSerializeNetworkSpecific(istr);
280 sector->insertBlock(block);
284 ServerMap::saveBlock(block, m_localdb);
288 Add it to mesh update queue and set it to be acknowledged after update.
290 addUpdateMeshTaskWithEdge(p, true);
293 void Client::handleCommand_Inventory(NetworkPacket* pkt)
295 if (pkt->getSize() < 1)
298 std::string datastring(pkt->getString(0), pkt->getSize());
299 std::istringstream is(datastring, std::ios_base::binary);
301 LocalPlayer *player = m_env.getLocalPlayer();
302 assert(player != NULL);
304 player->inventory.deSerialize(is);
306 m_inventory_updated = true;
308 delete m_inventory_from_server;
309 m_inventory_from_server = new Inventory(player->inventory);
310 m_inventory_from_server_age = 0.0;
313 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
315 if (pkt->getSize() < 2)
322 time_of_day = time_of_day % 24000;
323 float time_speed = 0;
325 if (pkt->getSize() >= 2 + 4) {
329 // Old message; try to approximate speed of time by ourselves
330 float time_of_day_f = (float)time_of_day / 24000.0f;
331 float tod_diff_f = 0;
333 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
334 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
336 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
338 m_last_time_of_day_f = time_of_day_f;
339 float time_diff = m_time_of_day_update_timer;
340 m_time_of_day_update_timer = 0;
342 if (m_time_of_day_set) {
343 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
344 infostream << "Client: Measured time_of_day speed (old format): "
345 << time_speed << " tod_diff_f=" << tod_diff_f
346 << " time_diff=" << time_diff << std::endl;
350 // Update environment
351 m_env.setTimeOfDay(time_of_day);
352 m_env.setTimeOfDaySpeed(time_speed);
353 m_time_of_day_set = true;
355 u32 dr = m_env.getDayNightRatio();
356 infostream << "Client: time_of_day=" << time_of_day
357 << " time_speed=" << time_speed
358 << " dr=" << dr << std::endl;
361 void Client::handleCommand_ChatMessageOld(NetworkPacket *pkt)
372 std::wstring message;
373 for (u32 i = 0; i < len; i++) {
375 message += (wchar_t)read_wchar;
378 // If chat message not consummed by client lua API
379 // @TODO send this to CSM using ChatMessage object
380 if (!moddingEnabled() || !m_script->on_receiving_message(wide_to_utf8(message))) {
381 pushToChatQueue(new ChatMessage(message));
385 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
390 u16 sendername length
396 ChatMessage *chatMessage = new ChatMessage();
397 u8 version, message_type;
398 *pkt >> version >> message_type;
400 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
405 *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
407 chatMessage->type = (ChatMessageType) message_type;
409 // @TODO send this to CSM using ChatMessage object
410 if (!moddingEnabled() || !m_script->on_receiving_message(
411 wide_to_utf8(chatMessage->message))) {
412 pushToChatQueue(chatMessage);
414 // Message was consumed by CSM and should not handled by client, destroying
419 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
422 u16 count of removed objects
423 for all removed objects {
426 u16 count of added objects
427 for all added objects {
430 u32 initialization data length
431 string initialization data
437 u16 removed_count, added_count, id;
439 // Read removed objects
440 *pkt >> removed_count;
442 for (u16 i = 0; i < removed_count; i++) {
444 m_env.removeActiveObject(id);
447 // Read added objects
450 for (u16 i = 0; i < added_count; i++) {
452 m_env.addActiveObject(id, type, pkt->readLongString());
454 } catch (PacketError &e) {
455 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
456 << ". The packet is unreliable, ignoring" << std::endl;
460 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
470 std::string datastring(pkt->getString(0), pkt->getSize());
471 std::istringstream is(datastring, std::ios_base::binary);
475 u16 id = readU16(is);
479 std::string message = deSerializeString(is);
481 // Pass on to the environment
482 m_env.processActiveObjectMessage(id, message);
484 } catch (SerializationError &e) {
485 errorstream << "Client::handleCommand_ActiveObjectMessages: "
486 << "caught SerializationError: " << e.what() << std::endl;
490 void Client::handleCommand_Movement(NetworkPacket* pkt)
492 LocalPlayer *player = m_env.getLocalPlayer();
493 assert(player != NULL);
495 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
497 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
498 >> lf >> lfs >> ls >> g;
500 player->movement_acceleration_default = mad * BS;
501 player->movement_acceleration_air = maa * BS;
502 player->movement_acceleration_fast = maf * BS;
503 player->movement_speed_walk = msw * BS;
504 player->movement_speed_crouch = mscr * BS;
505 player->movement_speed_fast = msf * BS;
506 player->movement_speed_climb = mscl * BS;
507 player->movement_speed_jump = msj * BS;
508 player->movement_liquid_fluidity = lf * BS;
509 player->movement_liquid_fluidity_smooth = lfs * BS;
510 player->movement_liquid_sink = ls * BS;
511 player->movement_gravity = g * BS;
514 void Client::handleCommand_HP(NetworkPacket* pkt)
517 LocalPlayer *player = m_env.getLocalPlayer();
518 assert(player != NULL);
520 u16 oldhp = player->hp;
527 if (moddingEnabled()) {
528 m_script->on_hp_modification(hp);
532 // Add to ClientEvent queue
533 ClientEvent *event = new ClientEvent();
534 event->type = CE_PLAYER_DAMAGE;
535 event->player_damage.amount = oldhp - hp;
536 m_client_event_queue.push(event);
540 void Client::handleCommand_Breath(NetworkPacket* pkt)
542 LocalPlayer *player = m_env.getLocalPlayer();
543 assert(player != NULL);
549 player->setBreath(breath);
552 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
554 LocalPlayer *player = m_env.getLocalPlayer();
555 assert(player != NULL);
560 *pkt >> pos >> pitch >> yaw;
562 player->setPosition(pos);
564 infostream << "Client got TOCLIENT_MOVE_PLAYER"
565 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
566 << " pitch=" << pitch
571 Add to ClientEvent queue.
572 This has to be sent to the main program because otherwise
573 it would just force the pitch and yaw values to whatever
574 the camera points to.
576 ClientEvent *event = new ClientEvent();
577 event->type = CE_PLAYER_FORCE_MOVE;
578 event->player_force_move.pitch = pitch;
579 event->player_force_move.yaw = yaw;
580 m_client_event_queue.push(event);
583 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
585 bool set_camera_point_target;
586 v3f camera_point_target;
588 *pkt >> set_camera_point_target;
589 *pkt >> camera_point_target;
591 ClientEvent *event = new ClientEvent();
592 event->type = CE_DEATHSCREEN;
593 event->deathscreen.set_camera_point_target = set_camera_point_target;
594 event->deathscreen.camera_point_target_x = camera_point_target.X;
595 event->deathscreen.camera_point_target_y = camera_point_target.Y;
596 event->deathscreen.camera_point_target_z = camera_point_target.Z;
597 m_client_event_queue.push(event);
600 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
606 infostream << "Client: Received media announcement: packet size: "
607 << pkt->getSize() << std::endl;
609 if (m_media_downloader == NULL ||
610 m_media_downloader->isStarted()) {
611 const char *problem = m_media_downloader ?
612 "we already saw another announcement" :
613 "all media has been received already";
614 errorstream << "Client: Received media announcement but "
616 << " files=" << num_files
617 << " size=" << pkt->getSize() << std::endl;
621 // Mesh update thread must be stopped while
622 // updating content definitions
623 sanity_check(!m_mesh_update_thread.isRunning());
625 for (u16 i = 0; i < num_files; i++) {
626 std::string name, sha1_base64;
628 *pkt >> name >> sha1_base64;
630 std::string sha1_raw = base64_decode(sha1_base64);
631 m_media_downloader->addFile(name, sha1_raw);
640 while(!sf.at_end()) {
641 std::string baseurl = trim(sf.next(","));
642 if (!baseurl.empty())
643 m_media_downloader->addRemoteServer(baseurl);
646 catch(SerializationError& e) {
647 // not supported by server or turned off
650 m_media_downloader->step(this);
653 void Client::handleCommand_Media(NetworkPacket* pkt)
657 u16 total number of file bunches
658 u16 index of this bunch
659 u32 number of files in this bunch
671 *pkt >> num_bunches >> bunch_i >> num_files;
673 infostream << "Client: Received files: bunch " << bunch_i << "/"
674 << num_bunches << " files=" << num_files
675 << " size=" << pkt->getSize() << std::endl;
680 if (!m_media_downloader || !m_media_downloader->isStarted()) {
681 const char *problem = m_media_downloader ?
682 "media has not been requested" :
683 "all media has been received already";
684 errorstream << "Client: Received media but "
686 << " bunch " << bunch_i << "/" << num_bunches
687 << " files=" << num_files
688 << " size=" << pkt->getSize() << std::endl;
692 // Mesh update thread must be stopped while
693 // updating content definitions
694 sanity_check(!m_mesh_update_thread.isRunning());
696 for (u32 i=0; i < num_files; i++) {
701 std::string data = pkt->readLongString();
703 m_media_downloader->conventionalTransferDone(
708 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
710 infostream << "Client: Received node definitions: packet size: "
711 << pkt->getSize() << std::endl;
713 // Mesh update thread must be stopped while
714 // updating content definitions
715 sanity_check(!m_mesh_update_thread.isRunning());
717 // Decompress node definitions
718 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
719 std::ostringstream tmp_os;
720 decompressZlib(tmp_is, tmp_os);
722 // Deserialize node definitions
723 std::istringstream tmp_is2(tmp_os.str());
724 m_nodedef->deSerialize(tmp_is2);
725 m_nodedef_received = true;
728 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
730 infostream << "Client: Received item definitions: packet size: "
731 << pkt->getSize() << std::endl;
733 // Mesh update thread must be stopped while
734 // updating content definitions
735 sanity_check(!m_mesh_update_thread.isRunning());
737 // Decompress item definitions
738 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
739 std::ostringstream tmp_os;
740 decompressZlib(tmp_is, tmp_os);
742 // Deserialize node definitions
743 std::istringstream tmp_is2(tmp_os.str());
744 m_itemdef->deSerialize(tmp_is2);
745 m_itemdef_received = true;
748 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
756 [11 + len] (f32 * 3) pos
757 [23 + len] u16 object_id
767 u8 type; // 0=local, 1=positional, 2=object
774 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
779 } catch (PacketError &e) {};
785 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
787 case 1: // positional
788 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
792 ClientActiveObject *cao = m_env.getActiveObject(object_id);
794 pos = cao->getPosition();
795 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
796 // TODO: Set up sound to move with object
803 if (client_id != -1) {
804 m_sounds_server_to_client[server_id] = client_id;
805 m_sounds_client_to_server[client_id] = server_id;
807 m_sounds_to_objects[client_id] = object_id;
811 void Client::handleCommand_StopSound(NetworkPacket* pkt)
817 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
818 if (i != m_sounds_server_to_client.end()) {
819 int client_id = i->second;
820 m_sound->stopSound(client_id);
824 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
830 *pkt >> sound_id >> step >> gain;
832 std::unordered_map<s32, int>::const_iterator i =
833 m_sounds_server_to_client.find(sound_id);
835 if (i != m_sounds_server_to_client.end())
836 m_sound->fadeSound(i->second, step, gain);
839 void Client::handleCommand_Privileges(NetworkPacket* pkt)
841 m_privileges.clear();
842 infostream << "Client: Privileges updated: ";
845 *pkt >> num_privileges;
847 for (u16 i = 0; i < num_privileges; i++) {
852 m_privileges.insert(priv);
853 infostream << priv << " ";
855 infostream << std::endl;
858 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
860 LocalPlayer *player = m_env.getLocalPlayer();
861 assert(player != NULL);
863 // Store formspec in LocalPlayer
864 player->inventory_formspec = pkt->readLongString();
867 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
869 std::string datastring(pkt->getString(0), pkt->getSize());
870 std::istringstream is(datastring, std::ios_base::binary);
872 std::string name = deSerializeString(is);
874 infostream << "Client: Detached inventory update: \"" << name
875 << "\"" << std::endl;
877 Inventory *inv = NULL;
878 if (m_detached_inventories.count(name) > 0)
879 inv = m_detached_inventories[name];
881 inv = new Inventory(m_itemdef);
882 m_detached_inventories[name] = inv;
884 inv->deSerialize(is);
887 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
889 std::string formspec = pkt->readLongString();
890 std::string formname;
894 ClientEvent *event = new ClientEvent();
895 event->type = CE_SHOW_FORMSPEC;
896 // pointer is required as event is a struct only!
897 // adding a std:string to a struct isn't possible
898 event->show_formspec.formspec = new std::string(formspec);
899 event->show_formspec.formname = new std::string(formname);
900 m_client_event_queue.push(event);
903 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
905 std::string datastring(pkt->getString(0), pkt->getSize());
906 std::istringstream is(datastring, std::ios_base::binary);
908 v3f pos = readV3F1000(is);
909 v3f vel = readV3F1000(is);
910 v3f acc = readV3F1000(is);
911 float expirationtime = readF1000(is);
912 float size = readF1000(is);
913 bool collisiondetection = readU8(is);
914 std::string texture = deSerializeLongString(is);
915 bool vertical = false;
916 bool collision_removal = false;
917 TileAnimationParams animation;
918 animation.type = TAT_NONE;
921 vertical = readU8(is);
922 collision_removal = readU8(is);
923 animation.deSerialize(is, m_proto_ver);
927 ClientEvent *event = new ClientEvent();
928 event->type = CE_SPAWN_PARTICLE;
929 event->spawn_particle.pos = new v3f (pos);
930 event->spawn_particle.vel = new v3f (vel);
931 event->spawn_particle.acc = new v3f (acc);
932 event->spawn_particle.expirationtime = expirationtime;
933 event->spawn_particle.size = size;
934 event->spawn_particle.collisiondetection = collisiondetection;
935 event->spawn_particle.collision_removal = collision_removal;
936 event->spawn_particle.vertical = vertical;
937 event->spawn_particle.texture = new std::string(texture);
938 event->spawn_particle.animation = animation;
939 event->spawn_particle.glow = glow;
941 m_client_event_queue.push(event);
944 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
958 bool collisiondetection;
961 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
962 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
963 >> maxsize >> collisiondetection;
965 std::string texture = pkt->readLongString();
969 bool vertical = false;
970 bool collision_removal = false;
971 TileAnimationParams animation;
972 animation.type = TAT_NONE;
977 *pkt >> collision_removal;
980 // This is horrible but required (why are there two ways to deserialize pkts?)
981 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
982 std::istringstream is(datastring, std::ios_base::binary);
983 animation.deSerialize(is, m_proto_ver);
987 ClientEvent *event = new ClientEvent();
988 event->type = CE_ADD_PARTICLESPAWNER;
989 event->add_particlespawner.amount = amount;
990 event->add_particlespawner.spawntime = spawntime;
991 event->add_particlespawner.minpos = new v3f (minpos);
992 event->add_particlespawner.maxpos = new v3f (maxpos);
993 event->add_particlespawner.minvel = new v3f (minvel);
994 event->add_particlespawner.maxvel = new v3f (maxvel);
995 event->add_particlespawner.minacc = new v3f (minacc);
996 event->add_particlespawner.maxacc = new v3f (maxacc);
997 event->add_particlespawner.minexptime = minexptime;
998 event->add_particlespawner.maxexptime = maxexptime;
999 event->add_particlespawner.minsize = minsize;
1000 event->add_particlespawner.maxsize = maxsize;
1001 event->add_particlespawner.collisiondetection = collisiondetection;
1002 event->add_particlespawner.collision_removal = collision_removal;
1003 event->add_particlespawner.attached_id = attached_id;
1004 event->add_particlespawner.vertical = vertical;
1005 event->add_particlespawner.texture = new std::string(texture);
1006 event->add_particlespawner.id = id;
1007 event->add_particlespawner.animation = animation;
1008 event->add_particlespawner.glow = glow;
1010 m_client_event_queue.push(event);
1014 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1019 ClientEvent *event = new ClientEvent();
1020 event->type = CE_DELETE_PARTICLESPAWNER;
1021 event->delete_particlespawner.id = id;
1023 m_client_event_queue.push(event);
1026 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1028 std::string datastring(pkt->getString(0), pkt->getSize());
1029 std::istringstream is(datastring, std::ios_base::binary);
1045 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1046 >> dir >> align >> offset;
1050 catch(SerializationError &e) {};
1054 } catch(SerializationError &e) {};
1055 u32 client_id = getEnv().getLocalPlayer()->getFreeHudID();
1056 m_hud_server_to_client[server_id] = client_id;
1058 ClientEvent *event = new ClientEvent();
1059 event->type = CE_HUDADD;
1060 event->hudadd.id = client_id;
1061 event->hudadd.type = type;
1062 event->hudadd.pos = new v2f(pos);
1063 event->hudadd.name = new std::string(name);
1064 event->hudadd.scale = new v2f(scale);
1065 event->hudadd.text = new std::string(text);
1066 event->hudadd.number = number;
1067 event->hudadd.item = item;
1068 event->hudadd.dir = dir;
1069 event->hudadd.align = new v2f(align);
1070 event->hudadd.offset = new v2f(offset);
1071 event->hudadd.world_pos = new v3f(world_pos);
1072 event->hudadd.size = new v2s32(size);
1073 m_client_event_queue.push(event);
1076 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1082 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1083 if (i != m_hud_server_to_client.end()) {
1084 int client_id = i->second;
1085 m_hud_server_to_client.erase(i);
1087 ClientEvent *event = new ClientEvent();
1088 event->type = CE_HUDRM;
1089 event->hudrm.id = client_id;
1090 m_client_event_queue.push(event);
1094 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1104 *pkt >> server_id >> stat;
1106 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1107 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1109 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1111 else if (stat == HUD_STAT_WORLD_POS)
1113 else if (stat == HUD_STAT_SIZE )
1118 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1119 if (i != m_hud_server_to_client.end()) {
1120 ClientEvent *event = new ClientEvent();
1121 event->type = CE_HUDCHANGE;
1122 event->hudchange.id = i->second;
1123 event->hudchange.stat = (HudElementStat)stat;
1124 event->hudchange.v2fdata = new v2f(v2fdata);
1125 event->hudchange.v3fdata = new v3f(v3fdata);
1126 event->hudchange.sdata = new std::string(sdata);
1127 event->hudchange.data = intdata;
1128 event->hudchange.v2s32data = new v2s32(v2s32data);
1129 m_client_event_queue.push(event);
1133 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1137 *pkt >> flags >> mask;
1139 LocalPlayer *player = m_env.getLocalPlayer();
1140 assert(player != NULL);
1142 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1143 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1145 player->hud_flags &= ~mask;
1146 player->hud_flags |= flags;
1148 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1149 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1151 // Hide minimap if it has been disabled by the server
1152 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1153 // defers a minimap update, therefore only call it if really
1154 // needed, by checking that minimap was visible before
1155 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1157 // Switch to surface mode if radar disabled by server
1158 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1159 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1162 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1164 u16 param; std::string value;
1166 *pkt >> param >> value;
1168 LocalPlayer *player = m_env.getLocalPlayer();
1169 assert(player != NULL);
1171 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1172 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1173 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1174 player->hud_hotbar_itemcount = hotbar_itemcount;
1176 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1177 // If value not empty verify image exists in texture source
1178 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1179 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1180 << value << "')" << std::endl;
1183 player->hotbar_image = value;
1185 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1186 // If value not empty verify image exists in texture source
1187 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1188 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1189 << value << "')" << std::endl;
1192 player->hotbar_selected_image = value;
1196 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1198 std::string datastring(pkt->getString(0), pkt->getSize());
1199 std::istringstream is(datastring, std::ios_base::binary);
1201 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1202 std::string *type = new std::string(deSerializeString(is));
1203 u16 count = readU16(is);
1204 std::vector<std::string> *params = new std::vector<std::string>;
1206 for (size_t i = 0; i < count; i++)
1207 params->push_back(deSerializeString(is));
1211 clouds = readU8(is);
1214 ClientEvent *event = new ClientEvent();
1215 event->type = CE_SET_SKY;
1216 event->set_sky.bgcolor = bgcolor;
1217 event->set_sky.type = type;
1218 event->set_sky.params = params;
1219 event->set_sky.clouds = clouds;
1220 m_client_event_queue.push(event);
1223 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1226 video::SColor color_bright;
1227 video::SColor color_ambient;
1232 *pkt >> density >> color_bright >> color_ambient
1233 >> height >> thickness >> speed;
1235 ClientEvent *event = new ClientEvent();
1236 event->type = CE_CLOUD_PARAMS;
1237 event->cloud_params.density = density;
1238 // use the underlying u32 representation, because we can't
1239 // use struct members with constructors here, and this way
1240 // we avoid using new() and delete() for no good reason
1241 event->cloud_params.color_bright = color_bright.color;
1242 event->cloud_params.color_ambient = color_ambient.color;
1243 event->cloud_params.height = height;
1244 event->cloud_params.thickness = thickness;
1245 // same here: deconstruct to skip constructor
1246 event->cloud_params.speed_x = speed.X;
1247 event->cloud_params.speed_y = speed.Y;
1248 m_client_event_queue.push(event);
1251 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1254 u16 day_night_ratio_u;
1256 *pkt >> do_override >> day_night_ratio_u;
1258 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1260 ClientEvent *event = new ClientEvent();
1261 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1262 event->override_day_night_ratio.do_override = do_override;
1263 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1264 m_client_event_queue.push(event);
1267 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1269 LocalPlayer *player = m_env.getLocalPlayer();
1270 assert(player != NULL);
1272 *pkt >> player->local_animations[0];
1273 *pkt >> player->local_animations[1];
1274 *pkt >> player->local_animations[2];
1275 *pkt >> player->local_animations[3];
1276 *pkt >> player->local_animation_speed;
1279 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1281 LocalPlayer *player = m_env.getLocalPlayer();
1282 assert(player != NULL);
1284 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1287 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1291 *pkt >> type >> num_players;
1292 PlayerListModifer notice_type = (PlayerListModifer) type;
1294 for (u16 i = 0; i < num_players; i++) {
1297 switch (notice_type) {
1298 case PLAYER_LIST_INIT:
1299 case PLAYER_LIST_ADD:
1300 m_env.addPlayerName(name);
1302 case PLAYER_LIST_REMOVE:
1303 m_env.removePlayerName(name);
1309 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1311 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1312 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1313 errorstream << "Client: Received SRP S_B login message,"
1314 << " but wasn't supposed to (chosen_mech="
1315 << m_chosen_auth_mech << ")." << std::endl;
1321 SRPUser *usr = (SRPUser *) m_auth_data;
1326 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1328 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1329 (const unsigned char *) B.c_str(), B.size(),
1330 (unsigned char **) &bytes_M, &len_M);
1333 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1337 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1338 resp_pkt << std::string(bytes_M, len_M);
1342 void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
1344 *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;
1346 // Now we have flavours, load mods if it's enabled
1347 // Note: this should be moved after mods receptions from server instead
1355 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1357 std::string channel_name, sender, channel_msg;
1358 *pkt >> channel_name >> sender >> channel_msg;
1360 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1361 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1362 << channel_msg << std::endl;
1364 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1365 verbosestream << "Server sent us messages on unregistered channel "
1366 << channel_name << ", ignoring." << std::endl;
1370 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1373 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1376 ModChannelSignal signal;
1377 std::string channel;
1379 *pkt >> signal_tmp >> channel;
1381 signal = (ModChannelSignal)signal_tmp;
1383 bool valid_signal = true;
1384 // @TODO: send Signal to Lua API
1386 case MODCHANNEL_SIGNAL_JOIN_OK:
1387 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1388 infostream << "Server ack our mod channel join on channel `" << channel
1389 << "`, joining." << std::endl;
1391 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1392 // Unable to join, remove channel
1393 m_modchannel_mgr->leaveChannel(channel, 0);
1394 infostream << "Server refused our mod channel join on channel `" << channel
1395 << "`" << std::endl;
1397 case MODCHANNEL_SIGNAL_LEAVE_OK:
1399 infostream << "Server ack our mod channel leave on channel " << channel
1400 << "`, leaving." << std::endl;
1403 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1404 infostream << "Server refused our mod channel leave on channel `" << channel
1405 << "`" << std::endl;
1407 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1409 // Generally unused, but ensure we don't do an implementation error
1410 infostream << "Server tells us we sent a message on channel `" << channel
1411 << "` but we are not registered. Message was dropped." << std::endl;
1414 case MODCHANNEL_SIGNAL_SET_STATE: {
1418 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1419 infostream << "Received wrong channel state " << state
1420 << ", ignoring." << std::endl;
1424 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1425 infostream << "Server sets mod channel `" << channel
1426 << "` in read-only mode." << std::endl;
1431 warningstream << "Received unhandled mod channel signal ID "
1432 << signal << ", ignoring." << std::endl;
1434 valid_signal = false;
1438 // If signal is valid, forward it to client side mods
1440 m_script->on_modchannel_signal(channel, signal);