Line data Source code
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.h"
21 :
22 : #include "util/base64.h"
23 : #include "clientmedia.h"
24 : #include "log.h"
25 : #include "map.h"
26 : #include "mapsector.h"
27 : #include "nodedef.h"
28 : #include "serialization.h"
29 : #include "server.h"
30 : #include "strfnd.h"
31 : #include "network/clientopcodes.h"
32 : #include "util/serialize.h"
33 : #include "util/srp.h"
34 :
35 0 : void Client::handleCommand_Deprecated(NetworkPacket* pkt)
36 : {
37 0 : infostream << "Got deprecated command "
38 0 : << toClientCommandTable[pkt->getCommand()].name << " from peer "
39 0 : << pkt->getPeerId() << "!" << std::endl;
40 0 : }
41 :
42 0 : void Client::handleCommand_Hello(NetworkPacket* pkt)
43 : {
44 0 : if (pkt->getSize() < 1)
45 0 : return;
46 :
47 : u8 serialization_ver;
48 : u16 proto_ver;
49 : u16 compression_mode;
50 : u32 auth_mechs;
51 0 : std::string username_legacy; // for case insensitivity
52 0 : *pkt >> serialization_ver >> compression_mode >> proto_ver
53 0 : >> auth_mechs >> username_legacy;
54 :
55 : // Chose an auth method we support
56 0 : AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
57 :
58 0 : infostream << "Client: TOCLIENT_HELLO received with "
59 0 : << "serialization_ver=" << (u32)serialization_ver
60 0 : << ", auth_mechs=" << auth_mechs
61 0 : << ", proto_ver=" << proto_ver
62 0 : << ", compression_mode=" << compression_mode
63 0 : << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
64 :
65 0 : if (!ser_ver_supported(serialization_ver)) {
66 0 : infostream << "Client: TOCLIENT_HELLO: Server sent "
67 0 : << "unsupported ser_fmt_ver"<< std::endl;
68 0 : return;
69 : }
70 :
71 0 : m_server_ser_ver = serialization_ver;
72 0 : m_proto_ver = proto_ver;
73 :
74 : //TODO verify that username_legacy matches sent username, only
75 : // differs in casing (make both uppercase and compare)
76 : // This is only neccessary though when we actually want to add casing support
77 :
78 0 : if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
79 : // we recieved a TOCLIENT_HELLO while auth was already going on
80 0 : errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
81 0 : << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
82 0 : if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP)
83 0 : || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) {
84 0 : srp_user_delete((SRPUser *) m_auth_data);
85 0 : m_auth_data = 0;
86 : }
87 : }
88 :
89 : // Authenticate using that method, or abort if there wasn't any method found
90 0 : if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
91 0 : startAuth(chosen_auth_mechanism);
92 : } else {
93 0 : m_chosen_auth_mech = AUTH_MECHANISM_NONE;
94 0 : m_access_denied = true;
95 0 : m_access_denied_reason = "Unknown";
96 0 : m_con.Disconnect();
97 : }
98 :
99 : }
100 :
101 0 : void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
102 : {
103 0 : m_chosen_auth_mech = AUTH_MECHANISM_NONE;
104 0 : deleteAuthData();
105 :
106 0 : v3f playerpos;
107 0 : *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
108 0 : >> m_sudo_auth_methods;
109 :
110 0 : playerpos -= v3f(0, BS / 2, 0);
111 :
112 : // Set player position
113 0 : Player *player = m_env.getLocalPlayer();
114 : assert(player != NULL);
115 0 : player->setPosition(playerpos);
116 :
117 0 : infostream << "Client: received map seed: " << m_map_seed << std::endl;
118 0 : infostream << "Client: received recommended send interval "
119 0 : << m_recommended_send_interval<<std::endl;
120 :
121 : // Reply to server
122 0 : NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
123 0 : Send(&resp_pkt);
124 :
125 0 : m_state = LC_Init;
126 0 : }
127 0 : void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
128 : {
129 0 : m_chosen_auth_mech = AUTH_MECHANISM_NONE;
130 0 : deleteAuthData();
131 :
132 0 : m_password = m_new_password;
133 :
134 0 : verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
135 :
136 : // send packet to actually set the password
137 0 : startAuth(AUTH_MECHANISM_FIRST_SRP);
138 :
139 : // reset again
140 0 : m_chosen_auth_mech = AUTH_MECHANISM_NONE;
141 0 : }
142 0 : void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
143 : {
144 0 : m_chat_queue.push(L"Password change denied. Password NOT changed.");
145 : // reset everything and be sad
146 0 : deleteAuthData();
147 0 : m_chosen_auth_mech = AUTH_MECHANISM_NONE;
148 0 : }
149 1 : void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
150 : {
151 1 : if (pkt->getSize() < 1)
152 0 : return;
153 :
154 : u8 deployed;
155 1 : *pkt >> deployed;
156 :
157 : infostream << "Client: TOCLIENT_INIT_LEGACY received with "
158 1 : "deployed=" << ((int)deployed & 0xff) << std::endl;
159 :
160 1 : if (!ser_ver_supported(deployed)) {
161 0 : infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
162 0 : << "unsupported ser_fmt_ver"<< std::endl;
163 0 : return;
164 : }
165 :
166 1 : m_server_ser_ver = deployed;
167 1 : m_proto_ver = deployed;
168 :
169 : // Get player position
170 1 : v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
171 1 : if (pkt->getSize() >= 1 + 6) {
172 1 : *pkt >> playerpos_s16;
173 : }
174 1 : v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
175 :
176 :
177 : // Set player position
178 1 : Player *player = m_env.getLocalPlayer();
179 : assert(player != NULL);
180 1 : player->setPosition(playerpos_f);
181 :
182 1 : if (pkt->getSize() >= 1 + 6 + 8) {
183 : // Get map seed
184 1 : *pkt >> m_map_seed;
185 1 : infostream << "Client: received map seed: " << m_map_seed << std::endl;
186 : }
187 :
188 1 : if (pkt->getSize() >= 1 + 6 + 8 + 4) {
189 1 : *pkt >> m_recommended_send_interval;
190 1 : infostream << "Client: received recommended send interval "
191 2 : << m_recommended_send_interval<<std::endl;
192 : }
193 :
194 : // Reply to server
195 2 : NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
196 1 : Send(&resp_pkt);
197 :
198 1 : m_state = LC_Init;
199 : }
200 :
201 0 : void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
202 : {
203 : // The server didn't like our password. Note, this needs
204 : // to be processed even if the serialisation format has
205 : // not been agreed yet, the same as TOCLIENT_INIT.
206 0 : m_access_denied = true;
207 0 : m_access_denied_reason = "Unknown";
208 :
209 0 : if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
210 0 : if (pkt->getSize() < 1)
211 0 : return;
212 :
213 0 : u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
214 0 : *pkt >> denyCode;
215 0 : if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
216 0 : *pkt >> m_access_denied_reason;
217 : }
218 0 : else if (denyCode < SERVER_ACCESSDENIED_MAX) {
219 0 : m_access_denied_reason = accessDeniedStrings[denyCode];
220 : }
221 : }
222 : // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
223 : // for compat with old clients
224 : else {
225 0 : if (pkt->getSize() >= 2) {
226 0 : std::wstring wide_reason;
227 0 : *pkt >> wide_reason;
228 0 : m_access_denied_reason = wide_to_narrow(wide_reason);
229 : }
230 : }
231 : }
232 :
233 6 : void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
234 : {
235 6 : if (pkt->getSize() < 6)
236 0 : return;
237 :
238 6 : v3s16 p;
239 6 : *pkt >> p;
240 6 : removeNode(p);
241 : }
242 :
243 49 : void Client::handleCommand_AddNode(NetworkPacket* pkt)
244 : {
245 49 : if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
246 0 : return;
247 :
248 49 : v3s16 p;
249 49 : *pkt >> p;
250 :
251 49 : MapNode n;
252 49 : n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
253 :
254 49 : bool remove_metadata = true;
255 49 : u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
256 49 : if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
257 42 : remove_metadata = false;
258 : }
259 :
260 49 : addNode(p, n, remove_metadata);
261 : }
262 786 : void Client::handleCommand_BlockData(NetworkPacket* pkt)
263 : {
264 : // Ignore too small packet
265 786 : if (pkt->getSize() < 6)
266 0 : return;
267 :
268 786 : v3s16 p;
269 786 : *pkt >> p;
270 :
271 1572 : std::string datastring(pkt->getString(6), pkt->getSize() - 6);
272 1572 : std::istringstream istr(datastring, std::ios_base::binary);
273 :
274 : MapSector *sector;
275 : MapBlock *block;
276 :
277 786 : v2s16 p2d(p.X, p.Z);
278 786 : sector = m_env.getMap().emergeSector(p2d);
279 :
280 : assert(sector->getPos() == p2d);
281 :
282 786 : block = sector->getBlockNoCreateNoEx(p.Y);
283 786 : if (block) {
284 : /*
285 : Update an existing block
286 : */
287 35 : block->deSerialize(istr, m_server_ser_ver, false);
288 35 : block->deSerializeNetworkSpecific(istr);
289 : }
290 : else {
291 : /*
292 : Create a new block
293 : */
294 751 : block = new MapBlock(&m_env.getMap(), p, this);
295 751 : block->deSerialize(istr, m_server_ser_ver, false);
296 751 : block->deSerializeNetworkSpecific(istr);
297 751 : sector->insertBlock(block);
298 : }
299 :
300 786 : if (m_localdb) {
301 0 : ServerMap::saveBlock(block, m_localdb);
302 : }
303 :
304 : /*
305 : Add it to mesh update queue and set it to be acknowledged after update.
306 : */
307 786 : addUpdateMeshTaskWithEdge(p, true);
308 : }
309 :
310 6 : void Client::handleCommand_Inventory(NetworkPacket* pkt)
311 : {
312 6 : if (pkt->getSize() < 1)
313 0 : return;
314 :
315 12 : std::string datastring(pkt->getString(0), pkt->getSize());
316 12 : std::istringstream is(datastring, std::ios_base::binary);
317 :
318 6 : Player *player = m_env.getLocalPlayer();
319 : assert(player != NULL);
320 :
321 6 : player->inventory.deSerialize(is);
322 :
323 6 : m_inventory_updated = true;
324 :
325 6 : delete m_inventory_from_server;
326 6 : m_inventory_from_server = new Inventory(player->inventory);
327 6 : m_inventory_from_server_age = 0.0;
328 : }
329 :
330 10 : void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
331 : {
332 10 : if (pkt->getSize() < 2)
333 0 : return;
334 :
335 : u16 time_of_day;
336 :
337 10 : *pkt >> time_of_day;
338 :
339 10 : time_of_day = time_of_day % 24000;
340 10 : float time_speed = 0;
341 :
342 10 : if (pkt->getSize() >= 2 + 4) {
343 10 : *pkt >> time_speed;
344 : }
345 : else {
346 : // Old message; try to approximate speed of time by ourselves
347 0 : float time_of_day_f = (float)time_of_day / 24000.0;
348 0 : float tod_diff_f = 0;
349 :
350 0 : if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
351 0 : tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
352 : else
353 0 : tod_diff_f = time_of_day_f - m_last_time_of_day_f;
354 :
355 0 : m_last_time_of_day_f = time_of_day_f;
356 0 : float time_diff = m_time_of_day_update_timer;
357 0 : m_time_of_day_update_timer = 0;
358 :
359 0 : if (m_time_of_day_set) {
360 0 : time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
361 0 : infostream << "Client: Measured time_of_day speed (old format): "
362 0 : << time_speed << " tod_diff_f=" << tod_diff_f
363 0 : << " time_diff=" << time_diff << std::endl;
364 : }
365 : }
366 :
367 : // Update environment
368 10 : m_env.setTimeOfDay(time_of_day);
369 10 : m_env.setTimeOfDaySpeed(time_speed);
370 10 : m_time_of_day_set = true;
371 :
372 10 : u32 dr = m_env.getDayNightRatio();
373 10 : infostream << "Client: time_of_day=" << time_of_day
374 10 : << " time_speed=" << time_speed
375 20 : << " dr=" << dr << std::endl;
376 : }
377 :
378 2 : void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
379 : {
380 : /*
381 : u16 command
382 : u16 length
383 : wstring message
384 : */
385 : u16 len, read_wchar;
386 :
387 2 : *pkt >> len;
388 :
389 4 : std::wstring message;
390 247 : for (u32 i = 0; i < len; i++) {
391 245 : *pkt >> read_wchar;
392 245 : message += (wchar_t)read_wchar;
393 : }
394 :
395 2 : m_chat_queue.push(message);
396 2 : }
397 :
398 36 : void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
399 : {
400 : /*
401 : u16 count of removed objects
402 : for all removed objects {
403 : u16 id
404 : }
405 : u16 count of added objects
406 : for all added objects {
407 : u16 id
408 : u8 type
409 : u32 initialization data length
410 : string initialization data
411 : }
412 : */
413 :
414 : try {
415 : u8 type;
416 : u16 removed_count, added_count, id;
417 :
418 : // Read removed objects
419 36 : *pkt >> removed_count;
420 :
421 55 : for (u16 i = 0; i < removed_count; i++) {
422 19 : *pkt >> id;
423 19 : m_env.removeActiveObject(id);
424 : }
425 :
426 : // Read added objects
427 36 : *pkt >> added_count;
428 :
429 80 : for (u16 i = 0; i < added_count; i++) {
430 44 : *pkt >> id >> type;
431 44 : m_env.addActiveObject(id, type, pkt->readLongString());
432 : }
433 0 : } catch (PacketError &e) {
434 0 : infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
435 0 : << ". The packet is unreliable, ignoring" << std::endl;
436 : }
437 36 : }
438 :
439 353 : void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
440 : {
441 : /*
442 : for all objects
443 : {
444 : u16 id
445 : u16 message length
446 : string message
447 : }
448 : */
449 : char buf[6];
450 : // Get all data except the command number
451 706 : std::string datastring(pkt->getString(0), pkt->getSize());
452 : // Throw them in an istringstream
453 706 : std::istringstream is(datastring, std::ios_base::binary);
454 :
455 : try {
456 57491 : while(is.eof() == false) {
457 28770 : is.read(buf, 2);
458 28770 : u16 id = readU16((u8*)buf);
459 28770 : if (is.eof())
460 201 : break;
461 28569 : is.read(buf, 2);
462 28569 : size_t message_size = readU16((u8*)buf);
463 57138 : std::string message;
464 28569 : message.reserve(message_size);
465 131031 : for (u32 i = 0; i < message_size; i++) {
466 102462 : is.read(buf, 1);
467 102462 : message.append(buf, 1);
468 : }
469 : // Pass on to the environment
470 28569 : m_env.processActiveObjectMessage(id, message);
471 : }
472 : // Packet could be unreliable then ignore it
473 0 : } catch (PacketError &e) {
474 0 : infostream << "handleCommand_ActiveObjectMessages: " << e.what()
475 0 : << ". The packet is unreliable, ignoring" << std::endl;
476 : }
477 353 : }
478 :
479 1 : void Client::handleCommand_Movement(NetworkPacket* pkt)
480 : {
481 1 : Player *player = m_env.getLocalPlayer();
482 : assert(player != NULL);
483 :
484 : float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
485 :
486 1 : *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
487 1 : >> lf >> lfs >> ls >> g;
488 :
489 1 : player->movement_acceleration_default = mad * BS;
490 1 : player->movement_acceleration_air = maa * BS;
491 1 : player->movement_acceleration_fast = maf * BS;
492 1 : player->movement_speed_walk = msw * BS;
493 1 : player->movement_speed_crouch = mscr * BS;
494 1 : player->movement_speed_fast = msf * BS;
495 1 : player->movement_speed_climb = mscl * BS;
496 1 : player->movement_speed_jump = msj * BS;
497 1 : player->movement_liquid_fluidity = lf * BS;
498 1 : player->movement_liquid_fluidity_smooth = lfs * BS;
499 1 : player->movement_liquid_sink = ls * BS;
500 1 : player->movement_gravity = g * BS;
501 1 : }
502 :
503 1 : void Client::handleCommand_HP(NetworkPacket* pkt)
504 : {
505 :
506 1 : Player *player = m_env.getLocalPlayer();
507 : assert(player != NULL);
508 :
509 1 : u8 oldhp = player->hp;
510 :
511 : u8 hp;
512 1 : *pkt >> hp;
513 :
514 1 : player->hp = hp;
515 :
516 1 : if (hp < oldhp) {
517 : // Add to ClientEvent queue
518 : ClientEvent event;
519 0 : event.type = CE_PLAYER_DAMAGE;
520 0 : event.player_damage.amount = oldhp - hp;
521 0 : m_client_event_queue.push(event);
522 : }
523 1 : }
524 :
525 1 : void Client::handleCommand_Breath(NetworkPacket* pkt)
526 : {
527 1 : Player *player = m_env.getLocalPlayer();
528 : assert(player != NULL);
529 :
530 : u16 breath;
531 :
532 1 : *pkt >> breath;
533 :
534 1 : player->setBreath(breath);
535 1 : }
536 :
537 1 : void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
538 : {
539 1 : Player *player = m_env.getLocalPlayer();
540 : assert(player != NULL);
541 :
542 1 : v3f pos;
543 : f32 pitch, yaw;
544 :
545 1 : *pkt >> pos >> pitch >> yaw;
546 :
547 1 : player->setPosition(pos);
548 :
549 1 : infostream << "Client got TOCLIENT_MOVE_PLAYER"
550 2 : << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
551 1 : << " pitch=" << pitch
552 2 : << " yaw=" << yaw
553 2 : << std::endl;
554 :
555 : /*
556 : Add to ClientEvent queue.
557 : This has to be sent to the main program because otherwise
558 : it would just force the pitch and yaw values to whatever
559 : the camera points to.
560 : */
561 : ClientEvent event;
562 1 : event.type = CE_PLAYER_FORCE_MOVE;
563 1 : event.player_force_move.pitch = pitch;
564 1 : event.player_force_move.yaw = yaw;
565 1 : m_client_event_queue.push(event);
566 :
567 : // Ignore damage for a few seconds, so that the player doesn't
568 : // get damage from falling on ground
569 1 : m_ignore_damage_timer = 3.0;
570 1 : }
571 :
572 0 : void Client::handleCommand_PlayerItem(NetworkPacket* pkt)
573 : {
574 0 : infostream << "Client: WARNING: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
575 0 : }
576 :
577 0 : void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
578 : {
579 : bool set_camera_point_target;
580 0 : v3f camera_point_target;
581 :
582 0 : *pkt >> set_camera_point_target;
583 0 : *pkt >> camera_point_target;
584 :
585 : ClientEvent event;
586 0 : event.type = CE_DEATHSCREEN;
587 0 : event.deathscreen.set_camera_point_target = set_camera_point_target;
588 0 : event.deathscreen.camera_point_target_x = camera_point_target.X;
589 0 : event.deathscreen.camera_point_target_y = camera_point_target.Y;
590 0 : event.deathscreen.camera_point_target_z = camera_point_target.Z;
591 0 : m_client_event_queue.push(event);
592 0 : }
593 :
594 1 : void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
595 : {
596 : u16 num_files;
597 :
598 1 : *pkt >> num_files;
599 :
600 1 : infostream << "Client: Received media announcement: packet size: "
601 2 : << pkt->getSize() << std::endl;
602 :
603 2 : if (m_media_downloader == NULL ||
604 1 : m_media_downloader->isStarted()) {
605 0 : const char *problem = m_media_downloader ?
606 : "we already saw another announcement" :
607 0 : "all media has been received already";
608 0 : errorstream << "Client: Received media announcement but "
609 0 : << problem << "! "
610 0 : << " files=" << num_files
611 0 : << " size=" << pkt->getSize() << std::endl;
612 0 : return;
613 : }
614 :
615 : // Mesh update thread must be stopped while
616 : // updating content definitions
617 1 : sanity_check(!m_mesh_update_thread.IsRunning());
618 :
619 2636 : for (u16 i = 0; i < num_files; i++) {
620 5270 : std::string name, sha1_base64;
621 :
622 2635 : *pkt >> name >> sha1_base64;
623 :
624 5270 : std::string sha1_raw = base64_decode(sha1_base64);
625 2635 : m_media_downloader->addFile(name, sha1_raw);
626 : }
627 :
628 2 : std::vector<std::string> remote_media;
629 : try {
630 2 : std::string str;
631 :
632 1 : *pkt >> str;
633 :
634 2 : Strfnd sf(str);
635 1 : while(!sf.atend()) {
636 0 : std::string baseurl = trim(sf.next(","));
637 0 : if (baseurl != "")
638 0 : m_media_downloader->addRemoteServer(baseurl);
639 : }
640 : }
641 0 : catch(SerializationError& e) {
642 : // not supported by server or turned off
643 : }
644 :
645 1 : m_media_downloader->step(this);
646 : }
647 :
648 0 : void Client::handleCommand_Media(NetworkPacket* pkt)
649 : {
650 : /*
651 : u16 command
652 : u16 total number of file bunches
653 : u16 index of this bunch
654 : u32 number of files in this bunch
655 : for each file {
656 : u16 length of name
657 : string name
658 : u32 length of data
659 : data
660 : }
661 : */
662 : u16 num_bunches;
663 : u16 bunch_i;
664 : u32 num_files;
665 :
666 0 : *pkt >> num_bunches >> bunch_i >> num_files;
667 :
668 0 : infostream << "Client: Received files: bunch " << bunch_i << "/"
669 0 : << num_bunches << " files=" << num_files
670 0 : << " size=" << pkt->getSize() << std::endl;
671 :
672 0 : if (num_files == 0)
673 0 : return;
674 :
675 0 : if (m_media_downloader == NULL ||
676 0 : !m_media_downloader->isStarted()) {
677 0 : const char *problem = m_media_downloader ?
678 : "media has not been requested" :
679 0 : "all media has been received already";
680 0 : errorstream << "Client: Received media but "
681 0 : << problem << "! "
682 0 : << " bunch " << bunch_i << "/" << num_bunches
683 0 : << " files=" << num_files
684 0 : << " size=" << pkt->getSize() << std::endl;
685 0 : return;
686 : }
687 :
688 : // Mesh update thread must be stopped while
689 : // updating content definitions
690 0 : sanity_check(!m_mesh_update_thread.IsRunning());
691 :
692 0 : for (u32 i=0; i < num_files; i++) {
693 0 : std::string name;
694 :
695 0 : *pkt >> name;
696 :
697 0 : std::string data = pkt->readLongString();
698 :
699 0 : m_media_downloader->conventionalTransferDone(
700 0 : name, data, this);
701 : }
702 : }
703 :
704 0 : void Client::handleCommand_ToolDef(NetworkPacket* pkt)
705 : {
706 0 : infostream << "Client: WARNING: Ignoring TOCLIENT_TOOLDEF" << std::endl;
707 0 : }
708 :
709 1 : void Client::handleCommand_NodeDef(NetworkPacket* pkt)
710 : {
711 1 : infostream << "Client: Received node definitions: packet size: "
712 2 : << pkt->getSize() << std::endl;
713 :
714 : // Mesh update thread must be stopped while
715 : // updating content definitions
716 1 : sanity_check(!m_mesh_update_thread.IsRunning());
717 :
718 : // Decompress node definitions
719 2 : std::string datastring(pkt->getString(0), pkt->getSize());
720 2 : std::istringstream is(datastring, std::ios_base::binary);
721 2 : std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
722 2 : std::ostringstream tmp_os;
723 1 : decompressZlib(tmp_is, tmp_os);
724 :
725 : // Deserialize node definitions
726 2 : std::istringstream tmp_is2(tmp_os.str());
727 1 : m_nodedef->deSerialize(tmp_is2);
728 1 : m_nodedef_received = true;
729 1 : }
730 :
731 0 : void Client::handleCommand_CraftItemDef(NetworkPacket* pkt)
732 : {
733 0 : infostream << "Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
734 0 : }
735 :
736 1 : void Client::handleCommand_ItemDef(NetworkPacket* pkt)
737 : {
738 1 : infostream << "Client: Received item definitions: packet size: "
739 2 : << pkt->getSize() << std::endl;
740 :
741 : // Mesh update thread must be stopped while
742 : // updating content definitions
743 1 : sanity_check(!m_mesh_update_thread.IsRunning());
744 :
745 : // Decompress item definitions
746 2 : std::string datastring(pkt->getString(0), pkt->getSize());
747 2 : std::istringstream is(datastring, std::ios_base::binary);
748 2 : std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
749 2 : std::ostringstream tmp_os;
750 1 : decompressZlib(tmp_is, tmp_os);
751 :
752 : // Deserialize node definitions
753 2 : std::istringstream tmp_is2(tmp_os.str());
754 1 : m_itemdef->deSerialize(tmp_is2);
755 1 : m_itemdef_received = true;
756 1 : }
757 :
758 2 : void Client::handleCommand_PlaySound(NetworkPacket* pkt)
759 : {
760 : s32 server_id;
761 4 : std::string name;
762 : float gain;
763 : u8 type; // 0=local, 1=positional, 2=object
764 2 : v3f pos;
765 : u16 object_id;
766 : bool loop;
767 :
768 2 : *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
769 :
770 : // Start playing
771 2 : int client_id = -1;
772 2 : switch(type) {
773 : case 0: // local
774 1 : client_id = m_sound->playSound(name, loop, gain);
775 1 : break;
776 : case 1: // positional
777 0 : client_id = m_sound->playSoundAt(name, loop, gain, pos);
778 0 : break;
779 : case 2:
780 : { // object
781 1 : ClientActiveObject *cao = m_env.getActiveObject(object_id);
782 1 : if (cao)
783 1 : pos = cao->getPosition();
784 1 : client_id = m_sound->playSoundAt(name, loop, gain, pos);
785 : // TODO: Set up sound to move with object
786 1 : break;
787 : }
788 : default:
789 0 : break;
790 : }
791 :
792 2 : if (client_id != -1) {
793 2 : m_sounds_server_to_client[server_id] = client_id;
794 2 : m_sounds_client_to_server[client_id] = server_id;
795 2 : if (object_id != 0)
796 1 : m_sounds_to_objects[client_id] = object_id;
797 : }
798 2 : }
799 :
800 0 : void Client::handleCommand_StopSound(NetworkPacket* pkt)
801 : {
802 : s32 server_id;
803 :
804 0 : *pkt >> server_id;
805 :
806 : std::map<s32, int>::iterator i =
807 0 : m_sounds_server_to_client.find(server_id);
808 :
809 0 : if (i != m_sounds_server_to_client.end()) {
810 0 : int client_id = i->second;
811 0 : m_sound->stopSound(client_id);
812 : }
813 0 : }
814 :
815 1 : void Client::handleCommand_Privileges(NetworkPacket* pkt)
816 : {
817 1 : m_privileges.clear();
818 1 : infostream << "Client: Privileges updated: ";
819 : u16 num_privileges;
820 :
821 1 : *pkt >> num_privileges;
822 :
823 21 : for (u16 i = 0; i < num_privileges; i++) {
824 40 : std::string priv;
825 :
826 20 : *pkt >> priv;
827 :
828 20 : m_privileges.insert(priv);
829 20 : infostream << priv << " ";
830 : }
831 1 : infostream << std::endl;
832 1 : }
833 :
834 4 : void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
835 : {
836 4 : Player *player = m_env.getLocalPlayer();
837 : assert(player != NULL);
838 :
839 : // Store formspec in LocalPlayer
840 4 : player->inventory_formspec = pkt->readLongString();
841 4 : }
842 :
843 28 : void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
844 : {
845 56 : std::string datastring(pkt->getString(0), pkt->getSize());
846 56 : std::istringstream is(datastring, std::ios_base::binary);
847 :
848 56 : std::string name = deSerializeString(is);
849 :
850 28 : infostream << "Client: Detached inventory update: \"" << name
851 28 : << "\"" << std::endl;
852 :
853 28 : Inventory *inv = NULL;
854 28 : if (m_detached_inventories.count(name) > 0)
855 19 : inv = m_detached_inventories[name];
856 : else {
857 9 : inv = new Inventory(m_itemdef);
858 9 : m_detached_inventories[name] = inv;
859 : }
860 28 : inv->deSerialize(is);
861 28 : }
862 :
863 0 : void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
864 : {
865 0 : std::string formspec = pkt->readLongString();
866 0 : std::string formname;
867 :
868 0 : *pkt >> formname;
869 :
870 : ClientEvent event;
871 0 : event.type = CE_SHOW_FORMSPEC;
872 : // pointer is required as event is a struct only!
873 : // adding a std:string to a struct isn't possible
874 0 : event.show_formspec.formspec = new std::string(formspec);
875 0 : event.show_formspec.formname = new std::string(formname);
876 0 : m_client_event_queue.push(event);
877 0 : }
878 :
879 0 : void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
880 : {
881 0 : std::string datastring(pkt->getString(0), pkt->getSize());
882 0 : std::istringstream is(datastring, std::ios_base::binary);
883 :
884 0 : v3f pos = readV3F1000(is);
885 0 : v3f vel = readV3F1000(is);
886 0 : v3f acc = readV3F1000(is);
887 0 : float expirationtime = readF1000(is);
888 0 : float size = readF1000(is);
889 0 : bool collisiondetection = readU8(is);
890 0 : std::string texture = deSerializeLongString(is);
891 0 : bool vertical = false;
892 : try {
893 0 : vertical = readU8(is);
894 0 : } catch (...) {}
895 :
896 : ClientEvent event;
897 0 : event.type = CE_SPAWN_PARTICLE;
898 0 : event.spawn_particle.pos = new v3f (pos);
899 0 : event.spawn_particle.vel = new v3f (vel);
900 0 : event.spawn_particle.acc = new v3f (acc);
901 0 : event.spawn_particle.expirationtime = expirationtime;
902 0 : event.spawn_particle.size = size;
903 0 : event.spawn_particle.collisiondetection = collisiondetection;
904 0 : event.spawn_particle.vertical = vertical;
905 0 : event.spawn_particle.texture = new std::string(texture);
906 :
907 0 : m_client_event_queue.push(event);
908 0 : }
909 :
910 9 : void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
911 : {
912 : u16 amount;
913 : float spawntime;
914 9 : v3f minpos;
915 9 : v3f maxpos;
916 9 : v3f minvel;
917 9 : v3f maxvel;
918 9 : v3f minacc;
919 9 : v3f maxacc;
920 : float minexptime;
921 : float maxexptime;
922 : float minsize;
923 : float maxsize;
924 : bool collisiondetection;
925 : u32 id;
926 :
927 9 : *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
928 9 : >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
929 9 : >> maxsize >> collisiondetection;
930 :
931 18 : std::string texture = pkt->readLongString();
932 :
933 9 : *pkt >> id;
934 :
935 9 : bool vertical = false;
936 : try {
937 9 : *pkt >> vertical;
938 0 : } catch (...) {}
939 :
940 : ClientEvent event;
941 9 : event.type = CE_ADD_PARTICLESPAWNER;
942 9 : event.add_particlespawner.amount = amount;
943 9 : event.add_particlespawner.spawntime = spawntime;
944 9 : event.add_particlespawner.minpos = new v3f (minpos);
945 9 : event.add_particlespawner.maxpos = new v3f (maxpos);
946 9 : event.add_particlespawner.minvel = new v3f (minvel);
947 9 : event.add_particlespawner.maxvel = new v3f (maxvel);
948 9 : event.add_particlespawner.minacc = new v3f (minacc);
949 9 : event.add_particlespawner.maxacc = new v3f (maxacc);
950 9 : event.add_particlespawner.minexptime = minexptime;
951 9 : event.add_particlespawner.maxexptime = maxexptime;
952 9 : event.add_particlespawner.minsize = minsize;
953 9 : event.add_particlespawner.maxsize = maxsize;
954 9 : event.add_particlespawner.collisiondetection = collisiondetection;
955 9 : event.add_particlespawner.vertical = vertical;
956 9 : event.add_particlespawner.texture = new std::string(texture);
957 9 : event.add_particlespawner.id = id;
958 :
959 9 : m_client_event_queue.push(event);
960 9 : }
961 :
962 :
963 0 : void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
964 : {
965 : u16 legacy_id;
966 : u32 id;
967 :
968 : // Modification set 13/03/15, 1 year of compat for protocol v24
969 0 : if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
970 0 : *pkt >> legacy_id;
971 : }
972 : else {
973 0 : *pkt >> id;
974 : }
975 :
976 :
977 : ClientEvent event;
978 0 : event.type = CE_DELETE_PARTICLESPAWNER;
979 : event.delete_particlespawner.id =
980 0 : (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
981 :
982 0 : m_client_event_queue.push(event);
983 0 : }
984 :
985 2 : void Client::handleCommand_HudAdd(NetworkPacket* pkt)
986 : {
987 4 : std::string datastring(pkt->getString(0), pkt->getSize());
988 4 : std::istringstream is(datastring, std::ios_base::binary);
989 :
990 : u32 id;
991 : u8 type;
992 2 : v2f pos;
993 4 : std::string name;
994 2 : v2f scale;
995 4 : std::string text;
996 : u32 number;
997 : u32 item;
998 : u32 dir;
999 2 : v2f align;
1000 2 : v2f offset;
1001 2 : v3f world_pos;
1002 2 : v2s32 size;
1003 :
1004 2 : *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
1005 2 : >> dir >> align >> offset;
1006 : try {
1007 2 : *pkt >> world_pos;
1008 : }
1009 0 : catch(SerializationError &e) {};
1010 :
1011 : try {
1012 2 : *pkt >> size;
1013 0 : } catch(SerializationError &e) {};
1014 :
1015 : ClientEvent event;
1016 2 : event.type = CE_HUDADD;
1017 2 : event.hudadd.id = id;
1018 2 : event.hudadd.type = type;
1019 2 : event.hudadd.pos = new v2f(pos);
1020 2 : event.hudadd.name = new std::string(name);
1021 2 : event.hudadd.scale = new v2f(scale);
1022 2 : event.hudadd.text = new std::string(text);
1023 2 : event.hudadd.number = number;
1024 2 : event.hudadd.item = item;
1025 2 : event.hudadd.dir = dir;
1026 2 : event.hudadd.align = new v2f(align);
1027 2 : event.hudadd.offset = new v2f(offset);
1028 2 : event.hudadd.world_pos = new v3f(world_pos);
1029 2 : event.hudadd.size = new v2s32(size);
1030 2 : m_client_event_queue.push(event);
1031 2 : }
1032 :
1033 0 : void Client::handleCommand_HudRemove(NetworkPacket* pkt)
1034 : {
1035 : u32 id;
1036 :
1037 0 : *pkt >> id;
1038 :
1039 : ClientEvent event;
1040 0 : event.type = CE_HUDRM;
1041 0 : event.hudrm.id = id;
1042 0 : m_client_event_queue.push(event);
1043 0 : }
1044 :
1045 15 : void Client::handleCommand_HudChange(NetworkPacket* pkt)
1046 : {
1047 30 : std::string sdata;
1048 15 : v2f v2fdata;
1049 15 : v3f v3fdata;
1050 15 : u32 intdata = 0;
1051 15 : v2s32 v2s32data;
1052 : u32 id;
1053 : u8 stat;
1054 :
1055 15 : *pkt >> id >> stat;
1056 :
1057 30 : if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
1058 30 : stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
1059 0 : *pkt >> v2fdata;
1060 15 : else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
1061 14 : *pkt >> sdata;
1062 1 : else if (stat == HUD_STAT_WORLD_POS)
1063 0 : *pkt >> v3fdata;
1064 1 : else if (stat == HUD_STAT_SIZE )
1065 0 : *pkt >> v2s32data;
1066 : else
1067 1 : *pkt >> intdata;
1068 :
1069 : ClientEvent event;
1070 15 : event.type = CE_HUDCHANGE;
1071 15 : event.hudchange.id = id;
1072 15 : event.hudchange.stat = (HudElementStat)stat;
1073 15 : event.hudchange.v2fdata = new v2f(v2fdata);
1074 15 : event.hudchange.v3fdata = new v3f(v3fdata);
1075 15 : event.hudchange.sdata = new std::string(sdata);
1076 15 : event.hudchange.data = intdata;
1077 15 : event.hudchange.v2s32data = new v2s32(v2s32data);
1078 15 : m_client_event_queue.push(event);
1079 15 : }
1080 :
1081 1 : void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
1082 : {
1083 : u32 flags, mask;
1084 :
1085 1 : *pkt >> flags >> mask;
1086 :
1087 1 : Player *player = m_env.getLocalPlayer();
1088 : assert(player != NULL);
1089 :
1090 1 : player->hud_flags &= ~mask;
1091 1 : player->hud_flags |= flags;
1092 1 : }
1093 :
1094 2 : void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
1095 : {
1096 4 : u16 param; std::string value;
1097 :
1098 2 : *pkt >> param >> value;
1099 :
1100 2 : Player *player = m_env.getLocalPlayer();
1101 : assert(player != NULL);
1102 :
1103 2 : if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
1104 0 : s32 hotbar_itemcount = readS32((u8*) value.c_str());
1105 0 : if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
1106 0 : player->hud_hotbar_itemcount = hotbar_itemcount;
1107 : }
1108 2 : else if (param == HUD_PARAM_HOTBAR_IMAGE) {
1109 1 : ((LocalPlayer *) player)->hotbar_image = value;
1110 : }
1111 1 : else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
1112 1 : ((LocalPlayer *) player)->hotbar_selected_image = value;
1113 : }
1114 2 : }
1115 :
1116 0 : void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
1117 : {
1118 0 : std::string datastring(pkt->getString(0), pkt->getSize());
1119 0 : std::istringstream is(datastring, std::ios_base::binary);
1120 :
1121 0 : video::SColor *bgcolor = new video::SColor(readARGB8(is));
1122 0 : std::string *type = new std::string(deSerializeString(is));
1123 0 : u16 count = readU16(is);
1124 0 : std::vector<std::string> *params = new std::vector<std::string>;
1125 :
1126 0 : for (size_t i = 0; i < count; i++)
1127 0 : params->push_back(deSerializeString(is));
1128 :
1129 : ClientEvent event;
1130 0 : event.type = CE_SET_SKY;
1131 0 : event.set_sky.bgcolor = bgcolor;
1132 0 : event.set_sky.type = type;
1133 0 : event.set_sky.params = params;
1134 0 : m_client_event_queue.push(event);
1135 0 : }
1136 :
1137 0 : void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
1138 : {
1139 : bool do_override;
1140 : u16 day_night_ratio_u;
1141 :
1142 0 : *pkt >> do_override >> day_night_ratio_u;
1143 :
1144 0 : float day_night_ratio_f = (float)day_night_ratio_u / 65536;
1145 :
1146 : ClientEvent event;
1147 0 : event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
1148 0 : event.override_day_night_ratio.do_override = do_override;
1149 0 : event.override_day_night_ratio.ratio_f = day_night_ratio_f;
1150 0 : m_client_event_queue.push(event);
1151 0 : }
1152 :
1153 1 : void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
1154 : {
1155 1 : LocalPlayer *player = m_env.getLocalPlayer();
1156 : assert(player != NULL);
1157 :
1158 1 : *pkt >> player->local_animations[0];
1159 1 : *pkt >> player->local_animations[1];
1160 1 : *pkt >> player->local_animations[2];
1161 1 : *pkt >> player->local_animations[3];
1162 1 : *pkt >> player->local_animation_speed;
1163 1 : }
1164 :
1165 0 : void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
1166 : {
1167 0 : LocalPlayer *player = m_env.getLocalPlayer();
1168 : assert(player != NULL);
1169 :
1170 0 : *pkt >> player->eye_offset_first >> player->eye_offset_third;
1171 0 : }
1172 :
1173 0 : void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
1174 : {
1175 0 : if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
1176 0 : && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
1177 0 : errorstream << "Client: Recieved SRP S_B login message,"
1178 0 : << " but wasn't supposed to (chosen_mech="
1179 0 : << m_chosen_auth_mech << ")." << std::endl;
1180 0 : return;
1181 : }
1182 :
1183 0 : char *bytes_M = 0;
1184 0 : size_t len_M = 0;
1185 0 : SRPUser *usr = (SRPUser *) m_auth_data;
1186 0 : std::string s;
1187 0 : std::string B;
1188 0 : *pkt >> s >> B;
1189 :
1190 0 : infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
1191 :
1192 0 : srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
1193 0 : (const unsigned char *) B.c_str(), B.size(),
1194 0 : (unsigned char **) &bytes_M, &len_M);
1195 :
1196 0 : if ( !bytes_M ) {
1197 0 : errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
1198 0 : return;
1199 : }
1200 :
1201 0 : NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
1202 0 : resp_pkt << std::string(bytes_M, len_M);
1203 0 : Send(&resp_pkt);
1204 3 : }
|