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 "chatmessage.h"
24 #include "client/clientmedia.h"
27 #include "mapsector.h"
28 #include "client/minimap.h"
29 #include "modchannels.h"
31 #include "serialization.h"
33 #include "util/strfnd.h"
34 #include "client/clientevent.h"
35 #include "client/sound.h"
36 #include "network/clientopcodes.h"
37 #include "network/connection.h"
38 #include "script/scripting_client.h"
39 #include "util/serialize.h"
41 #include "tileanimation.h"
44 void Client::handleCommand_Deprecated(NetworkPacket
* pkt
)
46 infostream
<< "Got deprecated command "
47 << toClientCommandTable
[pkt
->getCommand()].name
<< " from peer "
48 << pkt
->getPeerId() << "!" << std::endl
;
51 void Client::handleCommand_Hello(NetworkPacket
* pkt
)
53 if (pkt
->getSize() < 1)
60 std::string username_legacy
; // for case insensitivity
61 *pkt
>> serialization_ver
>> compression_mode
>> proto_ver
62 >> auth_mechs
>> username_legacy
;
64 // Chose an auth method we support
65 AuthMechanism chosen_auth_mechanism
= choseAuthMech(auth_mechs
);
67 infostream
<< "Client: TOCLIENT_HELLO received with "
68 << "serialization_ver=" << (u32
)serialization_ver
69 << ", auth_mechs=" << auth_mechs
70 << ", proto_ver=" << proto_ver
71 << ", compression_mode=" << compression_mode
72 << ". Doing auth with mech " << chosen_auth_mechanism
<< std::endl
;
74 if (!ser_ver_supported(serialization_ver
)) {
75 infostream
<< "Client: TOCLIENT_HELLO: Server sent "
76 << "unsupported ser_fmt_ver"<< std::endl
;
80 m_server_ser_ver
= serialization_ver
;
81 m_proto_ver
= proto_ver
;
83 //TODO verify that username_legacy matches sent username, only
84 // differs in casing (make both uppercase and compare)
85 // This is only neccessary though when we actually want to add casing support
87 if (m_chosen_auth_mech
!= AUTH_MECHANISM_NONE
) {
88 // we recieved a TOCLIENT_HELLO while auth was already going on
89 errorstream
<< "Client: TOCLIENT_HELLO while auth was already going on"
90 << "(chosen_mech=" << m_chosen_auth_mech
<< ")." << std::endl
;
91 if (m_chosen_auth_mech
== AUTH_MECHANISM_SRP
||
92 m_chosen_auth_mech
== AUTH_MECHANISM_LEGACY_PASSWORD
) {
93 srp_user_delete((SRPUser
*) m_auth_data
);
98 // Authenticate using that method, or abort if there wasn't any method found
99 if (chosen_auth_mechanism
!= AUTH_MECHANISM_NONE
) {
100 if (chosen_auth_mechanism
== AUTH_MECHANISM_FIRST_SRP
101 && !m_simple_singleplayer_mode
102 && g_settings
->getBool("enable_register_confirmation")) {
103 promptConfirmRegistration(chosen_auth_mechanism
);
105 startAuth(chosen_auth_mechanism
);
108 m_chosen_auth_mech
= AUTH_MECHANISM_NONE
;
109 m_access_denied
= true;
110 m_access_denied_reason
= "Unknown";
116 void Client::handleCommand_AuthAccept(NetworkPacket
* pkt
)
121 *pkt
>> playerpos
>> m_map_seed
>> m_recommended_send_interval
122 >> m_sudo_auth_methods
;
124 playerpos
-= v3f(0, BS
/ 2, 0);
126 // Set player position
127 LocalPlayer
*player
= m_env
.getLocalPlayer();
128 assert(player
!= NULL
);
129 player
->setPosition(playerpos
);
131 infostream
<< "Client: received map seed: " << m_map_seed
<< std::endl
;
132 infostream
<< "Client: received recommended send interval "
133 << m_recommended_send_interval
<<std::endl
;
136 std::string lang
= gettext("LANG_CODE");
137 if (lang
== "LANG_CODE")
140 NetworkPacket
resp_pkt(TOSERVER_INIT2
, sizeof(u16
) + lang
.size());
146 void Client::handleCommand_AcceptSudoMode(NetworkPacket
* pkt
)
150 m_password
= m_new_password
;
152 verbosestream
<< "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl
;
154 // send packet to actually set the password
155 startAuth(AUTH_MECHANISM_FIRST_SRP
);
158 m_chosen_auth_mech
= AUTH_MECHANISM_NONE
;
160 void Client::handleCommand_DenySudoMode(NetworkPacket
* pkt
)
162 ChatMessage
*chatMessage
= new ChatMessage(CHATMESSAGE_TYPE_SYSTEM
,
163 L
"Password change denied. Password NOT changed.");
164 pushToChatQueue(chatMessage
);
165 // reset everything and be sad
169 void Client::handleCommand_AccessDenied(NetworkPacket
* pkt
)
171 // The server didn't like our password. Note, this needs
172 // to be processed even if the serialisation format has
173 // not been agreed yet, the same as TOCLIENT_INIT.
174 m_access_denied
= true;
175 m_access_denied_reason
= "Unknown";
177 if (pkt
->getCommand() != TOCLIENT_ACCESS_DENIED
) {
178 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
179 // in some places of the server code
180 if (pkt
->getSize() >= 2) {
181 std::wstring wide_reason
;
183 m_access_denied_reason
= wide_to_utf8(wide_reason
);
188 if (pkt
->getSize() < 1)
191 u8 denyCode
= SERVER_ACCESSDENIED_UNEXPECTED_DATA
;
193 if (denyCode
== SERVER_ACCESSDENIED_SHUTDOWN
||
194 denyCode
== SERVER_ACCESSDENIED_CRASH
) {
195 *pkt
>> m_access_denied_reason
;
196 if (m_access_denied_reason
.empty()) {
197 m_access_denied_reason
= accessDeniedStrings
[denyCode
];
201 m_access_denied_reconnect
= reconnect
& 1;
202 } else if (denyCode
== SERVER_ACCESSDENIED_CUSTOM_STRING
) {
203 *pkt
>> m_access_denied_reason
;
204 } else if (denyCode
< SERVER_ACCESSDENIED_MAX
) {
205 m_access_denied_reason
= accessDeniedStrings
[denyCode
];
207 // Allow us to add new error messages to the
208 // protocol without raising the protocol version, if we want to.
209 // Until then (which may be never), this is outside
210 // of the defined protocol.
211 *pkt
>> m_access_denied_reason
;
212 if (m_access_denied_reason
.empty()) {
213 m_access_denied_reason
= "Unknown";
218 void Client::handleCommand_RemoveNode(NetworkPacket
* pkt
)
220 if (pkt
->getSize() < 6)
228 void Client::handleCommand_AddNode(NetworkPacket
* pkt
)
230 if (pkt
->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver
))
237 n
.deSerialize(pkt
->getU8Ptr(6), m_server_ser_ver
);
239 bool remove_metadata
= true;
240 u32 index
= 6 + MapNode::serializedLength(m_server_ser_ver
);
241 if ((pkt
->getSize() >= index
+ 1) && pkt
->getU8(index
)) {
242 remove_metadata
= false;
245 addNode(p
, n
, remove_metadata
);
248 void Client::handleCommand_NodemetaChanged(NetworkPacket
*pkt
)
250 if (pkt
->getSize() < 1)
253 std::istringstream
is(pkt
->readLongString(), std::ios::binary
);
254 std::stringstream sstr
;
255 decompressZlib(is
, sstr
);
257 NodeMetadataList
meta_updates_list(false);
258 meta_updates_list
.deSerialize(sstr
, m_itemdef
, true);
260 Map
&map
= m_env
.getMap();
261 for (NodeMetadataMap::const_iterator i
= meta_updates_list
.begin();
262 i
!= meta_updates_list
.end(); ++i
) {
263 v3s16 pos
= i
->first
;
265 if (map
.isValidPosition(pos
) &&
266 map
.setNodeMetadata(pos
, i
->second
))
267 continue; // Prevent from deleting metadata
269 // Meta couldn't be set, unused metadata
274 void Client::handleCommand_BlockData(NetworkPacket
* pkt
)
276 // Ignore too small packet
277 if (pkt
->getSize() < 6)
283 std::string
datastring(pkt
->getString(6), pkt
->getSize() - 6);
284 std::istringstream
istr(datastring
, std::ios_base::binary
);
290 sector
= m_env
.getMap().emergeSector(p2d
);
292 assert(sector
->getPos() == p2d
);
294 block
= sector
->getBlockNoCreateNoEx(p
.Y
);
297 Update an existing block
299 block
->deSerialize(istr
, m_server_ser_ver
, false);
300 block
->deSerializeNetworkSpecific(istr
);
306 block
= new MapBlock(&m_env
.getMap(), p
, this);
307 block
->deSerialize(istr
, m_server_ser_ver
, false);
308 block
->deSerializeNetworkSpecific(istr
);
309 sector
->insertBlock(block
);
313 ServerMap::saveBlock(block
, m_localdb
);
317 Add it to mesh update queue and set it to be acknowledged after update.
319 addUpdateMeshTaskWithEdge(p
, true);
322 void Client::handleCommand_Inventory(NetworkPacket
* pkt
)
324 if (pkt
->getSize() < 1)
327 std::string
datastring(pkt
->getString(0), pkt
->getSize());
328 std::istringstream
is(datastring
, std::ios_base::binary
);
330 LocalPlayer
*player
= m_env
.getLocalPlayer();
331 assert(player
!= NULL
);
333 player
->inventory
.deSerialize(is
);
335 m_inventory_updated
= true;
337 delete m_inventory_from_server
;
338 m_inventory_from_server
= new Inventory(player
->inventory
);
339 m_inventory_from_server_age
= 0.0;
342 void Client::handleCommand_TimeOfDay(NetworkPacket
* pkt
)
344 if (pkt
->getSize() < 2)
351 time_of_day
= time_of_day
% 24000;
352 float time_speed
= 0;
354 if (pkt
->getSize() >= 2 + 4) {
358 // Old message; try to approximate speed of time by ourselves
359 float time_of_day_f
= (float)time_of_day
/ 24000.0f
;
360 float tod_diff_f
= 0;
362 if (time_of_day_f
< 0.2 && m_last_time_of_day_f
> 0.8)
363 tod_diff_f
= time_of_day_f
- m_last_time_of_day_f
+ 1.0f
;
365 tod_diff_f
= time_of_day_f
- m_last_time_of_day_f
;
367 m_last_time_of_day_f
= time_of_day_f
;
368 float time_diff
= m_time_of_day_update_timer
;
369 m_time_of_day_update_timer
= 0;
371 if (m_time_of_day_set
) {
372 time_speed
= (3600.0f
* 24.0f
) * tod_diff_f
/ time_diff
;
373 infostream
<< "Client: Measured time_of_day speed (old format): "
374 << time_speed
<< " tod_diff_f=" << tod_diff_f
375 << " time_diff=" << time_diff
<< std::endl
;
379 // Update environment
380 m_env
.setTimeOfDay(time_of_day
);
381 m_env
.setTimeOfDaySpeed(time_speed
);
382 m_time_of_day_set
= true;
384 u32 dr
= m_env
.getDayNightRatio();
385 infostream
<< "Client: time_of_day=" << time_of_day
386 << " time_speed=" << time_speed
387 << " dr=" << dr
<< std::endl
;
390 void Client::handleCommand_ChatMessage(NetworkPacket
*pkt
)
395 u16 sendername length
401 ChatMessage
*chatMessage
= new ChatMessage();
402 u8 version
, message_type
;
403 *pkt
>> version
>> message_type
;
405 if (version
!= 1 || message_type
>= CHATMESSAGE_TYPE_MAX
) {
410 *pkt
>> chatMessage
->sender
>> chatMessage
->message
>> chatMessage
->timestamp
;
412 chatMessage
->type
= (ChatMessageType
) message_type
;
414 // @TODO send this to CSM using ChatMessage object
415 if (moddingEnabled() && m_script
->on_receiving_message(
416 wide_to_utf8(chatMessage
->message
))) {
417 // Message was consumed by CSM and should not be handled by client
420 pushToChatQueue(chatMessage
);
424 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket
* pkt
)
427 u16 count of removed objects
428 for all removed objects {
431 u16 count of added objects
432 for all added objects {
435 u32 initialization data length
436 string initialization data
442 u16 removed_count
, added_count
, id
;
444 // Read removed objects
445 *pkt
>> removed_count
;
447 for (u16 i
= 0; i
< removed_count
; i
++) {
449 m_env
.removeActiveObject(id
);
452 // Read added objects
455 for (u16 i
= 0; i
< added_count
; i
++) {
457 m_env
.addActiveObject(id
, type
, pkt
->readLongString());
459 } catch (PacketError
&e
) {
460 infostream
<< "handleCommand_ActiveObjectRemoveAdd: " << e
.what()
461 << ". The packet is unreliable, ignoring" << std::endl
;
465 void Client::handleCommand_ActiveObjectMessages(NetworkPacket
* pkt
)
475 std::string
datastring(pkt
->getString(0), pkt
->getSize());
476 std::istringstream
is(datastring
, std::ios_base::binary
);
480 u16 id
= readU16(is
);
484 std::string message
= deSerializeString(is
);
486 // Pass on to the environment
487 m_env
.processActiveObjectMessage(id
, message
);
489 } catch (SerializationError
&e
) {
490 errorstream
<< "Client::handleCommand_ActiveObjectMessages: "
491 << "caught SerializationError: " << e
.what() << std::endl
;
495 void Client::handleCommand_Movement(NetworkPacket
* pkt
)
497 LocalPlayer
*player
= m_env
.getLocalPlayer();
498 assert(player
!= NULL
);
500 float mad
, maa
, maf
, msw
, mscr
, msf
, mscl
, msj
, lf
, lfs
, ls
, g
;
502 *pkt
>> mad
>> maa
>> maf
>> msw
>> mscr
>> msf
>> mscl
>> msj
503 >> lf
>> lfs
>> ls
>> g
;
505 player
->movement_acceleration_default
= mad
* BS
;
506 player
->movement_acceleration_air
= maa
* BS
;
507 player
->movement_acceleration_fast
= maf
* BS
;
508 player
->movement_speed_walk
= msw
* BS
;
509 player
->movement_speed_crouch
= mscr
* BS
;
510 player
->movement_speed_fast
= msf
* BS
;
511 player
->movement_speed_climb
= mscl
* BS
;
512 player
->movement_speed_jump
= msj
* BS
;
513 player
->movement_liquid_fluidity
= lf
* BS
;
514 player
->movement_liquid_fluidity_smooth
= lfs
* BS
;
515 player
->movement_liquid_sink
= ls
* BS
;
516 player
->movement_gravity
= g
* BS
;
519 void Client::handleCommand_HP(NetworkPacket
* pkt
)
522 LocalPlayer
*player
= m_env
.getLocalPlayer();
523 assert(player
!= NULL
);
525 u16 oldhp
= player
->hp
;
532 if (moddingEnabled()) {
533 m_script
->on_hp_modification(hp
);
537 // Add to ClientEvent queue
538 ClientEvent
*event
= new ClientEvent();
539 event
->type
= CE_PLAYER_DAMAGE
;
540 event
->player_damage
.amount
= oldhp
- hp
;
541 m_client_event_queue
.push(event
);
545 void Client::handleCommand_Breath(NetworkPacket
* pkt
)
547 LocalPlayer
*player
= m_env
.getLocalPlayer();
548 assert(player
!= NULL
);
554 player
->setBreath(breath
);
557 void Client::handleCommand_MovePlayer(NetworkPacket
* pkt
)
559 LocalPlayer
*player
= m_env
.getLocalPlayer();
560 assert(player
!= NULL
);
565 *pkt
>> pos
>> pitch
>> yaw
;
567 player
->setPosition(pos
);
569 infostream
<< "Client got TOCLIENT_MOVE_PLAYER"
570 << " pos=(" << pos
.X
<< "," << pos
.Y
<< "," << pos
.Z
<< ")"
571 << " pitch=" << pitch
576 Add to ClientEvent queue.
577 This has to be sent to the main program because otherwise
578 it would just force the pitch and yaw values to whatever
579 the camera points to.
581 ClientEvent
*event
= new ClientEvent();
582 event
->type
= CE_PLAYER_FORCE_MOVE
;
583 event
->player_force_move
.pitch
= pitch
;
584 event
->player_force_move
.yaw
= yaw
;
585 m_client_event_queue
.push(event
);
588 void Client::handleCommand_DeathScreen(NetworkPacket
* pkt
)
590 bool set_camera_point_target
;
591 v3f camera_point_target
;
593 *pkt
>> set_camera_point_target
;
594 *pkt
>> camera_point_target
;
596 ClientEvent
*event
= new ClientEvent();
597 event
->type
= CE_DEATHSCREEN
;
598 event
->deathscreen
.set_camera_point_target
= set_camera_point_target
;
599 event
->deathscreen
.camera_point_target_x
= camera_point_target
.X
;
600 event
->deathscreen
.camera_point_target_y
= camera_point_target
.Y
;
601 event
->deathscreen
.camera_point_target_z
= camera_point_target
.Z
;
602 m_client_event_queue
.push(event
);
605 void Client::handleCommand_AnnounceMedia(NetworkPacket
* pkt
)
611 infostream
<< "Client: Received media announcement: packet size: "
612 << pkt
->getSize() << std::endl
;
614 if (m_media_downloader
== NULL
||
615 m_media_downloader
->isStarted()) {
616 const char *problem
= m_media_downloader
?
617 "we already saw another announcement" :
618 "all media has been received already";
619 errorstream
<< "Client: Received media announcement but "
621 << " files=" << num_files
622 << " size=" << pkt
->getSize() << std::endl
;
626 // Mesh update thread must be stopped while
627 // updating content definitions
628 sanity_check(!m_mesh_update_thread
.isRunning());
630 for (u16 i
= 0; i
< num_files
; i
++) {
631 std::string name
, sha1_base64
;
633 *pkt
>> name
>> sha1_base64
;
635 std::string sha1_raw
= base64_decode(sha1_base64
);
636 m_media_downloader
->addFile(name
, sha1_raw
);
645 while(!sf
.at_end()) {
646 std::string baseurl
= trim(sf
.next(","));
647 if (!baseurl
.empty())
648 m_media_downloader
->addRemoteServer(baseurl
);
651 catch(SerializationError
& e
) {
652 // not supported by server or turned off
655 m_media_downloader
->step(this);
658 void Client::handleCommand_Media(NetworkPacket
* pkt
)
662 u16 total number of file bunches
663 u16 index of this bunch
664 u32 number of files in this bunch
676 *pkt
>> num_bunches
>> bunch_i
>> num_files
;
678 infostream
<< "Client: Received files: bunch " << bunch_i
<< "/"
679 << num_bunches
<< " files=" << num_files
680 << " size=" << pkt
->getSize() << std::endl
;
685 if (!m_media_downloader
|| !m_media_downloader
->isStarted()) {
686 const char *problem
= m_media_downloader
?
687 "media has not been requested" :
688 "all media has been received already";
689 errorstream
<< "Client: Received media but "
691 << " bunch " << bunch_i
<< "/" << num_bunches
692 << " files=" << num_files
693 << " size=" << pkt
->getSize() << std::endl
;
697 // Mesh update thread must be stopped while
698 // updating content definitions
699 sanity_check(!m_mesh_update_thread
.isRunning());
701 for (u32 i
=0; i
< num_files
; i
++) {
706 std::string data
= pkt
->readLongString();
708 m_media_downloader
->conventionalTransferDone(
713 void Client::handleCommand_NodeDef(NetworkPacket
* pkt
)
715 infostream
<< "Client: Received node definitions: packet size: "
716 << pkt
->getSize() << std::endl
;
718 // Mesh update thread must be stopped while
719 // updating content definitions
720 sanity_check(!m_mesh_update_thread
.isRunning());
722 // Decompress node definitions
723 std::istringstream
tmp_is(pkt
->readLongString(), std::ios::binary
);
724 std::ostringstream tmp_os
;
725 decompressZlib(tmp_is
, tmp_os
);
727 // Deserialize node definitions
728 std::istringstream
tmp_is2(tmp_os
.str());
729 m_nodedef
->deSerialize(tmp_is2
);
730 m_nodedef_received
= true;
733 void Client::handleCommand_ItemDef(NetworkPacket
* pkt
)
735 infostream
<< "Client: Received item definitions: packet size: "
736 << pkt
->getSize() << std::endl
;
738 // Mesh update thread must be stopped while
739 // updating content definitions
740 sanity_check(!m_mesh_update_thread
.isRunning());
742 // Decompress item definitions
743 std::istringstream
tmp_is(pkt
->readLongString(), std::ios::binary
);
744 std::ostringstream tmp_os
;
745 decompressZlib(tmp_is
, tmp_os
);
747 // Deserialize node definitions
748 std::istringstream
tmp_is2(tmp_os
.str());
749 m_itemdef
->deSerialize(tmp_is2
);
750 m_itemdef_received
= true;
753 void Client::handleCommand_PlaySound(NetworkPacket
* pkt
)
761 [11 + len] (f32 * 3) pos
762 [23 + len] u16 object_id
772 u8 type
; // 0=local, 1=positional, 2=object
779 *pkt
>> server_id
>> name
>> gain
>> type
>> pos
>> object_id
>> loop
;
784 } catch (PacketError
&e
) {};
790 client_id
= m_sound
->playSound(name
, loop
, gain
, fade
, pitch
);
792 case 1: // positional
793 client_id
= m_sound
->playSoundAt(name
, loop
, gain
, pos
, pitch
);
797 ClientActiveObject
*cao
= m_env
.getActiveObject(object_id
);
799 pos
= cao
->getPosition();
800 client_id
= m_sound
->playSoundAt(name
, loop
, gain
, pos
, pitch
);
801 // TODO: Set up sound to move with object
808 if (client_id
!= -1) {
809 m_sounds_server_to_client
[server_id
] = client_id
;
810 m_sounds_client_to_server
[client_id
] = server_id
;
812 m_sounds_to_objects
[client_id
] = object_id
;
816 void Client::handleCommand_StopSound(NetworkPacket
* pkt
)
822 std::unordered_map
<s32
, int>::iterator i
= m_sounds_server_to_client
.find(server_id
);
823 if (i
!= m_sounds_server_to_client
.end()) {
824 int client_id
= i
->second
;
825 m_sound
->stopSound(client_id
);
829 void Client::handleCommand_FadeSound(NetworkPacket
*pkt
)
835 *pkt
>> sound_id
>> step
>> gain
;
837 std::unordered_map
<s32
, int>::const_iterator i
=
838 m_sounds_server_to_client
.find(sound_id
);
840 if (i
!= m_sounds_server_to_client
.end())
841 m_sound
->fadeSound(i
->second
, step
, gain
);
844 void Client::handleCommand_Privileges(NetworkPacket
* pkt
)
846 m_privileges
.clear();
847 infostream
<< "Client: Privileges updated: ";
850 *pkt
>> num_privileges
;
852 for (u16 i
= 0; i
< num_privileges
; i
++) {
857 m_privileges
.insert(priv
);
858 infostream
<< priv
<< " ";
860 infostream
<< std::endl
;
863 void Client::handleCommand_InventoryFormSpec(NetworkPacket
* pkt
)
865 LocalPlayer
*player
= m_env
.getLocalPlayer();
866 assert(player
!= NULL
);
868 // Store formspec in LocalPlayer
869 player
->inventory_formspec
= pkt
->readLongString();
872 void Client::handleCommand_DetachedInventory(NetworkPacket
* pkt
)
875 bool keep_inv
= true;
876 *pkt
>> name
>> keep_inv
;
878 infostream
<< "Client: Detached inventory update: \"" << name
879 << "\", mode=" << (keep_inv
? "update" : "remove") << std::endl
;
881 const auto &inv_it
= m_detached_inventories
.find(name
);
883 if (inv_it
!= m_detached_inventories
.end()) {
884 delete inv_it
->second
;
885 m_detached_inventories
.erase(inv_it
);
889 Inventory
*inv
= nullptr;
890 if (inv_it
== m_detached_inventories
.end()) {
891 inv
= new Inventory(m_itemdef
);
892 m_detached_inventories
[name
] = inv
;
894 inv
= inv_it
->second
;
897 std::string contents
;
899 std::istringstream
is(contents
, std::ios::binary
);
900 inv
->deSerialize(is
);
903 void Client::handleCommand_ShowFormSpec(NetworkPacket
* pkt
)
905 std::string formspec
= pkt
->readLongString();
906 std::string formname
;
910 ClientEvent
*event
= new ClientEvent();
911 event
->type
= CE_SHOW_FORMSPEC
;
912 // pointer is required as event is a struct only!
913 // adding a std:string to a struct isn't possible
914 event
->show_formspec
.formspec
= new std::string(formspec
);
915 event
->show_formspec
.formname
= new std::string(formname
);
916 m_client_event_queue
.push(event
);
919 void Client::handleCommand_SpawnParticle(NetworkPacket
* pkt
)
921 std::string
datastring(pkt
->getString(0), pkt
->getSize());
922 std::istringstream
is(datastring
, std::ios_base::binary
);
924 v3f pos
= readV3F32(is
);
925 v3f vel
= readV3F32(is
);
926 v3f acc
= readV3F32(is
);
927 float expirationtime
= readF32(is
);
928 float size
= readF32(is
);
929 bool collisiondetection
= readU8(is
);
930 std::string texture
= deSerializeLongString(is
);
932 bool vertical
= false;
933 bool collision_removal
= false;
934 TileAnimationParams animation
;
935 animation
.type
= TAT_NONE
;
937 bool object_collision
= false;
939 vertical
= readU8(is
);
940 collision_removal
= readU8(is
);
941 animation
.deSerialize(is
, m_proto_ver
);
943 object_collision
= readU8(is
);
946 ClientEvent
*event
= new ClientEvent();
947 event
->type
= CE_SPAWN_PARTICLE
;
948 event
->spawn_particle
.pos
= new v3f (pos
);
949 event
->spawn_particle
.vel
= new v3f (vel
);
950 event
->spawn_particle
.acc
= new v3f (acc
);
951 event
->spawn_particle
.expirationtime
= expirationtime
;
952 event
->spawn_particle
.size
= size
;
953 event
->spawn_particle
.collisiondetection
= collisiondetection
;
954 event
->spawn_particle
.collision_removal
= collision_removal
;
955 event
->spawn_particle
.object_collision
= object_collision
;
956 event
->spawn_particle
.vertical
= vertical
;
957 event
->spawn_particle
.texture
= new std::string(texture
);
958 event
->spawn_particle
.animation
= animation
;
959 event
->spawn_particle
.glow
= glow
;
961 m_client_event_queue
.push(event
);
964 void Client::handleCommand_AddParticleSpawner(NetworkPacket
* pkt
)
978 bool collisiondetection
;
981 *pkt
>> amount
>> spawntime
>> minpos
>> maxpos
>> minvel
>> maxvel
982 >> minacc
>> maxacc
>> minexptime
>> maxexptime
>> minsize
983 >> maxsize
>> collisiondetection
;
985 std::string texture
= pkt
->readLongString();
989 bool vertical
= false;
990 bool collision_removal
= false;
992 TileAnimationParams animation
;
993 animation
.type
= TAT_NONE
;
995 bool object_collision
= false;
998 *pkt
>> collision_removal
;
1001 // This is horrible but required (why are there two ways to deserialize pkts?)
1002 std::string
datastring(pkt
->getRemainingString(), pkt
->getRemainingBytes());
1003 std::istringstream
is(datastring
, std::ios_base::binary
);
1004 animation
.deSerialize(is
, m_proto_ver
);
1006 object_collision
= readU8(is
);
1009 u32 client_id
= m_particle_manager
.getSpawnerId();
1010 m_particles_server_to_client
[server_id
] = client_id
;
1012 ClientEvent
*event
= new ClientEvent();
1013 event
->type
= CE_ADD_PARTICLESPAWNER
;
1014 event
->add_particlespawner
.amount
= amount
;
1015 event
->add_particlespawner
.spawntime
= spawntime
;
1016 event
->add_particlespawner
.minpos
= new v3f (minpos
);
1017 event
->add_particlespawner
.maxpos
= new v3f (maxpos
);
1018 event
->add_particlespawner
.minvel
= new v3f (minvel
);
1019 event
->add_particlespawner
.maxvel
= new v3f (maxvel
);
1020 event
->add_particlespawner
.minacc
= new v3f (minacc
);
1021 event
->add_particlespawner
.maxacc
= new v3f (maxacc
);
1022 event
->add_particlespawner
.minexptime
= minexptime
;
1023 event
->add_particlespawner
.maxexptime
= maxexptime
;
1024 event
->add_particlespawner
.minsize
= minsize
;
1025 event
->add_particlespawner
.maxsize
= maxsize
;
1026 event
->add_particlespawner
.collisiondetection
= collisiondetection
;
1027 event
->add_particlespawner
.collision_removal
= collision_removal
;
1028 event
->add_particlespawner
.object_collision
= object_collision
;
1029 event
->add_particlespawner
.attached_id
= attached_id
;
1030 event
->add_particlespawner
.vertical
= vertical
;
1031 event
->add_particlespawner
.texture
= new std::string(texture
);
1032 event
->add_particlespawner
.id
= client_id
;
1033 event
->add_particlespawner
.animation
= animation
;
1034 event
->add_particlespawner
.glow
= glow
;
1036 m_client_event_queue
.push(event
);
1040 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket
* pkt
)
1046 auto i
= m_particles_server_to_client
.find(server_id
);
1047 if (i
!= m_particles_server_to_client
.end())
1048 client_id
= i
->second
;
1052 ClientEvent
*event
= new ClientEvent();
1053 event
->type
= CE_DELETE_PARTICLESPAWNER
;
1054 event
->delete_particlespawner
.id
= client_id
;
1056 m_client_event_queue
.push(event
);
1059 void Client::handleCommand_HudAdd(NetworkPacket
* pkt
)
1061 std::string
datastring(pkt
->getString(0), pkt
->getSize());
1062 std::istringstream
is(datastring
, std::ios_base::binary
);
1078 *pkt
>> server_id
>> type
>> pos
>> name
>> scale
>> text
>> number
>> item
1079 >> dir
>> align
>> offset
;
1083 catch(SerializationError
&e
) {};
1087 } catch(SerializationError
&e
) {};
1089 ClientEvent
*event
= new ClientEvent();
1090 event
->type
= CE_HUDADD
;
1091 event
->hudadd
.server_id
= server_id
;
1092 event
->hudadd
.type
= type
;
1093 event
->hudadd
.pos
= new v2f(pos
);
1094 event
->hudadd
.name
= new std::string(name
);
1095 event
->hudadd
.scale
= new v2f(scale
);
1096 event
->hudadd
.text
= new std::string(text
);
1097 event
->hudadd
.number
= number
;
1098 event
->hudadd
.item
= item
;
1099 event
->hudadd
.dir
= dir
;
1100 event
->hudadd
.align
= new v2f(align
);
1101 event
->hudadd
.offset
= new v2f(offset
);
1102 event
->hudadd
.world_pos
= new v3f(world_pos
);
1103 event
->hudadd
.size
= new v2s32(size
);
1104 m_client_event_queue
.push(event
);
1107 void Client::handleCommand_HudRemove(NetworkPacket
* pkt
)
1113 auto i
= m_hud_server_to_client
.find(server_id
);
1114 if (i
!= m_hud_server_to_client
.end()) {
1115 int client_id
= i
->second
;
1116 m_hud_server_to_client
.erase(i
);
1118 ClientEvent
*event
= new ClientEvent();
1119 event
->type
= CE_HUDRM
;
1120 event
->hudrm
.id
= client_id
;
1121 m_client_event_queue
.push(event
);
1125 void Client::handleCommand_HudChange(NetworkPacket
* pkt
)
1135 *pkt
>> server_id
>> stat
;
1137 if (stat
== HUD_STAT_POS
|| stat
== HUD_STAT_SCALE
||
1138 stat
== HUD_STAT_ALIGN
|| stat
== HUD_STAT_OFFSET
)
1140 else if (stat
== HUD_STAT_NAME
|| stat
== HUD_STAT_TEXT
)
1142 else if (stat
== HUD_STAT_WORLD_POS
)
1144 else if (stat
== HUD_STAT_SIZE
)
1149 std::unordered_map
<u32
, u32
>::const_iterator i
= m_hud_server_to_client
.find(server_id
);
1150 if (i
!= m_hud_server_to_client
.end()) {
1151 ClientEvent
*event
= new ClientEvent();
1152 event
->type
= CE_HUDCHANGE
;
1153 event
->hudchange
.id
= i
->second
;
1154 event
->hudchange
.stat
= (HudElementStat
)stat
;
1155 event
->hudchange
.v2fdata
= new v2f(v2fdata
);
1156 event
->hudchange
.v3fdata
= new v3f(v3fdata
);
1157 event
->hudchange
.sdata
= new std::string(sdata
);
1158 event
->hudchange
.data
= intdata
;
1159 event
->hudchange
.v2s32data
= new v2s32(v2s32data
);
1160 m_client_event_queue
.push(event
);
1164 void Client::handleCommand_HudSetFlags(NetworkPacket
* pkt
)
1168 *pkt
>> flags
>> mask
;
1170 LocalPlayer
*player
= m_env
.getLocalPlayer();
1171 assert(player
!= NULL
);
1173 bool was_minimap_visible
= player
->hud_flags
& HUD_FLAG_MINIMAP_VISIBLE
;
1174 bool was_minimap_radar_visible
= player
->hud_flags
& HUD_FLAG_MINIMAP_RADAR_VISIBLE
;
1176 player
->hud_flags
&= ~mask
;
1177 player
->hud_flags
|= flags
;
1179 m_minimap_disabled_by_server
= !(player
->hud_flags
& HUD_FLAG_MINIMAP_VISIBLE
);
1180 bool m_minimap_radar_disabled_by_server
= !(player
->hud_flags
& HUD_FLAG_MINIMAP_RADAR_VISIBLE
);
1182 // Hide minimap if it has been disabled by the server
1183 if (m_minimap
&& m_minimap_disabled_by_server
&& was_minimap_visible
)
1184 // defers a minimap update, therefore only call it if really
1185 // needed, by checking that minimap was visible before
1186 m_minimap
->setMinimapMode(MINIMAP_MODE_OFF
);
1188 // Switch to surface mode if radar disabled by server
1189 if (m_minimap
&& m_minimap_radar_disabled_by_server
&& was_minimap_radar_visible
)
1190 m_minimap
->setMinimapMode(MINIMAP_MODE_SURFACEx1
);
1193 void Client::handleCommand_HudSetParam(NetworkPacket
* pkt
)
1195 u16 param
; std::string value
;
1197 *pkt
>> param
>> value
;
1199 LocalPlayer
*player
= m_env
.getLocalPlayer();
1200 assert(player
!= NULL
);
1202 if (param
== HUD_PARAM_HOTBAR_ITEMCOUNT
&& value
.size() == 4) {
1203 s32 hotbar_itemcount
= readS32((u8
*) value
.c_str());
1204 if (hotbar_itemcount
> 0 && hotbar_itemcount
<= HUD_HOTBAR_ITEMCOUNT_MAX
)
1205 player
->hud_hotbar_itemcount
= hotbar_itemcount
;
1207 else if (param
== HUD_PARAM_HOTBAR_IMAGE
) {
1208 // If value not empty verify image exists in texture source
1209 if (!value
.empty() && !getTextureSource()->isKnownSourceImage(value
)) {
1210 errorstream
<< "Server sent wrong Hud hotbar image (sent value: '"
1211 << value
<< "')" << std::endl
;
1214 player
->hotbar_image
= value
;
1216 else if (param
== HUD_PARAM_HOTBAR_SELECTED_IMAGE
) {
1217 // If value not empty verify image exists in texture source
1218 if (!value
.empty() && !getTextureSource()->isKnownSourceImage(value
)) {
1219 errorstream
<< "Server sent wrong Hud hotbar selected image (sent value: '"
1220 << value
<< "')" << std::endl
;
1223 player
->hotbar_selected_image
= value
;
1227 void Client::handleCommand_HudSetSky(NetworkPacket
* pkt
)
1229 std::string
datastring(pkt
->getString(0), pkt
->getSize());
1230 std::istringstream
is(datastring
, std::ios_base::binary
);
1232 video::SColor
*bgcolor
= new video::SColor(readARGB8(is
));
1233 std::string
*type
= new std::string(deSerializeString(is
));
1234 u16 count
= readU16(is
);
1235 std::vector
<std::string
> *params
= new std::vector
<std::string
>;
1237 for (size_t i
= 0; i
< count
; i
++)
1238 params
->push_back(deSerializeString(is
));
1242 clouds
= readU8(is
);
1245 ClientEvent
*event
= new ClientEvent();
1246 event
->type
= CE_SET_SKY
;
1247 event
->set_sky
.bgcolor
= bgcolor
;
1248 event
->set_sky
.type
= type
;
1249 event
->set_sky
.params
= params
;
1250 event
->set_sky
.clouds
= clouds
;
1251 m_client_event_queue
.push(event
);
1254 void Client::handleCommand_CloudParams(NetworkPacket
* pkt
)
1257 video::SColor color_bright
;
1258 video::SColor color_ambient
;
1263 *pkt
>> density
>> color_bright
>> color_ambient
1264 >> height
>> thickness
>> speed
;
1266 ClientEvent
*event
= new ClientEvent();
1267 event
->type
= CE_CLOUD_PARAMS
;
1268 event
->cloud_params
.density
= density
;
1269 // use the underlying u32 representation, because we can't
1270 // use struct members with constructors here, and this way
1271 // we avoid using new() and delete() for no good reason
1272 event
->cloud_params
.color_bright
= color_bright
.color
;
1273 event
->cloud_params
.color_ambient
= color_ambient
.color
;
1274 event
->cloud_params
.height
= height
;
1275 event
->cloud_params
.thickness
= thickness
;
1276 // same here: deconstruct to skip constructor
1277 event
->cloud_params
.speed_x
= speed
.X
;
1278 event
->cloud_params
.speed_y
= speed
.Y
;
1279 m_client_event_queue
.push(event
);
1282 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket
* pkt
)
1285 u16 day_night_ratio_u
;
1287 *pkt
>> do_override
>> day_night_ratio_u
;
1289 float day_night_ratio_f
= (float)day_night_ratio_u
/ 65536;
1291 ClientEvent
*event
= new ClientEvent();
1292 event
->type
= CE_OVERRIDE_DAY_NIGHT_RATIO
;
1293 event
->override_day_night_ratio
.do_override
= do_override
;
1294 event
->override_day_night_ratio
.ratio_f
= day_night_ratio_f
;
1295 m_client_event_queue
.push(event
);
1298 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket
* pkt
)
1300 LocalPlayer
*player
= m_env
.getLocalPlayer();
1301 assert(player
!= NULL
);
1303 *pkt
>> player
->local_animations
[0];
1304 *pkt
>> player
->local_animations
[1];
1305 *pkt
>> player
->local_animations
[2];
1306 *pkt
>> player
->local_animations
[3];
1307 *pkt
>> player
->local_animation_speed
;
1310 void Client::handleCommand_EyeOffset(NetworkPacket
* pkt
)
1312 LocalPlayer
*player
= m_env
.getLocalPlayer();
1313 assert(player
!= NULL
);
1315 *pkt
>> player
->eye_offset_first
>> player
->eye_offset_third
;
1318 void Client::handleCommand_UpdatePlayerList(NetworkPacket
* pkt
)
1322 *pkt
>> type
>> num_players
;
1323 PlayerListModifer notice_type
= (PlayerListModifer
) type
;
1325 for (u16 i
= 0; i
< num_players
; i
++) {
1328 switch (notice_type
) {
1329 case PLAYER_LIST_INIT
:
1330 case PLAYER_LIST_ADD
:
1331 m_env
.addPlayerName(name
);
1333 case PLAYER_LIST_REMOVE
:
1334 m_env
.removePlayerName(name
);
1340 void Client::handleCommand_SrpBytesSandB(NetworkPacket
* pkt
)
1342 if (m_chosen_auth_mech
!= AUTH_MECHANISM_SRP
&&
1343 m_chosen_auth_mech
!= AUTH_MECHANISM_LEGACY_PASSWORD
) {
1344 errorstream
<< "Client: Received SRP S_B login message,"
1345 << " but wasn't supposed to (chosen_mech="
1346 << m_chosen_auth_mech
<< ")." << std::endl
;
1352 SRPUser
*usr
= (SRPUser
*) m_auth_data
;
1357 infostream
<< "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl
;
1359 srp_user_process_challenge(usr
, (const unsigned char *) s
.c_str(), s
.size(),
1360 (const unsigned char *) B
.c_str(), B
.size(),
1361 (unsigned char **) &bytes_M
, &len_M
);
1364 errorstream
<< "Client: SRP-6a S_B safety check violation!" << std::endl
;
1368 NetworkPacket
resp_pkt(TOSERVER_SRP_BYTES_M
, 0);
1369 resp_pkt
<< std::string(bytes_M
, len_M
);
1373 void Client::handleCommand_FormspecPrepend(NetworkPacket
*pkt
)
1375 LocalPlayer
*player
= m_env
.getLocalPlayer();
1376 assert(player
!= NULL
);
1378 // Store formspec in LocalPlayer
1379 *pkt
>> player
->formspec_prepend
;
1382 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket
*pkt
)
1384 *pkt
>> m_csm_restriction_flags
>> m_csm_restriction_noderange
;
1386 // Restrictions were received -> load mods if it's enabled
1387 // Note: this should be moved after mods receptions from server instead
1395 void Client::handleCommand_ModChannelMsg(NetworkPacket
*pkt
)
1397 std::string channel_name
, sender
, channel_msg
;
1398 *pkt
>> channel_name
>> sender
>> channel_msg
;
1400 verbosestream
<< "Mod channel message received from server " << pkt
->getPeerId()
1401 << " on channel " << channel_name
<< ". sender: `" << sender
<< "`, message: "
1402 << channel_msg
<< std::endl
;
1404 if (!m_modchannel_mgr
->channelRegistered(channel_name
)) {
1405 verbosestream
<< "Server sent us messages on unregistered channel "
1406 << channel_name
<< ", ignoring." << std::endl
;
1410 m_script
->on_modchannel_message(channel_name
, sender
, channel_msg
);
1413 void Client::handleCommand_ModChannelSignal(NetworkPacket
*pkt
)
1416 ModChannelSignal signal
;
1417 std::string channel
;
1419 *pkt
>> signal_tmp
>> channel
;
1421 signal
= (ModChannelSignal
)signal_tmp
;
1423 bool valid_signal
= true;
1424 // @TODO: send Signal to Lua API
1426 case MODCHANNEL_SIGNAL_JOIN_OK
:
1427 m_modchannel_mgr
->setChannelState(channel
, MODCHANNEL_STATE_READ_WRITE
);
1428 infostream
<< "Server ack our mod channel join on channel `" << channel
1429 << "`, joining." << std::endl
;
1431 case MODCHANNEL_SIGNAL_JOIN_FAILURE
:
1432 // Unable to join, remove channel
1433 m_modchannel_mgr
->leaveChannel(channel
, 0);
1434 infostream
<< "Server refused our mod channel join on channel `" << channel
1435 << "`" << std::endl
;
1437 case MODCHANNEL_SIGNAL_LEAVE_OK
:
1439 infostream
<< "Server ack our mod channel leave on channel " << channel
1440 << "`, leaving." << std::endl
;
1443 case MODCHANNEL_SIGNAL_LEAVE_FAILURE
:
1444 infostream
<< "Server refused our mod channel leave on channel `" << channel
1445 << "`" << std::endl
;
1447 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED
:
1449 // Generally unused, but ensure we don't do an implementation error
1450 infostream
<< "Server tells us we sent a message on channel `" << channel
1451 << "` but we are not registered. Message was dropped." << std::endl
;
1454 case MODCHANNEL_SIGNAL_SET_STATE
: {
1458 if (state
== MODCHANNEL_STATE_INIT
|| state
>= MODCHANNEL_STATE_MAX
) {
1459 infostream
<< "Received wrong channel state " << state
1460 << ", ignoring." << std::endl
;
1464 m_modchannel_mgr
->setChannelState(channel
, (ModChannelState
) state
);
1465 infostream
<< "Server sets mod channel `" << channel
1466 << "` in read-only mode." << std::endl
;
1471 warningstream
<< "Received unhandled mod channel signal ID "
1472 << signal
<< ", ignoring." << std::endl
;
1474 valid_signal
= false;
1478 // If signal is valid, forward it to client side mods
1480 m_script
->on_modchannel_signal(channel
, signal
);