aboutsummaryrefslogtreecommitdiff
path: root/textures/base/pack/server_favorite.png
blob: 6a3fc5efe7ce3a9ffa0f2e0a800afdace65285c4 (plain)
ofshex dumpascii
0000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 10 00 00 00 10 08 06 00 00 00 1f f3 ff .PNG........IHDR................
0020 61 00 00 00 04 67 41 4d 41 00 00 af c8 37 05 8a e9 00 00 00 19 74 45 58 74 53 6f 66 74 77 61 72 a....gAMA....7.......tEXtSoftwar
0040 65 00 41 64 6f 62 65 20 49 6d 61 67 65 52 65 61 64 79 71 c9 65 3c 00 00 03 26 49 44 41 54 78 da e.Adobe.ImageReadyq.e<...&IDATx.
0060 62 fc d7 c9 c0 c0 f0 87 81 e1 ff 6f 06 86 df 3f 81 f4 7f 06 38 60 67 62 d8 d8 78 80 61 45 e3 31 b..........o...?....8`gb..x.aE.1
0080 86 e5 30 b1 2c 55 06 86 32 75 a0 5a a0 fa d2 4b 0c 0c 00 01 c4 04 93 60 64 04 62 20 fd 1f 64 d8 ..0.,U..2u.Z...K.......`d.b...d.
00a0 5f b0 81 16 0c 6a b2 7e 16 ea 1c e9 ac 8c 0c cc 20 35 cc 40 05 fa 42 0c 0c 3f fe 21 2c 01 08 20 _....j.~.........5.@..B..?.!,...
00c0 26 06 24 c0 04 32 01 84 81 06 30 b0 32 67 30 f8 86 32 b8 79 ab 59 99 8b 30 d8 82 e4 cd 44 18 18 &.$..2....0.2g0..2.y.Y..0....D..
00e0 dc a5 18 18 be fd 41 e8 01 08 20 14 03 c0 9a 41 ae f8 c1 a0 cc a4 ae 1c c0 20 78 8f 81 d1 46 99 ......A........A..........x...F.
0100 35 4a 97 39 85 0d 28 97 a7 01 34 fb 3f 8a 0e 06 80 00 62 01 93 7f 81 5a 7f 32 08 31 fc 66 10 66 5J.9..(...4.?.....b....Z.2.1.f.f
0120 fc c9 20 f8 9f 89 21 99 c5 52 9d 9f 81 e1 21 03 83 98 22 83 93 93 8a cf a2 af 37 a3 4c f9 19 ae ......!..R....!...".......7.L...
0140 7d fa c4 f0 fe ef 1f 86 d7 0c 1c 0c df 40 f6 01 04 10 0b c3 7f 60 00 49 09 eb 33 f0 c8 72 30 32 }............@.......`.I..3..r02
0160 ff e2 63 e1 66 e7 61 14 95 64 67 52 fd 05 94 06 86 14 c3 23 06 b5 14 63 7e 05 6d a1 a5 1f 5e be ..c.f.a..dgR.......#...c~.m...^.
0180 fb c2 fc 96 f1 f3 df f7 5f 3e b1 dd 79 fa f7 df bf ff f5 00 01 c4 f2 f5 3b c3 56 a6 7f ec 81 9c ........_>..y...........;.V.....
01a0 fe c6 ec 4c ec 4f 81 1a be 02 f1 6b a0 de 8f 40 bf 80 ec 00 46 8d c8 65 06 36 7f 1e 06 31 06 31 ...L.O.....k...@....F..e.6...1.1
01c0 1e 06 06 1e 9e 7f 2b 6f 4b 4e db c3 70 72 cf 6b 86 fb 00 01 c4 f8 b6 9e 81 e1 c9 63 06 5b 59 15 ......+oKN..pr.k...........c.[Y.
01e0 96 05 02 41 22 4a 0c a2 c0 60 f9 09 f4 28 23 72 f0 fc 83 18 f6 8d 99 e1 c7 fa d7 0c 3d 73 7e ac ...A"J...`...(#r............=s~.
0200 6a be c9 50 0e 74 df 03 80 00 62 fc d0 c8 c0 70 f3 09 50 ee 03 83 b6 9c 04 e3 5c 79 3f 6e 73 26 j..P.t....b....p..P.......\y?ns&
0220 19 56 70 da 80 03 50 24 be 63 65 78 b6 e1 e3 b7 8e 55 3f 17 4c 7b c4 50 0f 34 f2 0d 48 0a 20 80 .Vp...P$.cex.....U?.L{.P.4..H...
0240 98 fe 01 5d c8 09 4a 03 2c 0c 57 ef 3f f8 5f f5 eb f2 d7 3f 0c 9f 7e 30 30 7c fc 0e c1 1f 80 18 ...]..J.,.W.?._....?..~00|......
0260 c4 7f fe 89 61 df e1 9f 17 a7 3c 62 a8 86 69 06 01 80 00 62 f9 f9 12 18 d0 40 03 c4 b8 81 3c 2e ....a.....<b..i....b.....@....<.
0280 06 69 26 ce ff 2c 0c ef 81 a6 7e 67 80 38 9b 03 e4 9d ff 60 b6 98 20 83 24 07 30 51 fd 40 8a 4a .i&..,....~g.8.....`....$.0Q.@.J
02a0 80 00 62 02 25 dd 7f 40 23 ff fd 05 ab b1 64 01 79 fd 19 50 e0 ea 3f 86 cf bb ff 32 fc 38 0c 64 ..b.%..@#.....d.y..P..?....2.8.d
02c0 3f 87 28 d2 14 61 90 54 e4 60 d0 42 4e 07 00 01 c4 f2 07 ea 57 a0 41 8c 1c 6c 0c 46 4c 40 17 fd ?.(..a.T.`.BN.......W.A..l.FL@..
02e0 07 e2 35 97 19 8e 4d ba ca 30 dd 52 90 41 31 46 8b 21 57 47 9b 41 54 8a 8b 81 5d 88 8d 41 1f e8 ..5...M..0.R.A1F.!WG.AT...]..A..
0300 ba c3 30 03 00 02 88 e5 df 3f 78 22 54 e2 fd cb 60 70 ee 2c c3 fd e6 b3 0c 53 b7 3d 65 58 fe fb ..0......?x"T...`p.,.....S.=eX..
0320 3f c3 b3 e7 40 ef 5f fa c0 b0 d6 fd 36 43 69 b8 3c 43 bc 97 08 83 fd d1 8f 0c 53 60 06 00 04 10 ?...@._.....6Ci.<C........S`....
0340 0b 28 86 40 19 09 e8 82 3f 79 47 18 ea d7 dd 63 d8 f5 ee 17 c3 79 98 02 0e 60 0c bc fc c5 70 6d .(.@....?yG....c.....y...`....pm
0360 df 1b 86 c4 c3 af 19 56 01 d9 8a 40 61 4e 06 48 28 31 00 04 10 e3 ff ff 68 89 9b 44 00 10 60 00 .......V...@aN.H(1......h..D..`.
0380 01 99 09 29 dc 0d 5f a5 00 00 00 00 49 45 4e 44 ae 42 60 82 ...).._.....IEND.B`.
#n209'>209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
/*
Minetest
Copyright (C) 2010-2014 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <sstream>
#include "clientiface.h"
#include "network/connection.h"
#include "network/serveropcodes.h"
#include "remoteplayer.h"
#include "settings.h"
#include "mapblock.h"
#include "serverenvironment.h"
#include "map.h"
#include "emerge.h"
#include "server/luaentity_sao.h"
#include "server/player_sao.h"
#include "log.h"
#include "util/srp.h"
#include "face_position_cache.h"

const char *ClientInterface::statenames[] = {
	"Invalid",
	"Disconnecting",
	"Denied",
	"Created",
	"AwaitingInit2",
	"HelloSent",
	"InitDone",
	"DefinitionsSent",
	"Active",
	"SudoMode",
};



std::string ClientInterface::state2Name(ClientState state)
{
	return statenames[state];
}

RemoteClient::RemoteClient() :
	m_max_simul_sends(g_settings->getU16("max_simultaneous_block_sends_per_client")),
	m_min_time_from_building(
		g_settings->getFloat("full_block_send_enable_min_time_from_building")),
	m_max_send_distance(g_settings->getS16("max_block_send_distance")),
	m_block_optimize_distance(g_settings->getS16("block_send_optimize_distance")),
	m_max_gen_distance(g_settings->getS16("max_block_generate_distance")),
	m_occ_cull(g_settings->getBool("server_side_occlusion_culling"))
{
}

void RemoteClient::ResendBlockIfOnWire(v3s16 p)
{
	// if this block is on wire, mark it for sending again as soon as possible
	if (m_blocks_sending.find(p) != m_blocks_sending.end()) {
		SetBlockNotSent(p);
	}
}

LuaEntitySAO *getAttachedObject(PlayerSAO *sao, ServerEnvironment *env)
{
	if (!sao->isAttached())
		return nullptr;

	int id;
	std::string bone;
	v3f dummy;
	bool force_visible;
	sao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible);
	ServerActiveObject *ao = env->getActiveObject(id);
	while (id && ao) {
		ao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible);
		if (id)
			ao = env->getActiveObject(id);
	}
	return dynamic_cast<LuaEntitySAO *>(ao);
}

void RemoteClient::GetNextBlocks (
		ServerEnvironment *env,
		EmergeManager * emerge,
		float dtime,
		std::vector<PrioritySortedBlockTransfer> &dest)
{
	// Increment timers
	m_nothing_to_send_pause_timer -= dtime;

	if (m_nothing_to_send_pause_timer >= 0)
		return;

	RemotePlayer *player = env->getPlayer(peer_id);
	// This can happen sometimes; clients and players are not in perfect sync.
	if (!player)
		return;

	PlayerSAO *sao = player->getPlayerSAO();
	if (!sao)
		return;

	// Won't send anything if already sending
	if (m_blocks_sending.size() >= m_max_simul_sends) {
		//infostream<<"Not sending any blocks, Queue full."<<std::endl;
		return;
	}

	v3f playerpos = sao->getBasePosition();
	// if the player is attached, get the velocity from the attached object
	LuaEntitySAO *lsao = getAttachedObject(sao, env);
	const v3f &playerspeed = lsao? lsao->getVelocity() : player->getSpeed();
	v3f playerspeeddir(0,0,0);
	if (playerspeed.getLength() > 1.0f * BS)
		playerspeeddir = playerspeed / playerspeed.getLength();
	// Predict to next block
	v3f playerpos_predicted = playerpos + playerspeeddir * (MAP_BLOCKSIZE * BS);

	v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);

	v3s16 center = getNodeBlockPos(center_nodepos);

	// Camera position and direction
	v3f camera_pos = sao->getEyePosition();
	v3f camera_dir = v3f(0,0,1);
	camera_dir.rotateYZBy(sao->getLookPitch());
	camera_dir.rotateXZBy(sao->getRotation().Y);

	u16 max_simul_sends_usually = m_max_simul_sends;

	/*
		Check the time from last addNode/removeNode.

		Decrease send rate if player is building stuff.
	*/
	m_time_from_building += dtime;
	if (m_time_from_building < m_min_time_from_building) {
		max_simul_sends_usually
			= LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
	}

	/*
		Number of blocks sending + number of blocks selected for sending
	*/
	u32 num_blocks_selected = m_blocks_sending.size();

	/*
		next time d will be continued from the d from which the nearest
		unsent block was found this time.

		This is because not necessarily any of the blocks found this
		time are actually sent.
	*/
	s32 new_nearest_unsent_d = -1;

	// Get view range and camera fov (radians) from the client
	s16 wanted_range = sao->getWantedRange() + 1;
	float camera_fov = sao->getFov();

	/*
		Get the starting value of the block finder radius.
	*/
	if (m_last_center != center) {
		m_nearest_unsent_d = 0;
		m_last_center = center;
	}
	// reset the unsent distance if the view angle has changed more that 10% of the fov
	// (this matches isBlockInSight which allows for an extra 10%)
	if (camera_dir.dotProduct(m_last_camera_dir) < std::cos(camera_fov * 0.1f)) {
		m_nearest_unsent_d = 0;
		m_last_camera_dir = camera_dir;
	}
	if (m_nearest_unsent_d > 0) {
		// make sure any blocks modified since the last time we sent blocks are resent
		for (const v3s16 &p : m_blocks_modified) {
			m_nearest_unsent_d = std::min(m_nearest_unsent_d, center.getDistanceFrom(p));
		}
	}
	m_blocks_modified.clear();

	s16 d_start = m_nearest_unsent_d;

	// Distrust client-sent FOV and get server-set player object property
	// zoom FOV (degrees) as a check to avoid hacked clients using FOV to load
	// distant world.
	// (zoom is disabled by value 0)
	float prop_zoom_fov = sao->getZoomFOV() < 0.001f ?
		0.0f :
		std::max(camera_fov, sao->getZoomFOV() * core::DEGTORAD);

	const s16 full_d_max = std::min(adjustDist(m_max_send_distance, prop_zoom_fov),
		wanted_range);
	const s16 d_opt = std::min(adjustDist(m_block_optimize_distance, prop_zoom_fov),
		wanted_range);
	const s16 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE;

	s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, prop_zoom_fov),
		wanted_range);

	s16 d_max = full_d_max;

	// Don't loop very much at a time
	s16 max_d_increment_at_time = 2;
	if (d_max > d_start + max_d_increment_at_time)
		d_max = d_start + max_d_increment_at_time;

	// cos(angle between velocity and camera) * |velocity|
	// Limit to 0.0f in case player moves backwards.
	f32 dot = rangelim(camera_dir.dotProduct(playerspeed), 0.0f, 300.0f);

	// Reduce the field of view when a player moves and looks forward.
	// limit max fov effect to 50%, 60% at 20n/s fly speed
	camera_fov = camera_fov / (1 + dot / 300.0f);

	s32 nearest_emerged_d = -1;
	s32 nearest_emergefull_d = -1;
	s32 nearest_sent_d = -1;
	//bool queue_is_full = false;

	const v3s16 cam_pos_nodes = floatToInt(camera_pos, BS);

	s16 d;
	for (d = d_start; d <= d_max; d++) {
		/*
			Get the border/face dot coordinates of a "d-radiused"
			box
		*/
		std::vector<v3s16> list = FacePositionCache::getFacePositions(d);

		std::vector<v3s16>::iterator li;
		for (li = list.begin(); li != list.end(); ++li) {
			v3s16 p = *li + center;

			/*
				Send throttling
				- Don't allow too many simultaneous transfers
				- EXCEPT when the blocks are very close

				Also, don't send blocks that are already flying.
			*/

			// Start with the usual maximum
			u16 max_simul_dynamic = max_simul_sends_usually;

			// If block is very close, allow full maximum
			if (d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
				max_simul_dynamic = m_max_simul_sends;

			// Don't select too many blocks for sending
			if (num_blocks_selected >= max_simul_dynamic) {
				//queue_is_full = true;
				goto queue_full_break;
			}

			// Don't send blocks that are currently being transferred
			if (m_blocks_sending.find(p) != m_blocks_sending.end())
				continue;

			/*
				Do not go over max mapgen limit
			*/
			if (blockpos_over_max_limit(p))
				continue;

			// If this is true, inexistent block will be made from scratch
			bool generate = d <= d_max_gen;

			/*
				Don't generate or send if not in sight
				FIXME This only works if the client uses a small enough
				FOV setting. The default of 72 degrees is fine.
				Also retrieve a smaller view cone in the direction of the player's
				movement.
				(0.1 is about 4 degrees)
			*/
			f32 dist;
			if (!(isBlockInSight(p, camera_pos, camera_dir, camera_fov,
						d_blocks_in_sight, &dist) ||
					(playerspeed.getLength() > 1.0f * BS &&
					isBlockInSight(p, camera_pos, playerspeeddir, 0.1f,
						d_blocks_in_sight)))) {
				continue;
			}

			/*
				Don't send already sent blocks
			*/
			if (m_blocks_sent.find(p) != m_blocks_sent.end())
				continue;

			/*
				Check if map has this block
			*/
			MapBlock *block = env->getMap().getBlockNoCreateNoEx(p);

			bool block_not_found = false;
			if (block) {
				// Reset usage timer, this block will be of use in the future.
				block->resetUsageTimer();

				// Check whether the block exists (with data)
				if (block->isDummy() || !block->isGenerated())
					block_not_found = true;

				/*
					If block is not close, don't send it unless it is near
					ground level.

					Block is near ground level if night-time mesh
					differs from day-time mesh.
				*/
				if (d >= d_opt) {
					if (!block->getIsUnderground() && !block->getDayNightDiff())
						continue;
				}

				if (m_occ_cull && !block_not_found &&
						env->getMap().isBlockOccluded(block, cam_pos_nodes)) {
					continue;
				}
			}

			/*
				If block has been marked to not exist on disk (dummy) or is
				not generated and generating new ones is not wanted, skip block.
			*/
			if (!generate && block_not_found) {
				// get next one.
				continue;
			}

			/*
				Add inexistent block to emerge queue.
			*/
			if (block == NULL || block_not_found) {
				if (emerge->enqueueBlockEmerge(peer_id, p, generate)) {
					if (nearest_emerged_d == -1)
						nearest_emerged_d = d;
				} else {
					if (nearest_emergefull_d == -1)
						nearest_emergefull_d = d;
					goto queue_full_break;
				}

				// get next one.
				continue;
			}

			if (nearest_sent_d == -1)
				nearest_sent_d = d;

			/*
				Add block to send queue
			*/
			PrioritySortedBlockTransfer q((float)dist, p, peer_id);

			dest.push_back(q);

			num_blocks_selected += 1;
		}
	}
queue_full_break:

	// If nothing was found for sending and nothing was queued for
	// emerging, continue next time browsing from here
	if (nearest_emerged_d != -1) {
		new_nearest_unsent_d = nearest_emerged_d;
	} else if (nearest_emergefull_d != -1) {
		new_nearest_unsent_d = nearest_emergefull_d;
	} else {
		if (d > full_d_max) {
			new_nearest_unsent_d = 0;
			m_nothing_to_send_pause_timer = 2.0f;
		} else {
			if (nearest_sent_d != -1)
				new_nearest_unsent_d = nearest_sent_d;
			else
				new_nearest_unsent_d = d;
		}
	}

	if (new_nearest_unsent_d != -1)
		m_nearest_unsent_d = new_nearest_unsent_d;
}

