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 ParticleParameters p;
962 p.deSerialize(is, m_proto_ver);
964 ClientEvent *event = new ClientEvent();
965 event->type = CE_SPAWN_PARTICLE;
966 event->spawn_particle = new ParticleParameters(p);
968 m_client_event_queue.push(event);
971 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
973 std::string datastring(pkt->getString(0), pkt->getSize());
974 std::istringstream is(datastring, std::ios_base::binary);
976 ParticleSpawnerParameters p;
980 p.amount = readU16(is);
981 p.time = readF32(is);
982 p.minpos = readV3F32(is);
983 p.maxpos = readV3F32(is);
984 p.minvel = readV3F32(is);
985 p.maxvel = readV3F32(is);
986 p.minacc = readV3F32(is);
987 p.maxacc = readV3F32(is);
988 p.minexptime = readF32(is);
989 p.maxexptime = readF32(is);
990 p.minsize = readF32(is);
991 p.maxsize = readF32(is);
992 p.collisiondetection = readU8(is);
993 p.texture = deSerializeLongString(is);
995 server_id = readU32(is);
997 p.vertical = readU8(is);
998 p.collision_removal = readU8(is);
1000 attached_id = readU16(is);
1002 p.animation.deSerialize(is, m_proto_ver);
1003 p.glow = readU8(is);
1004 p.object_collision = readU8(is);
1006 // This is kinda awful
1008 u16 tmp_param0 = readU16(is);
1011 p.node.param0 = tmp_param0;
1012 p.node.param2 = readU8(is);
1013 p.node_tile = readU8(is);
1016 auto event = new ClientEvent();
1017 event->type = CE_ADD_PARTICLESPAWNER;
1018 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1019 event->add_particlespawner.attached_id = attached_id;
1020 event->add_particlespawner.id = server_id;
1022 m_client_event_queue.push(event);
1026 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1031 ClientEvent *event = new ClientEvent();
1032 event->type = CE_DELETE_PARTICLESPAWNER;
1033 event->delete_particlespawner.id = server_id;
1035 m_client_event_queue.push(event);
1038 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1040 std::string datastring(pkt->getString(0), pkt->getSize());
1041 std::istringstream is(datastring, std::ios_base::binary);
1059 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1060 >> dir >> align >> offset;
1066 } catch(PacketError &e) {};
1068 ClientEvent *event = new ClientEvent();
1069 event->type = CE_HUDADD;
1070 event->hudadd.server_id = server_id;
1071 event->hudadd.type = type;
1072 event->hudadd.pos = new v2f(pos);
1073 event->hudadd.name = new std::string(name);
1074 event->hudadd.scale = new v2f(scale);
1075 event->hudadd.text = new std::string(text);
1076 event->hudadd.number = number;
1077 event->hudadd.item = item;
1078 event->hudadd.dir = dir;
1079 event->hudadd.align = new v2f(align);
1080 event->hudadd.offset = new v2f(offset);
1081 event->hudadd.world_pos = new v3f(world_pos);
1082 event->hudadd.size = new v2s32(size);
1083 event->hudadd.z_index = z_index;
1084 event->hudadd.text2 = new std::string(text2);
1085 m_client_event_queue.push(event);
1088 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1094 auto i = m_hud_server_to_client.find(server_id);
1095 if (i != m_hud_server_to_client.end()) {
1096 int client_id = i->second;
1097 m_hud_server_to_client.erase(i);
1099 ClientEvent *event = new ClientEvent();
1100 event->type = CE_HUDRM;
1101 event->hudrm.id = client_id;
1102 m_client_event_queue.push(event);
1106 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1116 *pkt >> server_id >> stat;
1118 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1119 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1121 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1123 else if (stat == HUD_STAT_WORLD_POS)
1125 else if (stat == HUD_STAT_SIZE )
1130 std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
1131 if (i != m_hud_server_to_client.end()) {
1132 ClientEvent *event = new ClientEvent();
1133 event->type = CE_HUDCHANGE;
1134 event->hudchange.id = i->second;
1135 event->hudchange.stat = (HudElementStat)stat;
1136 event->hudchange.v2fdata = new v2f(v2fdata);
1137 event->hudchange.v3fdata = new v3f(v3fdata);
1138 event->hudchange.sdata = new std::string(sdata);
1139 event->hudchange.data = intdata;
1140 event->hudchange.v2s32data = new v2s32(v2s32data);
1141 m_client_event_queue.push(event);
1145 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1149 *pkt >> flags >> mask;
1151 LocalPlayer *player = m_env.getLocalPlayer();
1152 assert(player != NULL);
1154 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1155 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1157 player->hud_flags &= ~mask;
1158 player->hud_flags |= flags;
1160 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1161 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1163 // Hide minimap if it has been disabled by the server
1164 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1165 // defers a minimap update, therefore only call it if really
1166 // needed, by checking that minimap was visible before
1167 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1169 // Switch to surface mode if radar disabled by server
1170 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1171 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1174 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1176 u16 param; std::string value;
1178 *pkt >> param >> value;
1180 LocalPlayer *player = m_env.getLocalPlayer();
1181 assert(player != NULL);
1183 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1184 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1185 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1186 player->hud_hotbar_itemcount = hotbar_itemcount;
1188 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1189 player->hotbar_image = value;
1191 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1192 player->hotbar_selected_image = value;
1196 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1198 if (m_proto_ver < 39) {
1199 // Handle Protocol 38 and below servers with old set_sky,
1200 // ensuring the classic look is kept.
1201 std::string datastring(pkt->getString(0), pkt->getSize());
1202 std::istringstream is(datastring, std::ios_base::binary);
1204 SkyboxParams skybox;
1205 skybox.bgcolor = video::SColor(readARGB8(is));
1206 skybox.type = std::string(deSerializeString(is));
1207 u16 count = readU16(is);
1209 for (size_t i = 0; i < count; i++)
1210 skybox.textures.emplace_back(deSerializeString(is));
1212 skybox.clouds = true;
1214 skybox.clouds = readU8(is);
1217 // Use default skybox settings:
1218 SkyboxDefaults sky_defaults;
1219 SunParams sun = sky_defaults.getSunDefaults();
1220 MoonParams moon = sky_defaults.getMoonDefaults();
1221 StarParams stars = sky_defaults.getStarDefaults();
1223 // Fix for "regular" skies, as color isn't kept:
1224 if (skybox.type == "regular") {
1225 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1226 skybox.fog_tint_type = "default";
1227 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1228 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1231 sun.visible = false;
1232 sun.sunrise_visible = false;
1233 moon.visible = false;
1234 stars.visible = false;
1237 // Skybox, sun, moon and stars ClientEvents:
1238 ClientEvent *sky_event = new ClientEvent();
1239 sky_event->type = CE_SET_SKY;
1240 sky_event->set_sky = new SkyboxParams(skybox);
1241 m_client_event_queue.push(sky_event);
1243 ClientEvent *sun_event = new ClientEvent();
1244 sun_event->type = CE_SET_SUN;
1245 sun_event->sun_params = new SunParams(sun);
1246 m_client_event_queue.push(sun_event);
1248 ClientEvent *moon_event = new ClientEvent();
1249 moon_event->type = CE_SET_MOON;
1250 moon_event->moon_params = new MoonParams(moon);
1251 m_client_event_queue.push(moon_event);
1253 ClientEvent *star_event = new ClientEvent();
1254 star_event->type = CE_SET_STARS;
1255 star_event->star_params = new StarParams(stars);
1256 m_client_event_queue.push(star_event);
1258 SkyboxParams skybox;
1260 std::string texture;
1262 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1263 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1265 if (skybox.type == "skybox") {
1266 *pkt >> texture_count;
1267 for (int i = 0; i < texture_count; i++) {
1269 skybox.textures.emplace_back(texture);
1272 else if (skybox.type == "regular") {
1273 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1274 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1275 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1276 >> skybox.sky_color.indoors;
1279 ClientEvent *event = new ClientEvent();
1280 event->type = CE_SET_SKY;
1281 event->set_sky = new SkyboxParams(skybox);
1282 m_client_event_queue.push(event);
1286 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1290 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1291 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1293 ClientEvent *event = new ClientEvent();
1294 event->type = CE_SET_SUN;
1295 event->sun_params = new SunParams(sun);
1296 m_client_event_queue.push(event);
1299 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1303 *pkt >> moon.visible >> moon.texture
1304 >> moon.tonemap >> moon.scale;
1306 ClientEvent *event = new ClientEvent();
1307 event->type = CE_SET_MOON;
1308 event->moon_params = new MoonParams(moon);
1309 m_client_event_queue.push(event);
1312 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1316 *pkt >> stars.visible >> stars.count
1317 >> stars.starcolor >> stars.scale;
1319 ClientEvent *event = new ClientEvent();
1320 event->type = CE_SET_STARS;
1321 event->star_params = new StarParams(stars);
1323 m_client_event_queue.push(event);
1326 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1329 video::SColor color_bright;
1330 video::SColor color_ambient;
1335 *pkt >> density >> color_bright >> color_ambient
1336 >> height >> thickness >> speed;
1338 ClientEvent *event = new ClientEvent();
1339 event->type = CE_CLOUD_PARAMS;
1340 event->cloud_params.density = density;
1341 // use the underlying u32 representation, because we can't
1342 // use struct members with constructors here, and this way
1343 // we avoid using new() and delete() for no good reason
1344 event->cloud_params.color_bright = color_bright.color;
1345 event->cloud_params.color_ambient = color_ambient.color;
1346 event->cloud_params.height = height;
1347 event->cloud_params.thickness = thickness;
1348 // same here: deconstruct to skip constructor
1349 event->cloud_params.speed_x = speed.X;
1350 event->cloud_params.speed_y = speed.Y;
1351 m_client_event_queue.push(event);
1354 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1357 u16 day_night_ratio_u;
1359 *pkt >> do_override >> day_night_ratio_u;
1361 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1363 ClientEvent *event = new ClientEvent();
1364 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1365 event->override_day_night_ratio.do_override = do_override;
1366 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1367 m_client_event_queue.push(event);
1370 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1372 LocalPlayer *player = m_env.getLocalPlayer();
1373 assert(player != NULL);
1375 *pkt >> player->local_animations[0];
1376 *pkt >> player->local_animations[1];
1377 *pkt >> player->local_animations[2];
1378 *pkt >> player->local_animations[3];
1379 *pkt >> player->local_animation_speed;
1382 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1384 LocalPlayer *player = m_env.getLocalPlayer();
1385 assert(player != NULL);
1387 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1390 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1394 *pkt >> type >> num_players;
1395 PlayerListModifer notice_type = (PlayerListModifer) type;
1397 for (u16 i = 0; i < num_players; i++) {
1400 switch (notice_type) {
1401 case PLAYER_LIST_INIT:
1402 case PLAYER_LIST_ADD:
1403 m_env.addPlayerName(name);
1405 case PLAYER_LIST_REMOVE:
1406 m_env.removePlayerName(name);
1412 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1414 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1415 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1416 errorstream << "Client: Received SRP S_B login message,"
1417 << " but wasn't supposed to (chosen_mech="
1418 << m_chosen_auth_mech << ")." << std::endl;
1424 SRPUser *usr = (SRPUser *) m_auth_data;
1429 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1431 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1432 (const unsigned char *) B.c_str(), B.size(),
1433 (unsigned char **) &bytes_M, &len_M);
1436 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1440 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1441 resp_pkt << std::string(bytes_M, len_M);
1445 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1447 LocalPlayer *player = m_env.getLocalPlayer();
1448 assert(player != NULL);
1450 // Store formspec in LocalPlayer
1451 *pkt >> player->formspec_prepend;
1454 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1456 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1458 // Restrictions were received -> load mods if it's enabled
1459 // Note: this should be moved after mods receptions from server instead
1463 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1469 LocalPlayer *player = m_env.getLocalPlayer();
1470 assert(player != NULL);
1471 player->addVelocity(added_vel);
1478 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1480 std::string channel_name, sender, channel_msg;
1481 *pkt >> channel_name >> sender >> channel_msg;
1483 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1484 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1485 << channel_msg << std::endl;
1487 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1488 verbosestream << "Server sent us messages on unregistered channel "
1489 << channel_name << ", ignoring." << std::endl;
1493 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1496 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1499 ModChannelSignal signal;
1500 std::string channel;
1502 *pkt >> signal_tmp >> channel;
1504 signal = (ModChannelSignal)signal_tmp;
1506 bool valid_signal = true;
1507 // @TODO: send Signal to Lua API
1509 case MODCHANNEL_SIGNAL_JOIN_OK:
1510 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1511 infostream << "Server ack our mod channel join on channel `" << channel
1512 << "`, joining." << std::endl;
1514 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1515 // Unable to join, remove channel
1516 m_modchannel_mgr->leaveChannel(channel, 0);
1517 infostream << "Server refused our mod channel join on channel `" << channel
1518 << "`" << std::endl;
1520 case MODCHANNEL_SIGNAL_LEAVE_OK:
1522 infostream << "Server ack our mod channel leave on channel " << channel
1523 << "`, leaving." << std::endl;
1526 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1527 infostream << "Server refused our mod channel leave on channel `" << channel
1528 << "`" << std::endl;
1530 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1532 // Generally unused, but ensure we don't do an implementation error
1533 infostream << "Server tells us we sent a message on channel `" << channel
1534 << "` but we are not registered. Message was dropped." << std::endl;
1537 case MODCHANNEL_SIGNAL_SET_STATE: {
1541 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1542 infostream << "Received wrong channel state " << state
1543 << ", ignoring." << std::endl;
1547 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1548 infostream << "Server sets mod channel `" << channel
1549 << "` in read-only mode." << std::endl;
1554 warningstream << "Received unhandled mod channel signal ID "
1555 << signal << ", ignoring." << std::endl;
1557 valid_signal = false;
1561 // If signal is valid, forward it to client side mods
1563 m_script->on_modchannel_signal(channel, signal);