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.
20 #include "client/client.h"
22 #include "util/base64.h"
23 #include "client/camera.h"
24 #include "chatmessage.h"
25 #include "client/clientmedia.h"
28 #include "mapsector.h"
29 #include "client/minimap.h"
30 #include "modchannels.h"
32 #include "serialization.h"
34 #include "util/strfnd.h"
35 #include "client/clientevent.h"
36 #include "client/sound.h"
37 #include "network/clientopcodes.h"
38 #include "network/connection.h"
39 #include "script/scripting_client.h"
40 #include "util/serialize.h"
42 #include "tileanimation.h"
44 #include "skyparams.h"
46 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
48 infostream << "Got deprecated command "
49 << toClientCommandTable[pkt->getCommand()].name << " from peer "
50 << pkt->getPeerId() << "!" << std::endl;
53 void Client::handleCommand_Hello(NetworkPacket* pkt)
55 if (pkt->getSize() < 1)
62 std::string username_legacy; // for case insensitivity
63 *pkt >> serialization_ver >> compression_mode >> proto_ver
64 >> auth_mechs >> username_legacy;
66 // Chose an auth method we support
67 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
69 infostream << "Client: TOCLIENT_HELLO received with "
70 << "serialization_ver=" << (u32)serialization_ver
71 << ", auth_mechs=" << auth_mechs
72 << ", proto_ver=" << proto_ver
73 << ", compression_mode=" << compression_mode
74 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
76 if (!ser_ver_supported(serialization_ver)) {
77 infostream << "Client: TOCLIENT_HELLO: Server sent "
78 << "unsupported ser_fmt_ver"<< std::endl;
82 m_server_ser_ver = serialization_ver;
83 m_proto_ver = proto_ver;
85 //TODO verify that username_legacy matches sent username, only
86 // differs in casing (make both uppercase and compare)
87 // This is only neccessary though when we actually want to add casing support
89 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
90 // we recieved a TOCLIENT_HELLO while auth was already going on
91 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
92 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
93 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
94 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
95 srp_user_delete((SRPUser *) m_auth_data);
100 // Authenticate using that method, or abort if there wasn't any method found
101 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
102 if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
103 !m_simple_singleplayer_mode &&
104 !getServerAddress().isLocalhost() &&
105 g_settings->getBool("enable_register_confirmation")) {
106 promptConfirmRegistration(chosen_auth_mechanism);
108 startAuth(chosen_auth_mechanism);
111 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
112 m_access_denied = true;
113 m_access_denied_reason = "Unknown";
119 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
124 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
125 >> m_sudo_auth_methods;
127 playerpos -= v3f(0, BS / 2, 0);
129 // Set player position
130 LocalPlayer *player = m_env.getLocalPlayer();
131 assert(player != NULL);
132 player->setPosition(playerpos);
134 infostream << "Client: received map seed: " << m_map_seed << std::endl;
135 infostream << "Client: received recommended send interval "
136 << m_recommended_send_interval<<std::endl;
139 /*~ DO NOT TRANSLATE THIS LITERALLY!
140 This is a special string which needs to contain the translation's
141 language code (e.g. "de" for German). */
142 std::string lang = gettext("LANG_CODE");
143 if (lang == "LANG_CODE")
146 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
152 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
156 m_password = m_new_password;
158 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
160 // send packet to actually set the password
161 startAuth(AUTH_MECHANISM_FIRST_SRP);
164 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
166 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
168 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
169 L"Password change denied. Password NOT changed.");
170 pushToChatQueue(chatMessage);
171 // reset everything and be sad
175 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
177 // The server didn't like our password. Note, this needs
178 // to be processed even if the serialisation format has
179 // not been agreed yet, the same as TOCLIENT_INIT.
180 m_access_denied = true;
181 m_access_denied_reason = "Unknown";
183 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
184 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
185 // in some places of the server code
186 if (pkt->getSize() >= 2) {
187 std::wstring wide_reason;
189 m_access_denied_reason = wide_to_utf8(wide_reason);
194 if (pkt->getSize() < 1)
197 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
199 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
200 denyCode == SERVER_ACCESSDENIED_CRASH) {
201 *pkt >> m_access_denied_reason;
202 if (m_access_denied_reason.empty()) {
203 m_access_denied_reason = accessDeniedStrings[denyCode];
207 m_access_denied_reconnect = reconnect & 1;
208 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
209 *pkt >> m_access_denied_reason;
210 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
211 m_access_denied_reason = accessDeniedStrings[denyCode];
213 // Allow us to add new error messages to the
214 // protocol without raising the protocol version, if we want to.
215 // Until then (which may be never), this is outside
216 // of the defined protocol.
217 *pkt >> m_access_denied_reason;
218 if (m_access_denied_reason.empty()) {
219 m_access_denied_reason = "Unknown";
224 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
226 if (pkt->getSize() < 6)
234 void Client::handleCommand_AddNode(NetworkPacket* pkt)
236 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
243 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
245 bool remove_metadata = true;
246 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
247 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
248 remove_metadata = false;
251 addNode(p, n, remove_metadata);
254 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
256 if (pkt->getSize() < 1)
259 std::istringstream is(pkt->readLongString(), std::ios::binary);
260 std::stringstream sstr;
261 decompressZlib(is, sstr);
263 NodeMetadataList meta_updates_list(false);
264 meta_updates_list.deSerialize(sstr, m_itemdef, true);
266 Map &map = m_env.getMap();
267 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
268 i != meta_updates_list.end(); ++i) {
269 v3s16 pos = i->first;
271 if (map.isValidPosition(pos) &&
272 map.setNodeMetadata(pos, i->second))
273 continue; // Prevent from deleting metadata
275 // Meta couldn't be set, unused metadata
280 void Client::handleCommand_BlockData(NetworkPacket* pkt)
282 // Ignore too small packet
283 if (pkt->getSize() < 6)
289 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
290 std::istringstream istr(datastring, std::ios_base::binary);
296 sector = m_env.getMap().emergeSector(p2d);
298 assert(sector->getPos() == p2d);
300 block = sector->getBlockNoCreateNoEx(p.Y);
303 Update an existing block
305 block->deSerialize(istr, m_server_ser_ver, false);
306 block->deSerializeNetworkSpecific(istr);
312 block = new MapBlock(&m_env.getMap(), p, this);
313 block->deSerialize(istr, m_server_ser_ver, false);
314 block->deSerializeNetworkSpecific(istr);
315 sector->insertBlock(block);
319 ServerMap::saveBlock(block, m_localdb);
323 Add it to mesh update queue and set it to be acknowledged after update.
325 addUpdateMeshTaskWithEdge(p, true);
328 void Client::handleCommand_Inventory(NetworkPacket* pkt)
330 if (pkt->getSize() < 1)
333 std::string datastring(pkt->getString(0), pkt->getSize());
334 std::istringstream is(datastring, std::ios_base::binary);
336 LocalPlayer *player = m_env.getLocalPlayer();
337 assert(player != NULL);
339 player->inventory.deSerialize(is);
341 m_update_wielded_item = true;
343 delete m_inventory_from_server;
344 m_inventory_from_server = new Inventory(player->inventory);
345 m_inventory_from_server_age = 0.0;
348 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
350 if (pkt->getSize() < 2)
357 time_of_day = time_of_day % 24000;
358 float time_speed = 0;
360 if (pkt->getSize() >= 2 + 4) {
364 // Old message; try to approximate speed of time by ourselves
365 float time_of_day_f = (float)time_of_day / 24000.0f;
366 float tod_diff_f = 0;
368 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
369 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
371 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
373 m_last_time_of_day_f = time_of_day_f;
374 float time_diff = m_time_of_day_update_timer;
375 m_time_of_day_update_timer = 0;
377 if (m_time_of_day_set) {
378 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
379 infostream << "Client: Measured time_of_day speed (old format): "
380 << time_speed << " tod_diff_f=" << tod_diff_f
381 << " time_diff=" << time_diff << std::endl;
385 // Update environment
386 m_env.setTimeOfDay(time_of_day);
387 m_env.setTimeOfDaySpeed(time_speed);
388 m_time_of_day_set = true;
390 //u32 dr = m_env.getDayNightRatio();
391 //infostream << "Client: time_of_day=" << time_of_day
392 // << " time_speed=" << time_speed
393 // << " dr=" << dr << std::endl;
396 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
401 u16 sendername length
407 ChatMessage *chatMessage = new ChatMessage();
408 u8 version, message_type;
409 *pkt >> version >> message_type;
411 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
417 *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
418 chatMessage->timestamp = static_cast<std::time_t>(timestamp);
420 chatMessage->type = (ChatMessageType) message_type;
422 // @TODO send this to CSM using ChatMessage object
423 if (modsLoaded() && m_script->on_receiving_message(
424 wide_to_utf8(chatMessage->message))) {
425 // Message was consumed by CSM and should not be handled by client
428 pushToChatQueue(chatMessage);
432 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
435 u16 count of removed objects
436 for all removed objects {
439 u16 count of added objects
440 for all added objects {
443 u32 initialization data length
444 string initialization data
450 u16 removed_count, added_count, id;
452 // Read removed objects
453 *pkt >> removed_count;
455 for (u16 i = 0; i < removed_count; i++) {
457 m_env.removeActiveObject(id);
460 // Read added objects
463 for (u16 i = 0; i < added_count; i++) {
465 m_env.addActiveObject(id, type, pkt->readLongString());
467 } catch (PacketError &e) {
468 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
469 << ". The packet is unreliable, ignoring" << std::endl;
472 // m_activeobjects_received is false before the first
473 // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
474 m_activeobjects_received = true;
477 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
487 std::string datastring(pkt->getString(0), pkt->getSize());
488 std::istringstream is(datastring, std::ios_base::binary);
492 u16 id = readU16(is);
496 std::string message = deSerializeString(is);
498 // Pass on to the environment
499 m_env.processActiveObjectMessage(id, message);
501 } catch (SerializationError &e) {
502 errorstream << "Client::handleCommand_ActiveObjectMessages: "
503 << "caught SerializationError: " << e.what() << std::endl;
507 void Client::handleCommand_Movement(NetworkPacket* pkt)
509 LocalPlayer *player = m_env.getLocalPlayer();
510 assert(player != NULL);
512 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
514 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
515 >> lf >> lfs >> ls >> g;
517 player->movement_acceleration_default = mad * BS;
518 player->movement_acceleration_air = maa * BS;
519 player->movement_acceleration_fast = maf * BS;
520 player->movement_speed_walk = msw * BS;
521 player->movement_speed_crouch = mscr * BS;
522 player->movement_speed_fast = msf * BS;
523 player->movement_speed_climb = mscl * BS;
524 player->movement_speed_jump = msj * BS;
525 player->movement_liquid_fluidity = lf * BS;
526 player->movement_liquid_fluidity_smooth = lfs * BS;
527 player->movement_liquid_sink = ls * BS;
528 player->movement_gravity = g * BS;
531 void Client::handleCommand_Fov(NetworkPacket *pkt)
534 bool is_multiplier = false;
535 f32 transition_time = 0.0f;
537 *pkt >> fov >> is_multiplier;
539 // Wrap transition_time extraction within a
540 // try-catch to preserve backwards compat
542 *pkt >> transition_time;
543 } catch (PacketError &e) {};
545 LocalPlayer *player = m_env.getLocalPlayer();
547 player->setFov({ fov, is_multiplier, transition_time });
548 m_camera->notifyFovChange();
551 void Client::handleCommand_HP(NetworkPacket *pkt)
553 LocalPlayer *player = m_env.getLocalPlayer();
554 assert(player != NULL);
556 u16 oldhp = player->hp;
564 m_script->on_hp_modification(hp);
567 // Add to ClientEvent queue
568 ClientEvent *event = new ClientEvent();
569 event->type = CE_PLAYER_DAMAGE;
570 event->player_damage.amount = oldhp - hp;
571 m_client_event_queue.push(event);
575 void Client::handleCommand_Breath(NetworkPacket* pkt)
577 LocalPlayer *player = m_env.getLocalPlayer();
578 assert(player != NULL);
584 player->setBreath(breath);
587 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
589 LocalPlayer *player = m_env.getLocalPlayer();
590 assert(player != NULL);
595 *pkt >> pos >> pitch >> yaw;
597 player->setPosition(pos);
599 infostream << "Client got TOCLIENT_MOVE_PLAYER"
600 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
601 << " pitch=" << pitch
606 Add to ClientEvent queue.
607 This has to be sent to the main program because otherwise
608 it would just force the pitch and yaw values to whatever
609 the camera points to.
611 ClientEvent *event = new ClientEvent();
612 event->type = CE_PLAYER_FORCE_MOVE;
613 event->player_force_move.pitch = pitch;
614 event->player_force_move.yaw = yaw;
615 m_client_event_queue.push(event);
618 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
620 bool set_camera_point_target;
621 v3f camera_point_target;
623 *pkt >> set_camera_point_target;
624 *pkt >> camera_point_target;
626 ClientEvent *event = new ClientEvent();
627 event->type = CE_DEATHSCREEN;
628 event->deathscreen.set_camera_point_target = set_camera_point_target;
629 event->deathscreen.camera_point_target_x = camera_point_target.X;
630 event->deathscreen.camera_point_target_y = camera_point_target.Y;
631 event->deathscreen.camera_point_target_z = camera_point_target.Z;
632 m_client_event_queue.push(event);
635 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
641 infostream << "Client: Received media announcement: packet size: "
642 << pkt->getSize() << std::endl;
644 if (m_media_downloader == NULL ||
645 m_media_downloader->isStarted()) {
646 const char *problem = m_media_downloader ?
647 "we already saw another announcement" :
648 "all media has been received already";
649 errorstream << "Client: Received media announcement but "
651 << " files=" << num_files
652 << " size=" << pkt->getSize() << std::endl;
656 // Mesh update thread must be stopped while
657 // updating content definitions
658 sanity_check(!m_mesh_update_thread.isRunning());
660 for (u16 i = 0; i < num_files; i++) {
661 std::string name, sha1_base64;
663 *pkt >> name >> sha1_base64;
665 std::string sha1_raw = base64_decode(sha1_base64);
666 m_media_downloader->addFile(name, sha1_raw);
675 while(!sf.at_end()) {
676 std::string baseurl = trim(sf.next(","));
677 if (!baseurl.empty())
678 m_media_downloader->addRemoteServer(baseurl);
681 catch(SerializationError& e) {
682 // not supported by server or turned off
685 m_media_downloader->step(this);
688 void Client::handleCommand_Media(NetworkPacket* pkt)
692 u16 total number of file bunches
693 u16 index of this bunch
694 u32 number of files in this bunch
706 *pkt >> num_bunches >> bunch_i >> num_files;
708 infostream << "Client: Received files: bunch " << bunch_i << "/"
709 << num_bunches << " files=" << num_files
710 << " size=" << pkt->getSize() << std::endl;
715 if (!m_media_downloader || !m_media_downloader->isStarted()) {
716 const char *problem = m_media_downloader ?
717 "media has not been requested" :
718 "all media has been received already";
719 errorstream << "Client: Received media but "
721 << " bunch " << bunch_i << "/" << num_bunches
722 << " files=" << num_files
723 << " size=" << pkt->getSize() << std::endl;
727 // Mesh update thread must be stopped while
728 // updating content definitions
729 sanity_check(!m_mesh_update_thread.isRunning());
731 for (u32 i=0; i < num_files; i++) {
736 std::string data = pkt->readLongString();
738 m_media_downloader->conventionalTransferDone(
743 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
745 infostream << "Client: Received node definitions: packet size: "
746 << pkt->getSize() << std::endl;
748 // Mesh update thread must be stopped while
749 // updating content definitions
750 sanity_check(!m_mesh_update_thread.isRunning());
752 // Decompress node definitions
753 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
754 std::ostringstream tmp_os;
755 decompressZlib(tmp_is, tmp_os);
757 // Deserialize node definitions
758 std::istringstream tmp_is2(tmp_os.str());
759 m_nodedef->deSerialize(tmp_is2);
760 m_nodedef_received = true;
763 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
765 infostream << "Client: Received item definitions: packet size: "
766 << pkt->getSize() << std::endl;
768 // Mesh update thread must be stopped while
769 // updating content definitions
770 sanity_check(!m_mesh_update_thread.isRunning());
772 // Decompress item definitions
773 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
774 std::ostringstream tmp_os;
775 decompressZlib(tmp_is, tmp_os);
777 // Deserialize node definitions
778 std::istringstream tmp_is2(tmp_os.str());
779 m_itemdef->deSerialize(tmp_is2);
780 m_itemdef_received = true;
783 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
791 [11 + len] (f32 * 3) pos
792 [23 + len] u16 object_id
796 [34 + len] bool ephemeral
803 u8 type; // 0=local, 1=positional, 2=object
809 bool ephemeral = false;
811 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
817 } catch (PacketError &e) {};
823 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
825 case 1: // positional
826 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
830 ClientActiveObject *cao = m_env.getActiveObject(object_id);
832 pos = cao->getPosition();
833 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
840 if (client_id != -1) {
841 // for ephemeral sounds, server_id is not meaningful
843 m_sounds_server_to_client[server_id] = client_id;
844 m_sounds_client_to_server[client_id] = server_id;
847 m_sounds_to_objects[client_id] = object_id;
851 void Client::handleCommand_StopSound(NetworkPacket* pkt)
857 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
858 if (i != m_sounds_server_to_client.end()) {
859 int client_id = i->second;
860 m_sound->stopSound(client_id);
864 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
870 *pkt >> sound_id >> step >> gain;
872 std::unordered_map<s32, int>::const_iterator i =
873 m_sounds_server_to_client.find(sound_id);
875 if (i != m_sounds_server_to_client.end())
876 m_sound->fadeSound(i->second, step, gain);
879 void Client::handleCommand_Privileges(NetworkPacket* pkt)
881 m_privileges.clear();
882 infostream << "Client: Privileges updated: ";
885 *pkt >> num_privileges;
887 for (u16 i = 0; i < num_privileges; i++) {
892 m_privileges.insert(priv);
893 infostream << priv << " ";
895 infostream << std::endl;
898 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
900 LocalPlayer *player = m_env.getLocalPlayer();
901 assert(player != NULL);
903 // Store formspec in LocalPlayer
904 player->inventory_formspec = pkt->readLongString();
907 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
910 bool keep_inv = true;
911 *pkt >> name >> keep_inv;
913 infostream << "Client: Detached inventory update: \"" << name
914 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
916 const auto &inv_it = m_detached_inventories.find(name);
918 if (inv_it != m_detached_inventories.end()) {
919 delete inv_it->second;
920 m_detached_inventories.erase(inv_it);
924 Inventory *inv = nullptr;
925 if (inv_it == m_detached_inventories.end()) {
926 inv = new Inventory(m_itemdef);
927 m_detached_inventories[name] = inv;
929 inv = inv_it->second;
933 *pkt >> ignore; // this used to be the length of the following string, ignore it
935 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
936 std::istringstream is(contents, std::ios::binary);
937 inv->deSerialize(is);
940 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
942 std::string formspec = pkt->readLongString();
943 std::string formname;
947 ClientEvent *event = new ClientEvent();
948 event->type = CE_SHOW_FORMSPEC;
949 // pointer is required as event is a struct only!
950 // adding a std:string to a struct isn't possible
951 event->show_formspec.formspec = new std::string(formspec);
952 event->show_formspec.formname = new std::string(formname);
953 m_client_event_queue.push(event);
956 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
958 std::string datastring(pkt->getString(0), pkt->getSize());
959 std::istringstream is(datastring, std::ios_base::binary);
961 v3f pos = readV3F32(is);
962 v3f vel = readV3F32(is);
963 v3f acc = readV3F32(is);
964 float expirationtime = readF32(is);
965 float size = readF32(is);
966 bool collisiondetection = readU8(is);
967 std::string texture = deSerializeLongString(is);
969 bool vertical = false;
970 bool collision_removal = false;
971 TileAnimationParams animation;
972 animation.type = TAT_NONE;
974 bool object_collision = false;
976 vertical = readU8(is);
977 collision_removal = readU8(is);
978 animation.deSerialize(is, m_proto_ver);
980 object_collision = readU8(is);
983 ClientEvent *event = new ClientEvent();
984 event->type = CE_SPAWN_PARTICLE;
985 event->spawn_particle.pos = new v3f (pos);
986 event->spawn_particle.vel = new v3f (vel);
987 event->spawn_particle.acc = new v3f (acc);
988 event->spawn_particle.expirationtime = expirationtime;
989 event->spawn_particle.size = size;
990 event->spawn_particle.collisiondetection = collisiondetection;
991 event->spawn_particle.collision_removal = collision_removal;
992 event->spawn_particle.object_collision = object_collision;
993 event->spawn_particle.vertical = vertical;
994 event->spawn_particle.texture = new std::string(texture);
995 event->spawn_particle.animation = animation;
996 event->spawn_particle.glow = glow;
998 m_client_event_queue.push(event);
1001 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
1015 bool collisiondetection;
1018 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
1019 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
1020 >> maxsize >> collisiondetection;
1022 std::string texture = pkt->readLongString();
1026 bool vertical = false;
1027 bool collision_removal = false;
1028 u16 attached_id = 0;
1029 TileAnimationParams animation;
1030 animation.type = TAT_NONE;
1032 bool object_collision = false;
1035 *pkt >> collision_removal;
1036 *pkt >> attached_id;
1038 // This is horrible but required (why are there two ways to deserialize pkts?)
1039 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
1040 std::istringstream is(datastring, std::ios_base::binary);
1041 animation.deSerialize(is, m_proto_ver);
1043 object_collision = readU8(is);
1046 auto event = new ClientEvent();
1047 event->type = CE_ADD_PARTICLESPAWNER;
1048 event->add_particlespawner.amount = amount;
1049 event->add_particlespawner.spawntime = spawntime;
1050 event->add_particlespawner.minpos = new v3f (minpos);
1051 event->add_particlespawner.maxpos = new v3f (maxpos);
1052 event->add_particlespawner.minvel = new v3f (minvel);
1053 event->add_particlespawner.maxvel = new v3f (maxvel);
1054 event->add_particlespawner.minacc = new v3f (minacc);
1055 event->add_particlespawner.maxacc = new v3f (maxacc);
1056 event->add_particlespawner.minexptime = minexptime;
1057 event->add_particlespawner.maxexptime = maxexptime;
1058 event->add_particlespawner.minsize = minsize;
1059 event->add_particlespawner.maxsize = maxsize;
1060 event->add_particlespawner.collisiondetection = collisiondetection;
1061 event->add_particlespawner.collision_removal = collision_removal;
1062 event->add_particlespawner.object_collision = object_collision;
1063 event->add_particlespawner.attached_id = attached_id;
1064 event->add_particlespawner.vertical = vertical;
1065 event->add_particlespawner.texture = new std::string(texture);
1066 event->add_particlespawner.id = server_id;
1067 event->add_particlespawner.animation = animation;
1068 event->add_particlespawner.glow = glow;
1070 m_client_event_queue.push(event);
1074 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1079 ClientEvent *event = new ClientEvent();
1080 event->type = CE_DELETE_PARTICLESPAWNER;
1081 event->delete_particlespawner.id = server_id;
1083 m_client_event_queue.push(event);
1086 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1088 std::string datastring(pkt->getString(0), pkt->getSize());
1089 std::istringstream is(datastring, std::ios_base::binary);
1107 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1108 >> dir >> align >> offset;
1114 } catch(PacketError &e) {};
1116 ClientEvent *event = new ClientEvent();
1117 event->type = CE_HUDADD;
1118 event->hudadd.server_id = server_id;
1119 event->hudadd.type = type;
1120 event->hudadd.pos = new v2f(pos);
1121 event->hudadd.name = new std::string(name);
1122 event->hudadd.scale = new v2f(scale);
1123 event->hudadd.text = new std::string(text);
1124 event->hudadd.number = number;
1125 event->hudadd.item = item;
1126 event->hudadd.dir = dir;
1127 event->hudadd.align = new v2f(align);
1128 event->hudadd.offset = new v2f(offset);
1129 event->hudadd.world_pos = new v3f(world_pos);
1130 event->hudadd.size = new v2s32(size);
1131 event->hudadd.z_index = z_index;
1132 event->hudadd.text2 = new std::string(text2);
1133 m_client_event_queue.push(event);
1136 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1142 auto i = m_hud_server_to_client.find(server_id);
1143 if (i != m_hud_server_to_client.end()) {
1144 int client_id = i->second;
1145 m_hud_server_to_client.erase(i);
1147 ClientEvent *event = new ClientEvent();
1148 event->type = CE_HUDRM;
1149 event->hudrm.id = client_id;
1150 m_client_event_queue.push(event);
1154 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1164 *pkt >> server_id >> stat;
1166 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1167 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1169 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1171 else if (stat == HUD_STAT_WORLD_POS)
1173 else if (stat == HUD_STAT_SIZE )
1178 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1179 if (i != m_hud_server_to_client.end()) {
1180 ClientEvent *event = new ClientEvent();
1181 event->type = CE_HUDCHANGE;
1182 event->hudchange.id = i->second;
1183 event->hudchange.stat = (HudElementStat)stat;
1184 event->hudchange.v2fdata = new v2f(v2fdata);
1185 event->hudchange.v3fdata = new v3f(v3fdata);
1186 event->hudchange.sdata = new std::string(sdata);
1187 event->hudchange.data = intdata;
1188 event->hudchange.v2s32data = new v2s32(v2s32data);
1189 m_client_event_queue.push(event);
1193 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1197 *pkt >> flags >> mask;
1199 LocalPlayer *player = m_env.getLocalPlayer();
1200 assert(player != NULL);
1202 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1203 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1205 player->hud_flags &= ~mask;
1206 player->hud_flags |= flags;
1208 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1209 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1211 // Hide minimap if it has been disabled by the server
1212 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1213 // defers a minimap update, therefore only call it if really
1214 // needed, by checking that minimap was visible before
1215 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1217 // Switch to surface mode if radar disabled by server
1218 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1219 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1222 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1224 u16 param; std::string value;
1226 *pkt >> param >> value;
1228 LocalPlayer *player = m_env.getLocalPlayer();
1229 assert(player != NULL);
1231 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1232 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1233 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1234 player->hud_hotbar_itemcount = hotbar_itemcount;
1236 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1237 player->hotbar_image = value;
1239 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1240 player->hotbar_selected_image = value;
1244 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1246 if (m_proto_ver < 39) {
1247 // Handle Protocol 38 and below servers with old set_sky,
1248 // ensuring the classic look is kept.
1249 std::string datastring(pkt->getString(0), pkt->getSize());
1250 std::istringstream is(datastring, std::ios_base::binary);
1252 SkyboxParams skybox;
1253 skybox.bgcolor = video::SColor(readARGB8(is));
1254 skybox.type = std::string(deSerializeString(is));
1255 u16 count = readU16(is);
1257 for (size_t i = 0; i < count; i++)
1258 skybox.textures.emplace_back(deSerializeString(is));
1260 skybox.clouds = true;
1262 skybox.clouds = readU8(is);
1265 // Use default skybox settings:
1266 SkyboxDefaults sky_defaults;
1267 SunParams sun = sky_defaults.getSunDefaults();
1268 MoonParams moon = sky_defaults.getMoonDefaults();
1269 StarParams stars = sky_defaults.getStarDefaults();
1271 // Fix for "regular" skies, as color isn't kept:
1272 if (skybox.type == "regular") {
1273 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1274 skybox.fog_tint_type = "default";
1275 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1276 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1279 sun.visible = false;
1280 sun.sunrise_visible = false;
1281 moon.visible = false;
1282 stars.visible = false;
1285 // Skybox, sun, moon and stars ClientEvents:
1286 ClientEvent *sky_event = new ClientEvent();
1287 sky_event->type = CE_SET_SKY;
1288 sky_event->set_sky = new SkyboxParams(skybox);
1289 m_client_event_queue.push(sky_event);
1291 ClientEvent *sun_event = new ClientEvent();
1292 sun_event->type = CE_SET_SUN;
1293 sun_event->sun_params = new SunParams(sun);
1294 m_client_event_queue.push(sun_event);
1296 ClientEvent *moon_event = new ClientEvent();
1297 moon_event->type = CE_SET_MOON;
1298 moon_event->moon_params = new MoonParams(moon);
1299 m_client_event_queue.push(moon_event);
1301 ClientEvent *star_event = new ClientEvent();
1302 star_event->type = CE_SET_STARS;
1303 star_event->star_params = new StarParams(stars);
1304 m_client_event_queue.push(star_event);
1306 SkyboxParams skybox;
1308 std::string texture;
1310 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1311 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1313 if (skybox.type == "skybox") {
1314 *pkt >> texture_count;
1315 for (int i = 0; i < texture_count; i++) {
1317 skybox.textures.emplace_back(texture);
1320 else if (skybox.type == "regular") {
1321 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1322 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1323 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1324 >> skybox.sky_color.indoors;
1327 ClientEvent *event = new ClientEvent();
1328 event->type = CE_SET_SKY;
1329 event->set_sky = new SkyboxParams(skybox);
1330 m_client_event_queue.push(event);
1334 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1338 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1339 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1341 ClientEvent *event = new ClientEvent();
1342 event->type = CE_SET_SUN;
1343 event->sun_params = new SunParams(sun);
1344 m_client_event_queue.push(event);
1347 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1351 *pkt >> moon.visible >> moon.texture
1352 >> moon.tonemap >> moon.scale;
1354 ClientEvent *event = new ClientEvent();
1355 event->type = CE_SET_MOON;
1356 event->moon_params = new MoonParams(moon);
1357 m_client_event_queue.push(event);
1360 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1364 *pkt >> stars.visible >> stars.count
1365 >> stars.starcolor >> stars.scale;
1367 ClientEvent *event = new ClientEvent();
1368 event->type = CE_SET_STARS;
1369 event->star_params = new StarParams(stars);
1371 m_client_event_queue.push(event);
1374 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1377 video::SColor color_bright;
1378 video::SColor color_ambient;
1383 *pkt >> density >> color_bright >> color_ambient
1384 >> height >> thickness >> speed;
1386 ClientEvent *event = new ClientEvent();
1387 event->type = CE_CLOUD_PARAMS;
1388 event->cloud_params.density = density;
1389 // use the underlying u32 representation, because we can't
1390 // use struct members with constructors here, and this way
1391 // we avoid using new() and delete() for no good reason
1392 event->cloud_params.color_bright = color_bright.color;
1393 event->cloud_params.color_ambient = color_ambient.color;
1394 event->cloud_params.height = height;
1395 event->cloud_params.thickness = thickness;
1396 // same here: deconstruct to skip constructor
1397 event->cloud_params.speed_x = speed.X;
1398 event->cloud_params.speed_y = speed.Y;
1399 m_client_event_queue.push(event);
1402 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1405 u16 day_night_ratio_u;
1407 *pkt >> do_override >> day_night_ratio_u;
1409 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1411 ClientEvent *event = new ClientEvent();
1412 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1413 event->override_day_night_ratio.do_override = do_override;
1414 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1415 m_client_event_queue.push(event);
1418 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1420 LocalPlayer *player = m_env.getLocalPlayer();
1421 assert(player != NULL);
1423 *pkt >> player->local_animations[0];
1424 *pkt >> player->local_animations[1];
1425 *pkt >> player->local_animations[2];
1426 *pkt >> player->local_animations[3];
1427 *pkt >> player->local_animation_speed;
1430 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1432 LocalPlayer *player = m_env.getLocalPlayer();
1433 assert(player != NULL);
1435 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1438 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1442 *pkt >> type >> num_players;
1443 PlayerListModifer notice_type = (PlayerListModifer) type;
1445 for (u16 i = 0; i < num_players; i++) {
1448 switch (notice_type) {
1449 case PLAYER_LIST_INIT:
1450 case PLAYER_LIST_ADD:
1451 m_env.addPlayerName(name);
1453 case PLAYER_LIST_REMOVE:
1454 m_env.removePlayerName(name);
1460 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1462 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1463 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1464 errorstream << "Client: Received SRP S_B login message,"
1465 << " but wasn't supposed to (chosen_mech="
1466 << m_chosen_auth_mech << ")." << std::endl;
1472 SRPUser *usr = (SRPUser *) m_auth_data;
1477 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1479 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1480 (const unsigned char *) B.c_str(), B.size(),
1481 (unsigned char **) &bytes_M, &len_M);
1484 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1488 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1489 resp_pkt << std::string(bytes_M, len_M);
1493 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1495 LocalPlayer *player = m_env.getLocalPlayer();
1496 assert(player != NULL);
1498 // Store formspec in LocalPlayer
1499 *pkt >> player->formspec_prepend;
1502 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1504 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1506 // Restrictions were received -> load mods if it's enabled
1507 // Note: this should be moved after mods receptions from server instead
1511 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1517 LocalPlayer *player = m_env.getLocalPlayer();
1518 assert(player != NULL);
1519 player->addVelocity(added_vel);
1526 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1528 std::string channel_name, sender, channel_msg;
1529 *pkt >> channel_name >> sender >> channel_msg;
1531 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1532 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1533 << channel_msg << std::endl;
1535 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1536 verbosestream << "Server sent us messages on unregistered channel "
1537 << channel_name << ", ignoring." << std::endl;
1541 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1544 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1547 ModChannelSignal signal;
1548 std::string channel;
1550 *pkt >> signal_tmp >> channel;
1552 signal = (ModChannelSignal)signal_tmp;
1554 bool valid_signal = true;
1555 // @TODO: send Signal to Lua API
1557 case MODCHANNEL_SIGNAL_JOIN_OK:
1558 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1559 infostream << "Server ack our mod channel join on channel `" << channel
1560 << "`, joining." << std::endl;
1562 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1563 // Unable to join, remove channel
1564 m_modchannel_mgr->leaveChannel(channel, 0);
1565 infostream << "Server refused our mod channel join on channel `" << channel
1566 << "`" << std::endl;
1568 case MODCHANNEL_SIGNAL_LEAVE_OK:
1570 infostream << "Server ack our mod channel leave on channel " << channel
1571 << "`, leaving." << std::endl;
1574 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1575 infostream << "Server refused our mod channel leave on channel `" << channel
1576 << "`" << std::endl;
1578 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1580 // Generally unused, but ensure we don't do an implementation error
1581 infostream << "Server tells us we sent a message on channel `" << channel
1582 << "` but we are not registered. Message was dropped." << std::endl;
1585 case MODCHANNEL_SIGNAL_SET_STATE: {
1589 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1590 infostream << "Received wrong channel state " << state
1591 << ", ignoring." << std::endl;
1595 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1596 infostream << "Server sets mod channel `" << channel
1597 << "` in read-only mode." << std::endl;
1602 warningstream << "Received unhandled mod channel signal ID "
1603 << signal << ", ignoring." << std::endl;
1605 valid_signal = false;
1609 // If signal is valid, forward it to client side mods
1611 m_script->on_modchannel_signal(channel, signal);