1 /*
2 Minetest
3 Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
4
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.
9
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.
14
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.
18 */
19
20 #include "client/client.h"
21
22 #include "util/base64.h"
23 #include "client/camera.h"
24 #include "chatmessage.h"
25 #include "client/clientmedia.h"
26 #include "log.h"
27 #include "map.h"
28 #include "mapsector.h"
29 #include "client/minimap.h"
30 #include "modchannels.h"
31 #include "nodedef.h"
32 #include "serialization.h"
33 #include "server.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"
41 #include "util/srp.h"
42 #include "util/sha1.h"
43 #include "tileanimation.h"
44 #include "gettext.h"
45 #include "skyparams.h"
46
handleCommand_Deprecated(NetworkPacket * pkt)47 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
48 {
49 infostream << "Got deprecated command "
50 << toClientCommandTable[pkt->getCommand()].name << " from peer "
51 << pkt->getPeerId() << "!" << std::endl;
52 }
53
handleCommand_Hello(NetworkPacket * pkt)54 void Client::handleCommand_Hello(NetworkPacket* pkt)
55 {
56 if (pkt->getSize() < 1)
57 return;
58
59 u8 serialization_ver;
60 u16 proto_ver;
61 u16 compression_mode;
62 u32 auth_mechs;
63 std::string username_legacy; // for case insensitivity
64 *pkt >> serialization_ver >> compression_mode >> proto_ver
65 >> auth_mechs >> username_legacy;
66
67 // Chose an auth method we support
68 AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
69
70 infostream << "Client: TOCLIENT_HELLO received with "
71 << "serialization_ver=" << (u32)serialization_ver
72 << ", auth_mechs=" << auth_mechs
73 << ", proto_ver=" << proto_ver
74 << ", compression_mode=" << compression_mode
75 << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
76
77 if (!ser_ver_supported(serialization_ver)) {
78 infostream << "Client: TOCLIENT_HELLO: Server sent "
79 << "unsupported ser_fmt_ver"<< std::endl;
80 return;
81 }
82
83 m_server_ser_ver = serialization_ver;
84 m_proto_ver = proto_ver;
85
86 //TODO verify that username_legacy matches sent username, only
87 // differs in casing (make both uppercase and compare)
88 // This is only neccessary though when we actually want to add casing support
89
90 if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
91 // we recieved a TOCLIENT_HELLO while auth was already going on
92 errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
93 << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
94 if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
95 m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD) {
96 srp_user_delete((SRPUser *) m_auth_data);
97 m_auth_data = 0;
98 }
99 }
100
101 // Authenticate using that method, or abort if there wasn't any method found
102 if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
103 if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
104 !m_simple_singleplayer_mode &&
105 !getServerAddress().isLocalhost() &&
106 g_settings->getBool("enable_register_confirmation")) {
107 promptConfirmRegistration(chosen_auth_mechanism);
108 } else {
109 startAuth(chosen_auth_mechanism);
110 }
111 } else {
112 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
113 m_access_denied = true;
114 m_access_denied_reason = "Unknown";
115 m_con->Disconnect();
116 }
117
118 }
119
handleCommand_AuthAccept(NetworkPacket * pkt)120 void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
121 {
122 deleteAuthData();
123
124 v3f playerpos;
125 *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
126 >> m_sudo_auth_methods;
127
128 playerpos -= v3f(0, BS / 2, 0);
129
130 // Set player position
131 LocalPlayer *player = m_env.getLocalPlayer();
132 assert(player != NULL);
133 player->setPosition(playerpos);
134
135 infostream << "Client: received map seed: " << m_map_seed << std::endl;
136 infostream << "Client: received recommended send interval "
137 << m_recommended_send_interval<<std::endl;
138
139 // Reply to server
140 /*~ DO NOT TRANSLATE THIS LITERALLY!
141 This is a special string which needs to contain the translation's
142 language code (e.g. "de" for German). */
143 std::string lang = gettext("LANG_CODE");
144 if (lang == "LANG_CODE")
145 lang = "";
146
147 NetworkPacket resp_pkt(TOSERVER_INIT2, sizeof(u16) + lang.size());
148 resp_pkt << lang;
149 Send(&resp_pkt);
150
151 m_state = LC_Init;
152 }
handleCommand_AcceptSudoMode(NetworkPacket * pkt)153 void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
154 {
155 deleteAuthData();
156
157 m_password = m_new_password;
158
159 verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
160
161 // send packet to actually set the password
162 startAuth(AUTH_MECHANISM_FIRST_SRP);
163
164 // reset again
165 m_chosen_auth_mech = AUTH_MECHANISM_NONE;
166 }
handleCommand_DenySudoMode(NetworkPacket * pkt)167 void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
168 {
169 ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
170 L"Password change denied. Password NOT changed.");
171 pushToChatQueue(chatMessage);
172 // reset everything and be sad
173 deleteAuthData();
174 }
175
handleCommand_AccessDenied(NetworkPacket * pkt)176 void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
177 {
178 // The server didn't like our password. Note, this needs
179 // to be processed even if the serialisation format has
180 // not been agreed yet, the same as TOCLIENT_INIT.
181 m_access_denied = true;
182 m_access_denied_reason = "Unknown";
183
184 if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
185 // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
186 // in some places of the server code
187 if (pkt->getSize() >= 2) {
188 std::wstring wide_reason;
189 *pkt >> wide_reason;
190 m_access_denied_reason = wide_to_utf8(wide_reason);
191 }
192 return;
193 }
194
195 if (pkt->getSize() < 1)
196 return;
197
198 u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
199 *pkt >> denyCode;
200 if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
201 denyCode == SERVER_ACCESSDENIED_CRASH) {
202 *pkt >> m_access_denied_reason;
203 if (m_access_denied_reason.empty()) {
204 m_access_denied_reason = accessDeniedStrings[denyCode];
205 }
206 u8 reconnect;
207 *pkt >> reconnect;
208 m_access_denied_reconnect = reconnect & 1;
209 } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
210 *pkt >> m_access_denied_reason;
211 } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) {
212 m_access_denied_reason = accessDeniedStrings[denyCode];
213 m_access_denied_reconnect = true;
214 } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
215 m_access_denied_reason = accessDeniedStrings[denyCode];
216 } else {
217 // Allow us to add new error messages to the
218 // protocol without raising the protocol version, if we want to.
219 // Until then (which may be never), this is outside
220 // of the defined protocol.
221 *pkt >> m_access_denied_reason;
222 if (m_access_denied_reason.empty()) {
223 m_access_denied_reason = "Unknown";
224 }
225 }
226 }
227
handleCommand_RemoveNode(NetworkPacket * pkt)228 void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
229 {
230 if (pkt->getSize() < 6)
231 return;
232
233 v3s16 p;
234 *pkt >> p;
235 removeNode(p);
236 }
237
handleCommand_AddNode(NetworkPacket * pkt)238 void Client::handleCommand_AddNode(NetworkPacket* pkt)
239 {
240 if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
241 return;
242
243 v3s16 p;
244 *pkt >> p;
245
246 MapNode n;
247 n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
248
249 bool remove_metadata = true;
250 u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
251 if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
252 remove_metadata = false;
253 }
254
255 addNode(p, n, remove_metadata);
256 }
257
handleCommand_NodemetaChanged(NetworkPacket * pkt)258 void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
259 {
260 if (pkt->getSize() < 1)
261 return;
262
263 std::istringstream is(pkt->readLongString(), std::ios::binary);
264 std::stringstream sstr;
265 decompressZlib(is, sstr);
266
267 NodeMetadataList meta_updates_list(false);
268 meta_updates_list.deSerialize(sstr, m_itemdef, true);
269
270 Map &map = m_env.getMap();
271 for (NodeMetadataMap::const_iterator i = meta_updates_list.begin();
272 i != meta_updates_list.end(); ++i) {
273 v3s16 pos = i->first;
274
275 if (map.isValidPosition(pos) &&
276 map.setNodeMetadata(pos, i->second))
277 continue; // Prevent from deleting metadata
278
279 // Meta couldn't be set, unused metadata
280 delete i->second;
281 }
282 }
283
handleCommand_BlockData(NetworkPacket * pkt)284 void Client::handleCommand_BlockData(NetworkPacket* pkt)
285 {
286 // Ignore too small packet
287 if (pkt->getSize() < 6)
288 return;
289
290 v3s16 p;
291 *pkt >> p;
292
293 std::string datastring(pkt->getString(6), pkt->getSize() - 6);
294 std::istringstream istr(datastring, std::ios_base::binary);
295
296 MapSector *sector;
297 MapBlock *block;
298
299 v2s16 p2d(p.X, p.Z);
300 sector = m_env.getMap().emergeSector(p2d);
301
302 assert(sector->getPos() == p2d);
303
304 block = sector->getBlockNoCreateNoEx(p.Y);
305 if (block) {
306 /*
307 Update an existing block
308 */
309 block->deSerialize(istr, m_server_ser_ver, false);
310 block->deSerializeNetworkSpecific(istr);
311 }
312 else {
313 /*
314 Create a new block
315 */
316 block = new MapBlock(&m_env.getMap(), p, this);
317 block->deSerialize(istr, m_server_ser_ver, false);
318 block->deSerializeNetworkSpecific(istr);
319 sector->insertBlock(block);
320 }
321
322 if (m_localdb) {
323 ServerMap::saveBlock(block, m_localdb);
324 }
325
326 /*
327 Add it to mesh update queue and set it to be acknowledged after update.
328 */
329 addUpdateMeshTaskWithEdge(p, true);
330 }
331
handleCommand_Inventory(NetworkPacket * pkt)332 void Client::handleCommand_Inventory(NetworkPacket* pkt)
333 {
334 if (pkt->getSize() < 1)
335 return;
336
337 std::string datastring(pkt->getString(0), pkt->getSize());
338 std::istringstream is(datastring, std::ios_base::binary);
339
340 LocalPlayer *player = m_env.getLocalPlayer();
341 assert(player != NULL);
342
343 player->inventory.deSerialize(is);
344
345 m_update_wielded_item = true;
346
347 delete m_inventory_from_server;
348 m_inventory_from_server = new Inventory(player->inventory);
349 m_inventory_from_server_age = 0.0;
350 }
351
handleCommand_TimeOfDay(NetworkPacket * pkt)352 void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
353 {
354 if (pkt->getSize() < 2)
355 return;
356
357 u16 time_of_day;
358
359 *pkt >> time_of_day;
360
361 time_of_day = time_of_day % 24000;
362 float time_speed = 0;
363
364 if (pkt->getSize() >= 2 + 4) {
365 *pkt >> time_speed;
366 }
367 else {
368 // Old message; try to approximate speed of time by ourselves
369 float time_of_day_f = (float)time_of_day / 24000.0f;
370 float tod_diff_f = 0;
371
372 if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
373 tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0f;
374 else
375 tod_diff_f = time_of_day_f - m_last_time_of_day_f;
376
377 m_last_time_of_day_f = time_of_day_f;
378 float time_diff = m_time_of_day_update_timer;
379 m_time_of_day_update_timer = 0;
380
381 if (m_time_of_day_set) {
382 time_speed = (3600.0f * 24.0f) * tod_diff_f / time_diff;
383 infostream << "Client: Measured time_of_day speed (old format): "
384 << time_speed << " tod_diff_f=" << tod_diff_f
385 << " time_diff=" << time_diff << std::endl;
386 }
387 }
388
389 // Update environment
390 m_env.setTimeOfDay(time_of_day);
391 m_env.setTimeOfDaySpeed(time_speed);
392 m_time_of_day_set = true;
393
394 //u32 dr = m_env.getDayNightRatio();
395 //infostream << "Client: time_of_day=" << time_of_day
396 // << " time_speed=" << time_speed
397 // << " dr=" << dr << std::endl;
398 }
399
handleCommand_ChatMessage(NetworkPacket * pkt)400 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
401 {
402 /*
403 u8 version
404 u8 message_type
405 u16 sendername length
406 wstring sendername
407 u16 length
408 wstring message
409 */
410
411 ChatMessage *chatMessage = new ChatMessage();
412 u8 version, message_type;
413 *pkt >> version >> message_type;
414
415 if (version != 1 || message_type >= CHATMESSAGE_TYPE_MAX) {
416 delete chatMessage;
417 return;
418 }
419
420 u64 timestamp;
421 *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
422 chatMessage->timestamp = static_cast<std::time_t>(timestamp);
423
424 chatMessage->type = (ChatMessageType) message_type;
425
426 // @TODO send this to CSM using ChatMessage object
427 if (modsLoaded() && m_script->on_receiving_message(
428 wide_to_utf8(chatMessage->message))) {
429 // Message was consumed by CSM and should not be handled by client
430 delete chatMessage;
431 } else {
432 pushToChatQueue(chatMessage);
433 }
434 }
435
handleCommand_ActiveObjectRemoveAdd(NetworkPacket * pkt)436 void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
437 {
438 /*
439 u16 count of removed objects
440 for all removed objects {
441 u16 id
442 }
443 u16 count of added objects
444 for all added objects {
445 u16 id
446 u8 type
447 u32 initialization data length
448 string initialization data
449 }
450 */
451
452 try {
453 u8 type;
454 u16 removed_count, added_count, id;
455
456 // Read removed objects
457 *pkt >> removed_count;
458
459 for (u16 i = 0; i < removed_count; i++) {
460 *pkt >> id;
461 m_env.removeActiveObject(id);
462 }
463
464 // Read added objects
465 *pkt >> added_count;
466
467 for (u16 i = 0; i < added_count; i++) {
468 *pkt >> id >> type;
469 m_env.addActiveObject(id, type, pkt->readLongString());
470 }
471 } catch (PacketError &e) {
472 infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
473 << ". The packet is unreliable, ignoring" << std::endl;
474 }
475
476 // m_activeobjects_received is false before the first
477 // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
478 m_activeobjects_received = true;
479 }
480
handleCommand_ActiveObjectMessages(NetworkPacket * pkt)481 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
482 {
483 /*
484 for all objects
485 {
486 u16 id
487 u16 message length
488 string message
489 }
490 */
491 std::string datastring(pkt->getString(0), pkt->getSize());
492 std::istringstream is(datastring, std::ios_base::binary);
493
494 try {
495 while (is.good()) {
496 u16 id = readU16(is);
497 if (!is.good())
498 break;
499
500 std::string message = deSerializeString16(is);
501
502 // Pass on to the environment
503 m_env.processActiveObjectMessage(id, message);
504 }
505 } catch (SerializationError &e) {
506 errorstream << "Client::handleCommand_ActiveObjectMessages: "
507 << "caught SerializationError: " << e.what() << std::endl;
508 }
509 }
510
handleCommand_Movement(NetworkPacket * pkt)511 void Client::handleCommand_Movement(NetworkPacket* pkt)
512 {
513 LocalPlayer *player = m_env.getLocalPlayer();
514 assert(player != NULL);
515
516 float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
517
518 *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
519 >> lf >> lfs >> ls >> g;
520
521 player->movement_acceleration_default = mad * BS;
522 player->movement_acceleration_air = maa * BS;
523 player->movement_acceleration_fast = maf * BS;
524 player->movement_speed_walk = msw * BS;
525 player->movement_speed_crouch = mscr * BS;
526 player->movement_speed_fast = msf * BS;
527 player->movement_speed_climb = mscl * BS;
528 player->movement_speed_jump = msj * BS;
529 player->movement_liquid_fluidity = lf * BS;
530 player->movement_liquid_fluidity_smooth = lfs * BS;
531 player->movement_liquid_sink = ls * BS;
532 player->movement_gravity = g * BS;
533 }
534
handleCommand_Fov(NetworkPacket * pkt)535 void Client::handleCommand_Fov(NetworkPacket *pkt)
536 {
537 f32 fov;
538 bool is_multiplier = false;
539 f32 transition_time = 0.0f;
540
541 *pkt >> fov >> is_multiplier;
542
543 // Wrap transition_time extraction within a
544 // try-catch to preserve backwards compat
545 try {
546 *pkt >> transition_time;
547 } catch (PacketError &e) {};
548
549 LocalPlayer *player = m_env.getLocalPlayer();
550 assert(player);
551 player->setFov({ fov, is_multiplier, transition_time });
552 m_camera->notifyFovChange();
553 }
554
handleCommand_HP(NetworkPacket * pkt)555 void Client::handleCommand_HP(NetworkPacket *pkt)
556 {
557 LocalPlayer *player = m_env.getLocalPlayer();
558 assert(player != NULL);
559
560 u16 oldhp = player->hp;
561
562 u16 hp;
563 *pkt >> hp;
564
565 player->hp = hp;
566
567 if (modsLoaded())
568 m_script->on_hp_modification(hp);
569
570 if (hp < oldhp) {
571 // Add to ClientEvent queue
572 ClientEvent *event = new ClientEvent();
573 event->type = CE_PLAYER_DAMAGE;
574 event->player_damage.amount = oldhp - hp;
575 m_client_event_queue.push(event);
576 }
577 }
578
handleCommand_Breath(NetworkPacket * pkt)579 void Client::handleCommand_Breath(NetworkPacket* pkt)
580 {
581 LocalPlayer *player = m_env.getLocalPlayer();
582 assert(player != NULL);
583
584 u16 breath;
585
586 *pkt >> breath;
587
588 player->setBreath(breath);
589 }
590
handleCommand_MovePlayer(NetworkPacket * pkt)591 void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
592 {
593 LocalPlayer *player = m_env.getLocalPlayer();
594 assert(player != NULL);
595
596 v3f pos;
597 f32 pitch, yaw;
598
599 *pkt >> pos >> pitch >> yaw;
600
601 player->setPosition(pos);
602
603 infostream << "Client got TOCLIENT_MOVE_PLAYER"
604 << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
605 << " pitch=" << pitch
606 << " yaw=" << yaw
607 << std::endl;
608
609 /*
610 Add to ClientEvent queue.
611 This has to be sent to the main program because otherwise
612 it would just force the pitch and yaw values to whatever
613 the camera points to.
614 */
615 ClientEvent *event = new ClientEvent();
616 event->type = CE_PLAYER_FORCE_MOVE;
617 event->player_force_move.pitch = pitch;
618 event->player_force_move.yaw = yaw;
619 m_client_event_queue.push(event);
620 }
621
handleCommand_DeathScreen(NetworkPacket * pkt)622 void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
623 {
624 bool set_camera_point_target;
625 v3f camera_point_target;
626
627 *pkt >> set_camera_point_target;
628 *pkt >> camera_point_target;
629
630 ClientEvent *event = new ClientEvent();
631 event->type = CE_DEATHSCREEN;
632 event->deathscreen.set_camera_point_target = set_camera_point_target;
633 event->deathscreen.camera_point_target_x = camera_point_target.X;
634 event->deathscreen.camera_point_target_y = camera_point_target.Y;
635 event->deathscreen.camera_point_target_z = camera_point_target.Z;
636 m_client_event_queue.push(event);
637 }
638
handleCommand_AnnounceMedia(NetworkPacket * pkt)639 void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
640 {
641 u16 num_files;
642
643 *pkt >> num_files;
644
645 infostream << "Client: Received media announcement: packet size: "
646 << pkt->getSize() << std::endl;
647
648 if (m_media_downloader == NULL ||
649 m_media_downloader->isStarted()) {
650 const char *problem = m_media_downloader ?
651 "we already saw another announcement" :
652 "all media has been received already";
653 errorstream << "Client: Received media announcement but "
654 << problem << "! "
655 << " files=" << num_files
656 << " size=" << pkt->getSize() << std::endl;
657 return;
658 }
659
660 // Mesh update thread must be stopped while
661 // updating content definitions
662 sanity_check(!m_mesh_update_thread.isRunning());
663
664 for (u16 i = 0; i < num_files; i++) {
665 std::string name, sha1_base64;
666
667 *pkt >> name >> sha1_base64;
668
669 std::string sha1_raw = base64_decode(sha1_base64);
670 m_media_downloader->addFile(name, sha1_raw);
671 }
672
673 try {
674 std::string str;
675
676 *pkt >> str;
677
678 Strfnd sf(str);
679 while(!sf.at_end()) {
680 std::string baseurl = trim(sf.next(","));
681 if (!baseurl.empty())
682 m_media_downloader->addRemoteServer(baseurl);
683 }
684 }
685 catch(SerializationError& e) {
686 // not supported by server or turned off
687 }
688
689 m_media_downloader->step(this);
690 }
691
handleCommand_Media(NetworkPacket * pkt)692 void Client::handleCommand_Media(NetworkPacket* pkt)
693 {
694 /*
695 u16 command
696 u16 total number of file bunches
697 u16 index of this bunch
698 u32 number of files in this bunch
699 for each file {
700 u16 length of name
701 string name
702 u32 length of data
703 data
704 }
705 */
706 u16 num_bunches;
707 u16 bunch_i;
708 u32 num_files;
709
710 *pkt >> num_bunches >> bunch_i >> num_files;
711
712 infostream << "Client: Received files: bunch " << bunch_i << "/"
713 << num_bunches << " files=" << num_files
714 << " size=" << pkt->getSize() << std::endl;
715
716 if (num_files == 0)
717 return;
718
719 if (!m_media_downloader || !m_media_downloader->isStarted()) {
720 const char *problem = m_media_downloader ?
721 "media has not been requested" :
722 "all media has been received already";
723 errorstream << "Client: Received media but "
724 << problem << "! "
725 << " bunch " << bunch_i << "/" << num_bunches
726 << " files=" << num_files
727 << " size=" << pkt->getSize() << std::endl;
728 return;
729 }
730
731 // Mesh update thread must be stopped while
732 // updating content definitions
733 sanity_check(!m_mesh_update_thread.isRunning());
734
735 for (u32 i=0; i < num_files; i++) {
736 std::string name;
737
738 *pkt >> name;
739
740 std::string data = pkt->readLongString();
741
742 m_media_downloader->conventionalTransferDone(
743 name, data, this);
744 }
745 }
746
handleCommand_NodeDef(NetworkPacket * pkt)747 void Client::handleCommand_NodeDef(NetworkPacket* pkt)
748 {
749 infostream << "Client: Received node definitions: packet size: "
750 << pkt->getSize() << std::endl;
751
752 // Mesh update thread must be stopped while
753 // updating content definitions
754 sanity_check(!m_mesh_update_thread.isRunning());
755
756 // Decompress node definitions
757 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
758 std::ostringstream tmp_os;
759 decompressZlib(tmp_is, tmp_os);
760
761 // Deserialize node definitions
762 std::istringstream tmp_is2(tmp_os.str());
763 m_nodedef->deSerialize(tmp_is2);
764 m_nodedef_received = true;
765 }
766
handleCommand_ItemDef(NetworkPacket * pkt)767 void Client::handleCommand_ItemDef(NetworkPacket* pkt)
768 {
769 infostream << "Client: Received item definitions: packet size: "
770 << pkt->getSize() << std::endl;
771
772 // Mesh update thread must be stopped while
773 // updating content definitions
774 sanity_check(!m_mesh_update_thread.isRunning());
775
776 // Decompress item definitions
777 std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
778 std::ostringstream tmp_os;
779 decompressZlib(tmp_is, tmp_os);
780
781 // Deserialize node definitions
782 std::istringstream tmp_is2(tmp_os.str());
783 m_itemdef->deSerialize(tmp_is2);
784 m_itemdef_received = true;
785 }
786
handleCommand_PlaySound(NetworkPacket * pkt)787 void Client::handleCommand_PlaySound(NetworkPacket* pkt)
788 {
789 /*
790 [0] u32 server_id
791 [4] u16 name length
792 [6] char name[len]
793 [ 6 + len] f32 gain
794 [10 + len] u8 type
795 [11 + len] (f32 * 3) pos
796 [23 + len] u16 object_id
797 [25 + len] bool loop
798 [26 + len] f32 fade
799 [30 + len] f32 pitch
800 [34 + len] bool ephemeral
801 */
802
803 s32 server_id;
804 std::string name;
805
806 float gain;
807 u8 type; // 0=local, 1=positional, 2=object
808 v3f pos;
809 u16 object_id;
810 bool loop;
811 float fade = 0.0f;
812 float pitch = 1.0f;
813 bool ephemeral = false;
814
815 *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
816
817 try {
818 *pkt >> fade;
819 *pkt >> pitch;
820 *pkt >> ephemeral;
821 } catch (PacketError &e) {};
822
823 // Start playing
824 int client_id = -1;
825 switch(type) {
826 case 0: // local
827 client_id = m_sound->playSound(name, loop, gain, fade, pitch);
828 break;
829 case 1: // positional
830 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
831 break;
832 case 2:
833 { // object
834 ClientActiveObject *cao = m_env.getActiveObject(object_id);
835 if (cao)
836 pos = cao->getPosition();
837 client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
838 break;
839 }
840 default:
841 break;
842 }
843
844 if (client_id != -1) {
845 // for ephemeral sounds, server_id is not meaningful
846 if (!ephemeral) {
847 m_sounds_server_to_client[server_id] = client_id;
848 m_sounds_client_to_server[client_id] = server_id;
849 }
850 if (object_id != 0)
851 m_sounds_to_objects[client_id] = object_id;
852 }
853 }
854
handleCommand_StopSound(NetworkPacket * pkt)855 void Client::handleCommand_StopSound(NetworkPacket* pkt)
856 {
857 s32 server_id;
858
859 *pkt >> server_id;
860
861 std::unordered_map<s32, int>::iterator i = m_sounds_server_to_client.find(server_id);
862 if (i != m_sounds_server_to_client.end()) {
863 int client_id = i->second;
864 m_sound->stopSound(client_id);
865 }
866 }
867
handleCommand_FadeSound(NetworkPacket * pkt)868 void Client::handleCommand_FadeSound(NetworkPacket *pkt)
869 {
870 s32 sound_id;
871 float step;
872 float gain;
873
874 *pkt >> sound_id >> step >> gain;
875
876 std::unordered_map<s32, int>::const_iterator i =
877 m_sounds_server_to_client.find(sound_id);
878
879 if (i != m_sounds_server_to_client.end())
880 m_sound->fadeSound(i->second, step, gain);
881 }
882
handleCommand_Privileges(NetworkPacket * pkt)883 void Client::handleCommand_Privileges(NetworkPacket* pkt)
884 {
885 m_privileges.clear();
886 infostream << "Client: Privileges updated: ";
887 u16 num_privileges;
888
889 *pkt >> num_privileges;
890
891 for (u16 i = 0; i < num_privileges; i++) {
892 std::string priv;
893
894 *pkt >> priv;
895
896 m_privileges.insert(priv);
897 infostream << priv << " ";
898 }
899 infostream << std::endl;
900 }
901
handleCommand_InventoryFormSpec(NetworkPacket * pkt)902 void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
903 {
904 LocalPlayer *player = m_env.getLocalPlayer();
905 assert(player != NULL);
906
907 // Store formspec in LocalPlayer
908 player->inventory_formspec = pkt->readLongString();
909 }
910
handleCommand_DetachedInventory(NetworkPacket * pkt)911 void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
912 {
913 std::string name;
914 bool keep_inv = true;
915 *pkt >> name >> keep_inv;
916
917 infostream << "Client: Detached inventory update: \"" << name
918 << "\", mode=" << (keep_inv ? "update" : "remove") << std::endl;
919
920 const auto &inv_it = m_detached_inventories.find(name);
921 if (!keep_inv) {
922 if (inv_it != m_detached_inventories.end()) {
923 delete inv_it->second;
924 m_detached_inventories.erase(inv_it);
925 }
926 return;
927 }
928 Inventory *inv = nullptr;
929 if (inv_it == m_detached_inventories.end()) {
930 inv = new Inventory(m_itemdef);
931 m_detached_inventories[name] = inv;
932 } else {
933 inv = inv_it->second;
934 }
935
936 u16 ignore;
937 *pkt >> ignore; // this used to be the length of the following string, ignore it
938
939 std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
940 std::istringstream is(contents, std::ios::binary);
941 inv->deSerialize(is);
942 }
943
handleCommand_ShowFormSpec(NetworkPacket * pkt)944 void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
945 {
946 std::string formspec = pkt->readLongString();
947 std::string formname;
948
949 *pkt >> formname;
950
951 ClientEvent *event = new ClientEvent();
952 event->type = CE_SHOW_FORMSPEC;
953 // pointer is required as event is a struct only!
954 // adding a std:string to a struct isn't possible
955 event->show_formspec.formspec = new std::string(formspec);
956 event->show_formspec.formname = new std::string(formname);
957 m_client_event_queue.push(event);
958 }
959
handleCommand_SpawnParticle(NetworkPacket * pkt)960 void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
961 {
962 std::string datastring(pkt->getString(0), pkt->getSize());
963 std::istringstream is(datastring, std::ios_base::binary);
964
965 ParticleParameters p;
966 p.deSerialize(is, m_proto_ver);
967
968 ClientEvent *event = new ClientEvent();
969 event->type = CE_SPAWN_PARTICLE;
970 event->spawn_particle = new ParticleParameters(p);
971
972 m_client_event_queue.push(event);
973 }
974
handleCommand_AddParticleSpawner(NetworkPacket * pkt)975 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
976 {
977 std::string datastring(pkt->getString(0), pkt->getSize());
978 std::istringstream is(datastring, std::ios_base::binary);
979
980 ParticleSpawnerParameters p;
981 u32 server_id;
982 u16 attached_id = 0;
983
984 p.amount = readU16(is);
985 p.time = readF32(is);
986 p.minpos = readV3F32(is);
987 p.maxpos = readV3F32(is);
988 p.minvel = readV3F32(is);
989 p.maxvel = readV3F32(is);
990 p.minacc = readV3F32(is);
991 p.maxacc = readV3F32(is);
992 p.minexptime = readF32(is);
993 p.maxexptime = readF32(is);
994 p.minsize = readF32(is);
995 p.maxsize = readF32(is);
996 p.collisiondetection = readU8(is);
997 p.texture = deSerializeString32(is);
998
999 server_id = readU32(is);
1000
1001 p.vertical = readU8(is);
1002 p.collision_removal = readU8(is);
1003
1004 attached_id = readU16(is);
1005
1006 p.animation.deSerialize(is, m_proto_ver);
1007 p.glow = readU8(is);
1008 p.object_collision = readU8(is);
1009
1010 // This is kinda awful
1011 do {
1012 u16 tmp_param0 = readU16(is);
1013 if (is.eof())
1014 break;
1015 p.node.param0 = tmp_param0;
1016 p.node.param2 = readU8(is);
1017 p.node_tile = readU8(is);
1018 } while (0);
1019
1020 auto event = new ClientEvent();
1021 event->type = CE_ADD_PARTICLESPAWNER;
1022 event->add_particlespawner.p = new ParticleSpawnerParameters(p);
1023 event->add_particlespawner.attached_id = attached_id;
1024 event->add_particlespawner.id = server_id;
1025
1026 m_client_event_queue.push(event);
1027 }
1028
1029
handleCommand_DeleteParticleSpawner(NetworkPacket * pkt)1030 void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
1031 {
1032 u32 server_id;
1033 *pkt >> server_id;
1034
1035 ClientEvent *event = new ClientEvent();
1036 event->type = CE_DELETE_PARTICLESPAWNER;
1037 event->delete_particlespawner.id = server_id;
1038
1039 m_client_event_queue.push(event);
1040 }
1041
handleCommand_HudAdd(NetworkPacket * pkt)1042 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
1043 {
1044 std::string datastring(pkt->getString(0), pkt->getSize());
1045 std::istringstream is(datastring, std::ios_base::binary);
1046
1047 u32 server_id;
1048 u8 type;
1049 v2f pos;
1050 std::string name;
1051 v2f scale;
1052 std::string text;
1053 u32 number;
1054 u32 item;
1055 u32 dir;
1056 v2f align;
1057 v2f offset;
1058 v3f world_pos;
1059 v2s32 size;
1060 s16 z_index = 0;
1061 std::string text2;
1062
1063 *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
1064 >> dir >> align >> offset;
1065 try {
1066 *pkt >> world_pos;
1067 *pkt >> size;
1068 *pkt >> z_index;
1069 *pkt >> text2;
1070 } catch(PacketError &e) {};
1071
1072 ClientEvent *event = new ClientEvent();
1073 event->type = CE_HUDADD;
1074 event->hudadd.server_id = server_id;
1075 event->hudadd.type = type;
1076 event->hudadd.pos = new v2f(pos);
1077 event->hudadd.name = new std::string(name);
1078 event->hudadd.scale = new v2f(scale);
1079 event->hudadd.text = new std::string(text);
1080 event->hudadd.number = number;
1081 event->hudadd.item = item;
1082 event->hudadd.dir = dir;
1083 event->hudadd.align = new v2f(align);
1084 event->hudadd.offset = new v2f(offset);
1085 event->hudadd.world_pos = new v3f(world_pos);
1086 event->hudadd.size = new v2s32(size);
1087 event->hudadd.z_index = z_index;
1088 event->hudadd.text2 = new std::string(text2);
1089 m_client_event_queue.push(event);
1090 }
1091
handleCommand_HudRemove(NetworkPacket * pkt)1092 void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1093 {
1094 u32 server_id;
1095
1096 *pkt >> server_id;
1097
1098 ClientEvent *event = new ClientEvent();
1099 event->type = CE_HUDRM;
1100 event->hudrm.id = server_id;
1101 m_client_event_queue.push(event);
1102 }
1103
handleCommand_HudChange(NetworkPacket * pkt)1104 void Client::handleCommand_HudChange(NetworkPacket* pkt)
1105 {
1106 std::string sdata;
1107 v2f v2fdata;
1108 v3f v3fdata;
1109 u32 intdata = 0;
1110 v2s32 v2s32data;
1111 u32 server_id;
1112 u8 stat;
1113
1114 *pkt >> server_id >> stat;
1115
1116 if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1117 stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1118 *pkt >> v2fdata;
1119 else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
1120 *pkt >> sdata;
1121 else if (stat == HUD_STAT_WORLD_POS)
1122 *pkt >> v3fdata;
1123 else if (stat == HUD_STAT_SIZE )
1124 *pkt >> v2s32data;
1125 else
1126 *pkt >> intdata;
1127
1128 ClientEvent *event = new ClientEvent();
1129 event->type = CE_HUDCHANGE;
1130 event->hudchange.id = server_id;
1131 event->hudchange.stat = (HudElementStat)stat;
1132 event->hudchange.v2fdata = new v2f(v2fdata);
1133 event->hudchange.v3fdata = new v3f(v3fdata);
1134 event->hudchange.sdata = new std::string(sdata);
1135 event->hudchange.data = intdata;
1136 event->hudchange.v2s32data = new v2s32(v2s32data);
1137 m_client_event_queue.push(event);
1138 }
1139
handleCommand_HudSetFlags(NetworkPacket * pkt)1140 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1141 {
1142 u32 flags, mask;
1143
1144 *pkt >> flags >> mask;
1145
1146 LocalPlayer *player = m_env.getLocalPlayer();
1147 assert(player != NULL);
1148
1149 bool was_minimap_visible = player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE;
1150 bool was_minimap_radar_visible = player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE;
1151
1152 player->hud_flags &= ~mask;
1153 player->hud_flags |= flags;
1154
1155 m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
1156 bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
1157
1158 // Not so satisying code to keep compatibility with old fixed mode system
1159 // -->
1160
1161 // Hide minimap if it has been disabled by the server
1162 if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
1163 // defers a minimap update, therefore only call it if really
1164 // needed, by checking that minimap was visible before
1165 m_minimap->setModeIndex(0);
1166
1167 // If radar has been disabled, try to find a non radar mode or fall back to 0
1168 if (m_minimap && m_minimap_radar_disabled_by_server
1169 && was_minimap_radar_visible) {
1170 while (m_minimap->getModeIndex() > 0 &&
1171 m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
1172 m_minimap->nextMode();
1173 }
1174 // <--
1175 // End of 'not so satifying code'
1176 }
1177
handleCommand_HudSetParam(NetworkPacket * pkt)1178 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1179 {
1180 u16 param; std::string value;
1181
1182 *pkt >> param >> value;
1183
1184 LocalPlayer *player = m_env.getLocalPlayer();
1185 assert(player != NULL);
1186
1187 if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1188 s32 hotbar_itemcount = readS32((u8*) value.c_str());
1189 if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1190 player->hud_hotbar_itemcount = hotbar_itemcount;
1191 }
1192 else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1193 player->hotbar_image = value;
1194 }
1195 else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1196 player->hotbar_selected_image = value;
1197 }
1198 }
1199
handleCommand_HudSetSky(NetworkPacket * pkt)1200 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1201 {
1202 if (m_proto_ver < 39) {
1203 // Handle Protocol 38 and below servers with old set_sky,
1204 // ensuring the classic look is kept.
1205 std::string datastring(pkt->getString(0), pkt->getSize());
1206 std::istringstream is(datastring, std::ios_base::binary);
1207
1208 SkyboxParams skybox;
1209 skybox.bgcolor = video::SColor(readARGB8(is));
1210 skybox.type = std::string(deSerializeString16(is));
1211 u16 count = readU16(is);
1212
1213 for (size_t i = 0; i < count; i++)
1214 skybox.textures.emplace_back(deSerializeString16(is));
1215
1216 skybox.clouds = true;
1217 try {
1218 skybox.clouds = readU8(is);
1219 } catch (...) {}
1220
1221 // Use default skybox settings:
1222 SkyboxDefaults sky_defaults;
1223 SunParams sun = sky_defaults.getSunDefaults();
1224 MoonParams moon = sky_defaults.getMoonDefaults();
1225 StarParams stars = sky_defaults.getStarDefaults();
1226
1227 // Fix for "regular" skies, as color isn't kept:
1228 if (skybox.type == "regular") {
1229 skybox.sky_color = sky_defaults.getSkyColorDefaults();
1230 skybox.fog_tint_type = "default";
1231 skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
1232 skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
1233 }
1234 else {
1235 sun.visible = false;
1236 sun.sunrise_visible = false;
1237 moon.visible = false;
1238 stars.visible = false;
1239 }
1240
1241 // Skybox, sun, moon and stars ClientEvents:
1242 ClientEvent *sky_event = new ClientEvent();
1243 sky_event->type = CE_SET_SKY;
1244 sky_event->set_sky = new SkyboxParams(skybox);
1245 m_client_event_queue.push(sky_event);
1246
1247 ClientEvent *sun_event = new ClientEvent();
1248 sun_event->type = CE_SET_SUN;
1249 sun_event->sun_params = new SunParams(sun);
1250 m_client_event_queue.push(sun_event);
1251
1252 ClientEvent *moon_event = new ClientEvent();
1253 moon_event->type = CE_SET_MOON;
1254 moon_event->moon_params = new MoonParams(moon);
1255 m_client_event_queue.push(moon_event);
1256
1257 ClientEvent *star_event = new ClientEvent();
1258 star_event->type = CE_SET_STARS;
1259 star_event->star_params = new StarParams(stars);
1260 m_client_event_queue.push(star_event);
1261 } else {
1262 SkyboxParams skybox;
1263 u16 texture_count;
1264 std::string texture;
1265
1266 *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
1267 skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
1268
1269 if (skybox.type == "skybox") {
1270 *pkt >> texture_count;
1271 for (int i = 0; i < texture_count; i++) {
1272 *pkt >> texture;
1273 skybox.textures.emplace_back(texture);
1274 }
1275 }
1276 else if (skybox.type == "regular") {
1277 *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
1278 >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
1279 >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
1280 >> skybox.sky_color.indoors;
1281 }
1282
1283 ClientEvent *event = new ClientEvent();
1284 event->type = CE_SET_SKY;
1285 event->set_sky = new SkyboxParams(skybox);
1286 m_client_event_queue.push(event);
1287 }
1288 }
1289
handleCommand_HudSetSun(NetworkPacket * pkt)1290 void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
1291 {
1292 SunParams sun;
1293
1294 *pkt >> sun.visible >> sun.texture>> sun.tonemap
1295 >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
1296
1297 ClientEvent *event = new ClientEvent();
1298 event->type = CE_SET_SUN;
1299 event->sun_params = new SunParams(sun);
1300 m_client_event_queue.push(event);
1301 }
1302
handleCommand_HudSetMoon(NetworkPacket * pkt)1303 void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
1304 {
1305 MoonParams moon;
1306
1307 *pkt >> moon.visible >> moon.texture
1308 >> moon.tonemap >> moon.scale;
1309
1310 ClientEvent *event = new ClientEvent();
1311 event->type = CE_SET_MOON;
1312 event->moon_params = new MoonParams(moon);
1313 m_client_event_queue.push(event);
1314 }
1315
handleCommand_HudSetStars(NetworkPacket * pkt)1316 void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
1317 {
1318 StarParams stars;
1319
1320 *pkt >> stars.visible >> stars.count
1321 >> stars.starcolor >> stars.scale;
1322
1323 ClientEvent *event = new ClientEvent();
1324 event->type = CE_SET_STARS;
1325 event->star_params = new StarParams(stars);
1326
1327 m_client_event_queue.push(event);
1328 }
1329
handleCommand_CloudParams(NetworkPacket * pkt)1330 void Client::handleCommand_CloudParams(NetworkPacket* pkt)
1331 {
1332 f32 density;
1333 video::SColor color_bright;
1334 video::SColor color_ambient;
1335 f32 height;
1336 f32 thickness;
1337 v2f speed;
1338
1339 *pkt >> density >> color_bright >> color_ambient
1340 >> height >> thickness >> speed;
1341
1342 ClientEvent *event = new ClientEvent();
1343 event->type = CE_CLOUD_PARAMS;
1344 event->cloud_params.density = density;
1345 // use the underlying u32 representation, because we can't
1346 // use struct members with constructors here, and this way
1347 // we avoid using new() and delete() for no good reason
1348 event->cloud_params.color_bright = color_bright.color;
1349 event->cloud_params.color_ambient = color_ambient.color;
1350 event->cloud_params.height = height;
1351 event->cloud_params.thickness = thickness;
1352 // same here: deconstruct to skip constructor
1353 event->cloud_params.speed_x = speed.X;
1354 event->cloud_params.speed_y = speed.Y;
1355 m_client_event_queue.push(event);
1356 }
1357
handleCommand_OverrideDayNightRatio(NetworkPacket * pkt)1358 void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1359 {
1360 bool do_override;
1361 u16 day_night_ratio_u;
1362
1363 *pkt >> do_override >> day_night_ratio_u;
1364
1365 float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1366
1367 ClientEvent *event = new ClientEvent();
1368 event->type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1369 event->override_day_night_ratio.do_override = do_override;
1370 event->override_day_night_ratio.ratio_f = day_night_ratio_f;
1371 m_client_event_queue.push(event);
1372 }
1373
handleCommand_LocalPlayerAnimations(NetworkPacket * pkt)1374 void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1375 {
1376 LocalPlayer *player = m_env.getLocalPlayer();
1377 assert(player != NULL);
1378
1379 *pkt >> player->local_animations[0];
1380 *pkt >> player->local_animations[1];
1381 *pkt >> player->local_animations[2];
1382 *pkt >> player->local_animations[3];
1383 *pkt >> player->local_animation_speed;
1384 }
1385
handleCommand_EyeOffset(NetworkPacket * pkt)1386 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1387 {
1388 LocalPlayer *player = m_env.getLocalPlayer();
1389 assert(player != NULL);
1390
1391 *pkt >> player->eye_offset_first >> player->eye_offset_third;
1392 }
1393
handleCommand_UpdatePlayerList(NetworkPacket * pkt)1394 void Client::handleCommand_UpdatePlayerList(NetworkPacket* pkt)
1395 {
1396 u8 type;
1397 u16 num_players;
1398 *pkt >> type >> num_players;
1399 PlayerListModifer notice_type = (PlayerListModifer) type;
1400
1401 for (u16 i = 0; i < num_players; i++) {
1402 std::string name;
1403 *pkt >> name;
1404 switch (notice_type) {
1405 case PLAYER_LIST_INIT:
1406 case PLAYER_LIST_ADD:
1407 m_env.addPlayerName(name);
1408 continue;
1409 case PLAYER_LIST_REMOVE:
1410 m_env.removePlayerName(name);
1411 continue;
1412 }
1413 }
1414 }
1415
handleCommand_SrpBytesSandB(NetworkPacket * pkt)1416 void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1417 {
1418 if (m_chosen_auth_mech != AUTH_MECHANISM_SRP &&
1419 m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) {
1420 errorstream << "Client: Received SRP S_B login message,"
1421 << " but wasn't supposed to (chosen_mech="
1422 << m_chosen_auth_mech << ")." << std::endl;
1423 return;
1424 }
1425
1426 char *bytes_M = 0;
1427 size_t len_M = 0;
1428 SRPUser *usr = (SRPUser *) m_auth_data;
1429 std::string s;
1430 std::string B;
1431 *pkt >> s >> B;
1432
1433 infostream << "Client: Received TOCLIENT_SRP_BYTES_S_B." << std::endl;
1434
1435 srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1436 (const unsigned char *) B.c_str(), B.size(),
1437 (unsigned char **) &bytes_M, &len_M);
1438
1439 if ( !bytes_M ) {
1440 errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1441 return;
1442 }
1443
1444 NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1445 resp_pkt << std::string(bytes_M, len_M);
1446 Send(&resp_pkt);
1447 }
1448
handleCommand_FormspecPrepend(NetworkPacket * pkt)1449 void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
1450 {
1451 LocalPlayer *player = m_env.getLocalPlayer();
1452 assert(player != NULL);
1453
1454 // Store formspec in LocalPlayer
1455 *pkt >> player->formspec_prepend;
1456 }
1457
handleCommand_CSMRestrictionFlags(NetworkPacket * pkt)1458 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
1459 {
1460 *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
1461
1462 // Restrictions were received -> load mods if it's enabled
1463 // Note: this should be moved after mods receptions from server instead
1464 loadMods();
1465 }
1466
handleCommand_PlayerSpeed(NetworkPacket * pkt)1467 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
1468 {
1469 v3f added_vel;
1470
1471 *pkt >> added_vel;
1472
1473 LocalPlayer *player = m_env.getLocalPlayer();
1474 assert(player != NULL);
1475 player->addVelocity(added_vel);
1476 }
1477
handleCommand_MediaPush(NetworkPacket * pkt)1478 void Client::handleCommand_MediaPush(NetworkPacket *pkt)
1479 {
1480 std::string raw_hash, filename, filedata;
1481 bool cached;
1482
1483 *pkt >> raw_hash >> filename >> cached;
1484 filedata = pkt->readLongString();
1485
1486 if (raw_hash.size() != 20 || filedata.empty() || filename.empty() ||
1487 !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
1488 throw PacketError("Illegal filename, data or hash");
1489 }
1490
1491 verbosestream << "Server pushes media file \"" << filename << "\" with "
1492 << filedata.size() << " bytes of data (cached=" << cached
1493 << ")" << std::endl;
1494
1495 if (m_media_pushed_files.count(filename) != 0) {
1496 // Silently ignore for synchronization purposes
1497 return;
1498 }
1499
1500 // Compute and check checksum of data
1501 std::string computed_hash;
1502 {
1503 SHA1 ctx;
1504 ctx.addBytes(filedata.c_str(), filedata.size());
1505 unsigned char *buf = ctx.getDigest();
1506 computed_hash.assign((char*) buf, 20);
1507 free(buf);
1508 }
1509 if (raw_hash != computed_hash) {
1510 verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
1511 return;
1512 }
1513
1514 // Actually load media
1515 loadMedia(filedata, filename, true);
1516 m_media_pushed_files.insert(filename);
1517
1518 // Cache file for the next time when this client joins the same server
1519 if (cached)
1520 clientMediaUpdateCache(raw_hash, filedata);
1521 }
1522
1523 /*
1524 * Mod channels
1525 */
1526
handleCommand_ModChannelMsg(NetworkPacket * pkt)1527 void Client::handleCommand_ModChannelMsg(NetworkPacket *pkt)
1528 {
1529 std::string channel_name, sender, channel_msg;
1530 *pkt >> channel_name >> sender >> channel_msg;
1531
1532 verbosestream << "Mod channel message received from server " << pkt->getPeerId()
1533 << " on channel " << channel_name << ". sender: `" << sender << "`, message: "
1534 << channel_msg << std::endl;
1535
1536 if (!m_modchannel_mgr->channelRegistered(channel_name)) {
1537 verbosestream << "Server sent us messages on unregistered channel "
1538 << channel_name << ", ignoring." << std::endl;
1539 return;
1540 }
1541
1542 m_script->on_modchannel_message(channel_name, sender, channel_msg);
1543 }
1544
handleCommand_ModChannelSignal(NetworkPacket * pkt)1545 void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
1546 {
1547 u8 signal_tmp;
1548 ModChannelSignal signal;
1549 std::string channel;
1550
1551 *pkt >> signal_tmp >> channel;
1552
1553 signal = (ModChannelSignal)signal_tmp;
1554
1555 bool valid_signal = true;
1556 // @TODO: send Signal to Lua API
1557 switch (signal) {
1558 case MODCHANNEL_SIGNAL_JOIN_OK:
1559 m_modchannel_mgr->setChannelState(channel, MODCHANNEL_STATE_READ_WRITE);
1560 infostream << "Server ack our mod channel join on channel `" << channel
1561 << "`, joining." << std::endl;
1562 break;
1563 case MODCHANNEL_SIGNAL_JOIN_FAILURE:
1564 // Unable to join, remove channel
1565 m_modchannel_mgr->leaveChannel(channel, 0);
1566 infostream << "Server refused our mod channel join on channel `" << channel
1567 << "`" << std::endl;
1568 break;
1569 case MODCHANNEL_SIGNAL_LEAVE_OK:
1570 #ifndef NDEBUG
1571 infostream << "Server ack our mod channel leave on channel " << channel
1572 << "`, leaving." << std::endl;
1573 #endif
1574 break;
1575 case MODCHANNEL_SIGNAL_LEAVE_FAILURE:
1576 infostream << "Server refused our mod channel leave on channel `" << channel
1577 << "`" << std::endl;
1578 break;
1579 case MODCHANNEL_SIGNAL_CHANNEL_NOT_REGISTERED:
1580 #ifndef NDEBUG
1581 // Generally unused, but ensure we don't do an implementation error
1582 infostream << "Server tells us we sent a message on channel `" << channel
1583 << "` but we are not registered. Message was dropped." << std::endl;
1584 #endif
1585 break;
1586 case MODCHANNEL_SIGNAL_SET_STATE: {
1587 u8 state;
1588 *pkt >> state;
1589
1590 if (state == MODCHANNEL_STATE_INIT || state >= MODCHANNEL_STATE_MAX) {
1591 infostream << "Received wrong channel state " << state
1592 << ", ignoring." << std::endl;
1593 return;
1594 }
1595
1596 m_modchannel_mgr->setChannelState(channel, (ModChannelState) state);
1597 infostream << "Server sets mod channel `" << channel
1598 << "` in read-only mode." << std::endl;
1599 break;
1600 }
1601 default:
1602 #ifndef NDEBUG
1603 warningstream << "Received unhandled mod channel signal ID "
1604 << signal << ", ignoring." << std::endl;
1605 #endif
1606 valid_signal = false;
1607 break;
1608 }
1609
1610 // If signal is valid, forward it to client side mods
1611 if (valid_signal)
1612 m_script->on_modchannel_signal(channel, signal);
1613 }
1614
handleCommand_MinimapModes(NetworkPacket * pkt)1615 void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
1616 {
1617 u16 count; // modes
1618 u16 mode; // wanted current mode index after change
1619
1620 *pkt >> count >> mode;
1621
1622 if (m_minimap)
1623 m_minimap->clearModes();
1624
1625 for (size_t index = 0; index < count; index++) {
1626 u16 type;
1627 std::string label;
1628 u16 size;
1629 std::string texture;
1630 u16 scale;
1631
1632 *pkt >> type >> label >> size >> texture >> scale;
1633
1634 if (m_minimap)
1635 m_minimap->addMode(MinimapType(type), size, label, texture, scale);
1636 }
1637
1638 if (m_minimap)
1639 m_minimap->setModeIndex(mode);
1640 }
1641