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 = new MapBlock(&m_env.getMap(), p, this);
326 block->deSerialize(istr, m_server_ser_ver, false);
327 block->deSerializeNetworkSpecific(istr);
328 sector->insertBlock(block);
332 ServerMap::saveBlock(block, m_localdb);
336 Add it to mesh update queue and set it to be acknowledged after update.
338 addUpdateMeshTaskWithEdge(p, true);
341 void Client::handleCommand_Inventory(NetworkPacket* pkt)
343 if (pkt->getSize() < 1)
346 std::string datastring(pkt->getString(0), pkt->getSize());
347 std::istringstream is(datastring, std::ios_base::binary);
349 LocalPlayer *player = m_env.getLocalPlayer();
350 assert(player != NULL);
352 player->inventory.deSerialize(is);
354 m_update_wielded_item = true;
356 delete m_inventory_from_server;
357 m_inventory_from_server = new Inventory(player->inventory);
358 m_inventory_from_server_age = 0.0;
361 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
363 if (pkt->getSize() < 2)
370 time_of_day = time_of_day % 24000;
371 float time_speed = 0;
373 if (pkt->getSize() >= 2 + 4) {
377 // Old message; try to approximate speed of time by ourselves
378 float time_of_day_f = (float)time_of_day / 24000.0f;
379 float tod_diff_f = 0;
381 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
382 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
384 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
386 m_last_time_of_day_f = time_of_day_f;
387 float time_diff = m_time_of_day_update_timer;
388 m_time_of_day_update_timer = 0;
390 if (m_time_of_day_set) {
391 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
392 infostream << "Client: Measured time_of_day speed (old format): "
393 << time_speed << " tod_diff_f=" << tod_diff_f
394 << " time_diff=" << time_diff << std::endl;
398 // Update environment
399 m_env.setTimeOfDay(time_of_day);
400 m_env.setTimeOfDaySpeed(time_speed);
401 m_time_of_day_set = true;
403 //u32 dr = m_env.getDayNightRatio();
404 //infostream << "Client: time_of_day=" << time_of_day
405 // << " time_speed=" << time_speed
406 // << " dr=" << dr << std::endl;
409 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
414 u16 sendername length
420 ChatMessage *chatMessage = new ChatMessage();
421 u8 version, message_type;
422 *pkt >> version >> message_type;
424 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
430 *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
431 chatMessage->timestamp = static_cast<std::time_t>(timestamp);
433 chatMessage->type = (ChatMessageType) message_type;
435 // @TODO send this to CSM using ChatMessage object
436 if (modsLoaded() && m_script->on_receiving_message(
437 wide_to_utf8(chatMessage->message))) {
438 // Message was consumed by CSM and should not be handled by client
441 pushToChatQueue(chatMessage);
445 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
448 u16 count of removed objects
449 for all removed objects {
452 u16 count of added objects
453 for all added objects {
456 u32 initialization data length
457 string initialization data
463 u16 removed_count, added_count, id;
465 // Read removed objects
466 *pkt >> removed_count;
468 for (u16 i = 0; i < removed_count; i++) {
470 m_env.removeActiveObject(id);
473 // Read added objects
476 for (u16 i = 0; i < added_count; i++) {
478 m_env.addActiveObject(id, type, pkt->readLongString());
480 } catch (PacketError &e) {
481 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
482 << ". The packet is unreliable, ignoring" << std::endl;
485 // m_activeobjects_received is false before the first
486 // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
487 m_activeobjects_received = true;
490 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
500 std::string datastring(pkt->getString(0), pkt->getSize());
501 std::istringstream is(datastring, std::ios_base::binary);
505 u16 id = readU16(is);
509 std::string message = deSerializeString16(is);
511 // Pass on to the environment
512 m_env.processActiveObjectMessage(id, message);
514 } catch (SerializationError &e) {
515 errorstream << "Client::handleCommand_ActiveObjectMessages: "
516 << "caught SerializationError: " << e.what() << std::endl;
520 void Client::handleCommand_Movement(NetworkPacket* pkt)
522 LocalPlayer *player = m_env.getLocalPlayer();
523 assert(player != NULL);
525 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
527 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
528 >> lf >> lfs >> ls >> g;
530 player->movement_acceleration_default = mad * BS;
531 player->movement_acceleration_air = maa * BS;
532 player->movement_acceleration_fast = maf * BS;
533 player->movement_speed_walk = msw * BS;
534 player->movement_speed_crouch = mscr * BS;
535 player->movement_speed_fast = msf * BS;
536 player->movement_speed_climb = mscl * BS;
537 player->movement_speed_jump = msj * BS;
538 player->movement_liquid_fluidity = lf * BS;
539 player->movement_liquid_fluidity_smooth = lfs * BS;
540 player->movement_liquid_sink = ls * BS;
541 player->movement_gravity = g * BS;
544 void Client::handleCommand_Fov(NetworkPacket *pkt)
547 bool is_multiplier = false;
548 f32 transition_time = 0.0f;
550 *pkt >> fov >> is_multiplier;
552 // Wrap transition_time extraction within a
553 // try-catch to preserve backwards compat
555 *pkt >> transition_time;
556 } catch (PacketError &e) {};
558 LocalPlayer *player = m_env.getLocalPlayer();
560 player->setFov({ fov, is_multiplier, transition_time });
561 m_camera->notifyFovChange();
564 void Client::handleCommand_HP(NetworkPacket *pkt)
566 LocalPlayer *player = m_env.getLocalPlayer();
567 assert(player != NULL);
569 u16 oldhp = player->hp;
573 bool damage_effect = true;
575 *pkt >> damage_effect;
576 } catch (PacketError &e) {};
581 m_script->on_hp_modification(hp);
584 // Add to ClientEvent queue
585 ClientEvent *event = new ClientEvent();
586 event->type = CE_PLAYER_DAMAGE;
587 event->player_damage.amount = oldhp - hp;
588 event->player_damage.effect = damage_effect;
589 m_client_event_queue.push(event);
593 void Client::handleCommand_Breath(NetworkPacket* pkt)
595 LocalPlayer *player = m_env.getLocalPlayer();
596 assert(player != NULL);
602 player->setBreath(breath);
605 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
607 LocalPlayer *player = m_env.getLocalPlayer();
608 assert(player != NULL);
613 *pkt >> pos >> pitch >> yaw;
615 player->setPosition(pos);
617 infostream << "Client got TOCLIENT_MOVE_PLAYER"
618 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
619 << " pitch=" << pitch
624 Add to ClientEvent queue.
625 This has to be sent to the main program because otherwise
626 it would just force the pitch and yaw values to whatever
627 the camera points to.
629 ClientEvent *event = new ClientEvent();
630 event->type = CE_PLAYER_FORCE_MOVE;
631 event->player_force_move.pitch = pitch;
632 event->player_force_move.yaw = yaw;
633 m_client_event_queue.push(event);
636 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
638 bool set_camera_point_target;
639 v3f camera_point_target;
641 *pkt >> set_camera_point_target;
642 *pkt >> camera_point_target;
644 ClientEvent *event = new ClientEvent();
645 event->type = CE_DEATHSCREEN;
646 event->deathscreen.set_camera_point_target = set_camera_point_target;
647 event->deathscreen.camera_point_target_x = camera_point_target.X;
648 event->deathscreen.camera_point_target_y = camera_point_target.Y;
649 event->deathscreen.camera_point_target_z = camera_point_target.Z;
650 m_client_event_queue.push(event);
653 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
659 infostream << "Client: Received media announcement: packet size: "
660 << pkt->getSize() << std::endl;
662 if (m_media_downloader == NULL ||
663 m_media_downloader->isStarted()) {
664 const char *problem = m_media_downloader ?
665 "we already saw another announcement" :
666 "all media has been received already";
667 errorstream << "Client: Received media announcement but "
669 << " files=" << num_files
670 << " size=" << pkt->getSize() << std::endl;
674 // Mesh update thread must be stopped while
675 // updating content definitions
676 sanity_check(!m_mesh_update_manager.isRunning());
678 for (u16 i = 0; i < num_files; i++) {
679 std::string name, sha1_base64;
681 *pkt >> name >> sha1_base64;
683 std::string sha1_raw = base64_decode(sha1_base64);
684 m_media_downloader->addFile(name, sha1_raw);
692 while (!sf.at_end()) {
693 std::string baseurl = trim(sf.next(","));
694 if (!baseurl.empty()) {
695 m_remote_media_servers.emplace_back(baseurl);
696 m_media_downloader->addRemoteServer(baseurl);
701 m_media_downloader->step(this);
704 void Client::handleCommand_Media(NetworkPacket* pkt)
708 u16 total number of file bunches
709 u16 index of this bunch
710 u32 number of files in this bunch
722 *pkt >> num_bunches >> bunch_i >> num_files;
724 infostream << "Client: Received files: bunch " << bunch_i << "/"
725 << num_bunches << " files=" << num_files
726 << " size=" << pkt->getSize() << std::endl;
731 bool init_phase = m_media_downloader && m_media_downloader->isStarted();
734 // Mesh update thread must be stopped while
735 // updating content definitions
736 sanity_check(!m_mesh_update_manager.isRunning());
739 for (u32 i = 0; i < num_files; i++) {
740 std::string name, data;
743 data = pkt->readLongString();
747 ok = m_media_downloader->conventionalTransferDone(name, data, this);
749 // Check pending dynamic transfers, one of them must be it
750 for (const auto &it : m_pending_media_downloads) {
751 if (it.second->conventionalTransferDone(name, data, this)) {
758 errorstream << "Client: Received media \"" << name
759 << "\" but no downloads pending. " << num_bunches << " bunches, "
760 << num_files << " in this one. (init_phase=" << init_phase
766 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
768 infostream << "Client: Received node definitions: packet size: "
769 << pkt->getSize() << std::endl;
771 // Mesh update thread must be stopped while
772 // updating content definitions
773 sanity_check(!m_mesh_update_manager.isRunning());
775 // Decompress node definitions
776 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
777 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
778 decompressZlib(tmp_is, tmp_os);
780 // Deserialize node definitions
781 m_nodedef->deSerialize(tmp_os, m_proto_ver);
782 m_nodedef_received = true;
785 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
787 infostream << "Client: Received item definitions: packet size: "
788 << pkt->getSize() << std::endl;
790 // Mesh update thread must be stopped while
791 // updating content definitions
792 sanity_check(!m_mesh_update_manager.isRunning());
794 // Decompress item definitions
795 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
796 std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
797 decompressZlib(tmp_is, tmp_os);
799 // Deserialize node definitions
800 m_itemdef->deSerialize(tmp_os, m_proto_ver);
801 m_itemdef_received = true;
804 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
812 [11 + len] (f32 * 3) pos
813 [23 + len] u16 object_id
817 [34 + len] bool ephemeral
822 SimpleSoundSpec spec;
823 SoundLocation type; // 0=local, 1=positional, 2=object
826 bool ephemeral = false;
828 *pkt >> server_id >> spec.name >> spec.gain >> (u8 &)type >> pos >> object_id >> spec.loop;
834 } catch (PacketError &e) {};
839 case SoundLocation::Local:
840 client_id = m_sound->playSound(spec);
842 case SoundLocation::Position:
843 client_id = m_sound->playSoundAt(spec, pos);
845 case SoundLocation::Object:
847 ClientActiveObject *cao = m_env.getActiveObject(object_id);
849 pos = cao->getPosition();
850 client_id = m_sound->playSoundAt(spec, pos);
855 if (client_id != -1) {
856 // for ephemeral sounds, server_id is not meaningful
858 m_sounds_server_to_client[server_id] = client_id;
859 m_sounds_client_to_server[client_id] = server_id;
862 m_sounds_to_objects[client_id] = object_id;
866 void Client::handleCommand_StopSound(NetworkPacket* pkt)
872 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
873 if (i != m_sounds_server_to_client.end()) {
874 int client_id = i->second;
875 m_sound->stopSound(client_id);
879 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
885 *pkt >> sound_id >> step >> gain;
887 std::unordered_map<s32, int>::const_iterator i =
888 m_sounds_server_to_client.find(sound_id);
890 if (i != m_sounds_server_to_client.end())
891 m_sound->fadeSound(i->second, step, gain);
894 void Client::handleCommand_Privileges(NetworkPacket* pkt)
896 m_privileges.clear();
897 infostream << "Client: Privileges updated: ";
900 *pkt >> num_privileges;
902 for (u16 i = 0; i < num_privileges; i++) {
907 m_privileges.insert(priv);
908 infostream << priv << " ";
910 infostream << std::endl;
913 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
915 LocalPlayer *player = m_env.getLocalPlayer();
916 assert(player != NULL);
918 // Store formspec in LocalPlayer
919 player->inventory_formspec = pkt->readLongString();
922 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
925 bool keep_inv = true;
926 *pkt >> name >> keep_inv;
928 infostream << "Client: Detached inventory update: \"" << name
929 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
931 const auto &inv_it = m_detached_inventories.find(name);
933 if (inv_it != m_detached_inventories.end()) {
934 delete inv_it->second;
935 m_detached_inventories.erase(inv_it);
939 Inventory *inv = nullptr;
940 if (inv_it == m_detached_inventories.end()) {
941 inv = new Inventory(m_itemdef);
942 m_detached_inventories[name] = inv;
944 inv = inv_it->second;
948 *pkt >> ignore; // this used to be the length of the following string, ignore it
950 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
951 std::istringstream is(contents, std::ios::binary);
952 inv->deSerialize(is);
955 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
957 std::string formspec = pkt->readLongString();
958 std::string formname;
962 ClientEvent *event = new ClientEvent();
963 event->type = CE_SHOW_FORMSPEC;
964 // pointer is required as event is a struct only!
965 // adding a std:string to a struct isn't possible
966 event->show_formspec.formspec = new std::string(formspec);
967 event->show_formspec.formname = new std::string(formname);
968 m_client_event_queue.push(event);
971 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
973 std::string datastring(pkt->getString(0), pkt->getSize());
974 std::istringstream is(datastring, std::ios_base::binary);
976 ParticleParameters p;
977 p.deSerialize(is, m_proto_ver);
979 ClientEvent *event = new ClientEvent();
980 event->type = CE_SPAWN_PARTICLE;
981 event->spawn_particle = new ParticleParameters(p);
983 m_client_event_queue.push(event);
986 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
988 std::string datastring(pkt->getString(0), pkt->getSize());
989 std::istringstream is(datastring, std::ios_base::binary);
991 ParticleSpawnerParameters p;
995 p.amount = readU16(is);
996 p.time = readF32(is);
998 // older protocols do not support tweening, and send only
999 // static ranges, so we can't just use the normal serialization
1000 // functions for the older values.
1001 p.pos.start.legacyDeSerialize(is);
1002 p.vel.start.legacyDeSerialize(is);
1003 p.acc.start.legacyDeSerialize(is);
1004 p.exptime.start.legacyDeSerialize(is);
1005 p.size.start.legacyDeSerialize(is);
1007 p.collisiondetection = readU8(is);
1008 p.texture.string = deSerializeString32(is);
1010 server_id = readU32(is);
1012 p.vertical = readU8(is);
1013 p.collision_removal = readU8(is);
1015 attached_id = readU16(is);
1017 p.animation.deSerialize(is, m_proto_ver);
1018 p.glow = readU8(is);
1019 p.object_collision = readU8(is);
1021 bool legacy_format = true;
1023 // This is kinda awful
1025 u16 tmp_param0 = readU16(is);
1028 p.node.param0 = tmp_param0;
1029 p.node.param2 = readU8(is);
1030 p.node_tile = readU8(is);
1033 f32 tmp_sbias = readF32(is);
1037 // initial bias must be stored separately in the stream to preserve
1038 // backwards compatibility with older clients, which do not support
1039 // a bias field in their range "format"
1040 p.pos.start.bias = tmp_sbias;
1041 p.vel.start.bias = readF32(is);
1042 p.acc.start.bias = readF32(is);
1043 p.exptime.start.bias = readF32(is);
1044 p.size.start.bias = readF32(is);
1046 p.pos.end.deSerialize(is);
1047 p.vel.end.deSerialize(is);
1048 p.acc.end.deSerialize(is);
1049 p.exptime.end.deSerialize(is);
1050 p.size.end.deSerialize(is);
1052 // properties for legacy texture field
1053 p.texture.deSerialize(is, m_proto_ver, true);
1055 p.drag.deSerialize(is);
1056 p.jitter.deSerialize(is);
1057 p.bounce.deSerialize(is);
1058 ParticleParamTypes::deSerializeParameterValue(is, p.attractor_kind);
1059 using ParticleParamTypes::AttractorKind;
1060 if (p.attractor_kind != AttractorKind::none) {
1061 p.attract.deSerialize(is);
1062 p.attractor_origin.deSerialize(is);
1063 p.attractor_attachment = readU16(is);
1064 /* we only check the first bit, in order to allow this value
1065 * to be turned into a bit flag field later if needed */
1066 p.attractor_kill = !!(readU8(is) & 1);
1067 if (p.attractor_kind != AttractorKind::point) {
1068 p.attractor_direction.deSerialize(is);
1069 p.attractor_direction_attachment = readU16(is);
1072 p.radius.deSerialize(is);
1074 u16 texpoolsz = readU16(is);
1075 p.texpool.reserve(texpoolsz);
1076 for (u16 i = 0; i < texpoolsz; ++i) {
1077 ServerParticleTexture newtex;
1078 newtex.deSerialize(is, m_proto_ver);
1079 p.texpool.push_back(newtex);
1082 legacy_format = false;
1085 if (legacy_format) {
1086 // there's no tweening data to be had, so we need to set the
1087 // legacy params to constant values, otherwise everything old
1088 // will tween to zero
1089 p.pos.end = p.pos.start;
1090 p.vel.end = p.vel.start;
1091 p.acc.end = p.acc.start;
1092 p.exptime.end = p.exptime.start;
1093 p.size.end = p.size.start;
1096 auto event = new ClientEvent();
1097 event->type = CE_ADD_PARTICLESPAWNER;
1098 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1099 event->add_particlespawner.attached_id = attached_id;
1100 event->add_particlespawner.id = server_id;
1102 m_client_event_queue.push(event);
1106 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1111 ClientEvent *event = new ClientEvent();
1112 event->type = CE_DELETE_PARTICLESPAWNER;
1113 event->delete_particlespawner.id = server_id;
1115 m_client_event_queue.push(event);
1118 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1137 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1138 >> dir >> align >> offset;
1145 } catch(PacketError &e) {};
1147 ClientEvent *event = new ClientEvent();
1148 event->type = CE_HUDADD;
1149 event->hudadd = new ClientEventHudAdd();
1150 event->hudadd->server_id = server_id;
1151 event->hudadd->type = type;
1152 event->hudadd->pos = pos;
1153 event->hudadd->name = name;
1154 event->hudadd->scale = scale;
1155 event->hudadd->text = text;
1156 event->hudadd->number = number;
1157 event->hudadd->item = item;
1158 event->hudadd->dir = dir;
1159 event->hudadd->align = align;
1160 event->hudadd->offset = offset;
1161 event->hudadd->world_pos = world_pos;
1162 event->hudadd->size = size;
1163 event->hudadd->z_index = z_index;
1164 event->hudadd->text2 = text2;
1165 event->hudadd->style = style;
1166 m_client_event_queue.push(event);
1169 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1175 ClientEvent *event = new ClientEvent();
1176 event->type = CE_HUDRM;
1177 event->hudrm.id = server_id;
1178 m_client_event_queue.push(event);
1181 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1191 *pkt >> server_id >> stat;
1193 // Keep in sync with:server.cpp -> SendHUDChange
1194 switch ((HudElementStat)stat) {
1196 case HUD_STAT_SCALE:
1197 case HUD_STAT_ALIGN:
1198 case HUD_STAT_OFFSET:
1203 case HUD_STAT_TEXT2:
1206 case HUD_STAT_WORLD_POS:
1217 ClientEvent *event = new ClientEvent();
1218 event->type = CE_HUDCHANGE;
1219 event->hudchange = new ClientEventHudChange();
1220 event->hudchange->id = server_id;
1221 event->hudchange->stat = static_cast<HudElementStat>(stat);
1222 event->hudchange->v2fdata = v2fdata;
1223 event->hudchange->v3fdata = v3fdata;
1224 event->hudchange->sdata = sdata;
1225 event->hudchange->data = intdata;
1226 event->hudchange->v2s32data = v2s32data;
1227 m_client_event_queue.push(event);
1230 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1234 *pkt >> flags >> mask;
1236 LocalPlayer *player = m_env.getLocalPlayer();
1237 assert(player != NULL);
1239 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1240 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1242 player->hud_flags &= ~mask;
1243 player->hud_flags |= flags;
1245 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1246 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1248 // Not so satisying code to keep compatibility with old fixed mode system
1251 // Hide minimap if it has been disabled by the server
1252 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1253 // defers a minimap update, therefore only call it if really
1254 // needed, by checking that minimap was visible before
1255 m_minimap->setModeIndex(0);
1257 // If radar has been disabled, try to find a non radar mode or fall back to 0
1258 if (m_minimap && m_minimap_radar_disabled_by_server
1259 && was_minimap_radar_visible) {
1260 while (m_minimap->getModeIndex() > 0 &&
1261 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1262 m_minimap->nextMode();
1265 // End of 'not so satifying code'
1268 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1270 u16 param; std::string value;
1272 *pkt >> param >> value;
1274 LocalPlayer *player = m_env.getLocalPlayer();
1275 assert(player != NULL);
1277 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1278 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1279 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1280 player->hud_hotbar_itemcount = hotbar_itemcount;
1282 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1283 player->hotbar_image = value;
1285 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1286 player->hotbar_selected_image = value;
1290 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1292 if (m_proto_ver < 39) {
1293 // Handle Protocol 38 and below servers with old set_sky,
1294 // ensuring the classic look is kept.
1295 std::string datastring(pkt->getString(0), pkt->getSize());
1296 std::istringstream is(datastring, std::ios_base::binary);
1298 SkyboxParams skybox;
1299 skybox.bgcolor = video::SColor(readARGB8(is));
1300 skybox.type = std::string(deSerializeString16(is));
1301 u16 count = readU16(is);
1303 for (size_t i = 0; i < count; i++)
1304 skybox.textures.emplace_back(deSerializeString16(is));
1306 skybox.clouds = true;
1308 skybox.clouds = readU8(is);
1311 // Use default skybox settings:
1312 SunParams sun = SkyboxDefaults::getSunDefaults();
1313 MoonParams moon = SkyboxDefaults::getMoonDefaults();
1314 StarParams stars = SkyboxDefaults::getStarDefaults();
1316 // Fix for "regular" skies, as color isn't kept:
1317 if (skybox.type == "regular") {
1318 skybox.sky_color = SkyboxDefaults::getSkyColorDefaults();
1319 skybox.fog_tint_type = "default";
1320 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1321 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1323 sun.visible = false;
1324 sun.sunrise_visible = false;
1325 moon.visible = false;
1326 stars.visible = false;
1329 // Skybox, sun, moon and stars ClientEvents:
1330 ClientEvent *sky_event = new ClientEvent();
1331 sky_event->type = CE_SET_SKY;
1332 sky_event->set_sky = new SkyboxParams(skybox);
1333 m_client_event_queue.push(sky_event);
1335 ClientEvent *sun_event = new ClientEvent();
1336 sun_event->type = CE_SET_SUN;
1337 sun_event->sun_params = new SunParams(sun);
1338 m_client_event_queue.push(sun_event);
1340 ClientEvent *moon_event = new ClientEvent();
1341 moon_event->type = CE_SET_MOON;
1342 moon_event->moon_params = new MoonParams(moon);
1343 m_client_event_queue.push(moon_event);
1345 ClientEvent *star_event = new ClientEvent();
1346 star_event->type = CE_SET_STARS;
1347 star_event->star_params = new StarParams(stars);
1348 m_client_event_queue.push(star_event);
1350 SkyboxParams skybox;
1352 std::string texture;
1354 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1355 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1357 if (skybox.type == "skybox") {
1358 *pkt >> texture_count;
1359 for (int i = 0; i < texture_count; i++) {
1361 skybox.textures.emplace_back(texture);
1364 else if (skybox.type == "regular") {
1365 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1366 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1367 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1368 >> skybox.sky_color.indoors;
1371 ClientEvent *event = new ClientEvent();
1372 event->type = CE_SET_SKY;
1373 event->set_sky = new SkyboxParams(skybox);
1374 m_client_event_queue.push(event);
1378 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1382 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1383 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1385 ClientEvent *event = new ClientEvent();
1386 event->type = CE_SET_SUN;
1387 event->sun_params = new SunParams(sun);
1388 m_client_event_queue.push(event);
1391 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1395 *pkt >> moon.visible >> moon.texture
1396 >> moon.tonemap >> moon.scale;
1398 ClientEvent *event = new ClientEvent();
1399 event->type = CE_SET_MOON;
1400 event->moon_params = new MoonParams(moon);
1401 m_client_event_queue.push(event);
1404 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1406 StarParams stars = SkyboxDefaults::getStarDefaults();
1408 *pkt >> stars.visible >> stars.count
1409 >> stars.starcolor >> stars.scale;
1411 *pkt >> stars.day_opacity;
1412 } catch (PacketError &e) {};
1414 ClientEvent *event = new ClientEvent();
1415 event->type = CE_SET_STARS;
1416 event->star_params = new StarParams(stars);
1418 m_client_event_queue.push(event);
1421 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1424 video::SColor color_bright;
1425 video::SColor color_ambient;
1430 *pkt >> density >> color_bright >> color_ambient
1431 >> height >> thickness >> speed;
1433 ClientEvent *event = new ClientEvent();
1434 event->type = CE_CLOUD_PARAMS;
1435 event->cloud_params.density = density;
1436 // use the underlying u32 representation, because we can't
1437 // use struct members with constructors here, and this way
1438 // we avoid using new() and delete() for no good reason
1439 event->cloud_params.color_bright = color_bright.color;
1440 event->cloud_params.color_ambient = color_ambient.color;
1441 event->cloud_params.height = height;
1442 event->cloud_params.thickness = thickness;
1443 // same here: deconstruct to skip constructor
1444 event->cloud_params.speed_x = speed.X;
1445 event->cloud_params.speed_y = speed.Y;
1446 m_client_event_queue.push(event);
1449 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1452 u16 day_night_ratio_u;
1454 *pkt >> do_override >> day_night_ratio_u;
1456 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1458 ClientEvent *event = new ClientEvent();
1459 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1460 event->override_day_night_ratio.do_override = do_override;
1461 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1462 m_client_event_queue.push(event);
1465 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1467 LocalPlayer *player = m_env.getLocalPlayer();
1468 assert(player != NULL);
1470 *pkt >> player->local_animations[0];
1471 *pkt >> player->local_animations[1];
1472 *pkt >> player->local_animations[2];
1473 *pkt >> player->local_animations[3];
1474 *pkt >> player->local_animation_speed;
1476 player->last_animation = -1;
1479 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1481 LocalPlayer *player = m_env.getLocalPlayer();
1482 assert(player != NULL);
1484 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1487 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1491 *pkt >> type >> num_players;
1492 PlayerListModifer notice_type = (PlayerListModifer) type;
1494 for (u16 i = 0; i < num_players; i++) {
1497 switch (notice_type) {
1498 case PLAYER_LIST_INIT:
1499 case PLAYER_LIST_ADD:
1500 m_env.addPlayerName(name);
1502 case PLAYER_LIST_REMOVE:
1503 m_env.removePlayerName(name);
1509 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1511 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1512 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1513 errorstream << "Client: Received SRP S_B login message,"
1514 << " but wasn't supposed to (chosen_mech="
1515 << m_chosen_auth_mech << ")." << std::endl;
1521 SRPUser *usr = (SRPUser *) m_auth_data;
1526 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1528 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1529 (const unsigned char *) B.c_str(), B.size(),
1530 (unsigned char **) &bytes_M, &len_M);
1533 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1537 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1538 resp_pkt << std::string(bytes_M, len_M);
1542 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1544 LocalPlayer *player = m_env.getLocalPlayer();
1545 assert(player != NULL);
1547 // Store formspec in LocalPlayer
1548 *pkt >> player->formspec_prepend;
1551 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1553 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1555 // Restrictions were received -> load mods if it's enabled
1556 // Note: this should be moved after mods receptions from server instead
1560 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1566 LocalPlayer *player = m_env.getLocalPlayer();
1567 assert(player != NULL);
1568 player->addVelocity(added_vel);
1571 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1573 std::string raw_hash, filename, filedata;
1577 *pkt >> raw_hash >> filename >> cached;
1578 if (m_proto_ver >= 40)
1581 filedata = pkt->readLongString();
1583 if (raw_hash.size() != 20 || filename.empty() ||
1584 (m_proto_ver < 40 && filedata.empty()) ||
1585 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1586 throw PacketError("Illegal filename, data or hash");
1589 verbosestream << "Server pushes media file \"" << filename << "\" ";
1590 if (filedata.empty())
1591 verbosestream << "to be fetched ";
1593 verbosestream << "with " << filedata.size() << " bytes ";
1594 verbosestream << "(cached=" << cached << ")" << std::endl;
1596 if (m_media_pushed_files.count(filename) != 0) {
1597 // Ignore (but acknowledge). Previously this was for sync purposes,
1598 // but even in new versions media cannot be replaced at runtime.
1599 if (m_proto_ver >= 40)
1600 sendHaveMedia({ token });
1604 if (!filedata.empty()) {
1606 // Compute and check checksum of data
1607 std::string computed_hash;
1610 ctx.addBytes(filedata.c_str(), filedata.size());
1611 unsigned char *buf = ctx.getDigest();
1612 computed_hash.assign((char*) buf, 20);
1615 if (raw_hash != computed_hash) {
1616 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1620 // Actually load media
1621 loadMedia(filedata, filename, true);
1622 m_media_pushed_files.insert(filename);
1624 // Cache file for the next time when this client joins the same server
1626 clientMediaUpdateCache(raw_hash, filedata);
1630 m_media_pushed_files.insert(filename);
1632 // create a downloader for this file
1633 auto downloader(std::make_shared<SingleMediaDownloader>(cached));
1634 m_pending_media_downloads.emplace_back(token, downloader);
1635 downloader->addFile(filename, raw_hash);
1636 for (const auto &baseurl : m_remote_media_servers)
1637 downloader->addRemoteServer(baseurl);
1639 downloader->step(this);
1646 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1648 std::string channel_name, sender, channel_msg;
1649 *pkt >> channel_name >> sender >> channel_msg;
1651 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1652 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1653 << channel_msg << std::endl;
1655 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1656 verbosestream << "Server sent us messages on unregistered channel "
1657 << channel_name << ", ignoring." << std::endl;
1661 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1664 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1667 ModChannelSignal signal;
1668 std::string channel;
1670 *pkt >> signal_tmp >> channel;
1672 signal = (ModChannelSignal)signal_tmp;
1674 bool valid_signal = true;
1675 // @TODO: send Signal to Lua API
1677 case MODCHANNEL_SIGNAL_JOIN_OK:
1678 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1679 infostream << "Server ack our mod channel join on channel `" << channel
1680 << "`, joining." << std::endl;
1682 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1683 // Unable to join, remove channel
1684 m_modchannel_mgr->leaveChannel(channel, 0);
1685 infostream << "Server refused our mod channel join on channel `" << channel
1686 << "`" << std::endl;
1688 case MODCHANNEL_SIGNAL_LEAVE_OK:
1690 infostream << "Server ack our mod channel leave on channel " << channel
1691 << "`, leaving." << std::endl;
1694 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1695 infostream << "Server refused our mod channel leave on channel `" << channel
1696 << "`" << std::endl;
1698 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1700 // Generally unused, but ensure we don't do an implementation error
1701 infostream << "Server tells us we sent a message on channel `" << channel
1702 << "` but we are not registered. Message was dropped." << std::endl;
1705 case MODCHANNEL_SIGNAL_SET_STATE: {
1709 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1710 infostream << "Received wrong channel state " << state
1711 << ", ignoring." << std::endl;
1715 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1716 infostream << "Server sets mod channel `" << channel
1717 << "` in read-only mode." << std::endl;
1722 warningstream << "Received unhandled mod channel signal ID "
1723 << signal << ", ignoring." << std::endl;
1725 valid_signal = false;
1729 // If signal is valid, forward it to client side mods
1731 m_script->on_modchannel_signal(channel, signal);
1734 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1737 u16 mode; // wanted current mode index after change
1739 *pkt >> count >> mode;
1742 m_minimap->clearModes();
1744 for (size_t index = 0; index < count; index++) {
1748 std::string texture;
1751 *pkt >> type >> label >> size >> texture >> scale;
1754 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1758 m_minimap->setModeIndex(mode);
1761 void Client::handleCommand_SetLighting(NetworkPacket *pkt)
1763 Lighting& lighting = m_env.getLocalPlayer()->getLighting();
1765 if (pkt->getRemainingBytes() >= 4)
1766 *pkt >> lighting.shadow_intensity;
1767 if (pkt->getRemainingBytes() >= 4)
1768 *pkt >> lighting.saturation;