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 u32 client_id = m_particle_manager.getSpawnerId();
988 m_particles_server_to_client[server_id] = client_id;
990 ClientEvent *event = new ClientEvent();
991 event->type = CE_ADD_PARTICLESPAWNER;
992 event->add_particlespawner.amount = amount;
993 event->add_particlespawner.spawntime = spawntime;
994 event->add_particlespawner.minpos = new v3f (minpos);
995 event->add_particlespawner.maxpos = new v3f (maxpos);
996 event->add_particlespawner.minvel = new v3f (minvel);
997 event->add_particlespawner.maxvel = new v3f (maxvel);
998 event->add_particlespawner.minacc = new v3f (minacc);
999 event->add_particlespawner.maxacc = new v3f (maxacc);
1000 event->add_particlespawner.minexptime = minexptime;
1001 event->add_particlespawner.maxexptime = maxexptime;
1002 event->add_particlespawner.minsize = minsize;
1003 event->add_particlespawner.maxsize = maxsize;
1004 event->add_particlespawner.collisiondetection = collisiondetection;
1005 event->add_particlespawner.collision_removal = collision_removal;
1006 event->add_particlespawner.attached_id = attached_id;
1007 event->add_particlespawner.vertical = vertical;
1008 event->add_particlespawner.texture = new std::string(texture);
1009 event->add_particlespawner.id = client_id;
1010 event->add_particlespawner.animation = animation;
1011 event->add_particlespawner.glow = glow;
1013 m_client_event_queue.push(event);
1017 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1023 auto i = m_particles_server_to_client.find(server_id);
1024 if (i != m_particles_server_to_client.end())
1025 client_id = i->second;
1029 ClientEvent *event = new ClientEvent();
1030 event->type = CE_DELETE_PARTICLESPAWNER;
1031 event->delete_particlespawner.id = client_id;
1033 m_client_event_queue.push(event);
1036 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1038 std::string datastring(pkt->getString(0), pkt->getSize());
1039 std::istringstream is(datastring, std::ios_base::binary);
1055 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1056 >> dir >> align >> offset;
1060 catch(SerializationError &e) {};
1064 } catch(SerializationError &e) {};
1065 u32 client_id = getEnv().getLocalPlayer()->getFreeHudID();
1066 m_hud_server_to_client[server_id] = client_id;
1068 ClientEvent *event = new ClientEvent();
1069 event->type = CE_HUDADD;
1070 event->hudadd.id = client_id;
1071 event->hudadd.type = type;
1072 event->hudadd.pos = new v2f(pos);
1073 event->hudadd.name = new std::string(name);
1074 event->hudadd.scale = new v2f(scale);
1075 event->hudadd.text = new std::string(text);
1076 event->hudadd.number = number;
1077 event->hudadd.item = item;
1078 event->hudadd.dir = dir;
1079 event->hudadd.align = new v2f(align);
1080 event->hudadd.offset = new v2f(offset);
1081 event->hudadd.world_pos = new v3f(world_pos);
1082 event->hudadd.size = new v2s32(size);
1083 m_client_event_queue.push(event);
1086 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1092 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1093 if (i != m_hud_server_to_client.end()) {
1094 int client_id = i->second;
1095 m_hud_server_to_client.erase(i);
1097 ClientEvent *event = new ClientEvent();
1098 event->type = CE_HUDRM;
1099 event->hudrm.id = client_id;
1100 m_client_event_queue.push(event);
1104 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1114 *pkt >> server_id >> stat;
1116 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1117 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1119 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1121 else if (stat == HUD_STAT_WORLD_POS)
1123 else if (stat == HUD_STAT_SIZE )
1128 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1129 if (i != m_hud_server_to_client.end()) {
1130 ClientEvent *event = new ClientEvent();
1131 event->type = CE_HUDCHANGE;
1132 event->hudchange.id = i->second;
1133 event->hudchange.stat = (HudElementStat)stat;
1134 event->hudchange.v2fdata = new v2f(v2fdata);
1135 event->hudchange.v3fdata = new v3f(v3fdata);
1136 event->hudchange.sdata = new std::string(sdata);
1137 event->hudchange.data = intdata;
1138 event->hudchange.v2s32data = new v2s32(v2s32data);
1139 m_client_event_queue.push(event);
1143 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1147 *pkt >> flags >> mask;
1149 LocalPlayer *player = m_env.getLocalPlayer();
1150 assert(player != NULL);
1152 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1153 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1155 player->hud_flags &= ~mask;
1156 player->hud_flags |= flags;
1158 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1159 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1161 // Hide minimap if it has been disabled by the server
1162 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1163 // defers a minimap update, therefore only call it if really
1164 // needed, by checking that minimap was visible before
1165 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1167 // Switch to surface mode if radar disabled by server
1168 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1169 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1172 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1174 u16 param; std::string value;
1176 *pkt >> param >> value;
1178 LocalPlayer *player = m_env.getLocalPlayer();
1179 assert(player != NULL);
1181 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1182 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1183 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1184 player->hud_hotbar_itemcount = hotbar_itemcount;
1186 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1187 // If value not empty verify image exists in texture source
1188 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1189 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1190 << value << "')" << std::endl;
1193 player->hotbar_image = value;
1195 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1196 // If value not empty verify image exists in texture source
1197 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1198 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1199 << value << "')" << std::endl;
1202 player->hotbar_selected_image = value;
1206 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1208 std::string datastring(pkt->getString(0), pkt->getSize());
1209 std::istringstream is(datastring, std::ios_base::binary);
1211 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1212 std::string *type = new std::string(deSerializeString(is));
1213 u16 count = readU16(is);
1214 std::vector<std::string> *params = new std::vector<std::string>;
1216 for (size_t i = 0; i < count; i++)
1217 params->push_back(deSerializeString(is));
1221 clouds = readU8(is);
1224 ClientEvent *event = new ClientEvent();
1225 event->type = CE_SET_SKY;
1226 event->set_sky.bgcolor = bgcolor;
1227 event->set_sky.type = type;
1228 event->set_sky.params = params;
1229 event->set_sky.clouds = clouds;
1230 m_client_event_queue.push(event);
1233 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1236 video::SColor color_bright;
1237 video::SColor color_ambient;
1242 *pkt >> density >> color_bright >> color_ambient
1243 >> height >> thickness >> speed;
1245 ClientEvent *event = new ClientEvent();
1246 event->type = CE_CLOUD_PARAMS;
1247 event->cloud_params.density = density;
1248 // use the underlying u32 representation, because we can't
1249 // use struct members with constructors here, and this way
1250 // we avoid using new() and delete() for no good reason
1251 event->cloud_params.color_bright = color_bright.color;
1252 event->cloud_params.color_ambient = color_ambient.color;
1253 event->cloud_params.height = height;
1254 event->cloud_params.thickness = thickness;
1255 // same here: deconstruct to skip constructor
1256 event->cloud_params.speed_x = speed.X;
1257 event->cloud_params.speed_y = speed.Y;
1258 m_client_event_queue.push(event);
1261 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1264 u16 day_night_ratio_u;
1266 *pkt >> do_override >> day_night_ratio_u;
1268 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1270 ClientEvent *event = new ClientEvent();
1271 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1272 event->override_day_night_ratio.do_override = do_override;
1273 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1274 m_client_event_queue.push(event);
1277 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1279 LocalPlayer *player = m_env.getLocalPlayer();
1280 assert(player != NULL);
1282 *pkt >> player->local_animations[0];
1283 *pkt >> player->local_animations[1];
1284 *pkt >> player->local_animations[2];
1285 *pkt >> player->local_animations[3];
1286 *pkt >> player->local_animation_speed;
1289 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1291 LocalPlayer *player = m_env.getLocalPlayer();
1292 assert(player != NULL);
1294 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1297 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1301 *pkt >> type >> num_players;
1302 PlayerListModifer notice_type = (PlayerListModifer) type;
1304 for (u16 i = 0; i < num_players; i++) {
1307 switch (notice_type) {
1308 case PLAYER_LIST_INIT:
1309 case PLAYER_LIST_ADD:
1310 m_env.addPlayerName(name);
1312 case PLAYER_LIST_REMOVE:
1313 m_env.removePlayerName(name);
1319 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1321 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1322 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1323 errorstream << "Client: Received SRP S_B login message,"
1324 << " but wasn't supposed to (chosen_mech="
1325 << m_chosen_auth_mech << ")." << std::endl;
1331 SRPUser *usr = (SRPUser *) m_auth_data;
1336 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1338 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1339 (const unsigned char *) B.c_str(), B.size(),
1340 (unsigned char **) &bytes_M, &len_M);
1343 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1347 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1348 resp_pkt << std::string(bytes_M, len_M);
1352 void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
1354 *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;
1356 // Now we have flavours, load mods if it's enabled
1357 // Note: this should be moved after mods receptions from server instead
1365 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1367 std::string channel_name, sender, channel_msg;
1368 *pkt >> channel_name >> sender >> channel_msg;
1370 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1371 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1372 << channel_msg << std::endl;
1374 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1375 verbosestream << "Server sent us messages on unregistered channel "
1376 << channel_name << ", ignoring." << std::endl;
1380 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1383 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1386 ModChannelSignal signal;
1387 std::string channel;
1389 *pkt >> signal_tmp >> channel;
1391 signal = (ModChannelSignal)signal_tmp;
1393 bool valid_signal = true;
1394 // @TODO: send Signal to Lua API
1396 case MODCHANNEL_SIGNAL_JOIN_OK:
1397 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1398 infostream << "Server ack our mod channel join on channel `" << channel
1399 << "`, joining." << std::endl;
1401 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1402 // Unable to join, remove channel
1403 m_modchannel_mgr->leaveChannel(channel, 0);
1404 infostream << "Server refused our mod channel join on channel `" << channel
1405 << "`" << std::endl;
1407 case MODCHANNEL_SIGNAL_LEAVE_OK:
1409 infostream << "Server ack our mod channel leave on channel " << channel
1410 << "`, leaving." << std::endl;
1413 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1414 infostream << "Server refused our mod channel leave on channel `" << channel
1415 << "`" << std::endl;
1417 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1419 // Generally unused, but ensure we don't do an implementation error
1420 infostream << "Server tells us we sent a message on channel `" << channel
1421 << "` but we are not registered. Message was dropped." << std::endl;
1424 case MODCHANNEL_SIGNAL_SET_STATE: {
1428 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1429 infostream << "Received wrong channel state " << state
1430 << ", ignoring." << std::endl;
1434 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1435 infostream << "Server sets mod channel `" << channel
1436 << "` in read-only mode." << std::endl;
1441 warningstream << "Received unhandled mod channel signal ID "
1442 << signal << ", ignoring." << std::endl;
1444 valid_signal = false;
1448 // If signal is valid, forward it to client side mods
1450 m_script->on_modchannel_signal(channel, signal);