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.
22 #include "util/base64.h"
23 #include "chatmessage.h"
24 #include "clientmedia.h"
27 #include "mapsector.h"
29 #include "modchannels.h"
31 #include "serialization.h"
33 #include "util/strfnd.h"
34 #include "client/clientevent.h"
35 #include "network/clientopcodes.h"
36 #include "network/connection.h"
37 #include "script/scripting_client.h"
38 #include "util/serialize.h"
40 #include "tileanimation.h"
43 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
45 infostream << "Got deprecated command "
46 << toClientCommandTable[pkt->getCommand()].name << " from peer "
47 << pkt->getPeerId() << "!" << std::endl;
50 void Client::handleCommand_Hello(NetworkPacket* pkt)
52 if (pkt->getSize() < 1)
59 std::string username_legacy; // for case insensitivity
60 *pkt >> serialization_ver >> compression_mode >> proto_ver
61 >> auth_mechs >> username_legacy;
63 // Chose an auth method we support
64 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
66 infostream << "Client: TOCLIENT_HELLO received with "
67 << "serialization_ver=" << (u32)serialization_ver
68 << ", auth_mechs=" << auth_mechs
69 << ", proto_ver=" << proto_ver
70 << ", compression_mode=" << compression_mode
71 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
73 if (!ser_ver_supported(serialization_ver)) {
74 infostream << "Client: TOCLIENT_HELLO: Server sent "
75 << "unsupported ser_fmt_ver"<< std::endl;
79 m_server_ser_ver = serialization_ver;
80 m_proto_ver = proto_ver;
82 //TODO verify that username_legacy matches sent username, only
83 // differs in casing (make both uppercase and compare)
84 // This is only neccessary though when we actually want to add casing support
86 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
87 // we recieved a TOCLIENT_HELLO while auth was already going on
88 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
89 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
90 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
91 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
92 srp_user_delete((SRPUser *) m_auth_data);
97 // Authenticate using that method, or abort if there wasn't any method found
98 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
99 startAuth(chosen_auth_mechanism);
101 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
102 m_access_denied = true;
103 m_access_denied_reason = "Unknown";
109 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
114 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
115 >> m_sudo_auth_methods;
117 playerpos -= v3f(0, BS / 2, 0);
119 // Set player position
120 LocalPlayer *player = m_env.getLocalPlayer();
121 assert(player != NULL);
122 player->setPosition(playerpos);
124 infostream << "Client: received map seed: " << m_map_seed << std::endl;
125 infostream << "Client: received recommended send interval "
126 << m_recommended_send_interval<<std::endl;
129 std::string lang = gettext("LANG_CODE");
130 if (lang == "LANG_CODE")
133 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
139 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
143 m_password = m_new_password;
145 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
147 // send packet to actually set the password
148 startAuth(AUTH_MECHANISM_FIRST_SRP);
151 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
153 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
155 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
156 L"Password change denied. Password NOT changed.");
157 pushToChatQueue(chatMessage);
158 // reset everything and be sad
162 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
164 // The server didn't like our password. Note, this needs
165 // to be processed even if the serialisation format has
166 // not been agreed yet, the same as TOCLIENT_INIT.
167 m_access_denied = true;
168 m_access_denied_reason = "Unknown";
170 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
171 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
172 // in some places of the server code
173 if (pkt->getSize() >= 2) {
174 std::wstring wide_reason;
176 m_access_denied_reason = wide_to_utf8(wide_reason);
181 if (pkt->getSize() < 1)
184 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
186 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
187 denyCode == SERVER_ACCESSDENIED_CRASH) {
188 *pkt >> m_access_denied_reason;
189 if (m_access_denied_reason.empty()) {
190 m_access_denied_reason = accessDeniedStrings[denyCode];
194 m_access_denied_reconnect = reconnect & 1;
195 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
196 *pkt >> m_access_denied_reason;
197 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
198 m_access_denied_reason = accessDeniedStrings[denyCode];
200 // Allow us to add new error messages to the
201 // protocol without raising the protocol version, if we want to.
202 // Until then (which may be never), this is outside
203 // of the defined protocol.
204 *pkt >> m_access_denied_reason;
205 if (m_access_denied_reason.empty()) {
206 m_access_denied_reason = "Unknown";
211 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
213 if (pkt->getSize() < 6)
221 void Client::handleCommand_AddNode(NetworkPacket* pkt)
223 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
230 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
232 bool remove_metadata = true;
233 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
234 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
235 remove_metadata = false;
238 addNode(p, n, remove_metadata);
240 void Client::handleCommand_BlockData(NetworkPacket* pkt)
242 // Ignore too small packet
243 if (pkt->getSize() < 6)
249 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
250 std::istringstream istr(datastring, std::ios_base::binary);
256 sector = m_env.getMap().emergeSector(p2d);
258 assert(sector->getPos() == p2d);
260 block = sector->getBlockNoCreateNoEx(p.Y);
263 Update an existing block
265 block->deSerialize(istr, m_server_ser_ver, false);
266 block->deSerializeNetworkSpecific(istr);
272 block = new MapBlock(&m_env.getMap(), p, this);
273 block->deSerialize(istr, m_server_ser_ver, false);
274 block->deSerializeNetworkSpecific(istr);
275 sector->insertBlock(block);
279 ServerMap::saveBlock(block, m_localdb);
283 Add it to mesh update queue and set it to be acknowledged after update.
285 addUpdateMeshTaskWithEdge(p, true);
288 void Client::handleCommand_Inventory(NetworkPacket* pkt)
290 if (pkt->getSize() < 1)
293 std::string datastring(pkt->getString(0), pkt->getSize());
294 std::istringstream is(datastring, std::ios_base::binary);
296 LocalPlayer *player = m_env.getLocalPlayer();
297 assert(player != NULL);
299 player->inventory.deSerialize(is);
301 m_inventory_updated = true;
303 delete m_inventory_from_server;
304 m_inventory_from_server = new Inventory(player->inventory);
305 m_inventory_from_server_age = 0.0;
308 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
310 if (pkt->getSize() < 2)
317 time_of_day = time_of_day % 24000;
318 float time_speed = 0;
320 if (pkt->getSize() >= 2 + 4) {
324 // Old message; try to approximate speed of time by ourselves
325 float time_of_day_f = (float)time_of_day / 24000.0f;
326 float tod_diff_f = 0;
328 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
329 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
331 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
333 m_last_time_of_day_f = time_of_day_f;
334 float time_diff = m_time_of_day_update_timer;
335 m_time_of_day_update_timer = 0;
337 if (m_time_of_day_set) {
338 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
339 infostream << "Client: Measured time_of_day speed (old format): "
340 << time_speed << " tod_diff_f=" << tod_diff_f
341 << " time_diff=" << time_diff << std::endl;
345 // Update environment
346 m_env.setTimeOfDay(time_of_day);
347 m_env.setTimeOfDaySpeed(time_speed);
348 m_time_of_day_set = true;
350 u32 dr = m_env.getDayNightRatio();
351 infostream << "Client: time_of_day=" << time_of_day
352 << " time_speed=" << time_speed
353 << " dr=" << dr << std::endl;
356 void Client::handleCommand_ChatMessageOld(NetworkPacket *pkt)
367 std::wstring message;
368 for (u32 i = 0; i < len; i++) {
370 message += (wchar_t)read_wchar;
373 // If chat message not consummed by client lua API
374 // @TODO send this to CSM using ChatMessage object
375 if (!moddingEnabled() || !m_script->on_receiving_message(wide_to_utf8(message))) {
376 pushToChatQueue(new ChatMessage(message));
380 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
385 u16 sendername length
391 ChatMessage *chatMessage = new ChatMessage();
392 u8 version, message_type;
393 *pkt >> version >> message_type;
395 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
400 *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
402 chatMessage->type = (ChatMessageType) message_type;
404 // @TODO send this to CSM using ChatMessage object
405 if (!moddingEnabled() || !m_script->on_receiving_message(
406 wide_to_utf8(chatMessage->message))) {
407 pushToChatQueue(chatMessage);
409 // Message was consumed by CSM and should not handled by client, destroying
414 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
417 u16 count of removed objects
418 for all removed objects {
421 u16 count of added objects
422 for all added objects {
425 u32 initialization data length
426 string initialization data
432 u16 removed_count, added_count, id;
434 // Read removed objects
435 *pkt >> removed_count;
437 for (u16 i = 0; i < removed_count; i++) {
439 m_env.removeActiveObject(id);
442 // Read added objects
445 for (u16 i = 0; i < added_count; i++) {
447 m_env.addActiveObject(id, type, pkt->readLongString());
449 } catch (PacketError &e) {
450 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
451 << ". The packet is unreliable, ignoring" << std::endl;
455 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
465 std::string datastring(pkt->getString(0), pkt->getSize());
466 std::istringstream is(datastring, std::ios_base::binary);
470 u16 id = readU16(is);
474 std::string message = deSerializeString(is);
476 // Pass on to the environment
477 m_env.processActiveObjectMessage(id, message);
479 } catch (SerializationError &e) {
480 errorstream << "Client::handleCommand_ActiveObjectMessages: "
481 << "caught SerializationError: " << e.what() << std::endl;
485 void Client::handleCommand_Movement(NetworkPacket* pkt)
487 LocalPlayer *player = m_env.getLocalPlayer();
488 assert(player != NULL);
490 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
492 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
493 >> lf >> lfs >> ls >> g;
495 player->movement_acceleration_default = mad * BS;
496 player->movement_acceleration_air = maa * BS;
497 player->movement_acceleration_fast = maf * BS;
498 player->movement_speed_walk = msw * BS;
499 player->movement_speed_crouch = mscr * BS;
500 player->movement_speed_fast = msf * BS;
501 player->movement_speed_climb = mscl * BS;
502 player->movement_speed_jump = msj * BS;
503 player->movement_liquid_fluidity = lf * BS;
504 player->movement_liquid_fluidity_smooth = lfs * BS;
505 player->movement_liquid_sink = ls * BS;
506 player->movement_gravity = g * BS;
509 void Client::handleCommand_HP(NetworkPacket* pkt)
512 LocalPlayer *player = m_env.getLocalPlayer();
513 assert(player != NULL);
515 u16 oldhp = player->hp;
522 if (moddingEnabled()) {
523 m_script->on_hp_modification(hp);
527 // Add to ClientEvent queue
528 ClientEvent *event = new ClientEvent();
529 event->type = CE_PLAYER_DAMAGE;
530 event->player_damage.amount = oldhp - hp;
531 m_client_event_queue.push(event);
535 void Client::handleCommand_Breath(NetworkPacket* pkt)
537 LocalPlayer *player = m_env.getLocalPlayer();
538 assert(player != NULL);
544 player->setBreath(breath);
547 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
549 LocalPlayer *player = m_env.getLocalPlayer();
550 assert(player != NULL);
555 *pkt >> pos >> pitch >> yaw;
557 player->setPosition(pos);
559 infostream << "Client got TOCLIENT_MOVE_PLAYER"
560 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
561 << " pitch=" << pitch
566 Add to ClientEvent queue.
567 This has to be sent to the main program because otherwise
568 it would just force the pitch and yaw values to whatever
569 the camera points to.
571 ClientEvent *event = new ClientEvent();
572 event->type = CE_PLAYER_FORCE_MOVE;
573 event->player_force_move.pitch = pitch;
574 event->player_force_move.yaw = yaw;
575 m_client_event_queue.push(event);
577 // Ignore damage for a few seconds, so that the player doesn't
578 // get damage from falling on ground
579 m_ignore_damage_timer = 3.0;
582 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
584 bool set_camera_point_target;
585 v3f camera_point_target;
587 *pkt >> set_camera_point_target;
588 *pkt >> camera_point_target;
590 ClientEvent *event = new ClientEvent();
591 event->type = CE_DEATHSCREEN;
592 event->deathscreen.set_camera_point_target = set_camera_point_target;
593 event->deathscreen.camera_point_target_x = camera_point_target.X;
594 event->deathscreen.camera_point_target_y = camera_point_target.Y;
595 event->deathscreen.camera_point_target_z = camera_point_target.Z;
596 m_client_event_queue.push(event);
599 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
605 infostream << "Client: Received media announcement: packet size: "
606 << pkt->getSize() << std::endl;
608 if (m_media_downloader == NULL ||
609 m_media_downloader->isStarted()) {
610 const char *problem = m_media_downloader ?
611 "we already saw another announcement" :
612 "all media has been received already";
613 errorstream << "Client: Received media announcement but "
615 << " files=" << num_files
616 << " size=" << pkt->getSize() << std::endl;
620 // Mesh update thread must be stopped while
621 // updating content definitions
622 sanity_check(!m_mesh_update_thread.isRunning());
624 for (u16 i = 0; i < num_files; i++) {
625 std::string name, sha1_base64;
627 *pkt >> name >> sha1_base64;
629 std::string sha1_raw = base64_decode(sha1_base64);
630 m_media_downloader->addFile(name, sha1_raw);
639 while(!sf.at_end()) {
640 std::string baseurl = trim(sf.next(","));
641 if (!baseurl.empty())
642 m_media_downloader->addRemoteServer(baseurl);
645 catch(SerializationError& e) {
646 // not supported by server or turned off
649 m_media_downloader->step(this);
652 void Client::handleCommand_Media(NetworkPacket* pkt)
656 u16 total number of file bunches
657 u16 index of this bunch
658 u32 number of files in this bunch
670 *pkt >> num_bunches >> bunch_i >> num_files;
672 infostream << "Client: Received files: bunch " << bunch_i << "/"
673 << num_bunches << " files=" << num_files
674 << " size=" << pkt->getSize() << std::endl;
679 if (!m_media_downloader || !m_media_downloader->isStarted()) {
680 const char *problem = m_media_downloader ?
681 "media has not been requested" :
682 "all media has been received already";
683 errorstream << "Client: Received media but "
685 << " bunch " << bunch_i << "/" << num_bunches
686 << " files=" << num_files
687 << " size=" << pkt->getSize() << std::endl;
691 // Mesh update thread must be stopped while
692 // updating content definitions
693 sanity_check(!m_mesh_update_thread.isRunning());
695 for (u32 i=0; i < num_files; i++) {
700 std::string data = pkt->readLongString();
702 m_media_downloader->conventionalTransferDone(
707 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
709 infostream << "Client: Received node definitions: packet size: "
710 << pkt->getSize() << std::endl;
712 // Mesh update thread must be stopped while
713 // updating content definitions
714 sanity_check(!m_mesh_update_thread.isRunning());
716 // Decompress node definitions
717 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
718 std::ostringstream tmp_os;
719 decompressZlib(tmp_is, tmp_os);
721 // Deserialize node definitions
722 std::istringstream tmp_is2(tmp_os.str());
723 m_nodedef->deSerialize(tmp_is2);
724 m_nodedef_received = true;
727 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
729 infostream << "Client: Received item definitions: packet size: "
730 << pkt->getSize() << std::endl;
732 // Mesh update thread must be stopped while
733 // updating content definitions
734 sanity_check(!m_mesh_update_thread.isRunning());
736 // Decompress item definitions
737 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
738 std::ostringstream tmp_os;
739 decompressZlib(tmp_is, tmp_os);
741 // Deserialize node definitions
742 std::istringstream tmp_is2(tmp_os.str());
743 m_itemdef->deSerialize(tmp_is2);
744 m_itemdef_received = true;
747 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
755 [11 + len] (f32 * 3) pos
756 [23 + len] u16 object_id
766 u8 type; // 0=local, 1=positional, 2=object
773 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
778 } catch (PacketError &e) {};
784 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
786 case 1: // positional
787 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
791 ClientActiveObject *cao = m_env.getActiveObject(object_id);
793 pos = cao->getPosition();
794 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
795 // TODO: Set up sound to move with object
802 if (client_id != -1) {
803 m_sounds_server_to_client[server_id] = client_id;
804 m_sounds_client_to_server[client_id] = server_id;
806 m_sounds_to_objects[client_id] = object_id;
810 void Client::handleCommand_StopSound(NetworkPacket* pkt)
816 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
817 if (i != m_sounds_server_to_client.end()) {
818 int client_id = i->second;
819 m_sound->stopSound(client_id);
823 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
829 *pkt >> sound_id >> step >> gain;
831 std::unordered_map<s32, int>::const_iterator i =
832 m_sounds_server_to_client.find(sound_id);
834 if (i != m_sounds_server_to_client.end())
835 m_sound->fadeSound(i->second, step, gain);
838 void Client::handleCommand_Privileges(NetworkPacket* pkt)
840 m_privileges.clear();
841 infostream << "Client: Privileges updated: ";
844 *pkt >> num_privileges;
846 for (u16 i = 0; i < num_privileges; i++) {
851 m_privileges.insert(priv);
852 infostream << priv << " ";
854 infostream << std::endl;
857 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
859 LocalPlayer *player = m_env.getLocalPlayer();
860 assert(player != NULL);
862 // Store formspec in LocalPlayer
863 player->inventory_formspec = pkt->readLongString();
866 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
868 std::string datastring(pkt->getString(0), pkt->getSize());
869 std::istringstream is(datastring, std::ios_base::binary);
871 std::string name = deSerializeString(is);
873 infostream << "Client: Detached inventory update: \"" << name
874 << "\"" << std::endl;
876 Inventory *inv = NULL;
877 if (m_detached_inventories.count(name) > 0)
878 inv = m_detached_inventories[name];
880 inv = new Inventory(m_itemdef);
881 m_detached_inventories[name] = inv;
883 inv->deSerialize(is);
886 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
888 std::string formspec = pkt->readLongString();
889 std::string formname;
893 ClientEvent *event = new ClientEvent();
894 event->type = CE_SHOW_FORMSPEC;
895 // pointer is required as event is a struct only!
896 // adding a std:string to a struct isn't possible
897 event->show_formspec.formspec = new std::string(formspec);
898 event->show_formspec.formname = new std::string(formname);
899 m_client_event_queue.push(event);
902 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
904 std::string datastring(pkt->getString(0), pkt->getSize());
905 std::istringstream is(datastring, std::ios_base::binary);
907 v3f pos = readV3F1000(is);
908 v3f vel = readV3F1000(is);
909 v3f acc = readV3F1000(is);
910 float expirationtime = readF1000(is);
911 float size = readF1000(is);
912 bool collisiondetection = readU8(is);
913 std::string texture = deSerializeLongString(is);
914 bool vertical = false;
915 bool collision_removal = false;
916 TileAnimationParams animation;
917 animation.type = TAT_NONE;
920 vertical = readU8(is);
921 collision_removal = readU8(is);
922 animation.deSerialize(is, m_proto_ver);
926 ClientEvent *event = new ClientEvent();
927 event->type = CE_SPAWN_PARTICLE;
928 event->spawn_particle.pos = new v3f (pos);
929 event->spawn_particle.vel = new v3f (vel);
930 event->spawn_particle.acc = new v3f (acc);
931 event->spawn_particle.expirationtime = expirationtime;
932 event->spawn_particle.size = size;
933 event->spawn_particle.collisiondetection = collisiondetection;
934 event->spawn_particle.collision_removal = collision_removal;
935 event->spawn_particle.vertical = vertical;
936 event->spawn_particle.texture = new std::string(texture);
937 event->spawn_particle.animation = animation;
938 event->spawn_particle.glow = glow;
940 m_client_event_queue.push(event);
943 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
957 bool collisiondetection;
960 *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
961 >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
962 >> maxsize >> collisiondetection;
964 std::string texture = pkt->readLongString();
968 bool vertical = false;
969 bool collision_removal = false;
970 TileAnimationParams animation;
971 animation.type = TAT_NONE;
976 *pkt >> collision_removal;
979 // This is horrible but required (why are there two ways to deserialize pkts?)
980 std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
981 std::istringstream is(datastring, std::ios_base::binary);
982 animation.deSerialize(is, m_proto_ver);
986 ClientEvent *event = new ClientEvent();
987 event->type = CE_ADD_PARTICLESPAWNER;
988 event->add_particlespawner.amount = amount;
989 event->add_particlespawner.spawntime = spawntime;
990 event->add_particlespawner.minpos = new v3f (minpos);
991 event->add_particlespawner.maxpos = new v3f (maxpos);
992 event->add_particlespawner.minvel = new v3f (minvel);
993 event->add_particlespawner.maxvel = new v3f (maxvel);
994 event->add_particlespawner.minacc = new v3f (minacc);
995 event->add_particlespawner.maxacc = new v3f (maxacc);
996 event->add_particlespawner.minexptime = minexptime;
997 event->add_particlespawner.maxexptime = maxexptime;
998 event->add_particlespawner.minsize = minsize;
999 event->add_particlespawner.maxsize = maxsize;
1000 event->add_particlespawner.collisiondetection = collisiondetection;
1001 event->add_particlespawner.collision_removal = collision_removal;
1002 event->add_particlespawner.attached_id = attached_id;
1003 event->add_particlespawner.vertical = vertical;
1004 event->add_particlespawner.texture = new std::string(texture);
1005 event->add_particlespawner.id = id;
1006 event->add_particlespawner.animation = animation;
1007 event->add_particlespawner.glow = glow;
1009 m_client_event_queue.push(event);
1013 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1018 ClientEvent *event = new ClientEvent();
1019 event->type = CE_DELETE_PARTICLESPAWNER;
1020 event->delete_particlespawner.id = id;
1022 m_client_event_queue.push(event);
1025 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1027 std::string datastring(pkt->getString(0), pkt->getSize());
1028 std::istringstream is(datastring, std::ios_base::binary);
1044 *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1045 >> dir >> align >> offset;
1049 catch(SerializationError &e) {};
1053 } catch(SerializationError &e) {};
1055 ClientEvent *event = new ClientEvent();
1056 event->type = CE_HUDADD;
1057 event->hudadd.id = id;
1058 event->hudadd.type = type;
1059 event->hudadd.pos = new v2f(pos);
1060 event->hudadd.name = new std::string(name);
1061 event->hudadd.scale = new v2f(scale);
1062 event->hudadd.text = new std::string(text);
1063 event->hudadd.number = number;
1064 event->hudadd.item = item;
1065 event->hudadd.dir = dir;
1066 event->hudadd.align = new v2f(align);
1067 event->hudadd.offset = new v2f(offset);
1068 event->hudadd.world_pos = new v3f(world_pos);
1069 event->hudadd.size = new v2s32(size);
1070 m_client_event_queue.push(event);
1073 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1079 ClientEvent *event = new ClientEvent();
1080 event->type = CE_HUDRM;
1081 event->hudrm.id = id;
1082 m_client_event_queue.push(event);
1085 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1097 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1098 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1100 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1102 else if (stat == HUD_STAT_WORLD_POS)
1104 else if (stat == HUD_STAT_SIZE )
1109 ClientEvent *event = new ClientEvent();
1110 event->type = CE_HUDCHANGE;
1111 event->hudchange.id = id;
1112 event->hudchange.stat = (HudElementStat)stat;
1113 event->hudchange.v2fdata = new v2f(v2fdata);
1114 event->hudchange.v3fdata = new v3f(v3fdata);
1115 event->hudchange.sdata = new std::string(sdata);
1116 event->hudchange.data = intdata;
1117 event->hudchange.v2s32data = new v2s32(v2s32data);
1118 m_client_event_queue.push(event);
1121 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1125 *pkt >> flags >> mask;
1127 LocalPlayer *player = m_env.getLocalPlayer();
1128 assert(player != NULL);
1130 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1131 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1133 player->hud_flags &= ~mask;
1134 player->hud_flags |= flags;
1136 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1137 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1139 // Hide minimap if it has been disabled by the server
1140 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1141 // defers a minimap update, therefore only call it if really
1142 // needed, by checking that minimap was visible before
1143 m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
1145 // Switch to surface mode if radar disabled by server
1146 if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
1147 m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
1150 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1152 u16 param; std::string value;
1154 *pkt >> param >> value;
1156 LocalPlayer *player = m_env.getLocalPlayer();
1157 assert(player != NULL);
1159 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1160 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1161 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1162 player->hud_hotbar_itemcount = hotbar_itemcount;
1164 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1165 // If value not empty verify image exists in texture source
1166 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1167 errorstream << "Server sent wrong Hud hotbar image (sent value: '"
1168 << value << "')" << std::endl;
1171 player->hotbar_image = value;
1173 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1174 // If value not empty verify image exists in texture source
1175 if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
1176 errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
1177 << value << "')" << std::endl;
1180 player->hotbar_selected_image = value;
1184 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1186 std::string datastring(pkt->getString(0), pkt->getSize());
1187 std::istringstream is(datastring, std::ios_base::binary);
1189 video::SColor *bgcolor = new video::SColor(readARGB8(is));
1190 std::string *type = new std::string(deSerializeString(is));
1191 u16 count = readU16(is);
1192 std::vector<std::string> *params = new std::vector<std::string>;
1194 for (size_t i = 0; i < count; i++)
1195 params->push_back(deSerializeString(is));
1199 clouds = readU8(is);
1202 ClientEvent *event = new ClientEvent();
1203 event->type = CE_SET_SKY;
1204 event->set_sky.bgcolor = bgcolor;
1205 event->set_sky.type = type;
1206 event->set_sky.params = params;
1207 event->set_sky.clouds = clouds;
1208 m_client_event_queue.push(event);
1211 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1214 video::SColor color_bright;
1215 video::SColor color_ambient;
1220 *pkt >> density >> color_bright >> color_ambient
1221 >> height >> thickness >> speed;
1223 ClientEvent *event = new ClientEvent();
1224 event->type = CE_CLOUD_PARAMS;
1225 event->cloud_params.density = density;
1226 // use the underlying u32 representation, because we can't
1227 // use struct members with constructors here, and this way
1228 // we avoid using new() and delete() for no good reason
1229 event->cloud_params.color_bright = color_bright.color;
1230 event->cloud_params.color_ambient = color_ambient.color;
1231 event->cloud_params.height = height;
1232 event->cloud_params.thickness = thickness;
1233 // same here: deconstruct to skip constructor
1234 event->cloud_params.speed_x = speed.X;
1235 event->cloud_params.speed_y = speed.Y;
1236 m_client_event_queue.push(event);
1239 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1242 u16 day_night_ratio_u;
1244 *pkt >> do_override >> day_night_ratio_u;
1246 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1248 ClientEvent *event = new ClientEvent();
1249 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1250 event->override_day_night_ratio.do_override = do_override;
1251 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1252 m_client_event_queue.push(event);
1255 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1257 LocalPlayer *player = m_env.getLocalPlayer();
1258 assert(player != NULL);
1260 *pkt >> player->local_animations[0];
1261 *pkt >> player->local_animations[1];
1262 *pkt >> player->local_animations[2];
1263 *pkt >> player->local_animations[3];
1264 *pkt >> player->local_animation_speed;
1267 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1269 LocalPlayer *player = m_env.getLocalPlayer();
1270 assert(player != NULL);
1272 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1275 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1279 *pkt >> type >> num_players;
1280 PlayerListModifer notice_type = (PlayerListModifer) type;
1282 for (u16 i = 0; i < num_players; i++) {
1285 switch (notice_type) {
1286 case PLAYER_LIST_INIT:
1287 case PLAYER_LIST_ADD:
1288 m_env.addPlayerName(name);
1290 case PLAYER_LIST_REMOVE:
1291 m_env.removePlayerName(name);
1297 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1299 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1300 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1301 errorstream << "Client: Received SRP S_B login message,"
1302 << " but wasn't supposed to (chosen_mech="
1303 << m_chosen_auth_mech << ")." << std::endl;
1309 SRPUser *usr = (SRPUser *) m_auth_data;
1314 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1316 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1317 (const unsigned char *) B.c_str(), B.size(),
1318 (unsigned char **) &bytes_M, &len_M);
1321 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1325 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1326 resp_pkt << std::string(bytes_M, len_M);
1330 void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
1332 *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;
1339 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1341 std::string channel_name, sender, channel_msg;
1342 *pkt >> channel_name >> sender >> channel_msg;
1344 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1345 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1346 << channel_msg << std::endl;
1348 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1349 verbosestream << "Server sent us messages on unregistered channel "
1350 << channel_name << ", ignoring." << std::endl;
1354 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1357 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1360 ModChannelSignal signal;
1361 std::string channel;
1363 *pkt >> signal_tmp >> channel;
1365 signal = (ModChannelSignal)signal_tmp;
1367 bool valid_signal = true;
1368 // @TODO: send Signal to Lua API
1370 case MODCHANNEL_SIGNAL_JOIN_OK:
1371 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1372 infostream << "Server ack our mod channel join on channel `" << channel
1373 << "`, joining." << std::endl;
1375 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1376 // Unable to join, remove channel
1377 m_modchannel_mgr->leaveChannel(channel, 0);
1378 infostream << "Server refused our mod channel join on channel `" << channel
1379 << "`" << std::endl;
1381 case MODCHANNEL_SIGNAL_LEAVE_OK:
1383 infostream << "Server ack our mod channel leave on channel " << channel
1384 << "`, leaving." << std::endl;
1387 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1388 infostream << "Server refused our mod channel leave on channel `" << channel
1389 << "`" << std::endl;
1391 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1393 // Generally unused, but ensure we don't do an implementation error
1394 infostream << "Server tells us we sent a message on channel `" << channel
1395 << "` but we are not registered. Message was dropped." << std::endl;
1398 case MODCHANNEL_SIGNAL_SET_STATE: {
1402 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1403 infostream << "Received wrong channel state " << state
1404 << ", ignoring." << std::endl;
1408 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1409 infostream << "Server sets mod channel `" << channel
1410 << "` in read-only mode." << std::endl;
1415 warningstream << "Received unhandled mod channel signal ID "
1416 << signal << ", ignoring." << std::endl;
1418 valid_signal = false;
1422 // If signal is valid, forward it to client side mods
1424 m_script->on_modchannel_signal(channel, signal);