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 startAuth(chosen_auth_mechanism);
101 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
102 m_access_denied = true;
103 m_access_denied_reason = "Unknown";
109 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
114 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
115 >> m_sudo_auth_methods;
117 playerpos -= v3f(0, BS / 2, 0);
119 // Set player position
120 LocalPlayer *player = m_env.getLocalPlayer();
121 assert(player != NULL);
122 player->setPosition(playerpos);
124 infostream << "Client: received map seed: " << m_map_seed << std::endl;
125 infostream << "Client: received recommended send interval "
126 << m_recommended_send_interval<<std::endl;
129 std::string lang = gettext("LANG_CODE");
130 if (lang == "LANG_CODE")
133 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
139 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
143 m_password = m_new_password;
145 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
147 // send packet to actually set the password
148 startAuth(AUTH_MECHANISM_FIRST_SRP);
151 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
153 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
155 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
156 L"Password change denied. Password NOT changed.");
157 pushToChatQueue(chatMessage);
158 // reset everything and be sad
162 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
164 // The server didn't like our password. Note, this needs
165 // to be processed even if the serialisation format has
166 // not been agreed yet, the same as TOCLIENT_INIT.
167 m_access_denied = true;
168 m_access_denied_reason = "Unknown";
170 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
171 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
172 // in some places of the server code
173 if (pkt->getSize() >= 2) {
174 std::wstring wide_reason;
176 m_access_denied_reason = wide_to_utf8(wide_reason);
181 if (pkt->getSize() < 1)
184 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
186 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
187 denyCode == SERVER_ACCESSDENIED_CRASH) {
188 *pkt >> m_access_denied_reason;
189 if (m_access_denied_reason.empty()) {
190 m_access_denied_reason = accessDeniedStrings[denyCode];
194 m_access_denied_reconnect = reconnect & 1;
195 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
196 *pkt >> m_access_denied_reason;
197 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
198 m_access_denied_reason = accessDeniedStrings[denyCode];
200 // Allow us to add new error messages to the
201 // protocol without raising the protocol version, if we want to.
202 // Until then (which may be never), this is outside
203 // of the defined protocol.
204 *pkt >> m_access_denied_reason;
205 if (m_access_denied_reason.empty()) {
206 m_access_denied_reason = "Unknown";
211 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
213 if (pkt->getSize() < 6)
221 void Client::handleCommand_AddNode(NetworkPacket* pkt)
223 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
230 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
232 bool remove_metadata = true;
233 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
234 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
235 remove_metadata = false;
238 addNode(p, n, remove_metadata);
240 void Client::handleCommand_BlockData(NetworkPacket* pkt)
242 // Ignore too small packet
243 if (pkt->getSize() < 6)
249 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
250 std::istringstream istr(datastring, std::ios_base::binary);
256 sector = m_env.getMap().emergeSector(p2d);
258 assert(sector->getPos() == p2d);
260 block = sector->getBlockNoCreateNoEx(p.Y);
263 Update an existing block
265 block->deSerialize(istr, m_server_ser_ver, false);
266 block->deSerializeNetworkSpecific(istr);
272 block = new MapBlock(&m_env.getMap(), p, this);
273 block->deSerialize(istr, m_server_ser_ver, false);
274 block->deSerializeNetworkSpecific(istr);
275 sector->insertBlock(block);
279 ServerMap::saveBlock(block, m_localdb);
283 Add it to mesh update queue and set it to be acknowledged after update.
285 addUpdateMeshTaskWithEdge(p, true);
288 void Client::handleCommand_Inventory(NetworkPacket* pkt)
290 if (pkt->getSize() < 1)
293 std::string datastring(pkt->getString(0), pkt->getSize());
294 std::istringstream is(datastring, std::ios_base::binary);
296 LocalPlayer *player = m_env.getLocalPlayer();
297 assert(player != NULL);
299 player->inventory.deSerialize(is);
301 m_inventory_updated = true;
303 delete m_inventory_from_server;
304 m_inventory_from_server = new Inventory(player->inventory);
305 m_inventory_from_server_age = 0.0;
308 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
310 if (pkt->getSize() < 2)
317 time_of_day = time_of_day % 24000;
318 float time_speed = 0;
320 if (pkt->getSize() >= 2 + 4) {
324 // Old message; try to approximate speed of time by ourselves
325 float time_of_day_f = (float)time_of_day / 24000.0f;
326 float tod_diff_f = 0;
328 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
329 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
331 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
333 m_last_time_of_day_f = time_of_day_f;
334 float time_diff = m_time_of_day_update_timer;
335 m_time_of_day_update_timer = 0;
337 if (m_time_of_day_set) {
338 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
339 infostream << "Client: Measured time_of_day speed (old format): "
340 << time_speed << " tod_diff_f=" << tod_diff_f
341 << " time_diff=" << time_diff << std::endl;
345 // Update environment
346 m_env.setTimeOfDay(time_of_day);
347 m_env.setTimeOfDaySpeed(time_speed);
348 m_time_of_day_set = true;
350 u32 dr = m_env.getDayNightRatio();
351 infostream << "Client: time_of_day=" << time_of_day
352 << " time_speed=" << time_speed
353 << " dr=" << dr << std::endl;
356 void Client::handleCommand_ChatMessageOld(NetworkPacket *pkt)
367 std::wstring message;
368 for (u32 i = 0; i < len; i++) {
370 message += (wchar_t)read_wchar;
373 // If chat message not consummed by client lua API
374 // @TODO send this to CSM using ChatMessage object
375 if (!moddingEnabled() || !m_script->on_receiving_message(wide_to_utf8(message))) {
376 pushToChatQueue(new ChatMessage(message));
380 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
385 u16 sendername length
391 ChatMessage *chatMessage = new ChatMessage();
392 u8 version, message_type;
393 *pkt >> version >> message_type;
395 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
400 *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
402 chatMessage->type = (ChatMessageType) message_type;
404 // @TODO send this to CSM using ChatMessage object
405 if (!moddingEnabled() || !m_script->on_receiving_message(
406 wide_to_utf8(chatMessage->message))) {
407 pushToChatQueue(chatMessage);
409 // Message was consumed by CSM and should not handled by client, destroying
414 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
417 u16 count of removed objects
418 for all removed objects {
421 u16 count of added objects
422 for all added objects {
425 u32 initialization data length
426 string initialization data
432 u16 removed_count, added_count, id;
434 // Read removed objects
435 *pkt >> removed_count;
437 for (u16 i = 0; i < removed_count; i++) {
439 m_env.removeActiveObject(id);
442 // Read added objects
445 for (u16 i = 0; i < added_count; i++) {
447 m_env.addActiveObject(id, type, pkt->readLongString());
449 } catch (PacketError &e) {
450 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
451 << ". The packet is unreliable, ignoring" << std::endl;
455 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
465 std::string datastring(pkt->getString(0), pkt->getSize());
466 std::istringstream is(datastring, std::ios_base::binary);
470 u16 id = readU16(is);
474 std::string message = deSerializeString(is);
476 // Pass on to the environment
477 m_env.processActiveObjectMessage(id, message);
479 } catch (SerializationError &e) {
480 errorstream << "Client::handleCommand_ActiveObjectMessages: "
481 << "caught SerializationError: " << e.what() << std::endl;
485 void Client::handleCommand_Movement(NetworkPacket* pkt)
487 LocalPlayer *player = m_env.getLocalPlayer();
488 assert(player != NULL);
490 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
492 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
493 >> lf >> lfs >> ls >> g;
495 player->movement_acceleration_default = mad * BS;
496 player->movement_acceleration_air = maa * BS;
497 player->movement_acceleration_fast = maf * BS;
498 player->movement_speed_walk = msw * BS;
499 player->movement_speed_crouch = mscr * BS;
500 player->movement_speed_fast = msf * BS;
501 player->movement_speed_climb = mscl * BS;
502 player->movement_speed_jump = msj * BS;
503 player->movement_liquid_fluidity = lf * BS;
504 player->movement_liquid_fluidity_smooth = lfs * BS;
505 player->movement_liquid_sink = ls * BS;
506 player->movement_gravity = g * BS;
509 void Client::handleCommand_HP(NetworkPacket* pkt)
512 LocalPlayer *player = m_env.getLocalPlayer();
513 assert(player != NULL);
515 u16 oldhp = player->hp;
522 if (moddingEnabled()) {
523 m_script->on_hp_modification(hp);
527 // Add to ClientEvent queue
528 ClientEvent *event = new ClientEvent();
529 event->type = CE_PLAYER_DAMAGE;
530 event->player_damage.amount = oldhp - hp;
531 m_client_event_queue.push(event);
535 void Client::handleCommand_Breath(NetworkPacket* pkt)
537 LocalPlayer *player = m_env.getLocalPlayer();
538 assert(player != NULL);
544 player->setBreath(breath);
547 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
549 LocalPlayer *player = m_env.getLocalPlayer();
550 assert(player != NULL);
555 *pkt >> pos >> pitch >> yaw;
557 player->setPosition(pos);
559 infostream << "Client got TOCLIENT_MOVE_PLAYER"
560 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
561 << " pitch=" << pitch
566 Add to ClientEvent queue.
567 This has to be sent to the main program because otherwise
568 it would just force the pitch and yaw values to whatever
569 the camera points to.
571 ClientEvent *event = new ClientEvent();
572 event->type = CE_PLAYER_FORCE_MOVE;
573 event->player_force_move.pitch = pitch;
574 event->player_force_move.yaw = yaw;
575 m_client_event_queue.push(event);
578 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
580 bool set_camera_point_target;
581 v3f camera_point_target;
583 *pkt >> set_camera_point_target;
584 *pkt >> camera_point_target;
586 ClientEvent *event = new ClientEvent();
587 event->type = CE_DEATHSCREEN;
588 event->deathscreen.set_camera_point_target = set_camera_point_target;
589 event->deathscreen.camera_point_target_x = camera_point_target.X;
590 event->deathscreen.camera_point_target_y = camera_point_target.Y;
591 event->deathscreen.camera_point_target_z = camera_point_target.Z;
592 m_client_event_queue.push(event);
595 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
601 infostream << "Client: Received media announcement: packet size: "
602 << pkt->getSize() << std::endl;
604 if (m_media_downloader == NULL ||
605 m_media_downloader->isStarted()) {
606 const char *problem = m_media_downloader ?
607 "we already saw another announcement" :
608 "all media has been received already";
609 errorstream << "Client: Received media announcement but "
611 << " files=" << num_files
612 << " size=" << pkt->getSize() << std::endl;
616 // Mesh update thread must be stopped while
617 // updating content definitions
618 sanity_check(!m_mesh_update_thread.isRunning());
620 for (u16 i = 0; i < num_files; i++) {
621 std::string name, sha1_base64;
623 *pkt >> name >> sha1_base64;
625 std::string sha1_raw = base64_decode(sha1_base64);
626 m_media_downloader->addFile(name, sha1_raw);
635 while(!sf.at_end()) {
636 std::string baseurl = trim(sf.next(","));
637 if (!baseurl.empty())
638 m_media_downloader->addRemoteServer(baseurl);
641 catch(SerializationError& e) {
642 // not supported by server or turned off
645 m_media_downloader->step(this);
648 void Client::handleCommand_Media(NetworkPacket* pkt)
652 u16 total number of file bunches
653 u16 index of this bunch
654 u32 number of files in this bunch
666 *pkt >> num_bunches >> bunch_i >> num_files;
668 infostream << "Client: Received files: bunch " << bunch_i << "/"
669 << num_bunches << " files=" << num_files
670 << " size=" << pkt->getSize() << std::endl;
675 if (!m_media_downloader || !m_media_downloader->isStarted()) {
676 const char *problem = m_media_downloader ?
677 "media has not been requested" :
678 "all media has been received already";
679 errorstream << "Client: Received media but "
681 << " bunch " << bunch_i << "/" << num_bunches
682 << " files=" << num_files
683 << " size=" << pkt->getSize() << std::endl;
687 // Mesh update thread must be stopped while
688 // updating content definitions
689 sanity_check(!m_mesh_update_thread.isRunning());
691 for (u32 i=0; i < num_files; i++) {
696 std::string data = pkt->readLongString();
698 m_media_downloader->conventionalTransferDone(
703 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
705 infostream << "Client: Received node definitions: packet size: "
706 << pkt->getSize() << std::endl;
708 // Mesh update thread must be stopped while
709 // updating content definitions
710 sanity_check(!m_mesh_update_thread.isRunning());
712 // Decompress node definitions
713 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
714 std::ostringstream tmp_os;
715 decompressZlib(tmp_is, tmp_os);
717 // Deserialize node definitions
718 std::istringstream tmp_is2(tmp_os.str());
719 m_nodedef->deSerialize(tmp_is2);
720 m_nodedef_received = true;
723 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
725 infostream << "Client: Received item definitions: packet size: "
726 << pkt->getSize() << std::endl;
728 // Mesh update thread must be stopped while
729 // updating content definitions
730 sanity_check(!m_mesh_update_thread.isRunning());
732 // Decompress item definitions
733 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
734 std::ostringstream tmp_os;
735 decompressZlib(tmp_is, tmp_os);
737 // Deserialize node definitions
738 std::istringstream tmp_is2(tmp_os.str());
739 m_itemdef->deSerialize(tmp_is2);
740 m_itemdef_received = true;
743 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
751 [11 + len] (f32 * 3) pos
752 [23 + len] u16 object_id
762 u8 type; // 0=local, 1=positional, 2=object
769 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
774 } catch (PacketError &e) {};
780 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
782 case 1: // positional
783 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
787 ClientActiveObject *cao = m_env.getActiveObject(object_id);
789 pos = cao->getPosition();
790 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
791 // TODO: Set up sound to move with object
798 if (client_id != -1) {
799 m_sounds_server_to_client[server_id] = client_id;
800 m_sounds_client_to_server[client_id] = server_id;
802 m_sounds_to_objects[client_id] = object_id;
806 void Client::handleCommand_StopSound(NetworkPacket* pkt)
812 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
813 if (i != m_sounds_server_to_client.end()) {
814 int client_id = i->second;
815 m_sound->stopSound(client_id);
819 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
825 *pkt >> sound_id >> step >> gain;
827 std::unordered_map<s32, int>::const_iterator i =
828 m_sounds_server_to_client.find(sound_id);
830 if (i != m_sounds_server_to_client.end())
831 m_sound->fadeSound(i->second, step, gain);
834 void Client::handleCommand_Privileges(NetworkPacket* pkt)
836 m_privileges.clear();
837 infostream << "Client: Privileges updated: ";
840 *pkt >> num_privileges;
842 for (u16 i = 0; i < num_privileges; i++) {
847 m_privileges.insert(priv);
848 infostream << priv << " ";
850 infostream << std::endl;
853 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
855 LocalPlayer *player = m_env.getLocalPlayer();
856 assert(player != NULL);
858 // Store formspec in LocalPlayer
859 player->inventory_formspec = pkt->readLongString();
862 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
864 std::string datastring(pkt->getString(0), pkt->getSize());
865 std::istringstream is(datastring, std::ios_base::binary);
867 std::string name = deSerializeString(is);
869 infostream << "Client: Detached inventory update: \"" << name
870 << "\"" << std::endl;
872 Inventory *inv = NULL;
873 if (m_detached_inventories.count(name) > 0)
874 inv = m_detached_inventories[name];
876 inv = new Inventory(m_itemdef);
877 m_detached_inventories[name] = inv;
879 inv->deSerialize(is);
882 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
884 std::string formspec = pkt->readLongString();
885 std::string formname;
889 ClientEvent *event = new ClientEvent();
890 event->type = CE_SHOW_FORMSPEC;
891 // pointer is required as event is a struct only!
892 // adding a std:string to a struct isn't possible
893 event->show_formspec.formspec = new std::string(formspec);
894 event->show_formspec.formname = new std::string(formname);
895 m_client_event_queue.push(event);
898 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
900 std::string datastring(pkt->getString(0), pkt->getSize());
901 std::istringstream is(datastring, std::ios_base::binary);
903 v3f pos = readV3F1000(is);
904 v3f vel = readV3F1000(is);
905 v3f acc = readV3F1000(is);
906 float expirationtime = readF1000(is);
907 float size = readF1000(is);
908 bool collisiondetection = readU8(is);
909 std::string texture = deSerializeLongString(is);
910 bool vertical = false;
911 bool collision_removal = false;
912 TileAnimationParams animation;
913 animation.type = TAT_NONE;
916 vertical = readU8(is);
917 collision_removal = readU8(is);
918 animation.deSerialize(is, m_proto_ver);
922 ClientEvent *event = new ClientEvent();
923 event->type = CE_SPAWN_PARTICLE;
924 event->spawn_particle.pos = new v3f (pos);
925 event->spawn_particle.vel = new v3f (vel);
926 event->spawn_particle.acc = new v3f (acc);
927 event->spawn_particle.expirationtime = expirationtime;
928 event->spawn_particle.size = size;
929 event->spawn_particle.collisiondetection = collisiondetection;
930 event->spawn_particle.collision_removal = collision_removal;
931 event->spawn_particle.vertical = vertical;
932 event->spawn_particle.texture = new std::string(texture);
933 event->spawn_particle.animation = animation;
934 event->spawn_particle.glow = glow;
936 m_client_event_queue.push(event);
939 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
953 bool collisiondetection;
956 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
957 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
958 >> maxsize >> collisiondetection;
960 std::string texture = pkt->readLongString();
964 bool vertical = false;
965 bool collision_removal = false;
966 TileAnimationParams animation;
967 animation.type = TAT_NONE;
972 *pkt >> collision_removal;
975 // This is horrible but required (why are there two ways to deserialize pkts?)
976 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
977 std::istringstream is(datastring, std::ios_base::binary);
978 animation.deSerialize(is, m_proto_ver);
982 ClientEvent *event = new ClientEvent();
983 event->type = CE_ADD_PARTICLESPAWNER;
984 event->add_particlespawner.amount = amount;
985 event->add_particlespawner.spawntime = spawntime;
986 event->add_particlespawner.minpos = new v3f (minpos);
987 event->add_particlespawner.maxpos = new v3f (maxpos);
988 event->add_particlespawner.minvel = new v3f (minvel);
989 event->add_particlespawner.maxvel = new v3f (maxvel);
990 event->add_particlespawner.minacc = new v3f (minacc);
991 event->add_particlespawner.maxacc = new v3f (maxacc);
992 event->add_particlespawner.minexptime = minexptime;
993 event->add_particlespawner.maxexptime = maxexptime;
994 event->add_particlespawner.minsize = minsize;
995 event->add_particlespawner.maxsize = maxsize;
996 event->add_particlespawner.collisiondetection = collisiondetection;
997 event->add_particlespawner.collision_removal = collision_removal;
998 event->add_particlespawner.attached_id = attached_id;
999 event->add_particlespawner.vertical = vertical;
1000 event->add_particlespawner.texture = new std::string(texture);
1001 event->add_particlespawner.id = id;
1002 event->add_particlespawner.animation = animation;
1003 event->add_particlespawner.glow = glow;
1005 m_client_event_queue.push(event);
1009 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1014 ClientEvent *event = new ClientEvent();
1015 event->type = CE_DELETE_PARTICLESPAWNER;
1016 event->delete_particlespawner.id = id;
1018 m_client_event_queue.push(event);
1021 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1023 std::string datastring(pkt->getString(0), pkt->getSize());
1024 std::istringstream is(datastring, std::ios_base::binary);
1040 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1041 >> dir >> align >> offset;
1045 catch(SerializationError &e) {};
1049 } catch(SerializationError &e) {};
1051 ClientEvent *event = new ClientEvent();
1052 event->type = CE_HUDADD;
1053 event->hudadd.id = id;
1054 event->hudadd.type = type;
1055 event->hudadd.pos = new v2f(pos);
1056 event->hudadd.name = new std::string(name);
1057 event->hudadd.scale = new v2f(scale);
1058 event->hudadd.text = new std::string(text);
1059 event->hudadd.number = number;
1060 event->hudadd.item = item;
1061 event->hudadd.dir = dir;
1062 event->hudadd.align = new v2f(align);
1063 event->hudadd.offset = new v2f(offset);
1064 event->hudadd.world_pos = new v3f(world_pos);
1065 event->hudadd.size = new v2s32(size);
1066 m_client_event_queue.push(event);
1069 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1075 ClientEvent *event = new ClientEvent();
1076 event->type = CE_HUDRM;
1077 event->hudrm.id = id;
1078 m_client_event_queue.push(event);
1081 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1093 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1094 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1096 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1098 else if (stat == HUD_STAT_WORLD_POS)
1100 else if (stat == HUD_STAT_SIZE )
1105 ClientEvent *event = new ClientEvent();
1106 event->type = CE_HUDCHANGE;
1107 event->hudchange.id = id;
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);
1117 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1121 *pkt >> flags >> mask;
1123 LocalPlayer *player = m_env.getLocalPlayer();
1124 assert(player != NULL);
1126 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1127 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1129 player->hud_flags &= ~mask;
1130 player->hud_flags |= flags;
1132 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1133 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1135 // Hide minimap if it has been disabled by the server
1136 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1137 // defers a minimap update, therefore only call it if really
1138 // needed, by checking that minimap was visible before
1139 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1141 // Switch to surface mode if radar disabled by server
1142 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1143 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1146 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1148 u16 param; std::string value;
1150 *pkt >> param >> value;
1152 LocalPlayer *player = m_env.getLocalPlayer();
1153 assert(player != NULL);
1155 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1156 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1157 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1158 player->hud_hotbar_itemcount = hotbar_itemcount;
1160 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1161 // If value not empty verify image exists in texture source
1162 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1163 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1164 << value << "')" << std::endl;
1167 player->hotbar_image = value;
1169 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1170 // If value not empty verify image exists in texture source
1171 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1172 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1173 << value << "')" << std::endl;
1176 player->hotbar_selected_image = value;
1180 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1182 std::string datastring(pkt->getString(0), pkt->getSize());
1183 std::istringstream is(datastring, std::ios_base::binary);
1185 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1186 std::string *type = new std::string(deSerializeString(is));
1187 u16 count = readU16(is);
1188 std::vector<std::string> *params = new std::vector<std::string>;
1190 for (size_t i = 0; i < count; i++)
1191 params->push_back(deSerializeString(is));
1195 clouds = readU8(is);
1198 ClientEvent *event = new ClientEvent();
1199 event->type = CE_SET_SKY;
1200 event->set_sky.bgcolor = bgcolor;
1201 event->set_sky.type = type;
1202 event->set_sky.params = params;
1203 event->set_sky.clouds = clouds;
1204 m_client_event_queue.push(event);
1207 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1210 video::SColor color_bright;
1211 video::SColor color_ambient;
1216 *pkt >> density >> color_bright >> color_ambient
1217 >> height >> thickness >> speed;
1219 ClientEvent *event = new ClientEvent();
1220 event->type = CE_CLOUD_PARAMS;
1221 event->cloud_params.density = density;
1222 // use the underlying u32 representation, because we can't
1223 // use struct members with constructors here, and this way
1224 // we avoid using new() and delete() for no good reason
1225 event->cloud_params.color_bright = color_bright.color;
1226 event->cloud_params.color_ambient = color_ambient.color;
1227 event->cloud_params.height = height;
1228 event->cloud_params.thickness = thickness;
1229 // same here: deconstruct to skip constructor
1230 event->cloud_params.speed_x = speed.X;
1231 event->cloud_params.speed_y = speed.Y;
1232 m_client_event_queue.push(event);
1235 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1238 u16 day_night_ratio_u;
1240 *pkt >> do_override >> day_night_ratio_u;
1242 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1244 ClientEvent *event = new ClientEvent();
1245 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1246 event->override_day_night_ratio.do_override = do_override;
1247 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1248 m_client_event_queue.push(event);
1251 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1253 LocalPlayer *player = m_env.getLocalPlayer();
1254 assert(player != NULL);
1256 *pkt >> player->local_animations[0];
1257 *pkt >> player->local_animations[1];
1258 *pkt >> player->local_animations[2];
1259 *pkt >> player->local_animations[3];
1260 *pkt >> player->local_animation_speed;
1263 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1265 LocalPlayer *player = m_env.getLocalPlayer();
1266 assert(player != NULL);
1268 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1271 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1275 *pkt >> type >> num_players;
1276 PlayerListModifer notice_type = (PlayerListModifer) type;
1278 for (u16 i = 0; i < num_players; i++) {
1281 switch (notice_type) {
1282 case PLAYER_LIST_INIT:
1283 case PLAYER_LIST_ADD:
1284 m_env.addPlayerName(name);
1286 case PLAYER_LIST_REMOVE:
1287 m_env.removePlayerName(name);
1293 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1295 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1296 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1297 errorstream << "Client: Received SRP S_B login message,"
1298 << " but wasn't supposed to (chosen_mech="
1299 << m_chosen_auth_mech << ")." << std::endl;
1305 SRPUser *usr = (SRPUser *) m_auth_data;
1310 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1312 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1313 (const unsigned char *) B.c_str(), B.size(),
1314 (unsigned char **) &bytes_M, &len_M);
1317 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1321 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1322 resp_pkt << std::string(bytes_M, len_M);
1326 void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
1328 *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;
1330 // Now we have flavours, load mods if it's enabled
1331 // Note: this should be moved after mods receptions from server instead
1339 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1341 std::string channel_name, sender, channel_msg;
1342 *pkt >> channel_name >> sender >> channel_msg;
1344 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1345 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1346 << channel_msg << std::endl;
1348 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1349 verbosestream << "Server sent us messages on unregistered channel "
1350 << channel_name << ", ignoring." << std::endl;
1354 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1357 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1360 ModChannelSignal signal;
1361 std::string channel;
1363 *pkt >> signal_tmp >> channel;
1365 signal = (ModChannelSignal)signal_tmp;
1367 bool valid_signal = true;
1368 // @TODO: send Signal to Lua API
1370 case MODCHANNEL_SIGNAL_JOIN_OK:
1371 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1372 infostream << "Server ack our mod channel join on channel `" << channel
1373 << "`, joining." << std::endl;
1375 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1376 // Unable to join, remove channel
1377 m_modchannel_mgr->leaveChannel(channel, 0);
1378 infostream << "Server refused our mod channel join on channel `" << channel
1379 << "`" << std::endl;
1381 case MODCHANNEL_SIGNAL_LEAVE_OK:
1383 infostream << "Server ack our mod channel leave on channel " << channel
1384 << "`, leaving." << std::endl;
1387 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1388 infostream << "Server refused our mod channel leave on channel `" << channel
1389 << "`" << std::endl;
1391 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1393 // Generally unused, but ensure we don't do an implementation error
1394 infostream << "Server tells us we sent a message on channel `" << channel
1395 << "` but we are not registered. Message was dropped." << std::endl;
1398 case MODCHANNEL_SIGNAL_SET_STATE: {
1402 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1403 infostream << "Received wrong channel state " << state
1404 << ", ignoring." << std::endl;
1408 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1409 infostream << "Server sets mod channel `" << channel
1410 << "` in read-only mode." << std::endl;
1415 warningstream << "Received unhandled mod channel signal ID "
1416 << signal << ", ignoring." << std::endl;
1418 valid_signal = false;
1422 // If signal is valid, forward it to client side mods
1424 m_script->on_modchannel_signal(channel, signal);