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 "util/sha1.h"
43 #include "tileanimation.h"
45 #include "skyparams.h"
48 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
50 infostream << "Got deprecated command "
51 << toClientCommandTable[pkt->getCommand()].name << " from peer "
52 << pkt->getPeerId() << "!" << std::endl;
55 void Client::handleCommand_Hello(NetworkPacket* pkt)
57 if (pkt->getSize() < 1)
64 std::string username_legacy; // for case insensitivity
65 *pkt >> serialization_ver >> compression_mode >> proto_ver
66 >> auth_mechs >> username_legacy;
68 // Chose an auth method we support
69 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
71 infostream << "Client: TOCLIENT_HELLO received with "
72 << "serialization_ver=" << (u32)serialization_ver
73 << ", auth_mechs=" << auth_mechs
74 << ", proto_ver=" << proto_ver
75 << ", compression_mode=" << compression_mode
76 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
78 if (!ser_ver_supported(serialization_ver)) {
79 infostream << "Client: TOCLIENT_HELLO: Server sent "
80 << "unsupported ser_fmt_ver"<< std::endl;
84 m_server_ser_ver = serialization_ver;
85 m_proto_ver = proto_ver;
87 //TODO verify that username_legacy matches sent username, only
88 // differs in casing (make both uppercase and compare)
89 // This is only necessary though when we actually want to add casing support
91 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
92 // we received a TOCLIENT_HELLO while auth was already going on
93 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
94 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
95 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
96 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
97 srp_user_delete((SRPUser *) m_auth_data);
102 // Authenticate using that method, or abort if there wasn't any method found
103 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
104 bool is_register = chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP;
105 ELoginRegister mode = is_register ? ELoginRegister::Register : ELoginRegister::Login;
106 if (m_allow_login_or_register != ELoginRegister::Any &&
107 m_allow_login_or_register != mode) {
108 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
109 m_access_denied = true;
110 if (m_allow_login_or_register == ELoginRegister::Login) {
111 m_access_denied_reason =
112 gettext("Name is not registered. To create an account on this server, click 'Register'");
114 m_access_denied_reason =
115 gettext("Name is taken. Please choose another name");
119 startAuth(chosen_auth_mechanism);
122 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
123 m_access_denied = true;
124 m_access_denied_reason = "Unknown";
130 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
135 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
136 >> m_sudo_auth_methods;
138 playerpos -= v3f(0, BS / 2, 0);
140 // Set player position
141 LocalPlayer *player = m_env.getLocalPlayer();
142 assert(player != NULL);
143 player->setPosition(playerpos);
145 infostream << "Client: received map seed: " << m_map_seed << std::endl;
146 infostream << "Client: received recommended send interval "
147 << m_recommended_send_interval<<std::endl;
150 /*~ DO NOT TRANSLATE THIS LITERALLY!
151 This is a special string which needs to contain the translation's
152 language code (e.g. "de" for German). */
153 std::string lang = gettext("LANG_CODE");
154 if (lang == "LANG_CODE")
157 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
163 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
167 m_password = m_new_password;
169 verbosestream << "Client: Received TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
171 // send packet to actually set the password
172 startAuth(AUTH_MECHANISM_FIRST_SRP);
175 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
177 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
179 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
180 L"Password change denied. Password NOT changed.");
181 pushToChatQueue(chatMessage);
182 // reset everything and be sad
186 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
188 // The server didn't like our password. Note, this needs
189 // to be processed even if the serialization format has
190 // not been agreed yet, the same as TOCLIENT_INIT.
191 m_access_denied = true;
192 m_access_denied_reason = "Unknown";
194 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
195 // Legacy code from 0.4.12 and older but is still used
196 // in some places of the server code
197 if (pkt->getSize() >= 2) {
198 std::wstring wide_reason;
200 m_access_denied_reason = wide_to_utf8(wide_reason);
205 if (pkt->getSize() < 1)
211 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
212 denyCode == SERVER_ACCESSDENIED_CRASH) {
213 *pkt >> m_access_denied_reason;
214 if (m_access_denied_reason.empty())
215 m_access_denied_reason = accessDeniedStrings[denyCode];
218 m_access_denied_reconnect = reconnect & 1;
219 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
220 *pkt >> m_access_denied_reason;
221 } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) {
222 m_access_denied_reason = accessDeniedStrings[denyCode];
223 m_access_denied_reconnect = true;
224 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
225 m_access_denied_reason = accessDeniedStrings[denyCode];
227 // Allow us to add new error messages to the
228 // protocol without raising the protocol version, if we want to.
229 // Until then (which may be never), this is outside
230 // of the defined protocol.
231 *pkt >> m_access_denied_reason;
232 if (m_access_denied_reason.empty())
233 m_access_denied_reason = "Unknown";
237 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
239 if (pkt->getSize() < 6)
247 void Client::handleCommand_AddNode(NetworkPacket* pkt)
249 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
256 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
258 bool remove_metadata = true;
259 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
260 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
261 remove_metadata = false;
264 addNode(p, n, remove_metadata);
267 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
269 if (pkt->getSize() < 1)
272 std::istringstream is(pkt->readLongString(), std::ios::binary);
273 std::stringstream sstr(std::ios::binary | std::ios::in | std::ios::out);
274 decompressZlib(is, sstr);
276 NodeMetadataList meta_updates_list(false);
277 meta_updates_list.deSerialize(sstr, m_itemdef, true);
279 Map &map = m_env.getMap();
280 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
281 i != meta_updates_list.end(); ++i) {
282 v3s16 pos = i->first;
284 if (map.isValidPosition(pos) &&
285 map.setNodeMetadata(pos, i->second))
286 continue; // Prevent from deleting metadata
288 // Meta couldn't be set, unused metadata
293 void Client::handleCommand_BlockData(NetworkPacket* pkt)
295 // Ignore too small packet
296 if (pkt->getSize() < 6)
302 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
303 std::istringstream istr(datastring, std::ios_base::binary);
309 sector = m_env.getMap().emergeSector(p2d);
311 assert(sector->getPos() == p2d);
313 block = sector->getBlockNoCreateNoEx(p.Y);
316 Update an existing block
318 block->deSerialize(istr, m_server_ser_ver, false);
319 block->deSerializeNetworkSpecific(istr);
325 block = sector->createBlankBlock(p.Y);
326 block->deSerialize(istr, m_server_ser_ver, false);
327 block->deSerializeNetworkSpecific(istr);
331 ServerMap::saveBlock(block, m_localdb);
335 Add it to mesh update queue and set it to be acknowledged after update.
337 addUpdateMeshTaskWithEdge(p, true);
340 void Client::handleCommand_Inventory(NetworkPacket* pkt)
342 if (pkt->getSize() < 1)
345 std::string datastring(pkt->getString(0), pkt->getSize());
346 std::istringstream is(datastring, std::ios_base::binary);
348 LocalPlayer *player = m_env.getLocalPlayer();
349 assert(player != NULL);
351 player->inventory.deSerialize(is);
353 m_update_wielded_item = true;
355 delete m_inventory_from_server;
356 m_inventory_from_server = new Inventory(player->inventory);
357 m_inventory_from_server_age = 0.0;
360 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
362 if (pkt->getSize() < 2)
369 time_of_day = time_of_day % 24000;
370 float time_speed = 0;
372 if (pkt->getSize() >= 2 + 4) {
376 // Old message; try to approximate speed of time by ourselves
377 float time_of_day_f = (float)time_of_day / 24000.0f;
378 float tod_diff_f = 0;
380 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
381 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
383 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
385 m_last_time_of_day_f = time_of_day_f;
386 float time_diff = m_time_of_day_update_timer;
387 m_time_of_day_update_timer = 0;
389 if (m_time_of_day_set) {
390 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
391 infostream << "Client: Measured time_of_day speed (old format): "
392 << time_speed << " tod_diff_f=" << tod_diff_f
393 << " time_diff=" << time_diff << std::endl;
397 // Update environment
398 m_env.setTimeOfDay(time_of_day);
399 m_env.setTimeOfDaySpeed(time_speed);
400 m_time_of_day_set = true;
402 //u32 dr = m_env.getDayNightRatio();
403 //infostream << "Client: time_of_day=" << time_of_day
404 // << " time_speed=" << time_speed
405 // << " dr=" << dr << std::endl;
408 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
413 u16 sendername length
419 ChatMessage *chatMessage = new ChatMessage();
420 u8 version, message_type;
421 *pkt >> version >> message_type;
423 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
429 *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
430 chatMessage->timestamp = static_cast<std::time_t>(timestamp);
432 chatMessage->type = (ChatMessageType) message_type;
434 // @TODO send this to CSM using ChatMessage object
435 if (modsLoaded() && m_script->on_receiving_message(
436 wide_to_utf8(chatMessage->message))) {
437 // Message was consumed by CSM and should not be handled by client
440 pushToChatQueue(chatMessage);
444 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
447 u16 count of removed objects
448 for all removed objects {
451 u16 count of added objects
452 for all added objects {
455 u32 initialization data length
456 string initialization data
462 u16 removed_count, added_count, id;
464 // Read removed objects
465 *pkt >> removed_count;
467 for (u16 i = 0; i < removed_count; i++) {
469 m_env.removeActiveObject(id);
472 // Read added objects
475 for (u16 i = 0; i < added_count; i++) {
477 m_env.addActiveObject(id, type, pkt->readLongString());
479 } catch (PacketError &e) {
480 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
481 << ". The packet is unreliable, ignoring" << std::endl;
484 // m_activeobjects_received is false before the first
485 // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
486 m_activeobjects_received = true;
489 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
499 std::string datastring(pkt->getString(0), pkt->getSize());
500 std::istringstream is(datastring, std::ios_base::binary);
504 u16 id = readU16(is);
508 std::string message = deSerializeString16(is);
510 // Pass on to the environment
511 m_env.processActiveObjectMessage(id, message);
513 } catch (SerializationError &e) {
514 errorstream << "Client::handleCommand_ActiveObjectMessages: "
515 << "caught SerializationError: " << e.what() << std::endl;
519 void Client::handleCommand_Movement(NetworkPacket* pkt)
521 LocalPlayer *player = m_env.getLocalPlayer();
522 assert(player != NULL);
524 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
526 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
527 >> lf >> lfs >> ls >> g;
529 player->movement_acceleration_default = mad * BS;
530 player->movement_acceleration_air = maa * BS;
531 player->movement_acceleration_fast = maf * BS;
532 player->movement_speed_walk = msw * BS;
533 player->movement_speed_crouch = mscr * BS;
534 player->movement_speed_fast = msf * BS;
535 player->movement_speed_climb = mscl * BS;
536 player->movement_speed_jump = msj * BS;
537 player->movement_liquid_fluidity = lf * BS;
538 player->movement_liquid_fluidity_smooth = lfs * BS;
539 player->movement_liquid_sink = ls * BS;
540 player->movement_gravity = g * BS;
543 void Client::handleCommand_Fov(NetworkPacket *pkt)
546 bool is_multiplier = false;
547 f32 transition_time = 0.0f;
549 *pkt >> fov >> is_multiplier;
551 // Wrap transition_time extraction within a
552 // try-catch to preserve backwards compat
554 *pkt >> transition_time;
555 } catch (PacketError &e) {};
557 LocalPlayer *player = m_env.getLocalPlayer();
559 player->setFov({ fov, is_multiplier, transition_time });
560 m_camera->notifyFovChange();
563 void Client::handleCommand_HP(NetworkPacket *pkt)
565 LocalPlayer *player = m_env.getLocalPlayer();
566 assert(player != NULL);
568 u16 oldhp = player->hp;
572 bool damage_effect = true;
574 *pkt >> damage_effect;
575 } catch (PacketError &e) {};
580 m_script->on_hp_modification(hp);
583 // Add to ClientEvent queue
584 ClientEvent *event = new ClientEvent();
585 event->type = CE_PLAYER_DAMAGE;
586 event->player_damage.amount = oldhp - hp;
587 event->player_damage.effect = damage_effect;
588 m_client_event_queue.push(event);
592 void Client::handleCommand_Breath(NetworkPacket* pkt)
594 LocalPlayer *player = m_env.getLocalPlayer();
595 assert(player != NULL);
601 player->setBreath(breath);
604 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
606 LocalPlayer *player = m_env.getLocalPlayer();
607 assert(player != NULL);
612 *pkt >> pos >> pitch >> yaw;
614 player->setPosition(pos);
616 infostream << "Client got TOCLIENT_MOVE_PLAYER"
617 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
618 << " pitch=" << pitch
623 Add to ClientEvent queue.
624 This has to be sent to the main program because otherwise
625 it would just force the pitch and yaw values to whatever
626 the camera points to.
628 ClientEvent *event = new ClientEvent();
629 event->type = CE_PLAYER_FORCE_MOVE;
630 event->player_force_move.pitch = pitch;
631 event->player_force_move.yaw = yaw;
632 m_client_event_queue.push(event);
635 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
637 bool set_camera_point_target;
638 v3f camera_point_target;
640 *pkt >> set_camera_point_target;
641 *pkt >> camera_point_target;
643 ClientEvent *event = new ClientEvent();
644 event->type = CE_DEATHSCREEN;
645 event->deathscreen.set_camera_point_target = set_camera_point_target;
646 event->deathscreen.camera_point_target_x = camera_point_target.X;
647 event->deathscreen.camera_point_target_y = camera_point_target.Y;
648 event->deathscreen.camera_point_target_z = camera_point_target.Z;
649 m_client_event_queue.push(event);
652 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
658 infostream << "Client: Received media announcement: packet size: "
659 << pkt->getSize() << std::endl;
661 if (m_media_downloader == NULL ||
662 m_media_downloader->isStarted()) {
663 const char *problem = m_media_downloader ?
664 "we already saw another announcement" :
665 "all media has been received already";
666 errorstream << "Client: Received media announcement but "
668 << " files=" << num_files
669 << " size=" << pkt->getSize() << std::endl;
673 // Mesh update thread must be stopped while
674 // updating content definitions
675 sanity_check(!m_mesh_update_manager.isRunning());
677 for (u16 i = 0; i < num_files; i++) {
678 std::string name, sha1_base64;
680 *pkt >> name >> sha1_base64;
682 std::string sha1_raw = base64_decode(sha1_base64);
683 m_media_downloader->addFile(name, sha1_raw);
691 while (!sf.at_end()) {
692 std::string baseurl = trim(sf.next(","));
693 if (!baseurl.empty()) {
694 m_remote_media_servers.emplace_back(baseurl);
695 m_media_downloader->addRemoteServer(baseurl);
700 m_media_downloader->step(this);
703 void Client::handleCommand_Media(NetworkPacket* pkt)
707 u16 total number of file bunches
708 u16 index of this bunch
709 u32 number of files in this bunch
721 *pkt >> num_bunches >> bunch_i >> num_files;
723 infostream << "Client: Received files: bunch " << bunch_i << "/"
724 << num_bunches << " files=" << num_files
725 << " size=" << pkt->getSize() << std::endl;
730 bool init_phase = m_media_downloader && m_media_downloader->isStarted();
733 // Mesh update thread must be stopped while
734 // updating content definitions
735 sanity_check(!m_mesh_update_manager.isRunning());
738 for (u32 i = 0; i < num_files; i++) {
739 std::string name, data;
742 data = pkt->readLongString();
746 ok = m_media_downloader->conventionalTransferDone(name, data, this);
748 // Check pending dynamic transfers, one of them must be it
749 for (const auto &it : m_pending_media_downloads) {
750 if (it.second->conventionalTransferDone(name, data, this)) {
757 errorstream << "Client: Received media \"" << name
758 << "\" but no downloads pending. " << num_bunches << " bunches, "
759 << num_files << " in this one. (init_phase=" << init_phase
765 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
767 infostream << "Client: Received node definitions: packet size: "
768 << pkt->getSize() << std::endl;
770 // Mesh update thread must be stopped while
771 // updating content definitions
772 sanity_check(!m_mesh_update_manager.isRunning());
774 // Decompress node definitions
775 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
776 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
777 decompressZlib(tmp_is, tmp_os);
779 // Deserialize node definitions
780 m_nodedef->deSerialize(tmp_os, m_proto_ver);
781 m_nodedef_received = true;
784 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
786 infostream << "Client: Received item definitions: packet size: "
787 << pkt->getSize() << std::endl;
789 // Mesh update thread must be stopped while
790 // updating content definitions
791 sanity_check(!m_mesh_update_manager.isRunning());
793 // Decompress item definitions
794 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
795 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
796 decompressZlib(tmp_is, tmp_os);
798 // Deserialize node definitions
799 m_itemdef->deSerialize(tmp_os, m_proto_ver);
800 m_itemdef_received = true;
803 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
811 [11 + len] (f32 * 3) pos
812 [23 + len] u16 object_id
816 [34 + len] bool ephemeral
821 SimpleSoundSpec spec;
822 SoundLocation type; // 0=local, 1=positional, 2=object
825 bool ephemeral = false;
827 *pkt >> server_id >> spec.name >> spec.gain >> (u8 &)type >> pos >> object_id >> spec.loop;
833 } catch (PacketError &e) {};
838 case SoundLocation::Local:
839 client_id = m_sound->playSound(spec);
841 case SoundLocation::Position:
842 client_id = m_sound->playSoundAt(spec, pos);
844 case SoundLocation::Object:
846 ClientActiveObject *cao = m_env.getActiveObject(object_id);
848 pos = cao->getPosition();
849 client_id = m_sound->playSoundAt(spec, pos);
854 if (client_id != -1) {
855 // for ephemeral sounds, server_id is not meaningful
857 m_sounds_server_to_client[server_id] = client_id;
858 m_sounds_client_to_server[client_id] = server_id;
861 m_sounds_to_objects[client_id] = object_id;
865 void Client::handleCommand_StopSound(NetworkPacket* pkt)
871 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
872 if (i != m_sounds_server_to_client.end()) {
873 int client_id = i->second;
874 m_sound->stopSound(client_id);
878 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
884 *pkt >> sound_id >> step >> gain;
886 std::unordered_map<s32, int>::const_iterator i =
887 m_sounds_server_to_client.find(sound_id);
889 if (i != m_sounds_server_to_client.end())
890 m_sound->fadeSound(i->second, step, gain);
893 void Client::handleCommand_Privileges(NetworkPacket* pkt)
895 m_privileges.clear();
896 infostream << "Client: Privileges updated: ";
899 *pkt >> num_privileges;
901 for (u16 i = 0; i < num_privileges; i++) {
906 m_privileges.insert(priv);
907 infostream << priv << " ";
909 infostream << std::endl;
912 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
914 LocalPlayer *player = m_env.getLocalPlayer();
915 assert(player != NULL);
917 // Store formspec in LocalPlayer
918 player->inventory_formspec = pkt->readLongString();
921 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
924 bool keep_inv = true;
925 *pkt >> name >> keep_inv;
927 infostream << "Client: Detached inventory update: \"" << name
928 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
930 const auto &inv_it = m_detached_inventories.find(name);
932 if (inv_it != m_detached_inventories.end()) {
933 delete inv_it->second;
934 m_detached_inventories.erase(inv_it);
938 Inventory *inv = nullptr;
939 if (inv_it == m_detached_inventories.end()) {
940 inv = new Inventory(m_itemdef);
941 m_detached_inventories[name] = inv;
943 inv = inv_it->second;
947 *pkt >> ignore; // this used to be the length of the following string, ignore it
949 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
950 std::istringstream is(contents, std::ios::binary);
951 inv->deSerialize(is);
954 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
956 std::string formspec = pkt->readLongString();
957 std::string formname;
961 ClientEvent *event = new ClientEvent();
962 event->type = CE_SHOW_FORMSPEC;
963 // pointer is required as event is a struct only!
964 // adding a std:string to a struct isn't possible
965 event->show_formspec.formspec = new std::string(formspec);
966 event->show_formspec.formname = new std::string(formname);
967 m_client_event_queue.push(event);
970 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
972 std::string datastring(pkt->getString(0), pkt->getSize());
973 std::istringstream is(datastring, std::ios_base::binary);
975 ParticleParameters p;
976 p.deSerialize(is, m_proto_ver);
978 ClientEvent *event = new ClientEvent();
979 event->type = CE_SPAWN_PARTICLE;
980 event->spawn_particle = new ParticleParameters(p);
982 m_client_event_queue.push(event);
985 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
987 std::string datastring(pkt->getString(0), pkt->getSize());
988 std::istringstream is(datastring, std::ios_base::binary);
990 ParticleSpawnerParameters p;
994 p.amount = readU16(is);
995 p.time = readF32(is);
997 // older protocols do not support tweening, and send only
998 // static ranges, so we can't just use the normal serialization
999 // functions for the older values.
1000 p.pos.start.legacyDeSerialize(is);
1001 p.vel.start.legacyDeSerialize(is);
1002 p.acc.start.legacyDeSerialize(is);
1003 p.exptime.start.legacyDeSerialize(is);
1004 p.size.start.legacyDeSerialize(is);
1006 p.collisiondetection = readU8(is);
1007 p.texture.string = deSerializeString32(is);
1009 server_id = readU32(is);
1011 p.vertical = readU8(is);
1012 p.collision_removal = readU8(is);
1014 attached_id = readU16(is);
1016 p.animation.deSerialize(is, m_proto_ver);
1017 p.glow = readU8(is);
1018 p.object_collision = readU8(is);
1020 bool legacy_format = true;
1022 // This is kinda awful
1024 u16 tmp_param0 = readU16(is);
1027 p.node.param0 = tmp_param0;
1028 p.node.param2 = readU8(is);
1029 p.node_tile = readU8(is);
1032 f32 tmp_sbias = readF32(is);
1036 // initial bias must be stored separately in the stream to preserve
1037 // backwards compatibility with older clients, which do not support
1038 // a bias field in their range "format"
1039 p.pos.start.bias = tmp_sbias;
1040 p.vel.start.bias = readF32(is);
1041 p.acc.start.bias = readF32(is);
1042 p.exptime.start.bias = readF32(is);
1043 p.size.start.bias = readF32(is);
1045 p.pos.end.deSerialize(is);
1046 p.vel.end.deSerialize(is);
1047 p.acc.end.deSerialize(is);
1048 p.exptime.end.deSerialize(is);
1049 p.size.end.deSerialize(is);
1051 // properties for legacy texture field
1052 p.texture.deSerialize(is, m_proto_ver, true);
1054 p.drag.deSerialize(is);
1055 p.jitter.deSerialize(is);
1056 p.bounce.deSerialize(is);
1057 ParticleParamTypes::deSerializeParameterValue(is, p.attractor_kind);
1058 using ParticleParamTypes::AttractorKind;
1059 if (p.attractor_kind != AttractorKind::none) {
1060 p.attract.deSerialize(is);
1061 p.attractor_origin.deSerialize(is);
1062 p.attractor_attachment = readU16(is);
1063 /* we only check the first bit, in order to allow this value
1064 * to be turned into a bit flag field later if needed */
1065 p.attractor_kill = !!(readU8(is) & 1);
1066 if (p.attractor_kind != AttractorKind::point) {
1067 p.attractor_direction.deSerialize(is);
1068 p.attractor_direction_attachment = readU16(is);
1071 p.radius.deSerialize(is);
1073 u16 texpoolsz = readU16(is);
1074 p.texpool.reserve(texpoolsz);
1075 for (u16 i = 0; i < texpoolsz; ++i) {
1076 ServerParticleTexture newtex;
1077 newtex.deSerialize(is, m_proto_ver);
1078 p.texpool.push_back(newtex);
1081 legacy_format = false;
1084 if (legacy_format) {
1085 // there's no tweening data to be had, so we need to set the
1086 // legacy params to constant values, otherwise everything old
1087 // will tween to zero
1088 p.pos.end = p.pos.start;
1089 p.vel.end = p.vel.start;
1090 p.acc.end = p.acc.start;
1091 p.exptime.end = p.exptime.start;
1092 p.size.end = p.size.start;
1095 auto event = new ClientEvent();
1096 event->type = CE_ADD_PARTICLESPAWNER;
1097 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1098 event->add_particlespawner.attached_id = attached_id;
1099 event->add_particlespawner.id = server_id;
1101 m_client_event_queue.push(event);
1105 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1110 ClientEvent *event = new ClientEvent();
1111 event->type = CE_DELETE_PARTICLESPAWNER;
1112 event->delete_particlespawner.id = server_id;
1114 m_client_event_queue.push(event);
1117 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1136 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1137 >> dir >> align >> offset;
1144 } catch(PacketError &e) {};
1146 ClientEvent *event = new ClientEvent();
1147 event->type = CE_HUDADD;
1148 event->hudadd = new ClientEventHudAdd();
1149 event->hudadd->server_id = server_id;
1150 event->hudadd->type = type;
1151 event->hudadd->pos = pos;
1152 event->hudadd->name = name;
1153 event->hudadd->scale = scale;
1154 event->hudadd->text = text;
1155 event->hudadd->number = number;
1156 event->hudadd->item = item;
1157 event->hudadd->dir = dir;
1158 event->hudadd->align = align;
1159 event->hudadd->offset = offset;
1160 event->hudadd->world_pos = world_pos;
1161 event->hudadd->size = size;
1162 event->hudadd->z_index = z_index;
1163 event->hudadd->text2 = text2;
1164 event->hudadd->style = style;
1165 m_client_event_queue.push(event);
1168 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1174 ClientEvent *event = new ClientEvent();
1175 event->type = CE_HUDRM;
1176 event->hudrm.id = server_id;
1177 m_client_event_queue.push(event);
1180 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1190 *pkt >> server_id >> stat;
1192 // Keep in sync with:server.cpp -> SendHUDChange
1193 switch ((HudElementStat)stat) {
1195 case HUD_STAT_SCALE:
1196 case HUD_STAT_ALIGN:
1197 case HUD_STAT_OFFSET:
1202 case HUD_STAT_TEXT2:
1205 case HUD_STAT_WORLD_POS:
1216 ClientEvent *event = new ClientEvent();
1217 event->type = CE_HUDCHANGE;
1218 event->hudchange = new ClientEventHudChange();
1219 event->hudchange->id = server_id;
1220 event->hudchange->stat = static_cast<HudElementStat>(stat);
1221 event->hudchange->v2fdata = v2fdata;
1222 event->hudchange->v3fdata = v3fdata;
1223 event->hudchange->sdata = sdata;
1224 event->hudchange->data = intdata;
1225 event->hudchange->v2s32data = v2s32data;
1226 m_client_event_queue.push(event);
1229 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1233 *pkt >> flags >> mask;
1235 LocalPlayer *player = m_env.getLocalPlayer();
1236 assert(player != NULL);
1238 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1239 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1241 player->hud_flags &= ~mask;
1242 player->hud_flags |= flags;
1244 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1245 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1247 // Not so satisying code to keep compatibility with old fixed mode system
1250 // Hide minimap if it has been disabled by the server
1251 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1252 // defers a minimap update, therefore only call it if really
1253 // needed, by checking that minimap was visible before
1254 m_minimap->setModeIndex(0);
1256 // If radar has been disabled, try to find a non radar mode or fall back to 0
1257 if (m_minimap && m_minimap_radar_disabled_by_server
1258 && was_minimap_radar_visible) {
1259 while (m_minimap->getModeIndex() > 0 &&
1260 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1261 m_minimap->nextMode();
1264 // End of 'not so satifying code'
1267 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1269 u16 param; std::string value;
1271 *pkt >> param >> value;
1273 LocalPlayer *player = m_env.getLocalPlayer();
1274 assert(player != NULL);
1276 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1277 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1278 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1279 player->hud_hotbar_itemcount = hotbar_itemcount;
1281 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1282 player->hotbar_image = value;
1284 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1285 player->hotbar_selected_image = value;
1289 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1291 if (m_proto_ver < 39) {
1292 // Handle Protocol 38 and below servers with old set_sky,
1293 // ensuring the classic look is kept.
1294 std::string datastring(pkt->getString(0), pkt->getSize());
1295 std::istringstream is(datastring, std::ios_base::binary);
1297 SkyboxParams skybox;
1298 skybox.bgcolor = video::SColor(readARGB8(is));
1299 skybox.type = std::string(deSerializeString16(is));
1300 u16 count = readU16(is);
1302 for (size_t i = 0; i < count; i++)
1303 skybox.textures.emplace_back(deSerializeString16(is));
1305 skybox.clouds = true;
1307 skybox.clouds = readU8(is);
1310 // Use default skybox settings:
1311 SunParams sun = SkyboxDefaults::getSunDefaults();
1312 MoonParams moon = SkyboxDefaults::getMoonDefaults();
1313 StarParams stars = SkyboxDefaults::getStarDefaults();
1315 // Fix for "regular" skies, as color isn't kept:
1316 if (skybox.type == "regular") {
1317 skybox.sky_color = SkyboxDefaults::getSkyColorDefaults();
1318 skybox.fog_tint_type = "default";
1319 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1320 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1322 sun.visible = false;
1323 sun.sunrise_visible = false;
1324 moon.visible = false;
1325 stars.visible = false;
1328 // Skybox, sun, moon and stars ClientEvents:
1329 ClientEvent *sky_event = new ClientEvent();
1330 sky_event->type = CE_SET_SKY;
1331 sky_event->set_sky = new SkyboxParams(skybox);
1332 m_client_event_queue.push(sky_event);
1334 ClientEvent *sun_event = new ClientEvent();
1335 sun_event->type = CE_SET_SUN;
1336 sun_event->sun_params = new SunParams(sun);
1337 m_client_event_queue.push(sun_event);
1339 ClientEvent *moon_event = new ClientEvent();
1340 moon_event->type = CE_SET_MOON;
1341 moon_event->moon_params = new MoonParams(moon);
1342 m_client_event_queue.push(moon_event);
1344 ClientEvent *star_event = new ClientEvent();
1345 star_event->type = CE_SET_STARS;
1346 star_event->star_params = new StarParams(stars);
1347 m_client_event_queue.push(star_event);
1349 SkyboxParams skybox;
1351 std::string texture;
1353 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1354 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1356 if (skybox.type == "skybox") {
1357 *pkt >> texture_count;
1358 for (int i = 0; i < texture_count; i++) {
1360 skybox.textures.emplace_back(texture);
1363 else if (skybox.type == "regular") {
1364 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1365 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1366 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1367 >> skybox.sky_color.indoors;
1370 ClientEvent *event = new ClientEvent();
1371 event->type = CE_SET_SKY;
1372 event->set_sky = new SkyboxParams(skybox);
1373 m_client_event_queue.push(event);
1377 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1381 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1382 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1384 ClientEvent *event = new ClientEvent();
1385 event->type = CE_SET_SUN;
1386 event->sun_params = new SunParams(sun);
1387 m_client_event_queue.push(event);
1390 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1394 *pkt >> moon.visible >> moon.texture
1395 >> moon.tonemap >> moon.scale;
1397 ClientEvent *event = new ClientEvent();
1398 event->type = CE_SET_MOON;
1399 event->moon_params = new MoonParams(moon);
1400 m_client_event_queue.push(event);
1403 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1405 StarParams stars = SkyboxDefaults::getStarDefaults();
1407 *pkt >> stars.visible >> stars.count
1408 >> stars.starcolor >> stars.scale;
1410 *pkt >> stars.day_opacity;
1411 } catch (PacketError &e) {};
1413 ClientEvent *event = new ClientEvent();
1414 event->type = CE_SET_STARS;
1415 event->star_params = new StarParams(stars);
1417 m_client_event_queue.push(event);
1420 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1423 video::SColor color_bright;
1424 video::SColor color_ambient;
1429 *pkt >> density >> color_bright >> color_ambient
1430 >> height >> thickness >> speed;
1432 ClientEvent *event = new ClientEvent();
1433 event->type = CE_CLOUD_PARAMS;
1434 event->cloud_params.density = density;
1435 // use the underlying u32 representation, because we can't
1436 // use struct members with constructors here, and this way
1437 // we avoid using new() and delete() for no good reason
1438 event->cloud_params.color_bright = color_bright.color;
1439 event->cloud_params.color_ambient = color_ambient.color;
1440 event->cloud_params.height = height;
1441 event->cloud_params.thickness = thickness;
1442 // same here: deconstruct to skip constructor
1443 event->cloud_params.speed_x = speed.X;
1444 event->cloud_params.speed_y = speed.Y;
1445 m_client_event_queue.push(event);
1448 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1451 u16 day_night_ratio_u;
1453 *pkt >> do_override >> day_night_ratio_u;
1455 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1457 ClientEvent *event = new ClientEvent();
1458 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1459 event->override_day_night_ratio.do_override = do_override;
1460 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1461 m_client_event_queue.push(event);
1464 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1466 LocalPlayer *player = m_env.getLocalPlayer();
1467 assert(player != NULL);
1469 *pkt >> player->local_animations[0];
1470 *pkt >> player->local_animations[1];
1471 *pkt >> player->local_animations[2];
1472 *pkt >> player->local_animations[3];
1473 *pkt >> player->local_animation_speed;
1475 player->last_animation = -1;
1478 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1480 LocalPlayer *player = m_env.getLocalPlayer();
1481 assert(player != NULL);
1483 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1486 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1490 *pkt >> type >> num_players;
1491 PlayerListModifer notice_type = (PlayerListModifer) type;
1493 for (u16 i = 0; i < num_players; i++) {
1496 switch (notice_type) {
1497 case PLAYER_LIST_INIT:
1498 case PLAYER_LIST_ADD:
1499 m_env.addPlayerName(name);
1501 case PLAYER_LIST_REMOVE:
1502 m_env.removePlayerName(name);
1508 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1510 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1511 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1512 errorstream << "Client: Received SRP S_B login message,"
1513 << " but wasn't supposed to (chosen_mech="
1514 << m_chosen_auth_mech << ")." << std::endl;
1520 SRPUser *usr = (SRPUser *) m_auth_data;
1525 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1527 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1528 (const unsigned char *) B.c_str(), B.size(),
1529 (unsigned char **) &bytes_M, &len_M);
1532 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1536 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1537 resp_pkt << std::string(bytes_M, len_M);
1541 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1543 LocalPlayer *player = m_env.getLocalPlayer();
1544 assert(player != NULL);
1546 // Store formspec in LocalPlayer
1547 *pkt >> player->formspec_prepend;
1550 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1552 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1554 // Restrictions were received -> load mods if it's enabled
1555 // Note: this should be moved after mods receptions from server instead
1559 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1565 LocalPlayer *player = m_env.getLocalPlayer();
1566 assert(player != NULL);
1567 player->addVelocity(added_vel);
1570 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1572 std::string raw_hash, filename, filedata;
1576 *pkt >> raw_hash >> filename >> cached;
1577 if (m_proto_ver >= 40)
1580 filedata = pkt->readLongString();
1582 if (raw_hash.size() != 20 || filename.empty() ||
1583 (m_proto_ver < 40 && filedata.empty()) ||
1584 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1585 throw PacketError("Illegal filename, data or hash");
1588 verbosestream << "Server pushes media file \"" << filename << "\" ";
1589 if (filedata.empty())
1590 verbosestream << "to be fetched ";
1592 verbosestream << "with " << filedata.size() << " bytes ";
1593 verbosestream << "(cached=" << cached << ")" << std::endl;
1595 if (m_media_pushed_files.count(filename) != 0) {
1596 // Ignore (but acknowledge). Previously this was for sync purposes,
1597 // but even in new versions media cannot be replaced at runtime.
1598 if (m_proto_ver >= 40)
1599 sendHaveMedia({ token });
1603 if (!filedata.empty()) {
1605 // Compute and check checksum of data
1606 std::string computed_hash;
1609 ctx.addBytes(filedata.c_str(), filedata.size());
1610 unsigned char *buf = ctx.getDigest();
1611 computed_hash.assign((char*) buf, 20);
1614 if (raw_hash != computed_hash) {
1615 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1619 // Actually load media
1620 loadMedia(filedata, filename, true);
1621 m_media_pushed_files.insert(filename);
1623 // Cache file for the next time when this client joins the same server
1625 clientMediaUpdateCache(raw_hash, filedata);
1629 m_media_pushed_files.insert(filename);
1631 // create a downloader for this file
1632 auto downloader(std::make_shared<SingleMediaDownloader>(cached));
1633 m_pending_media_downloads.emplace_back(token, downloader);
1634 downloader->addFile(filename, raw_hash);
1635 for (const auto &baseurl : m_remote_media_servers)
1636 downloader->addRemoteServer(baseurl);
1638 downloader->step(this);
1645 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1647 std::string channel_name, sender, channel_msg;
1648 *pkt >> channel_name >> sender >> channel_msg;
1650 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1651 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1652 << channel_msg << std::endl;
1654 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1655 verbosestream << "Server sent us messages on unregistered channel "
1656 << channel_name << ", ignoring." << std::endl;
1660 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1663 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1666 ModChannelSignal signal;
1667 std::string channel;
1669 *pkt >> signal_tmp >> channel;
1671 signal = (ModChannelSignal)signal_tmp;
1673 bool valid_signal = true;
1674 // @TODO: send Signal to Lua API
1676 case MODCHANNEL_SIGNAL_JOIN_OK:
1677 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1678 infostream << "Server ack our mod channel join on channel `" << channel
1679 << "`, joining." << std::endl;
1681 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1682 // Unable to join, remove channel
1683 m_modchannel_mgr->leaveChannel(channel, 0);
1684 infostream << "Server refused our mod channel join on channel `" << channel
1685 << "`" << std::endl;
1687 case MODCHANNEL_SIGNAL_LEAVE_OK:
1689 infostream << "Server ack our mod channel leave on channel " << channel
1690 << "`, leaving." << std::endl;
1693 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1694 infostream << "Server refused our mod channel leave on channel `" << channel
1695 << "`" << std::endl;
1697 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1699 // Generally unused, but ensure we don't do an implementation error
1700 infostream << "Server tells us we sent a message on channel `" << channel
1701 << "` but we are not registered. Message was dropped." << std::endl;
1704 case MODCHANNEL_SIGNAL_SET_STATE: {
1708 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1709 infostream << "Received wrong channel state " << state
1710 << ", ignoring." << std::endl;
1714 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1715 infostream << "Server sets mod channel `" << channel
1716 << "` in read-only mode." << std::endl;
1721 warningstream << "Received unhandled mod channel signal ID "
1722 << signal << ", ignoring." << std::endl;
1724 valid_signal = false;
1728 // If signal is valid, forward it to client side mods
1730 m_script->on_modchannel_signal(channel, signal);
1733 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1736 u16 mode; // wanted current mode index after change
1738 *pkt >> count >> mode;
1741 m_minimap->clearModes();
1743 for (size_t index = 0; index < count; index++) {
1747 std::string texture;
1750 *pkt >> type >> label >> size >> texture >> scale;
1753 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1757 m_minimap->setModeIndex(mode);
1760 void Client::handleCommand_SetLighting(NetworkPacket *pkt)
1762 Lighting& lighting = m_env.getLocalPlayer()->getLighting();
1764 if (pkt->getRemainingBytes() >= 4)
1765 *pkt >> lighting.shadow_intensity;
1766 if (pkt->getRemainingBytes() >= 4)
1767 *pkt >> lighting.saturation;
1768 if (pkt->getRemainingBytes() >= 24) {
1769 *pkt >> lighting.exposure.luminance_min
1770 >> lighting.exposure.luminance_max
1771 >> lighting.exposure.exposure_correction
1772 >> lighting.exposure.speed_dark_bright
1773 >> lighting.exposure.speed_bright_dark
1774 >> lighting.exposure.center_weight_power;