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);
1106 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1107 >> dir >> align >> offset;
1111 catch(SerializationError &e) {};
1115 } catch(SerializationError &e) {};
1120 catch(PacketError &e) {}
1122 ClientEvent *event = new ClientEvent();
1123 event->type = CE_HUDADD;
1124 event->hudadd.server_id = server_id;
1125 event->hudadd.type = type;
1126 event->hudadd.pos = new v2f(pos);
1127 event->hudadd.name = new std::string(name);
1128 event->hudadd.scale = new v2f(scale);
1129 event->hudadd.text = new std::string(text);
1130 event->hudadd.number = number;
1131 event->hudadd.item = item;
1132 event->hudadd.dir = dir;
1133 event->hudadd.align = new v2f(align);
1134 event->hudadd.offset = new v2f(offset);
1135 event->hudadd.world_pos = new v3f(world_pos);
1136 event->hudadd.size = new v2s32(size);
1137 event->hudadd.z_index = z_index;
1138 m_client_event_queue.push(event);
1141 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1147 auto i = m_hud_server_to_client.find(server_id);
1148 if (i != m_hud_server_to_client.end()) {
1149 int client_id = i->second;
1150 m_hud_server_to_client.erase(i);
1152 ClientEvent *event = new ClientEvent();
1153 event->type = CE_HUDRM;
1154 event->hudrm.id = client_id;
1155 m_client_event_queue.push(event);
1159 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1169 *pkt >> server_id >> stat;
1171 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1172 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1174 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1176 else if (stat == HUD_STAT_WORLD_POS)
1178 else if (stat == HUD_STAT_SIZE )
1183 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1184 if (i != m_hud_server_to_client.end()) {
1185 ClientEvent *event = new ClientEvent();
1186 event->type = CE_HUDCHANGE;
1187 event->hudchange.id = i->second;
1188 event->hudchange.stat = (HudElementStat)stat;
1189 event->hudchange.v2fdata = new v2f(v2fdata);
1190 event->hudchange.v3fdata = new v3f(v3fdata);
1191 event->hudchange.sdata = new std::string(sdata);
1192 event->hudchange.data = intdata;
1193 event->hudchange.v2s32data = new v2s32(v2s32data);
1194 m_client_event_queue.push(event);
1198 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1202 *pkt >> flags >> mask;
1204 LocalPlayer *player = m_env.getLocalPlayer();
1205 assert(player != NULL);
1207 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1208 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1210 player->hud_flags &= ~mask;
1211 player->hud_flags |= flags;
1213 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1214 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1216 // Hide minimap if it has been disabled by the server
1217 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1218 // defers a minimap update, therefore only call it if really
1219 // needed, by checking that minimap was visible before
1220 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1222 // Switch to surface mode if radar disabled by server
1223 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1224 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1227 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1229 u16 param; std::string value;
1231 *pkt >> param >> value;
1233 LocalPlayer *player = m_env.getLocalPlayer();
1234 assert(player != NULL);
1236 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1237 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1238 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1239 player->hud_hotbar_itemcount = hotbar_itemcount;
1241 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1242 player->hotbar_image = value;
1244 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1245 player->hotbar_selected_image = value;
1249 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1251 if (m_proto_ver < 39) {
1252 // Handle Protocol 38 and below servers with old set_sky,
1253 // ensuring the classic look is kept.
1254 std::string datastring(pkt->getString(0), pkt->getSize());
1255 std::istringstream is(datastring, std::ios_base::binary);
1257 SkyboxParams skybox;
1258 skybox.bgcolor = video::SColor(readARGB8(is));
1259 skybox.type = std::string(deSerializeString(is));
1260 u16 count = readU16(is);
1262 for (size_t i = 0; i < count; i++)
1263 skybox.textures.emplace_back(deSerializeString(is));
1265 skybox.clouds = true;
1267 skybox.clouds = readU8(is);
1270 // Use default skybox settings:
1271 SkyboxDefaults sky_defaults;
1272 SunParams sun = sky_defaults.getSunDefaults();
1273 MoonParams moon = sky_defaults.getMoonDefaults();
1274 StarParams stars = sky_defaults.getStarDefaults();
1276 // Fix for "regular" skies, as color isn't kept:
1277 if (skybox.type == "regular") {
1278 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1279 skybox.tint_type = "default";
1280 skybox.moon_tint = video::SColor(255, 255, 255, 255);
1281 skybox.sun_tint = video::SColor(255, 255, 255, 255);
1284 sun.visible = false;
1285 sun.sunrise_visible = false;
1286 moon.visible = false;
1287 stars.visible = false;
1290 // Skybox, sun, moon and stars ClientEvents:
1291 ClientEvent *sky_event = new ClientEvent();
1292 sky_event->type = CE_SET_SKY;
1293 sky_event->set_sky = new SkyboxParams(skybox);
1294 m_client_event_queue.push(sky_event);
1296 ClientEvent *sun_event = new ClientEvent();
1297 sun_event->type = CE_SET_SUN;
1298 sun_event->sun_params = new SunParams(sun);
1299 m_client_event_queue.push(sun_event);
1301 ClientEvent *moon_event = new ClientEvent();
1302 moon_event->type = CE_SET_MOON;
1303 moon_event->moon_params = new MoonParams(moon);
1304 m_client_event_queue.push(moon_event);
1306 ClientEvent *star_event = new ClientEvent();
1307 star_event->type = CE_SET_STARS;
1308 star_event->star_params = new StarParams(stars);
1309 m_client_event_queue.push(star_event);
1311 SkyboxParams skybox;
1313 std::string texture;
1315 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1316 skybox.sun_tint >> skybox.moon_tint >> skybox.tint_type;
1318 if (skybox.type == "skybox") {
1319 *pkt >> texture_count;
1320 for (int i = 0; i < texture_count; i++) {
1322 skybox.textures.emplace_back(texture);
1325 else if (skybox.type == "regular") {
1326 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1327 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1328 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1329 >> skybox.sky_color.indoors;
1332 ClientEvent *event = new ClientEvent();
1333 event->type = CE_SET_SKY;
1334 event->set_sky = new SkyboxParams(skybox);
1335 m_client_event_queue.push(event);
1339 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1343 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1344 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1346 ClientEvent *event = new ClientEvent();
1347 event->type = CE_SET_SUN;
1348 event->sun_params = new SunParams(sun);
1349 m_client_event_queue.push(event);
1352 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1356 *pkt >> moon.visible >> moon.texture
1357 >> moon.tonemap >> moon.scale;
1359 ClientEvent *event = new ClientEvent();
1360 event->type = CE_SET_MOON;
1361 event->moon_params = new MoonParams(moon);
1362 m_client_event_queue.push(event);
1365 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1369 *pkt >> stars.visible >> stars.count
1370 >> stars.starcolor >> stars.scale;
1372 ClientEvent *event = new ClientEvent();
1373 event->type = CE_SET_STARS;
1374 event->star_params = new StarParams(stars);
1376 m_client_event_queue.push(event);
1379 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1382 video::SColor color_bright;
1383 video::SColor color_ambient;
1388 *pkt >> density >> color_bright >> color_ambient
1389 >> height >> thickness >> speed;
1391 ClientEvent *event = new ClientEvent();
1392 event->type = CE_CLOUD_PARAMS;
1393 event->cloud_params.density = density;
1394 // use the underlying u32 representation, because we can't
1395 // use struct members with constructors here, and this way
1396 // we avoid using new() and delete() for no good reason
1397 event->cloud_params.color_bright = color_bright.color;
1398 event->cloud_params.color_ambient = color_ambient.color;
1399 event->cloud_params.height = height;
1400 event->cloud_params.thickness = thickness;
1401 // same here: deconstruct to skip constructor
1402 event->cloud_params.speed_x = speed.X;
1403 event->cloud_params.speed_y = speed.Y;
1404 m_client_event_queue.push(event);
1407 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1410 u16 day_night_ratio_u;
1412 *pkt >> do_override >> day_night_ratio_u;
1414 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1416 ClientEvent *event = new ClientEvent();
1417 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1418 event->override_day_night_ratio.do_override = do_override;
1419 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1420 m_client_event_queue.push(event);
1423 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1425 LocalPlayer *player = m_env.getLocalPlayer();
1426 assert(player != NULL);
1428 *pkt >> player->local_animations[0];
1429 *pkt >> player->local_animations[1];
1430 *pkt >> player->local_animations[2];
1431 *pkt >> player->local_animations[3];
1432 *pkt >> player->local_animation_speed;
1435 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1437 LocalPlayer *player = m_env.getLocalPlayer();
1438 assert(player != NULL);
1440 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1443 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1447 *pkt >> type >> num_players;
1448 PlayerListModifer notice_type = (PlayerListModifer) type;
1450 for (u16 i = 0; i < num_players; i++) {
1453 switch (notice_type) {
1454 case PLAYER_LIST_INIT:
1455 case PLAYER_LIST_ADD:
1456 m_env.addPlayerName(name);
1458 case PLAYER_LIST_REMOVE:
1459 m_env.removePlayerName(name);
1465 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1467 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1468 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1469 errorstream << "Client: Received SRP S_B login message,"
1470 << " but wasn't supposed to (chosen_mech="
1471 << m_chosen_auth_mech << ")." << std::endl;
1477 SRPUser *usr = (SRPUser *) m_auth_data;
1482 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1484 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1485 (const unsigned char *) B.c_str(), B.size(),
1486 (unsigned char **) &bytes_M, &len_M);
1489 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1493 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1494 resp_pkt << std::string(bytes_M, len_M);
1498 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1500 LocalPlayer *player = m_env.getLocalPlayer();
1501 assert(player != NULL);
1503 // Store formspec in LocalPlayer
1504 *pkt >> player->formspec_prepend;
1507 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1509 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1511 // Restrictions were received -> load mods if it's enabled
1512 // Note: this should be moved after mods receptions from server instead
1516 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1522 LocalPlayer *player = m_env.getLocalPlayer();
1523 assert(player != NULL);
1524 player->addVelocity(added_vel);
1531 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1533 std::string channel_name, sender, channel_msg;
1534 *pkt >> channel_name >> sender >> channel_msg;
1536 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1537 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1538 << channel_msg << std::endl;
1540 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1541 verbosestream << "Server sent us messages on unregistered channel "
1542 << channel_name << ", ignoring." << std::endl;
1546 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1549 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1552 ModChannelSignal signal;
1553 std::string channel;
1555 *pkt >> signal_tmp >> channel;
1557 signal = (ModChannelSignal)signal_tmp;
1559 bool valid_signal = true;
1560 // @TODO: send Signal to Lua API
1562 case MODCHANNEL_SIGNAL_JOIN_OK:
1563 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1564 infostream << "Server ack our mod channel join on channel `" << channel
1565 << "`, joining." << std::endl;
1567 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1568 // Unable to join, remove channel
1569 m_modchannel_mgr->leaveChannel(channel, 0);
1570 infostream << "Server refused our mod channel join on channel `" << channel
1571 << "`" << std::endl;
1573 case MODCHANNEL_SIGNAL_LEAVE_OK:
1575 infostream << "Server ack our mod channel leave on channel " << channel
1576 << "`, leaving." << std::endl;
1579 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1580 infostream << "Server refused our mod channel leave on channel `" << channel
1581 << "`" << std::endl;
1583 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1585 // Generally unused, but ensure we don't do an implementation error
1586 infostream << "Server tells us we sent a message on channel `" << channel
1587 << "` but we are not registered. Message was dropped." << std::endl;
1590 case MODCHANNEL_SIGNAL_SET_STATE: {
1594 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1595 infostream << "Received wrong channel state " << state
1596 << ", ignoring." << std::endl;
1600 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1601 infostream << "Server sets mod channel `" << channel
1602 << "` in read-only mode." << std::endl;
1607 warningstream << "Received unhandled mod channel signal ID "
1608 << signal << ", ignoring." << std::endl;
1610 valid_signal = false;
1614 // If signal is valid, forward it to client side mods
1616 m_script->on_modchannel_signal(channel, signal);