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 >> id >> type >> pos >> name >> scale >> text >> number >> item
1046 >> dir >> align >> offset;
1050 catch(SerializationError &e) {};
1054 } catch(SerializationError &e) {};
1056 ClientEvent *event = new ClientEvent();
1057 event->type = CE_HUDADD;
1058 event->hudadd.id = id;
1059 event->hudadd.type = type;
1060 event->hudadd.pos = new v2f(pos);
1061 event->hudadd.name = new std::string(name);
1062 event->hudadd.scale = new v2f(scale);
1063 event->hudadd.text = new std::string(text);
1064 event->hudadd.number = number;
1065 event->hudadd.item = item;
1066 event->hudadd.dir = dir;
1067 event->hudadd.align = new v2f(align);
1068 event->hudadd.offset = new v2f(offset);
1069 event->hudadd.world_pos = new v3f(world_pos);
1070 event->hudadd.size = new v2s32(size);
1071 m_client_event_queue.push(event);
1074 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1080 ClientEvent *event = new ClientEvent();
1081 event->type = CE_HUDRM;
1082 event->hudrm.id = id;
1083 m_client_event_queue.push(event);
1086 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1098 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1099 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1101 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1103 else if (stat == HUD_STAT_WORLD_POS)
1105 else if (stat == HUD_STAT_SIZE )
1110 ClientEvent *event = new ClientEvent();
1111 event->type = CE_HUDCHANGE;
1112 event->hudchange.id = id;
1113 event->hudchange.stat = (HudElementStat)stat;
1114 event->hudchange.v2fdata = new v2f(v2fdata);
1115 event->hudchange.v3fdata = new v3f(v3fdata);
1116 event->hudchange.sdata = new std::string(sdata);
1117 event->hudchange.data = intdata;
1118 event->hudchange.v2s32data = new v2s32(v2s32data);
1119 m_client_event_queue.push(event);
1122 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1126 *pkt >> flags >> mask;
1128 LocalPlayer *player = m_env.getLocalPlayer();
1129 assert(player != NULL);
1131 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1132 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1134 player->hud_flags &= ~mask;
1135 player->hud_flags |= flags;
1137 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1138 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1140 // Hide minimap if it has been disabled by the server
1141 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1142 // defers a minimap update, therefore only call it if really
1143 // needed, by checking that minimap was visible before
1144 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1146 // Switch to surface mode if radar disabled by server
1147 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1148 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1151 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1153 u16 param; std::string value;
1155 *pkt >> param >> value;
1157 LocalPlayer *player = m_env.getLocalPlayer();
1158 assert(player != NULL);
1160 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1161 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1162 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1163 player->hud_hotbar_itemcount = hotbar_itemcount;
1165 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1166 // If value not empty verify image exists in texture source
1167 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1168 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1169 << value << "')" << std::endl;
1172 player->hotbar_image = value;
1174 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1175 // If value not empty verify image exists in texture source
1176 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1177 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1178 << value << "')" << std::endl;
1181 player->hotbar_selected_image = value;
1185 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1187 std::string datastring(pkt->getString(0), pkt->getSize());
1188 std::istringstream is(datastring, std::ios_base::binary);
1190 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1191 std::string *type = new std::string(deSerializeString(is));
1192 u16 count = readU16(is);
1193 std::vector<std::string> *params = new std::vector<std::string>;
1195 for (size_t i = 0; i < count; i++)
1196 params->push_back(deSerializeString(is));
1200 clouds = readU8(is);
1203 ClientEvent *event = new ClientEvent();
1204 event->type = CE_SET_SKY;
1205 event->set_sky.bgcolor = bgcolor;
1206 event->set_sky.type = type;
1207 event->set_sky.params = params;
1208 event->set_sky.clouds = clouds;
1209 m_client_event_queue.push(event);
1212 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1215 video::SColor color_bright;
1216 video::SColor color_ambient;
1221 *pkt >> density >> color_bright >> color_ambient
1222 >> height >> thickness >> speed;
1224 ClientEvent *event = new ClientEvent();
1225 event->type = CE_CLOUD_PARAMS;
1226 event->cloud_params.density = density;
1227 // use the underlying u32 representation, because we can't
1228 // use struct members with constructors here, and this way
1229 // we avoid using new() and delete() for no good reason
1230 event->cloud_params.color_bright = color_bright.color;
1231 event->cloud_params.color_ambient = color_ambient.color;
1232 event->cloud_params.height = height;
1233 event->cloud_params.thickness = thickness;
1234 // same here: deconstruct to skip constructor
1235 event->cloud_params.speed_x = speed.X;
1236 event->cloud_params.speed_y = speed.Y;
1237 m_client_event_queue.push(event);
1240 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1243 u16 day_night_ratio_u;
1245 *pkt >> do_override >> day_night_ratio_u;
1247 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1249 ClientEvent *event = new ClientEvent();
1250 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1251 event->override_day_night_ratio.do_override = do_override;
1252 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1253 m_client_event_queue.push(event);
1256 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1258 LocalPlayer *player = m_env.getLocalPlayer();
1259 assert(player != NULL);
1261 *pkt >> player->local_animations[0];
1262 *pkt >> player->local_animations[1];
1263 *pkt >> player->local_animations[2];
1264 *pkt >> player->local_animations[3];
1265 *pkt >> player->local_animation_speed;
1268 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1270 LocalPlayer *player = m_env.getLocalPlayer();
1271 assert(player != NULL);
1273 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1276 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1280 *pkt >> type >> num_players;
1281 PlayerListModifer notice_type = (PlayerListModifer) type;
1283 for (u16 i = 0; i < num_players; i++) {
1286 switch (notice_type) {
1287 case PLAYER_LIST_INIT:
1288 case PLAYER_LIST_ADD:
1289 m_env.addPlayerName(name);
1291 case PLAYER_LIST_REMOVE:
1292 m_env.removePlayerName(name);
1298 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1300 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1301 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1302 errorstream << "Client: Received SRP S_B login message,"
1303 << " but wasn't supposed to (chosen_mech="
1304 << m_chosen_auth_mech << ")." << std::endl;
1310 SRPUser *usr = (SRPUser *) m_auth_data;
1315 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1317 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1318 (const unsigned char *) B.c_str(), B.size(),
1319 (unsigned char **) &bytes_M, &len_M);
1322 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1326 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1327 resp_pkt << std::string(bytes_M, len_M);
1331 void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
1333 *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;
1335 // Now we have flavours, load mods if it's enabled
1336 // Note: this should be moved after mods receptions from server instead
1344 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1346 std::string channel_name, sender, channel_msg;
1347 *pkt >> channel_name >> sender >> channel_msg;
1349 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1350 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1351 << channel_msg << std::endl;
1353 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1354 verbosestream << "Server sent us messages on unregistered channel "
1355 << channel_name << ", ignoring." << std::endl;
1359 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1362 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1365 ModChannelSignal signal;
1366 std::string channel;
1368 *pkt >> signal_tmp >> channel;
1370 signal = (ModChannelSignal)signal_tmp;
1372 bool valid_signal = true;
1373 // @TODO: send Signal to Lua API
1375 case MODCHANNEL_SIGNAL_JOIN_OK:
1376 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1377 infostream << "Server ack our mod channel join on channel `" << channel
1378 << "`, joining." << std::endl;
1380 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1381 // Unable to join, remove channel
1382 m_modchannel_mgr->leaveChannel(channel, 0);
1383 infostream << "Server refused our mod channel join on channel `" << channel
1384 << "`" << std::endl;
1386 case MODCHANNEL_SIGNAL_LEAVE_OK:
1388 infostream << "Server ack our mod channel leave on channel " << channel
1389 << "`, leaving." << std::endl;
1392 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1393 infostream << "Server refused our mod channel leave on channel `" << channel
1394 << "`" << std::endl;
1396 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1398 // Generally unused, but ensure we don't do an implementation error
1399 infostream << "Server tells us we sent a message on channel `" << channel
1400 << "` but we are not registered. Message was dropped." << std::endl;
1403 case MODCHANNEL_SIGNAL_SET_STATE: {
1407 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1408 infostream << "Received wrong channel state " << state
1409 << ", ignoring." << std::endl;
1413 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1414 infostream << "Server sets mod channel `" << channel
1415 << "` in read-only mode." << std::endl;
1420 warningstream << "Received unhandled mod channel signal ID "
1421 << signal << ", ignoring." << std::endl;
1423 valid_signal = false;
1427 // If signal is valid, forward it to client side mods
1429 m_script->on_modchannel_signal(channel, signal);