void RemoteClient::GotBlock(v3s16 p)
{
	if (m_blocks_sending.find(p) != m_blocks_sending.end()) {
		m_blocks_sending.erase(p);
		// only add to sent blocks if it actually was sending
		// (it might have been modified since)
		m_blocks_sent.insert(p);
	} else {
		m_excess_gotblocks++;
	}
}

void RemoteClient::SentBlock(v3s16 p)
{
	if (m_blocks_sending.find(p) == m_blocks_sending.end())
		m_blocks_sending[p] = 0.0f;
	else
		infostream<<"RemoteClient::SentBlock(): Sent block"
				" already in m_blocks_sending"<<std::endl;
}

void RemoteClient::SetBlockNotSent(v3s16 p)
{
	m_nothing_to_send_pause_timer = 0;

	// remove the block from sending and sent sets,
	// and mark as modified if found
	if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0)
		m_blocks_modified.insert(p);
}

void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
{
	m_nothing_to_send_pause_timer = 0;

	for (auto &block : blocks) {
		v3s16 p = block.first;
		// remove the block from sending and sent sets,
		// and mark as modified if found
		if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0)
			m_blocks_modified.insert(p);
	}
}

void RemoteClient::notifyEvent(ClientStateEvent event)
{
	std::ostringstream myerror;
	switch (m_state)
	{
	case CS_Invalid:
		//intentionally do nothing
		break;
	case CS_Created:
		switch (event) {
		case CSE_Hello:
			m_state = CS_HelloSent;
			break;
		case CSE_Disconnect:
			m_state = CS_Disconnecting;
			break;
		case CSE_SetDenied:
			m_state = CS_Denied;
			break;
		/* GotInit2 SetDefinitionsSent SetMediaSent */
		default:
			myerror << "Created: Invalid client state transition! " << event;
			throw ClientStateError(myerror.str());
		}
		break;
	case CS_Denied:
		/* don't do anything if in denied state */
		break;
	case CS_HelloSent:
		switch(event)
		{
		case CSE_AuthAccept:
			m_state = CS_AwaitingInit2;
			if (chosen_mech == AUTH_MECHANISM_SRP ||
					chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD)
				srp_verifier_delete((SRPVerifier *) auth_data);
			chosen_mech = AUTH_MECHANISM_NONE;
			break;
		case CSE_Disconnect:
			m_state = CS_Disconnecting;
			break;
		case CSE_SetDenied:
			m_state = CS_Denied;
			if (chosen_mech == AUTH_MECHANISM_SRP ||
					chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD)
				srp_verifier_delete((SRPVerifier *) auth_data);
			chosen_mech = AUTH_MECHANISM_NONE;
			break;
		default:
			myerror << "HelloSent: Invalid client state transition! " << event;
			throw ClientStateError(myerror.str());
		}
		break;
	case CS_AwaitingInit2:
		switch(event)
		{
		case CSE_GotInit2:
			confirmSerializationVersion();
			m_state = CS_InitDone;
			break;
		case CSE_Disconnect:
			m_state = CS_Disconnecting;
			break;
		case CSE_SetDenied:
			m_state = CS_Denied;
			break;

		/* Init SetDefinitionsSent SetMediaSent */
		default:
			myerror << "InitSent: Invalid client state transition! " << event;
			throw ClientStateError(myerror.str());
		}
		break;

	case CS_InitDone:
		switch(event)
		{
		case CSE_SetDefinitionsSent:
			m_state = CS_DefinitionsSent;
			break;
		case CSE_Disconnect:
			m_state = CS_Disconnecting;
			break;
		case CSE_SetDenied:
			m_state = CS_Denied;
			break;

		/* Init GotInit2 SetMediaSent */
		default:
			myerror << "InitDone: Invalid client state transition! " << event;
			throw ClientStateError(myerror.str());
		}
		break;
	case CS_DefinitionsSent:
		switch(event)
		{
		case CSE_SetClientReady:
			m_state = CS_Active;
			break;
		case CSE_Disconnect:
			m_state = CS_Disconnecting;
			break;
		case CSE_SetDenied:
			m_state = CS_Denied;
			break;
		/* Init GotInit2 SetDefinitionsSent */
		default:
			myerror << "DefinitionsSent: Invalid client state transition! " << event;
			throw ClientStateError(myerror.str());
		}
		break;
	case CS_Active:
		switch(event)
		{
		case CSE_SetDenied:
			m_state = CS_Denied;
			break;
		case CSE_Disconnect:
			m_state = CS_Disconnecting;
			break;
		case CSE_SudoSuccess:
			m_state = CS_SudoMode;
			if (chosen_mech == AUTH_MECHANISM_SRP)
				srp_verifier_delete((SRPVerifier *) auth_data);