1 /*****************************************************************************
2  * PokerTH - The open source texas holdem engine                             *
3  * Copyright (C) 2006-2012 Felix Hammer, Florian Thauer, Lothar May          *
4  *                                                                           *
5  * This program is free software: you can redistribute it and/or modify      *
6  * it under the terms of the GNU Affero General Public License as            *
7  * published by the Free Software Foundation, either version 3 of the        *
8  * License, or (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 Affero General Public License for more details.                       *
14  *                                                                           *
15  * You should have received a copy of the GNU Affero General Public License  *
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.     *
17  *                                                                           *
18  *                                                                           *
19  * Additional permission under GNU AGPL version 3 section 7                  *
20  *                                                                           *
21  * If you modify this program, or any covered work, by linking or            *
22  * combining it with the OpenSSL project's OpenSSL library (or a             *
23  * modified version of that library), containing parts covered by the        *
24  * terms of the OpenSSL or SSLeay licenses, the authors of PokerTH           *
25  * (Felix Hammer, Florian Thauer, Lothar May) grant you additional           *
26  * permission to convey the resulting work.                                  *
27  * Corresponding Source for a non-source form of such a combination          *
28  * shall include the source code for the parts of OpenSSL used as well       *
29  * as that of the covered work.                                              *
30  *****************************************************************************/
31 
32 #include <net/clientstate.h>
33 #include <net/clientthread.h>
34 #include <net/clientcontext.h>
35 #include <net/senderhelper.h>
36 #include <net/netpacket.h>
37 #include <net/clientexception.h>
38 #include <net/socket_helper.h>
39 #include <net/socket_msg.h>
40 #include <net/downloadhelper.h>
41 #include <core/avatarmanager.h>
42 #include <core/crypthelper.h>
43 #include <qttoolsinterface.h>
44 
45 #include <game.h>
46 #include <playerinterface.h>
47 
48 #include <tinyxml.h>
49 #include <boost/bind.hpp>
50 #include <boost/foreach.hpp>
51 #include <boost/iostreams/filtering_streambuf.hpp>
52 #include <boost/iostreams/copy.hpp>
53 #include <boost/iostreams/filter/zlib.hpp>
54 #include <boost/filesystem.hpp>
55 
56 #include <iostream>
57 #include <fstream>
58 #include <sstream>
59 
60 using namespace std;
61 using namespace boost::filesystem;
62 
63 #ifdef BOOST_ASIO_HAS_STD_CHRONO
64 using namespace std::chrono;
65 #else
66 using namespace boost::chrono;
67 #endif
68 
69 #define CLIENT_WAIT_TIMEOUT_MSEC	50
70 #define CLIENT_CONNECT_TIMEOUT_SEC	10
71 
72 
~ClientState()73 ClientState::~ClientState()
74 {
75 }
76 
77 //-----------------------------------------------------------------------------
78 
79 ClientStateInit &
Instance()80 ClientStateInit::Instance()
81 {
82 	static ClientStateInit state;
83 	return state;
84 }
85 
ClientStateInit()86 ClientStateInit::ClientStateInit()
87 {
88 }
89 
~ClientStateInit()90 ClientStateInit::~ClientStateInit()
91 {
92 }
93 
94 void
Enter(boost::shared_ptr<ClientThread> client)95 ClientStateInit::Enter(boost::shared_ptr<ClientThread> client)
96 {
97 	ClientContext &context = client->GetContext();
98 
99 	if (context.GetServerAddr().empty())
100 		throw ClientException(__FILE__, __LINE__, ERR_SOCK_SERVERADDR_NOT_SET, 0);
101 
102 	if (context.GetServerPort() < 1024)
103 		throw ClientException(__FILE__, __LINE__, ERR_SOCK_INVALID_PORT, 0);
104 
105 	client->CreateContextSession();
106 	client->GetCallback().SignalNetClientConnect(MSG_SOCK_INIT_DONE);
107 
108 	if (context.GetUseServerList())
109 		client->SetState(ClientStateStartServerListDownload::Instance());
110 	else
111 		client->SetState(ClientStateStartResolve::Instance());
112 }
113 
114 void
Exit(boost::shared_ptr<ClientThread>)115 ClientStateInit::Exit(boost::shared_ptr<ClientThread> /*client*/)
116 {
117 	// Nothing to do.
118 }
119 
120 //-----------------------------------------------------------------------------
121 
122 ClientStateStartResolve &
Instance()123 ClientStateStartResolve::Instance()
124 {
125 	static ClientStateStartResolve state;
126 	return state;
127 }
128 
ClientStateStartResolve()129 ClientStateStartResolve::ClientStateStartResolve()
130 {
131 }
132 
~ClientStateStartResolve()133 ClientStateStartResolve::~ClientStateStartResolve()
134 {
135 }
136 
137 void
Enter(boost::shared_ptr<ClientThread> client)138 ClientStateStartResolve::Enter(boost::shared_ptr<ClientThread> client)
139 {
140 	ClientContext &context = client->GetContext();
141 	ostringstream portStr;
142 	portStr << context.GetServerPort();
143 	boost::asio::ip::tcp::resolver::query q(context.GetServerAddr(), portStr.str());
144 
145 	context.GetResolver()->async_resolve(
146 		q,
147 		boost::bind(&ClientStateStartResolve::HandleResolve,
148 					this,
149 					boost::asio::placeholders::error,
150 					boost::asio::placeholders::iterator,
151 					client));
152 }
153 
154 void
Exit(boost::shared_ptr<ClientThread> client)155 ClientStateStartResolve::Exit(boost::shared_ptr<ClientThread> client)
156 {
157 	client->GetContext().GetResolver()->cancel();
158 }
159 
160 void
HandleResolve(const boost::system::error_code & ec,boost::asio::ip::tcp::resolver::iterator endpoint_iterator,boost::shared_ptr<ClientThread> client)161 ClientStateStartResolve::HandleResolve(const boost::system::error_code& ec, boost::asio::ip::tcp::resolver::iterator endpoint_iterator,
162 									   boost::shared_ptr<ClientThread> client)
163 {
164 	if (!ec && &client->GetState() == this) {
165 		client->GetCallback().SignalNetClientConnect(MSG_SOCK_RESOLVE_DONE);
166 		// Use the first resolver result.
167 		ClientStateStartConnect::Instance().SetRemoteEndpoint(endpoint_iterator);
168 		client->SetState(ClientStateStartConnect::Instance());
169 	} else {
170 		if (ec != boost::asio::error::operation_aborted)
171 			throw ClientException(__FILE__, __LINE__, ERR_SOCK_RESOLVE_FAILED, 0);
172 	}
173 }
174 
175 //-----------------------------------------------------------------------------
176 
177 ClientStateStartServerListDownload &
Instance()178 ClientStateStartServerListDownload::Instance()
179 {
180 	static ClientStateStartServerListDownload state;
181 	return state;
182 }
183 
ClientStateStartServerListDownload()184 ClientStateStartServerListDownload::ClientStateStartServerListDownload()
185 {
186 }
187 
~ClientStateStartServerListDownload()188 ClientStateStartServerListDownload::~ClientStateStartServerListDownload()
189 {
190 }
191 
192 void
Enter(boost::shared_ptr<ClientThread> client)193 ClientStateStartServerListDownload::Enter(boost::shared_ptr<ClientThread> client)
194 {
195 	path tmpServerListPath(client->GetCacheServerListFileName());
196 	if (tmpServerListPath.empty())
197 		throw ClientException(__FILE__, __LINE__, ERR_SOCK_INVALID_SERVERLIST_URL, 0);
198 
199 	if (exists(tmpServerListPath)) {
200 		// Download the current server list once a day.
201 		// If the previous file is older than one day, delete it.
202 		// Also delete the file if it is empty.
203 		if (file_size(tmpServerListPath) == 0 || (last_write_time(tmpServerListPath) + 86400 < time(NULL))) {
204 			remove(tmpServerListPath);
205 		}
206 	}
207 
208 	if (exists(tmpServerListPath)) {
209 		// Use the existing server list.
210 		client->SetState(ClientStateReadingServerList::Instance());
211 	} else {
212 		// Download the server list.
213 		boost::shared_ptr<DownloadHelper> downloader(new DownloadHelper);
214 		downloader->Init(client->GetContext().GetServerListUrl(), tmpServerListPath.directory_string());
215 		ClientStateDownloadingServerList::Instance().SetDownloadHelper(downloader);
216 		client->SetState(ClientStateDownloadingServerList::Instance());
217 	}
218 }
219 
220 void
Exit(boost::shared_ptr<ClientThread>)221 ClientStateStartServerListDownload::Exit(boost::shared_ptr<ClientThread> /*client*/)
222 {
223 	// Nothing to do.
224 }
225 
226 //-----------------------------------------------------------------------------
227 
228 ClientStateDownloadingServerList &
Instance()229 ClientStateDownloadingServerList::Instance()
230 {
231 	static ClientStateDownloadingServerList state;
232 	return state;
233 }
234 
ClientStateDownloadingServerList()235 ClientStateDownloadingServerList::ClientStateDownloadingServerList()
236 {
237 }
238 
~ClientStateDownloadingServerList()239 ClientStateDownloadingServerList::~ClientStateDownloadingServerList()
240 {
241 }
242 
243 void
Enter(boost::shared_ptr<ClientThread> client)244 ClientStateDownloadingServerList::Enter(boost::shared_ptr<ClientThread> client)
245 {
246 	client->GetStateTimer().expires_from_now(
247 		milliseconds(CLIENT_WAIT_TIMEOUT_MSEC));
248 	client->GetStateTimer().async_wait(
249 		boost::bind(
250 			&ClientStateDownloadingServerList::TimerLoop, this, boost::asio::placeholders::error, client));
251 }
252 
253 void
Exit(boost::shared_ptr<ClientThread> client)254 ClientStateDownloadingServerList::Exit(boost::shared_ptr<ClientThread> client)
255 {
256 	client->GetStateTimer().cancel();
257 }
258 
259 void
SetDownloadHelper(boost::shared_ptr<DownloadHelper> helper)260 ClientStateDownloadingServerList::SetDownloadHelper(boost::shared_ptr<DownloadHelper> helper)
261 {
262 	m_downloadHelper = helper;
263 }
264 
265 void
TimerLoop(const boost::system::error_code & ec,boost::shared_ptr<ClientThread> client)266 ClientStateDownloadingServerList::TimerLoop(const boost::system::error_code& ec, boost::shared_ptr<ClientThread> client)
267 {
268 	if (!ec && &client->GetState() == this) {
269 		if (m_downloadHelper->Process()) {
270 			m_downloadHelper.reset();
271 			client->SetState(ClientStateReadingServerList::Instance());
272 		} else {
273 			client->GetStateTimer().expires_from_now(
274 				milliseconds(CLIENT_WAIT_TIMEOUT_MSEC));
275 			client->GetStateTimer().async_wait(
276 				boost::bind(
277 					&ClientStateDownloadingServerList::TimerLoop, this, boost::asio::placeholders::error, client));
278 		}
279 	}
280 }
281 
282 //-----------------------------------------------------------------------------
283 
284 ClientStateReadingServerList &
Instance()285 ClientStateReadingServerList::Instance()
286 {
287 	static ClientStateReadingServerList state;
288 	return state;
289 }
290 
ClientStateReadingServerList()291 ClientStateReadingServerList::ClientStateReadingServerList()
292 {
293 }
294 
~ClientStateReadingServerList()295 ClientStateReadingServerList::~ClientStateReadingServerList()
296 {
297 }
298 
299 void
Enter(boost::shared_ptr<ClientThread> client)300 ClientStateReadingServerList::Enter(boost::shared_ptr<ClientThread> client)
301 {
302 	ClientContext &context = client->GetContext();
303 	path zippedServerListPath(context.GetCacheDir());
304 	zippedServerListPath /= context.GetServerListUrl().substr(context.GetServerListUrl().find_last_of('/') + 1);
305 	path xmlServerListPath;
306 	if (extension(zippedServerListPath) == ".z") {
307 		xmlServerListPath = change_extension(zippedServerListPath, "");
308 
309 		// Unzip the file using zlib.
310 		try {
311 			std::ifstream inFile(zippedServerListPath.directory_string().c_str(), ios_base::in | ios_base::binary);
312 			std::ofstream outFile(xmlServerListPath.directory_string().c_str(), ios_base::out | ios_base::trunc);
313 			boost::iostreams::filtering_streambuf<boost::iostreams::input> in;
314 			in.push(boost::iostreams::zlib_decompressor());
315 			in.push(inFile);
316 			boost::iostreams::copy(in, outFile);
317 		} catch (...) {
318 			throw ClientException(__FILE__, __LINE__, ERR_SOCK_UNZIP_FAILED, 0);
319 		}
320 	} else
321 		xmlServerListPath = zippedServerListPath;
322 
323 	// Parse the server address.
324 	TiXmlDocument doc(xmlServerListPath.directory_string());
325 
326 	if (doc.LoadFile()) {
327 		client->ClearServerInfoMap();
328 		int serverCount = 0;
329 		unsigned lastServerInfoId = 0;
330 		TiXmlHandle docHandle(&doc);
331 		const TiXmlElement *nextServer = docHandle.FirstChild("ServerList" ).FirstChild("Server").ToElement();
332 		while (nextServer) {
333 			ServerInfo serverInfo;
334 			{
335 				int tmpId;
336 				nextServer->QueryIntAttribute("id", &tmpId);
337 				serverInfo.id = (unsigned)tmpId;
338 			}
339 			const TiXmlNode *nameNode = nextServer->FirstChild("Name");
340 			const TiXmlNode *sponsorNode = nextServer->FirstChild("Sponsor");
341 			const TiXmlNode *countryNode = nextServer->FirstChild("Country");
342 			const TiXmlNode *addr4Node = nextServer->FirstChild("IPv4Address");
343 			const TiXmlNode *addr6Node = nextServer->FirstChild("IPv6Address");
344 			const TiXmlNode *sctpNode = nextServer->FirstChild("SCTP");
345 			const TiXmlNode *portNode = nextServer->FirstChild("ProtobufPort");
346 
347 			// IPv6 support for avatar servers depends on this address and on libcurl.
348 			const TiXmlNode *avatarNode = nextServer->FirstChild("AvatarServerAddress");
349 
350 			if (!nameNode || !nameNode->ToElement() || !addr4Node || !addr4Node->ToElement()
351 					|| !addr6Node || !addr6Node->ToElement() || !portNode || !portNode->ToElement())
352 				throw ClientException(__FILE__, __LINE__, ERR_SOCK_INVALID_SERVERLIST_XML, 0);
353 
354 			serverInfo.name = nameNode->ToElement()->Attribute("value");
355 			serverInfo.ipv4addr = addr4Node->ToElement()->Attribute("value");
356 			serverInfo.ipv6addr = addr6Node->ToElement()->Attribute("value");
357 			portNode->ToElement()->QueryIntAttribute("value", &serverInfo.port);
358 
359 			// Optional parameters:
360 			if (sponsorNode && sponsorNode->ToElement())
361 				serverInfo.sponsor = sponsorNode->ToElement()->Attribute("value");
362 			if (countryNode && countryNode->ToElement())
363 				serverInfo.country = countryNode->ToElement()->Attribute("value");
364 			if (sctpNode && sctpNode->ToElement()) {
365 				int tmpSctp;
366 				sctpNode->ToElement()->QueryIntAttribute("value", &tmpSctp);
367 				serverInfo.supportsSctp = tmpSctp == 1 ? true : false;
368 			}
369 			if (avatarNode && avatarNode->ToElement())
370 				serverInfo.avatarServerAddr = avatarNode->ToElement()->Attribute("value");
371 
372 			client->AddServerInfo(serverInfo.id, serverInfo);
373 			nextServer = nextServer->NextSiblingElement();
374 			lastServerInfoId = serverInfo.id;
375 			serverCount++;
376 		}
377 
378 		if (serverCount == 1) {
379 			client->UseServer(lastServerInfoId);
380 			client->GetCallback().SignalNetClientConnect(MSG_SOCK_SERVER_LIST_DONE);
381 			client->SetState(ClientStateStartResolve::Instance());
382 		} else if (serverCount > 1) {
383 			client->GetCallback().SignalNetClientServerListShow();
384 			client->SetState(ClientStateWaitChooseServer::Instance());
385 		} else
386 			throw ClientException(__FILE__, __LINE__, ERR_SOCK_INVALID_SERVERLIST_XML, 0);
387 	} else
388 		throw ClientException(__FILE__, __LINE__, ERR_SOCK_INVALID_SERVERLIST_XML, 0);
389 }
390 
391 void
Exit(boost::shared_ptr<ClientThread>)392 ClientStateReadingServerList::Exit(boost::shared_ptr<ClientThread> /*client*/)
393 {
394 	// Nothing to do.
395 }
396 
397 //-----------------------------------------------------------------------------
398 
399 ClientStateWaitChooseServer &
Instance()400 ClientStateWaitChooseServer::Instance()
401 {
402 	static ClientStateWaitChooseServer state;
403 	return state;
404 }
405 
ClientStateWaitChooseServer()406 ClientStateWaitChooseServer::ClientStateWaitChooseServer()
407 {
408 }
409 
~ClientStateWaitChooseServer()410 ClientStateWaitChooseServer::~ClientStateWaitChooseServer()
411 {
412 }
413 
414 void
Enter(boost::shared_ptr<ClientThread> client)415 ClientStateWaitChooseServer::Enter(boost::shared_ptr<ClientThread> client)
416 {
417 	client->GetStateTimer().expires_from_now(
418 		milliseconds(CLIENT_WAIT_TIMEOUT_MSEC));
419 	client->GetStateTimer().async_wait(
420 		boost::bind(
421 			&ClientStateWaitChooseServer::TimerLoop, this, boost::asio::placeholders::error, client));
422 }
423 
424 void
Exit(boost::shared_ptr<ClientThread> client)425 ClientStateWaitChooseServer::Exit(boost::shared_ptr<ClientThread> client)
426 {
427 	client->GetStateTimer().cancel();
428 }
429 
430 void
TimerLoop(const boost::system::error_code & ec,boost::shared_ptr<ClientThread> client)431 ClientStateWaitChooseServer::TimerLoop(const boost::system::error_code& ec, boost::shared_ptr<ClientThread> client)
432 {
433 	if (!ec && &client->GetState() == this) {
434 		unsigned serverId;
435 		if (client->GetSelectedServer(serverId)) {
436 			client->UseServer(serverId);
437 			client->GetCallback().SignalNetClientConnect(MSG_SOCK_SERVER_LIST_DONE);
438 			client->SetState(ClientStateStartResolve::Instance());
439 		} else {
440 			client->GetStateTimer().expires_from_now(
441 				milliseconds(CLIENT_WAIT_TIMEOUT_MSEC));
442 			client->GetStateTimer().async_wait(
443 				boost::bind(
444 					&ClientStateWaitChooseServer::TimerLoop, this, boost::asio::placeholders::error, client));
445 		}
446 	}
447 }
448 
449 //-----------------------------------------------------------------------------
450 
451 ClientStateStartConnect &
Instance()452 ClientStateStartConnect::Instance()
453 {
454 	static ClientStateStartConnect state;
455 	return state;
456 }
457 
ClientStateStartConnect()458 ClientStateStartConnect::ClientStateStartConnect()
459 {
460 }
461 
~ClientStateStartConnect()462 ClientStateStartConnect::~ClientStateStartConnect()
463 {
464 }
465 
466 void
Enter(boost::shared_ptr<ClientThread> client)467 ClientStateStartConnect::Enter(boost::shared_ptr<ClientThread> client)
468 {
469 	client->GetStateTimer().expires_from_now(
470 		seconds(CLIENT_CONNECT_TIMEOUT_SEC));
471 	client->GetStateTimer().async_wait(
472 		boost::bind(
473 			&ClientStateStartConnect::TimerTimeout, this, boost::asio::placeholders::error, client));
474 
475 	boost::asio::ip::tcp::endpoint endpoint = *m_remoteEndpointIterator;
476 	client->GetContext().GetSessionData()->GetAsioSocket()->async_connect(
477 		endpoint,
478 		boost::bind(&ClientStateStartConnect::HandleConnect,
479 					this,
480 					boost::asio::placeholders::error,
481 					++m_remoteEndpointIterator,
482 					client));
483 }
484 
485 void
Exit(boost::shared_ptr<ClientThread> client)486 ClientStateStartConnect::Exit(boost::shared_ptr<ClientThread> client)
487 {
488 	client->GetStateTimer().cancel();
489 }
490 
491 void
SetRemoteEndpoint(boost::asio::ip::tcp::resolver::iterator endpointIterator)492 ClientStateStartConnect::SetRemoteEndpoint(boost::asio::ip::tcp::resolver::iterator endpointIterator)
493 {
494 	m_remoteEndpointIterator = endpointIterator;
495 }
496 
497 void
HandleConnect(const boost::system::error_code & ec,boost::asio::ip::tcp::resolver::iterator endpoint_iterator,boost::shared_ptr<ClientThread> client)498 ClientStateStartConnect::HandleConnect(const boost::system::error_code& ec, boost::asio::ip::tcp::resolver::iterator endpoint_iterator,
499 									   boost::shared_ptr<ClientThread> client)
500 {
501 	if (&client->GetState() == this) {
502 		if (!ec) {
503 			client->GetCallback().SignalNetClientConnect(MSG_SOCK_CONNECT_DONE);
504 			client->SetState(ClientStateStartSession::Instance());
505 		} else if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator()) {
506 			// Try next resolve entry.
507 			ClientContext &context = client->GetContext();
508 			boost::system::error_code ec;
509 			context.GetSessionData()->GetAsioSocket()->close(ec);
510 			boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
511 			context.GetSessionData()->GetAsioSocket()->async_connect(
512 				endpoint,
513 				boost::bind(&ClientStateStartConnect::HandleConnect,
514 							this,
515 							boost::asio::placeholders::error,
516 							++m_remoteEndpointIterator,
517 							client));
518 		} else {
519 			if (ec != boost::asio::error::operation_aborted) {
520 				if (client->GetContext().GetAddrFamily() == AF_INET6) {
521 					throw ClientException(__FILE__, __LINE__, ERR_SOCK_CONNECT_IPV6_FAILED, ec.value());
522 				} else {
523 					throw ClientException(__FILE__, __LINE__, ERR_SOCK_CONNECT_FAILED, ec.value());
524 				}
525 			}
526 		}
527 	}
528 }
529 
530 void
TimerTimeout(const boost::system::error_code & ec,boost::shared_ptr<ClientThread> client)531 ClientStateStartConnect::TimerTimeout(const boost::system::error_code& ec, boost::shared_ptr<ClientThread> client)
532 {
533 	if (!ec && &client->GetState() == this) {
534 		boost::system::error_code ec;
535 		client->GetContext().GetSessionData()->GetAsioSocket()->close(ec);
536 		if (client->GetContext().GetAddrFamily() == AF_INET6) {
537 			throw ClientException(__FILE__, __LINE__, ERR_SOCK_CONNECT_IPV6_TIMEOUT, 0);
538 		} else {
539 			throw ClientException(__FILE__, __LINE__, ERR_SOCK_CONNECT_TIMEOUT, 0);
540 		}
541 	}
542 }
543 
544 //-----------------------------------------------------------------------------
545 
AbstractClientStateReceiving()546 AbstractClientStateReceiving::AbstractClientStateReceiving()
547 {
548 }
549 
~AbstractClientStateReceiving()550 AbstractClientStateReceiving::~AbstractClientStateReceiving()
551 {
552 }
553 
554 void
HandlePacket(boost::shared_ptr<ClientThread> client,boost::shared_ptr<NetPacket> tmpPacket)555 AbstractClientStateReceiving::HandlePacket(boost::shared_ptr<ClientThread> client, boost::shared_ptr<NetPacket> tmpPacket)
556 {
557 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_PlayerInfoReplyMessage) {
558 		const PlayerInfoReplyMessage &infoReply = tmpPacket->GetMsg()->playerinforeplymessage();
559 		unsigned playerId = infoReply.playerid();
560 		if (infoReply.has_playerinfodata()) {
561 			PlayerInfo tmpInfo;
562 			const PlayerInfoReplyMessage::PlayerInfoData &netInfo = infoReply.playerinfodata();
563 			tmpInfo.playerName = netInfo.playername();
564 			tmpInfo.ptype = netInfo.ishuman() ? PLAYER_TYPE_HUMAN : PLAYER_TYPE_COMPUTER;
565 			tmpInfo.isGuest = netInfo.playerrights() == netPlayerRightsGuest;
566 			tmpInfo.isAdmin = netInfo.playerrights() == netPlayerRightsAdmin;
567 			if (netInfo.has_countrycode()) {
568 				tmpInfo.countryCode = netInfo.countrycode();
569 			}
570 			if (netInfo.has_avatardata()) {
571 				tmpInfo.hasAvatar = true;
572 				memcpy(tmpInfo.avatar.GetData(), netInfo.avatardata().avatarhash().data(), MD5_DATA_SIZE);
573 				tmpInfo.avatarType = static_cast<AvatarFileType>(netInfo.avatardata().avatartype());
574 			}
575 			client->SetPlayerInfo(
576 				playerId,
577 				tmpInfo);
578 		} else {
579 			client->SetUnknownPlayer(playerId);
580 		}
581 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_RemovedFromGameMessage) {
582 		const RemovedFromGameMessage &netRemoved = tmpPacket->GetMsg()->removedfromgamemessage();
583 
584 		client->ClearPlayerDataList();
585 		// Resubscribe Lobby messages.
586 		client->ResubscribeLobbyMsg();
587 		// Show Lobby.
588 		client->GetCallback().SignalNetClientWaitDialog();
589 		int removeReason;
590 		switch (netRemoved.removedfromgamereason()) {
591 		case RemovedFromGameMessage::kickedFromGame :
592 			removeReason = NTF_NET_REMOVED_KICKED;
593 			break;
594 		case RemovedFromGameMessage::gameIsFull :
595 			removeReason = NTF_NET_REMOVED_GAME_FULL;
596 			break;
597 		case RemovedFromGameMessage::gameIsRunning :
598 			removeReason = NTF_NET_REMOVED_ALREADY_RUNNING;
599 			break;
600 		case RemovedFromGameMessage::gameTimeout :
601 			removeReason = NTF_NET_REMOVED_TIMEOUT;
602 			break;
603 		case RemovedFromGameMessage::removedStartFailed :
604 			removeReason = NTF_NET_REMOVED_START_FAILED;
605 			break;
606 		case RemovedFromGameMessage::gameClosed :
607 			removeReason = NTF_NET_REMOVED_GAME_CLOSED;
608 			break;
609 		default :
610 			removeReason = NTF_NET_REMOVED_ON_REQUEST;
611 			break;
612 		}
613 		client->GetCallback().SignalNetClientRemovedFromGame(removeReason);
614 		client->SetState(ClientStateWaitJoin::Instance());
615 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GamePlayerLeftMessage) {
616 		// A player left the game.
617 		const GamePlayerLeftMessage &netLeft = tmpPacket->GetMsg()->gameplayerleftmessage();
618 
619 		if (client->GetGame()) {
620 			boost::shared_ptr<PlayerInterface> tmpPlayer = client->GetGame()->getPlayerByUniqueId(netLeft.playerid());
621 			if (tmpPlayer) {
622 				tmpPlayer->setIsKicked(netLeft.gameplayerleftreason() == GamePlayerLeftMessage::leftKicked);
623 			}
624 		}
625 		// Signal to GUI and remove from data list.
626 		int removeReason;
627 		switch (netLeft.gameplayerleftreason()) {
628 		case GamePlayerLeftMessage::leftKicked :
629 			removeReason = NTF_NET_REMOVED_KICKED;
630 			break;
631 		default :
632 			removeReason = NTF_NET_REMOVED_ON_REQUEST;
633 			break;
634 		}
635 		client->RemovePlayerData(netLeft.playerid(), removeReason);
636 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameAdminChangedMessage) {
637 		// New admin for the game.
638 		const GameAdminChangedMessage &netChanged = tmpPacket->GetMsg()->gameadminchangedmessage();
639 
640 		// Set new game admin and signal to GUI.
641 		client->SetNewGameAdmin(netChanged.newadminplayerid());
642 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GamePlayerJoinedMessage) {
643 		// Another player joined the network game.
644 		const GamePlayerJoinedMessage &netPlayerJoined = tmpPacket->GetMsg()->gameplayerjoinedmessage();
645 
646 		boost::shared_ptr<PlayerData> playerData = client->CreatePlayerData(netPlayerJoined.playerid(), netPlayerJoined.isgameadmin());
647 		client->AddPlayerData(playerData);
648 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameSpectatorJoinedMessage) {
649 		// Another spectator joined the network game.
650 		const GameSpectatorJoinedMessage &netSpectatorJoined = tmpPacket->GetMsg()->gamespectatorjoinedmessage();
651 		// Request player info if needed.
652 		PlayerInfo info;
653 		if (!client->GetCachedPlayerInfo(netSpectatorJoined.playerid(), info)) {
654 			client->RequestPlayerInfo(netSpectatorJoined.playerid());
655 		}
656 		client->ModifyGameInfoAddSpectatorDuringGame(netSpectatorJoined.playerid());
657 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameSpectatorLeftMessage) {
658 		// A spectator left the network game.
659 		const GameSpectatorLeftMessage &netSpectatorLeft = tmpPacket->GetMsg()->gamespectatorleftmessage();
660 		// Signal to GUI and remove from data list.
661 		int removeReason;
662 		switch (netSpectatorLeft.gamespectatorleftreason()) {
663 		case GamePlayerLeftMessage::leftKicked :
664 			removeReason = NTF_NET_REMOVED_KICKED;
665 			break;
666 		default :
667 			removeReason = NTF_NET_REMOVED_ON_REQUEST;
668 			break;
669 		}
670 		client->ModifyGameInfoRemoveSpectatorDuringGame(netSpectatorLeft.playerid(), removeReason);
671 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_TimeoutWarningMessage) {
672 		const TimeoutWarningMessage &tmpTimeout = tmpPacket->GetMsg()->timeoutwarningmessage();
673 		client->GetCallback().SignalNetClientShowTimeoutDialog((NetTimeoutReason)tmpTimeout.timeoutreason(), tmpTimeout.remainingseconds());
674 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_ChatMessage) {
675 		// Chat message - display it in the GUI.
676 		const ChatMessage &netMessage = tmpPacket->GetMsg()->chatmessage();
677 
678 		string playerName;
679 		if (netMessage.chattype() == ChatMessage::chatTypeBroadcast) {
680 			client->GetCallback().SignalNetClientGameChatMsg("(global notice)", netMessage.chattext());
681 			client->GetCallback().SignalNetClientLobbyChatMsg("(global notice)", netMessage.chattext());
682 		} else if (netMessage.chattype() == ChatMessage::chatTypeBot) {
683 			client->GetCallback().SignalNetClientGameChatMsg("(chat bot)", netMessage.chattext());
684 			client->GetCallback().SignalNetClientLobbyChatMsg("(chat bot)", netMessage.chattext());
685 		} else if (netMessage.chattype() == ChatMessage::chatTypeGame) {
686 			unsigned playerId = netMessage.playerid();
687 			boost::shared_ptr<PlayerData> tmpPlayer = client->GetPlayerDataByUniqueId(playerId);
688 			if (tmpPlayer.get())
689 				playerName = tmpPlayer->GetName();
690 			if (!playerName.empty())
691 				client->GetCallback().SignalNetClientGameChatMsg(playerName, netMessage.chattext());
692 		} else if (netMessage.chattype() == ChatMessage::chatTypeLobby) {
693 			unsigned playerId = netMessage.playerid();
694 			PlayerInfo info;
695 			if (client->GetCachedPlayerInfo(playerId, info))
696 				client->GetCallback().SignalNetClientLobbyChatMsg(info.playerName, netMessage.chattext());
697 		} else if (netMessage.chattype() == ChatMessage::chatTypePrivate) {
698 			unsigned playerId = netMessage.playerid();
699 			PlayerInfo info;
700 			if (client->GetCachedPlayerInfo(playerId, info))
701 				client->GetCallback().SignalNetClientPrivateChatMsg(info.playerName, netMessage.chattext());
702 		}
703 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_ChatRejectMessage) {
704 		const ChatRejectMessage &netMessage = tmpPacket->GetMsg()->chatrejectmessage();
705 		client->GetCallback().SignalNetClientGameChatMsg("(notice)", "Chat rejected: " + netMessage.chattext());
706 		client->GetCallback().SignalNetClientLobbyChatMsg("(notice)", "Chat rejected: " + netMessage.chattext());
707 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_DialogMessage) {
708 		// Message box - display it in the GUI.
709 		const DialogMessage &netDialog = tmpPacket->GetMsg()->dialogmessage();
710 		client->GetCallback().SignalNetClientMsgBox(netDialog.notificationtext());
711 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_PlayerListMessage) {
712 		const PlayerListMessage &netPlayerList = tmpPacket->GetMsg()->playerlistmessage();
713 
714 		if (netPlayerList.playerlistnotification() == PlayerListMessage::playerListNew) {
715 			client->GetCallback().SignalLobbyPlayerJoined(netPlayerList.playerid(), client->GetPlayerName(netPlayerList.playerid()));
716 		} else if (netPlayerList.playerlistnotification() == PlayerListMessage::playerListLeft) {
717 			client->GetCallback().SignalLobbyPlayerLeft(netPlayerList.playerid());
718 		}
719 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameListNewMessage) {
720 		// A new game was created on the server.
721 		const GameListNewMessage &netListNew = tmpPacket->GetMsg()->gamelistnewmessage();
722 
723 		// Request player info for players if needed.
724 		GameInfo tmpInfo;
725 		list<unsigned> requestList;
726 		// All players.
727 		for (int i = 0; i < netListNew.playerids_size(); i++) {
728 			PlayerInfo info;
729 			unsigned playerId = netListNew.playerids(i);
730 			if (!client->GetCachedPlayerInfo(playerId, info)) {
731 				requestList.push_back(playerId);
732 			}
733 			tmpInfo.players.push_back(playerId);
734 		}
735 		// All spectators.
736 		for (int i = 0; i < netListNew.spectatorids_size(); i++) {
737 			PlayerInfo info;
738 			unsigned playerId = netListNew.spectatorids(i);
739 			if (!client->GetCachedPlayerInfo(playerId, info)) {
740 				requestList.push_back(playerId);
741 			}
742 			tmpInfo.spectators.push_back(playerId);
743 		}
744 		// Send request for multiple players (will only act if list is non-empty).
745 		client->RequestPlayerInfo(requestList);
746 
747 		tmpInfo.adminPlayerId = netListNew.adminplayerid();
748 		tmpInfo.isPasswordProtected = netListNew.isprivate();
749 		tmpInfo.mode = static_cast<GameMode>(netListNew.gamemode());
750 		tmpInfo.name = netListNew.gameinfo().gamename();
751 		NetPacket::GetGameData(netListNew.gameinfo(), tmpInfo.data);
752 
753 		client->AddGameInfo(netListNew.gameid(), tmpInfo);
754 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameListUpdateMessage) {
755 		// An existing game was updated on the server.
756 		const GameListUpdateMessage &netListUpdate = tmpPacket->GetMsg()->gamelistupdatemessage();
757 		if (netListUpdate.gamemode() == netGameClosed)
758 			client->RemoveGameInfo(netListUpdate.gameid());
759 		else
760 			client->UpdateGameInfoMode(netListUpdate.gameid(), static_cast<GameMode>(netListUpdate.gamemode()));
761 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameListPlayerJoinedMessage) {
762 		const GameListPlayerJoinedMessage &netListJoined = tmpPacket->GetMsg()->gamelistplayerjoinedmessage();
763 
764 		client->ModifyGameInfoAddPlayer(netListJoined.gameid(), netListJoined.playerid());
765 		// Request player info if needed.
766 		PlayerInfo info;
767 		if (!client->GetCachedPlayerInfo(netListJoined.playerid(), info)) {
768 			client->RequestPlayerInfo(netListJoined.playerid());
769 		}
770 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameListPlayerLeftMessage) {
771 		const GameListPlayerLeftMessage &netListLeft = tmpPacket->GetMsg()->gamelistplayerleftmessage();
772 
773 		client->ModifyGameInfoRemovePlayer(netListLeft.gameid(), netListLeft.playerid());
774 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameListSpectatorJoinedMessage) {
775 		const GameListSpectatorJoinedMessage &netListJoined = tmpPacket->GetMsg()->gamelistspectatorjoinedmessage();
776 
777 		client->ModifyGameInfoAddSpectator(netListJoined.gameid(), netListJoined.playerid());
778 		// Request player info if needed.
779 		PlayerInfo info;
780 		if (!client->GetCachedPlayerInfo(netListJoined.playerid(), info)) {
781 			client->RequestPlayerInfo(netListJoined.playerid());
782 		}
783 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameListSpectatorLeftMessage) {
784 		const GameListSpectatorLeftMessage &netListLeft = tmpPacket->GetMsg()->gamelistspectatorleftmessage();
785 
786 		client->ModifyGameInfoRemoveSpectator(netListLeft.gameid(), netListLeft.playerid());
787 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameListAdminChangedMessage) {
788 		const GameListAdminChangedMessage &netListAdmin = tmpPacket->GetMsg()->gamelistadminchangedmessage();
789 
790 		client->UpdateGameInfoAdmin(netListAdmin.gameid(), netListAdmin.newadminplayerid());
791 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_StartKickPetitionMessage) {
792 		const StartKickPetitionMessage &netStartPetition = tmpPacket->GetMsg()->startkickpetitionmessage();
793 		client->StartPetition(netStartPetition.petitionid(), netStartPetition.proposingplayerid(),
794 							  netStartPetition.kickplayerid(), netStartPetition.kicktimeoutsec(), netStartPetition.numvotesneededtokick());
795 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_KickPetitionUpdateMessage) {
796 		const KickPetitionUpdateMessage &netPetitionUpdate = tmpPacket->GetMsg()->kickpetitionupdatemessage();
797 		client->UpdatePetition(netPetitionUpdate.petitionid(), netPetitionUpdate.numvotesagainstkicking(),
798 							   netPetitionUpdate.numvotesinfavourofkicking(), netPetitionUpdate.numvotesneededtokick());
799 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_EndKickPetitionMessage) {
800 		const EndKickPetitionMessage &netEndPetition = tmpPacket->GetMsg()->endkickpetitionmessage();
801 		client->EndPetition(netEndPetition.petitionid());
802 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarHeaderMessage) {
803 		const AvatarHeaderMessage &netAvatarHeader = tmpPacket->GetMsg()->avatarheadermessage();
804 		client->AddTempAvatarFile(netAvatarHeader.requestid(), netAvatarHeader.avatarsize(), static_cast<AvatarFileType>(netAvatarHeader.avatartype()));
805 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarDataMessage) {
806 		const AvatarDataMessage &netAvatarData = tmpPacket->GetMsg()->avatardatamessage();
807 		vector<unsigned char> fileData(netAvatarData.avatarblock().size());
808 		memcpy(&fileData[0], netAvatarData.avatarblock().data(), netAvatarData.avatarblock().size());
809 		client->StoreInTempAvatarFile(netAvatarData.requestid(), fileData);
810 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarEndMessage) {
811 		const AvatarEndMessage &netAvatarEnd = tmpPacket->GetMsg()->avatarendmessage();
812 		client->CompleteTempAvatarFile(netAvatarEnd.requestid());
813 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_UnknownAvatarMessage) {
814 		const UnknownAvatarMessage &netUnknownAvatar = tmpPacket->GetMsg()->unknownavatarmessage();
815 		client->SetUnknownAvatar(netUnknownAvatar.requestid());
816 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_ReportAvatarAckMessage) {
817 		const ReportAvatarAckMessage &netReportAck = tmpPacket->GetMsg()->reportavatarackmessage();
818 		unsigned msgCode;
819 		switch (netReportAck.reportavatarresult()) {
820 		case ReportAvatarAckMessage::avatarReportAccepted:
821 			msgCode = MSG_NET_AVATAR_REPORT_ACCEPTED;
822 			break;
823 		case ReportAvatarAckMessage::avatarReportDuplicate:
824 			msgCode = MSG_NET_AVATAR_REPORT_DUP;
825 			break;
826 		default:
827 			msgCode = MSG_NET_AVATAR_REPORT_REJECTED;
828 			break;
829 		}
830 		client->GetCallback().SignalNetClientMsgBox(msgCode);
831 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_ReportGameAckMessage) {
832 		const ReportGameAckMessage &netReportAck = tmpPacket->GetMsg()->reportgameackmessage();
833 		unsigned msgCode;
834 		switch (netReportAck.reportgameresult()) {
835 		case ReportGameAckMessage::gameReportAccepted:
836 			msgCode = MSG_NET_GAMENAME_REPORT_ACCEPTED;
837 			break;
838 		case ReportGameAckMessage::gameReportDuplicate:
839 			msgCode = MSG_NET_GAMENAME_REPORT_DUP;
840 			break;
841 		default:
842 			msgCode = MSG_NET_GAMENAME_REPORT_REJECTED;
843 			break;
844 		}
845 		client->GetCallback().SignalNetClientMsgBox(msgCode);
846 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_AdminRemoveGameAckMessage) {
847 		const AdminRemoveGameAckMessage &netRemoveAck = tmpPacket->GetMsg()->adminremovegameackmessage();
848 		unsigned msgCode;
849 		switch (netRemoveAck.removegameresult()) {
850 		case AdminRemoveGameAckMessage::gameRemoveAccepted:
851 			msgCode = MSG_NET_ADMIN_REMOVE_GAME_ACCEPTED;
852 			break;
853 		default:
854 			msgCode = MSG_NET_ADMIN_REMOVE_GAME_REJECTED;
855 			break;
856 		}
857 		client->GetCallback().SignalNetClientMsgBox(msgCode);
858 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_AdminBanPlayerAckMessage) {
859 		const AdminBanPlayerAckMessage &netBanAck = tmpPacket->GetMsg()->adminbanplayerackmessage();
860 		unsigned msgCode;
861 		switch (netBanAck.banplayerresult()) {
862 		case AdminBanPlayerAckMessage::banPlayerAccepted:
863 			msgCode = MSG_NET_ADMIN_BAN_PLAYER_ACCEPTED;
864 			break;
865 		case AdminBanPlayerAckMessage::banPlayerPending:
866 			msgCode = MSG_NET_ADMIN_BAN_PLAYER_PENDING;
867 			break;
868 		case AdminBanPlayerAckMessage::banPlayerNoDB:
869 			msgCode = MSG_NET_ADMIN_BAN_PLAYER_NODB;
870 			break;
871 		case AdminBanPlayerAckMessage::banPlayerDBError:
872 			msgCode = MSG_NET_ADMIN_BAN_PLAYER_DBERROR;
873 			break;
874 		default:
875 			msgCode = MSG_NET_ADMIN_BAN_PLAYER_REJECTED;
876 			break;
877 		}
878 		client->GetCallback().SignalNetClientMsgBox(msgCode);
879 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_StatisticsMessage) {
880 		const StatisticsMessage &netStatistics = tmpPacket->GetMsg()->statisticsmessage();
881 
882 		unsigned numStats = netStatistics.statisticsdata_size();
883 		// Request player info for players if needed.
884 		if (numStats) {
885 			ServerStats tmpStats;
886 			for (unsigned i = 0; i < numStats; i++) {
887 				if (netStatistics.statisticsdata(i).statisticstype() == StatisticsMessage::StatisticsData::statNumberOfPlayers)
888 					tmpStats.numberOfPlayersOnServer = netStatistics.statisticsdata(i).statisticsvalue();
889 			}
890 			client->UpdateStatData(tmpStats);
891 		}
892 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_ErrorMessage) {
893 		// Server reported an error.
894 		const ErrorMessage &netError = tmpPacket->GetMsg()->errormessage();
895 		// Show the error.
896 		throw ClientException(__FILE__, __LINE__, NetPacket::NetErrorToGameError(netError.errorreason()), 0);
897 	}
898 
899 	InternalHandlePacket(client, tmpPacket);
900 }
901 
902 //-----------------------------------------------------------------------------
903 
904 ClientStateStartSession &
Instance()905 ClientStateStartSession::Instance()
906 {
907 	static ClientStateStartSession state;
908 	return state;
909 }
910 
ClientStateStartSession()911 ClientStateStartSession::ClientStateStartSession()
912 {
913 }
914 
~ClientStateStartSession()915 ClientStateStartSession::~ClientStateStartSession()
916 {
917 }
918 
919 void
Enter(boost::shared_ptr<ClientThread> client)920 ClientStateStartSession::Enter(boost::shared_ptr<ClientThread> client)
921 {
922 	// Now we finally start receiving data.
923 	client->StartAsyncRead();
924 }
925 
926 void
Exit(boost::shared_ptr<ClientThread>)927 ClientStateStartSession::Exit(boost::shared_ptr<ClientThread> /*client*/)
928 {
929 }
930 
931 void
InternalHandlePacket(boost::shared_ptr<ClientThread> client,boost::shared_ptr<NetPacket> tmpPacket)932 ClientStateStartSession::InternalHandlePacket(boost::shared_ptr<ClientThread> client, boost::shared_ptr<NetPacket> tmpPacket)
933 {
934 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_AnnounceMessage) {
935 		// Server has send announcement - check data.
936 		const AnnounceMessage &netAnnounce = tmpPacket->GetMsg()->announcemessage();
937 		// Check current game version.
938 		if (netAnnounce.latestgameversion().majorversion() != POKERTH_VERSION_MAJOR
939 				|| netAnnounce.latestgameversion().minorversion() != POKERTH_VERSION_MINOR) {
940 			client->GetCallback().SignalNetClientNotification(NTF_NET_NEW_RELEASE_AVAILABLE);
941 		} else if (POKERTH_BETA_REVISION && netAnnounce.latestbetarevision() != POKERTH_BETA_REVISION) {
942 			client->GetCallback().SignalNetClientNotification(NTF_NET_OUTDATED_BETA);
943 		}
944 		ClientContext &context = client->GetContext();
945 
946 		// CASE 1: Authenticated login (username, challenge/response for password).
947 		if (netAnnounce.servertype() == AnnounceMessage::serverTypeInternetAuth) {
948 			client->GetCallback().SignalNetClientLoginShow();
949 			client->SetState(ClientStateWaitEnterLogin::Instance());
950 		}
951 		// CASE 2: Unauthenticated login (network game or dedicated server without auth backend).
952 		else if (netAnnounce.servertype() == AnnounceMessage::serverTypeInternetNoAuth
953 				 || netAnnounce.servertype() == AnnounceMessage::serverTypeLAN) {
954 			boost::shared_ptr<NetPacket> init(new NetPacket);
955 			init->GetMsg()->set_messagetype(PokerTHMessage::Type_InitMessage);
956 			InitMessage *netInit = init->GetMsg()->mutable_initmessage();
957 			netInit->mutable_requestedversion()->set_majorversion(NET_VERSION_MAJOR);
958 			netInit->mutable_requestedversion()->set_minorversion(NET_VERSION_MINOR);
959 			netInit->set_buildid(0);
960 			if (!context.GetSessionGuid().empty()) {
961 				netInit->set_mylastsessionid(context.GetSessionGuid());
962 			}
963 			if (!context.GetServerPassword().empty()) {
964 				netInit->set_authserverpassword(context.GetServerPassword());
965 			}
966 			netInit->set_login(InitMessage::unauthenticatedLogin);
967 			netInit->set_nickname(context.GetPlayerName());
968 			string avatarFile = client->GetQtToolsInterface().stringFromUtf8(context.GetAvatarFile());
969 			if (!avatarFile.empty()) {
970 				MD5Buf tmpMD5;
971 				if (client->GetAvatarManager().GetHashForAvatar(avatarFile, tmpMD5)) {
972 					// Send MD5 hash of avatar.
973 					netInit->set_avatarhash(tmpMD5.GetData(), MD5_DATA_SIZE);
974 				}
975 			}
976 			client->GetSender().Send(context.GetSessionData(), init);
977 			client->SetState(ClientStateWaitSession::Instance());
978 		}
979 	}
980 }
981 
982 //-----------------------------------------------------------------------------
983 
984 ClientStateWaitEnterLogin &
Instance()985 ClientStateWaitEnterLogin::Instance()
986 {
987 	static ClientStateWaitEnterLogin state;
988 	return state;
989 }
990 
ClientStateWaitEnterLogin()991 ClientStateWaitEnterLogin::ClientStateWaitEnterLogin()
992 {
993 }
994 
~ClientStateWaitEnterLogin()995 ClientStateWaitEnterLogin::~ClientStateWaitEnterLogin()
996 {
997 }
998 
999 void
Enter(boost::shared_ptr<ClientThread> client)1000 ClientStateWaitEnterLogin::Enter(boost::shared_ptr<ClientThread> client)
1001 {
1002 	client->GetStateTimer().expires_from_now(
1003 		milliseconds(CLIENT_WAIT_TIMEOUT_MSEC));
1004 	client->GetStateTimer().async_wait(
1005 		boost::bind(
1006 			&ClientStateWaitEnterLogin::TimerLoop, this, boost::asio::placeholders::error, client));
1007 }
1008 
1009 void
Exit(boost::shared_ptr<ClientThread> client)1010 ClientStateWaitEnterLogin::Exit(boost::shared_ptr<ClientThread> client)
1011 {
1012 	client->GetStateTimer().cancel();
1013 }
1014 
1015 void
HandlePacket(boost::shared_ptr<ClientThread>,boost::shared_ptr<NetPacket> tmpPacket)1016 ClientStateWaitEnterLogin::HandlePacket(boost::shared_ptr<ClientThread> /*client*/, boost::shared_ptr<NetPacket> tmpPacket)
1017 {
1018 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_ErrorMessage) {
1019 		// Server reported an error.
1020 		const ErrorMessage &netError = tmpPacket->GetMsg()->errormessage();
1021 		// Show the error.
1022 		throw ClientException(__FILE__, __LINE__, NetPacket::NetErrorToGameError(netError.errorreason()), 0);
1023 	}
1024 }
1025 
1026 void
TimerLoop(const boost::system::error_code & ec,boost::shared_ptr<ClientThread> client)1027 ClientStateWaitEnterLogin::TimerLoop(const boost::system::error_code& ec, boost::shared_ptr<ClientThread> client)
1028 {
1029 	if (!ec && &client->GetState() == this) {
1030 		ClientThread::LoginData loginData;
1031 		if (client->GetLoginData(loginData)) {
1032 			ClientContext &context = client->GetContext();
1033 			boost::shared_ptr<NetPacket> init(new NetPacket);
1034 			init->GetMsg()->set_messagetype(PokerTHMessage::Type_InitMessage);
1035 			InitMessage *netInit = init->GetMsg()->mutable_initmessage();
1036 			netInit->mutable_requestedversion()->set_majorversion(NET_VERSION_MAJOR);
1037 			netInit->mutable_requestedversion()->set_minorversion(NET_VERSION_MINOR);
1038 			netInit->set_buildid(0);
1039 			if (!context.GetSessionGuid().empty()) {
1040 				netInit->set_mylastsessionid(context.GetSessionGuid());
1041 			}
1042 			if (!context.GetServerPassword().empty()) {
1043 				netInit->set_authserverpassword(context.GetServerPassword());
1044 			}
1045 
1046 			context.SetPlayerName(loginData.userName);
1047 
1048 			// Handle guest login first.
1049 			if (loginData.isGuest) {
1050 				context.SetPassword("");
1051 				context.SetPlayerRights(PLAYER_RIGHTS_GUEST);
1052 				netInit->set_login(InitMessage::guestLogin);
1053 				netInit->set_nickname(context.GetPlayerName());
1054 
1055 				client->GetSender().Send(context.GetSessionData(), init);
1056 				client->SetState(ClientStateWaitSession::Instance());
1057 			}
1058 			// If the player is not a guest, authenticate.
1059 			else {
1060 				context.SetPassword(loginData.password);
1061 				netInit->set_login(InitMessage::authenticatedLogin);
1062 				// Send authentication user data for challenge/response in init.
1063 				boost::shared_ptr<SessionData> tmpSession = context.GetSessionData();
1064 				tmpSession->CreateClientAuthSession(client->GetAuthContext(), context.GetPlayerName(), context.GetPassword());
1065 				if (!tmpSession->AuthStep(1, ""))
1066 					throw ClientException(__FILE__, __LINE__, ERR_NET_INVALID_PASSWORD, 0);
1067 				string outUserData(tmpSession->AuthGetNextOutMsg());
1068 				netInit->set_clientuserdata(outUserData);
1069 				string avatarFile = client->GetQtToolsInterface().stringFromUtf8(context.GetAvatarFile());
1070 				if (!avatarFile.empty()) {
1071 					MD5Buf tmpMD5;
1072 					if (client->GetAvatarManager().GetHashForAvatar(avatarFile, tmpMD5)) {
1073 						// TODO: use sha1.
1074 						netInit->set_avatarhash(tmpMD5.GetData(), MD5_DATA_SIZE);
1075 					}
1076 				}
1077 				client->GetSender().Send(context.GetSessionData(), init);
1078 				client->SetState(ClientStateWaitAuthChallenge::Instance());
1079 			}
1080 		} else {
1081 			client->GetStateTimer().expires_from_now(
1082 				milliseconds(CLIENT_WAIT_TIMEOUT_MSEC));
1083 			client->GetStateTimer().async_wait(
1084 				boost::bind(
1085 					&ClientStateWaitEnterLogin::TimerLoop, this, boost::asio::placeholders::error, client));
1086 		}
1087 	}
1088 }
1089 
1090 //-----------------------------------------------------------------------------
1091 
1092 ClientStateWaitAuthChallenge &
Instance()1093 ClientStateWaitAuthChallenge::Instance()
1094 {
1095 	static ClientStateWaitAuthChallenge state;
1096 	return state;
1097 }
1098 
ClientStateWaitAuthChallenge()1099 ClientStateWaitAuthChallenge::ClientStateWaitAuthChallenge()
1100 {
1101 }
1102 
~ClientStateWaitAuthChallenge()1103 ClientStateWaitAuthChallenge::~ClientStateWaitAuthChallenge()
1104 {
1105 }
1106 
1107 void
Enter(boost::shared_ptr<ClientThread>)1108 ClientStateWaitAuthChallenge::Enter(boost::shared_ptr<ClientThread> /*client*/)
1109 {
1110 }
1111 
1112 void
Exit(boost::shared_ptr<ClientThread>)1113 ClientStateWaitAuthChallenge::Exit(boost::shared_ptr<ClientThread> /*client*/)
1114 {
1115 }
1116 
1117 void
InternalHandlePacket(boost::shared_ptr<ClientThread> client,boost::shared_ptr<NetPacket> tmpPacket)1118 ClientStateWaitAuthChallenge::InternalHandlePacket(boost::shared_ptr<ClientThread> client, boost::shared_ptr<NetPacket> tmpPacket)
1119 {
1120 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_AuthServerChallengeMessage) {
1121 		const AuthServerChallengeMessage &netAuth = tmpPacket->GetMsg()->authserverchallengemessage();
1122 		string challengeStr(netAuth.serverchallenge());
1123 		boost::shared_ptr<SessionData> tmpSession = client->GetContext().GetSessionData();
1124 		if (!tmpSession->AuthStep(2, challengeStr.c_str()))
1125 			throw ClientException(__FILE__, __LINE__, ERR_NET_INVALID_PASSWORD, 0);
1126 		string outUserData(tmpSession->AuthGetNextOutMsg());
1127 
1128 		boost::shared_ptr<NetPacket> packet(new NetPacket);
1129 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_AuthClientResponseMessage);
1130 		AuthClientResponseMessage *outAuth = packet->GetMsg()->mutable_authclientresponsemessage();
1131 		outAuth->set_clientresponse(outUserData);
1132 		client->GetSender().Send(tmpSession, packet);
1133 		client->SetState(ClientStateWaitAuthVerify::Instance());
1134 	}
1135 }
1136 
1137 //-----------------------------------------------------------------------------
1138 
1139 ClientStateWaitAuthVerify &
Instance()1140 ClientStateWaitAuthVerify::Instance()
1141 {
1142 	static ClientStateWaitAuthVerify state;
1143 	return state;
1144 }
1145 
ClientStateWaitAuthVerify()1146 ClientStateWaitAuthVerify::ClientStateWaitAuthVerify()
1147 {
1148 }
1149 
~ClientStateWaitAuthVerify()1150 ClientStateWaitAuthVerify::~ClientStateWaitAuthVerify()
1151 {
1152 }
1153 
1154 void
Enter(boost::shared_ptr<ClientThread>)1155 ClientStateWaitAuthVerify::Enter(boost::shared_ptr<ClientThread> /*client*/)
1156 {
1157 }
1158 
1159 void
Exit(boost::shared_ptr<ClientThread>)1160 ClientStateWaitAuthVerify::Exit(boost::shared_ptr<ClientThread> /*client*/)
1161 {
1162 }
1163 
1164 void
InternalHandlePacket(boost::shared_ptr<ClientThread> client,boost::shared_ptr<NetPacket> tmpPacket)1165 ClientStateWaitAuthVerify::InternalHandlePacket(boost::shared_ptr<ClientThread> client, boost::shared_ptr<NetPacket> tmpPacket)
1166 {
1167 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_AuthServerVerificationMessage) {
1168 		// Check subtype.
1169 		const AuthServerVerificationMessage &netAuth = tmpPacket->GetMsg()->authserververificationmessage();
1170 		string verificationStr(netAuth.serververification());
1171 		boost::shared_ptr<SessionData> tmpSession = client->GetContext().GetSessionData();
1172 		if (!tmpSession->AuthStep(3, verificationStr.c_str()))
1173 			throw ClientException(__FILE__, __LINE__, ERR_NET_INVALID_PASSWORD, 0);
1174 
1175 		client->SetState(ClientStateWaitSession::Instance());
1176 	}
1177 }
1178 
1179 //-----------------------------------------------------------------------------
1180 
1181 ClientStateWaitSession &
Instance()1182 ClientStateWaitSession::Instance()
1183 {
1184 	static ClientStateWaitSession state;
1185 	return state;
1186 }
1187 
ClientStateWaitSession()1188 ClientStateWaitSession::ClientStateWaitSession()
1189 {
1190 }
1191 
~ClientStateWaitSession()1192 ClientStateWaitSession::~ClientStateWaitSession()
1193 {
1194 }
1195 
1196 void
Enter(boost::shared_ptr<ClientThread>)1197 ClientStateWaitSession::Enter(boost::shared_ptr<ClientThread> /*client*/)
1198 {
1199 }
1200 
1201 void
Exit(boost::shared_ptr<ClientThread>)1202 ClientStateWaitSession::Exit(boost::shared_ptr<ClientThread> /*client*/)
1203 {
1204 }
1205 
1206 void
InternalHandlePacket(boost::shared_ptr<ClientThread> client,boost::shared_ptr<NetPacket> tmpPacket)1207 ClientStateWaitSession::InternalHandlePacket(boost::shared_ptr<ClientThread> client, boost::shared_ptr<NetPacket> tmpPacket)
1208 {
1209 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_InitAckMessage) {
1210 		// Everything is fine - we are in the lobby.
1211 		const InitAckMessage &netInitAck = tmpPacket->GetMsg()->initackmessage();
1212 		client->SetGuiPlayerId(netInitAck.yourplayerid());
1213 
1214 		client->GetContext().SetSessionGuid(netInitAck.yoursessionid());
1215 		client->SetSessionEstablished(true);
1216 		client->GetCallback().SignalNetClientConnect(MSG_SOCK_SESSION_DONE);
1217 		if (netInitAck.has_rejoingameid())
1218 			client->GetCallback().SignalNetClientRejoinPossible(netInitAck.rejoingameid());
1219 		client->SetState(ClientStateWaitJoin::Instance());
1220 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarRequestMessage) {
1221 		// Before letting us join the lobby, the server requests our avatar.
1222 		const AvatarRequestMessage &netAvatarRequest = tmpPacket->GetMsg()->avatarrequestmessage();
1223 
1224 		// TODO compare SHA1.
1225 		NetPacketList tmpList;
1226 		int avatarError = client->GetAvatarManager().AvatarFileToNetPackets(
1227 							  client->GetQtToolsInterface().stringFromUtf8(client->GetContext().GetAvatarFile()),
1228 							  netAvatarRequest.requestid(),
1229 							  tmpList);
1230 
1231 		if (!avatarError)
1232 			client->GetSender().Send(client->GetContext().GetSessionData(), tmpList);
1233 		else
1234 			throw ClientException(__FILE__, __LINE__, avatarError, 0);
1235 	}
1236 }
1237 
1238 //-----------------------------------------------------------------------------
1239 
1240 ClientStateWaitJoin &
Instance()1241 ClientStateWaitJoin::Instance()
1242 {
1243 	static ClientStateWaitJoin state;
1244 	return state;
1245 }
1246 
ClientStateWaitJoin()1247 ClientStateWaitJoin::ClientStateWaitJoin()
1248 {
1249 }
1250 
~ClientStateWaitJoin()1251 ClientStateWaitJoin::~ClientStateWaitJoin()
1252 {
1253 }
1254 
1255 void
Enter(boost::shared_ptr<ClientThread>)1256 ClientStateWaitJoin::Enter(boost::shared_ptr<ClientThread> /*client*/)
1257 {
1258 }
1259 
1260 void
Exit(boost::shared_ptr<ClientThread>)1261 ClientStateWaitJoin::Exit(boost::shared_ptr<ClientThread> /*client*/)
1262 {
1263 }
1264 
1265 void
InternalHandlePacket(boost::shared_ptr<ClientThread> client,boost::shared_ptr<NetPacket> tmpPacket)1266 ClientStateWaitJoin::InternalHandlePacket(boost::shared_ptr<ClientThread> client, boost::shared_ptr<NetPacket> tmpPacket)
1267 {
1268 	ClientContext &context = client->GetContext();
1269 
1270 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_JoinGameAckMessage) {
1271 		const JoinGameAckMessage &netJoinAck = tmpPacket->GetMsg()->joingameackmessage();
1272 		// Successfully joined a game.
1273 		client->SetGameId(netJoinAck.gameid());
1274 		GameData tmpData;
1275 		NetPacket::GetGameData(netJoinAck.gameinfo(), tmpData);
1276 		client->SetGameData(tmpData);
1277 		client->ModifyGameInfoClearSpectatorsDuringGame();
1278 
1279 		// Player number is 0 on init. Will be set when the game starts.
1280 		boost::shared_ptr<PlayerData> playerData(
1281 			new PlayerData(client->GetGuiPlayerId(), 0, PLAYER_TYPE_HUMAN,
1282 						   context.GetPlayerRights(), netJoinAck.areyougameadmin()));
1283 		playerData->SetName(context.GetPlayerName());
1284 		playerData->SetAvatarFile(context.GetAvatarFile());
1285 		client->AddPlayerData(playerData);
1286 
1287 		client->GetCallback().SignalNetClientGameInfo(MSG_NET_GAME_CLIENT_JOIN);
1288 		client->SetState(ClientStateWaitGame::Instance());
1289 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_JoinGameFailedMessage) {
1290 		// Failed to join a game.
1291 		const JoinGameFailedMessage &netJoinFailed = tmpPacket->GetMsg()->joingamefailedmessage();
1292 
1293 		int failureCode;
1294 		switch (netJoinFailed.joingamefailurereason()) {
1295 		case JoinGameFailedMessage::invalidGame :
1296 			failureCode = NTF_NET_JOIN_GAME_INVALID;
1297 			break;
1298 		case JoinGameFailedMessage::gameIsFull :
1299 			failureCode = NTF_NET_JOIN_GAME_FULL;
1300 			break;
1301 		case JoinGameFailedMessage::gameIsRunning :
1302 			failureCode = NTF_NET_JOIN_ALREADY_RUNNING;
1303 			break;
1304 		case JoinGameFailedMessage::invalidPassword :
1305 			failureCode = NTF_NET_JOIN_INVALID_PASSWORD;
1306 			break;
1307 		case JoinGameFailedMessage::notAllowedAsGuest :
1308 			failureCode = NTF_NET_JOIN_GUEST_FORBIDDEN;
1309 			break;
1310 		case JoinGameFailedMessage::notInvited :
1311 			failureCode = NTF_NET_JOIN_NOT_INVITED;
1312 			break;
1313 		case JoinGameFailedMessage::gameNameInUse :
1314 			failureCode = NTF_NET_JOIN_GAME_NAME_IN_USE;
1315 			break;
1316 		case JoinGameFailedMessage::badGameName :
1317 			failureCode = NTF_NET_JOIN_GAME_BAD_NAME;
1318 			break;
1319 		case JoinGameFailedMessage::invalidSettings :
1320 			failureCode = NTF_NET_JOIN_INVALID_SETTINGS;
1321 			break;
1322 		case JoinGameFailedMessage::ipAddressBlocked :
1323 			failureCode = NTF_NET_JOIN_IP_BLOCKED;
1324 			break;
1325 		case JoinGameFailedMessage::rejoinFailed :
1326 			failureCode = NTF_NET_JOIN_REJOIN_FAILED;
1327 			break;
1328 		default :
1329 			failureCode = NTF_NET_INTERNAL;
1330 			break;
1331 		}
1332 
1333 		client->GetCallback().SignalNetClientNotification(failureCode);
1334 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_InviteNotifyMessage) {
1335 		const InviteNotifyMessage &netInvNotify = tmpPacket->GetMsg()->invitenotifymessage();
1336 		if (netInvNotify.playeridwho() == client->GetGuiPlayerId()) {
1337 			client->GetCallback().SignalSelfGameInvitation(netInvNotify.gameid(), netInvNotify.playeridbywhom());
1338 		}
1339 	}
1340 }
1341 
1342 //-----------------------------------------------------------------------------
1343 
1344 ClientStateWaitGame &
Instance()1345 ClientStateWaitGame::Instance()
1346 {
1347 	static ClientStateWaitGame state;
1348 	return state;
1349 }
1350 
ClientStateWaitGame()1351 ClientStateWaitGame::ClientStateWaitGame()
1352 {
1353 }
1354 
~ClientStateWaitGame()1355 ClientStateWaitGame::~ClientStateWaitGame()
1356 {
1357 }
1358 
1359 void
Enter(boost::shared_ptr<ClientThread>)1360 ClientStateWaitGame::Enter(boost::shared_ptr<ClientThread> /*client*/)
1361 {
1362 }
1363 
1364 void
Exit(boost::shared_ptr<ClientThread>)1365 ClientStateWaitGame::Exit(boost::shared_ptr<ClientThread> /*client*/)
1366 {
1367 }
1368 
1369 void
InternalHandlePacket(boost::shared_ptr<ClientThread> client,boost::shared_ptr<NetPacket> tmpPacket)1370 ClientStateWaitGame::InternalHandlePacket(boost::shared_ptr<ClientThread> client, boost::shared_ptr<NetPacket> tmpPacket)
1371 {
1372 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_StartEventMessage) {
1373 		const StartEventMessage &netStartEvent = tmpPacket->GetMsg()->starteventmessage();
1374 		if (netStartEvent.starteventtype() == StartEventMessage::rejoinEvent) {
1375 			client->GetCallback().SignalNetClientGameInfo(MSG_NET_GAME_CLIENT_SYNCREJOIN);
1376 		} else {
1377 			client->GetCallback().SignalNetClientGameInfo(MSG_NET_GAME_CLIENT_SYNCSTART);
1378 		}
1379 		client->SetState(ClientStateSynchronizeStart::Instance());
1380 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_InviteNotifyMessage) {
1381 		const InviteNotifyMessage &netInvNotify = tmpPacket->GetMsg()->invitenotifymessage();
1382 		client->GetCallback().SignalPlayerGameInvitation(
1383 			netInvNotify.gameid(),
1384 			netInvNotify.playeridwho(),
1385 			netInvNotify.playeridbywhom());
1386 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_RejectInvNotifyMessage) {
1387 		const RejectInvNotifyMessage &netRejNotify = tmpPacket->GetMsg()->rejectinvnotifymessage();
1388 		client->GetCallback().SignalRejectedGameInvitation(
1389 			netRejNotify.gameid(),
1390 			netRejNotify.playerid(),
1391 			static_cast<DenyGameInvitationReason>(netRejNotify.playerrejectreason()));
1392 	}
1393 }
1394 
1395 //-----------------------------------------------------------------------------
1396 
1397 ClientStateSynchronizeStart &
Instance()1398 ClientStateSynchronizeStart::Instance()
1399 {
1400 	static ClientStateSynchronizeStart state;
1401 	return state;
1402 }
1403 
ClientStateSynchronizeStart()1404 ClientStateSynchronizeStart::ClientStateSynchronizeStart()
1405 {
1406 }
1407 
~ClientStateSynchronizeStart()1408 ClientStateSynchronizeStart::~ClientStateSynchronizeStart()
1409 {
1410 }
1411 
1412 void
Enter(boost::shared_ptr<ClientThread> client)1413 ClientStateSynchronizeStart::Enter(boost::shared_ptr<ClientThread> client)
1414 {
1415 	client->GetStateTimer().expires_from_now(
1416 		milliseconds(CLIENT_WAIT_TIMEOUT_MSEC));
1417 	client->GetStateTimer().async_wait(
1418 		boost::bind(
1419 			&ClientStateSynchronizeStart::TimerLoop, this, boost::asio::placeholders::error, client));
1420 }
1421 
1422 void
Exit(boost::shared_ptr<ClientThread> client)1423 ClientStateSynchronizeStart::Exit(boost::shared_ptr<ClientThread> client)
1424 {
1425 	client->GetStateTimer().cancel();
1426 }
1427 
1428 void
TimerLoop(const boost::system::error_code & ec,boost::shared_ptr<ClientThread> client)1429 ClientStateSynchronizeStart::TimerLoop(const boost::system::error_code& ec, boost::shared_ptr<ClientThread> client)
1430 {
1431 	if (!ec && &client->GetState() == this) {
1432 		if (client->IsSynchronized()) {
1433 			// Acknowledge start.
1434 			boost::shared_ptr<NetPacket> startAck(new NetPacket);
1435 			startAck->GetMsg()->set_messagetype(PokerTHMessage::Type_StartEventAckMessage);
1436 			StartEventAckMessage *netStartAck = startAck->GetMsg()->mutable_starteventackmessage();
1437 			netStartAck->set_gameid(client->GetGameId());
1438 
1439 			client->GetSender().Send(client->GetContext().GetSessionData(), startAck);
1440 			// Unsubscribe lobby messages.
1441 			client->UnsubscribeLobbyMsg();
1442 
1443 			client->SetState(ClientStateWaitStart::Instance());
1444 		} else {
1445 			client->GetStateTimer().expires_from_now(
1446 				milliseconds(CLIENT_WAIT_TIMEOUT_MSEC));
1447 			client->GetStateTimer().async_wait(
1448 				boost::bind(
1449 					&ClientStateSynchronizeStart::TimerLoop, this, boost::asio::placeholders::error, client));
1450 		}
1451 	}
1452 }
1453 
1454 void
InternalHandlePacket(boost::shared_ptr<ClientThread> client,boost::shared_ptr<NetPacket> tmpPacket)1455 ClientStateSynchronizeStart::InternalHandlePacket(boost::shared_ptr<ClientThread> client, boost::shared_ptr<NetPacket> tmpPacket)
1456 {
1457 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameStartInitialMessage) {
1458 		// Try to start anyway. Terminating here is very bad because rejoin is not possible then.
1459 		// Unsubscribe lobby messages.
1460 		client->UnsubscribeLobbyMsg();
1461 		client->SetState(ClientStateWaitStart::Instance());
1462 		// Forward the game start message to the next state.
1463 		client->GetState().HandlePacket(client, tmpPacket);
1464 	}
1465 }
1466 
1467 //-----------------------------------------------------------------------------
1468 
1469 ClientStateWaitStart &
Instance()1470 ClientStateWaitStart::Instance()
1471 {
1472 	static ClientStateWaitStart state;
1473 	return state;
1474 }
1475 
ClientStateWaitStart()1476 ClientStateWaitStart::ClientStateWaitStart()
1477 {
1478 }
1479 
~ClientStateWaitStart()1480 ClientStateWaitStart::~ClientStateWaitStart()
1481 {
1482 }
1483 
1484 void
Enter(boost::shared_ptr<ClientThread>)1485 ClientStateWaitStart::Enter(boost::shared_ptr<ClientThread> /*client*/)
1486 {
1487 }
1488 
1489 void
Exit(boost::shared_ptr<ClientThread>)1490 ClientStateWaitStart::Exit(boost::shared_ptr<ClientThread> /*client*/)
1491 {
1492 }
1493 
1494 void
InternalHandlePacket(boost::shared_ptr<ClientThread> client,boost::shared_ptr<NetPacket> tmpPacket)1495 ClientStateWaitStart::InternalHandlePacket(boost::shared_ptr<ClientThread> client, boost::shared_ptr<NetPacket> tmpPacket)
1496 {
1497 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameStartInitialMessage
1498 			|| tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameStartRejoinMessage) {
1499 		PlayerIdList tmpPlayerList;
1500 		unsigned tmpHandId = 0;
1501 
1502 		if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameStartInitialMessage) {
1503 			// Start the network game as client.
1504 			const GameStartInitialMessage &netStartModeInitial = tmpPacket->GetMsg()->gamestartinitialmessage();
1505 
1506 			StartData startData;
1507 			startData.startDealerPlayerId = netStartModeInitial.startdealerplayerid();
1508 			startData.numberOfPlayers = netStartModeInitial.playerseats_size();
1509 			client->SetStartData(startData);
1510 
1511 			// Set player numbers using the game start data slots.
1512 			unsigned numPlayers = netStartModeInitial.playerseats_size();
1513 			// Request player info for players if needed.
1514 			if (numPlayers) {
1515 				for (unsigned i = 0; i < numPlayers; i++) {
1516 					unsigned playerId = netStartModeInitial.playerseats(i);
1517 					boost::shared_ptr<PlayerData> tmpPlayer = client->GetPlayerDataByUniqueId(playerId);
1518 					if (!tmpPlayer)
1519 						throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1520 					tmpPlayer->SetNumber(i);
1521 				}
1522 			} else {
1523 				throw ClientException(__FILE__, __LINE__, ERR_NET_INVALID_PLAYER_COUNT, 0);
1524 			}
1525 		} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_GameStartRejoinMessage) {
1526 			const GameStartRejoinMessage &netStartModeRejoin = tmpPacket->GetMsg()->gamestartrejoinmessage();
1527 
1528 			StartData startData;
1529 			startData.startDealerPlayerId = netStartModeRejoin.startdealerplayerid();
1530 			startData.numberOfPlayers = netStartModeRejoin.rejoinplayerdata_size();
1531 			client->SetStartData(startData);
1532 			tmpHandId = netStartModeRejoin.handnum();
1533 
1534 			// Set player numbers using the game start data slots.
1535 			unsigned numPlayers = netStartModeRejoin.rejoinplayerdata_size();
1536 			// Request player info for players if needed.
1537 			if (numPlayers) {
1538 				for (unsigned i = 0; i < numPlayers; i++) {
1539 					const GameStartRejoinMessage::RejoinPlayerData &playerData = netStartModeRejoin.rejoinplayerdata(i);
1540 					boost::shared_ptr<PlayerData> tmpPlayer = client->GetPlayerDataByUniqueId(playerData.playerid());
1541 					if (!tmpPlayer) {
1542 						// If the player is not found: The corresponding session left. We need to create a generic player object.
1543 						// In order to have a complete seat list, we need all players, even those who left.
1544 						tmpPlayer = client->CreatePlayerData(playerData.playerid(), false);
1545 						client->AddPlayerData(tmpPlayer);
1546 						tmpPlayerList.push_back(playerData.playerid());
1547 					}
1548 					tmpPlayer->SetNumber(i);
1549 					tmpPlayer->SetStartCash(playerData.playermoney());
1550 				}
1551 			} else
1552 				throw ClientException(__FILE__, __LINE__, ERR_NET_INVALID_PLAYER_COUNT, 0);
1553 		}
1554 		client->InitGame();
1555 		client->GetGame()->setCurrentHandID(tmpHandId);
1556 		// We need to remove the temporary player data objects after creating the game.
1557 		BOOST_FOREACH(unsigned tmpPlayerId, tmpPlayerList) {
1558 			client->RemovePlayerData(tmpPlayerId, NTF_NET_REMOVED_ON_REQUEST);
1559 		}
1560 		client->GetCallback().SignalNetClientGameInfo(MSG_NET_GAME_CLIENT_START);
1561 		client->SetState(ClientStateWaitHand::Instance());
1562 	}
1563 }
1564 
1565 //-----------------------------------------------------------------------------
1566 
1567 ClientStateWaitHand &
Instance()1568 ClientStateWaitHand::Instance()
1569 {
1570 	static ClientStateWaitHand state;
1571 	return state;
1572 }
1573 
ClientStateWaitHand()1574 ClientStateWaitHand::ClientStateWaitHand()
1575 {
1576 }
1577 
~ClientStateWaitHand()1578 ClientStateWaitHand::~ClientStateWaitHand()
1579 {
1580 }
1581 
1582 void
Enter(boost::shared_ptr<ClientThread>)1583 ClientStateWaitHand::Enter(boost::shared_ptr<ClientThread> /*client*/)
1584 {
1585 }
1586 
1587 void
Exit(boost::shared_ptr<ClientThread>)1588 ClientStateWaitHand::Exit(boost::shared_ptr<ClientThread> /*client*/)
1589 {
1590 }
1591 
1592 void
InternalHandlePacket(boost::shared_ptr<ClientThread> client,boost::shared_ptr<NetPacket> tmpPacket)1593 ClientStateWaitHand::InternalHandlePacket(boost::shared_ptr<ClientThread> client, boost::shared_ptr<NetPacket> tmpPacket)
1594 {
1595 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_HandStartMessage) {
1596 		// Hand was started.
1597 		// These are the cards. Good luck.
1598 		const HandStartMessage &netHandStart = tmpPacket->GetMsg()->handstartmessage();
1599 		int myCards[2];
1600 		string userPassword(client->GetContext().GetPassword());
1601 		if (netHandStart.has_plaincards() && userPassword.empty()) {
1602 			const HandStartMessage::PlainCards &plainCards = netHandStart.plaincards();
1603 			myCards[0] = (int)plainCards.plaincard1();
1604 			myCards[1] = (int)plainCards.plaincard2();
1605 		} else if (netHandStart.has_encryptedcards() && !userPassword.empty()) {
1606 			const string &encryptedCards = netHandStart.encryptedcards();
1607 			string plainCards;
1608 			if (!CryptHelper::AES128Decrypt((const unsigned char *)userPassword.c_str(),
1609 											(unsigned)userPassword.size(),
1610 											(const unsigned char *)encryptedCards.data(),
1611 											(unsigned)encryptedCards.size(),
1612 											plainCards)) {
1613 				throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1614 			}
1615 			istringstream cardDataStream(plainCards);
1616 			unsigned tmpPlayerId, tmpGameId;
1617 			int tmpHandNum;
1618 			cardDataStream >> tmpPlayerId;
1619 			cardDataStream >> tmpGameId;
1620 			cardDataStream >> tmpHandNum;
1621 			if (tmpPlayerId != client->GetGuiPlayerId()
1622 					|| tmpGameId != client->GetGameId()
1623 					|| tmpHandNum != client->GetGame()->getCurrentHandID() + 1) {
1624 				throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1625 			}
1626 			cardDataStream >> myCards[0];
1627 			cardDataStream >> myCards[1];
1628 		}
1629 		// Retrieve state for each seat (not based on player id).
1630 		unsigned numPlayers = netHandStart.seatstates_size();
1631 		// Request player info for players if needed.
1632 		for (int i = 0; i < (int)numPlayers; i++) {
1633 			NetPlayerState seatState = netHandStart.seatstates(i);
1634 			int numberDiff = client->GetStartData().numberOfPlayers - client->GetOrigGuiPlayerNum();
1635 			boost::shared_ptr<PlayerInterface> tmpPlayer = client->GetGame()->getPlayerByNumber((i + numberDiff) % client->GetStartData().numberOfPlayers);
1636 			if (!tmpPlayer)
1637 				throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1638 			switch (seatState) {
1639 			case netPlayerStateNormal :
1640 				tmpPlayer->setIsSessionActive(true);
1641 				break;
1642 			case netPlayerStateSessionInactive :
1643 				tmpPlayer->setIsSessionActive(false);
1644 				break;
1645 			case netPlayerStateNoMoney :
1646 				tmpPlayer->setMyCash(0);
1647 				break;
1648 			}
1649 		}
1650 
1651 		// Basic synchronisation before a new hand is started.
1652 		client->GetGui().waitForGuiUpdateDone();
1653 		// Start new hand.
1654 		client->GetGame()->getSeatsList()->front()->setMyCards(myCards);
1655 		client->GetGame()->initHand();
1656 		client->GetGame()->getCurrentHand()->setSmallBlind(netHandStart.smallblind());
1657 		client->GetGame()->getCurrentHand()->getCurrentBeRo()->setMinimumRaise(2 * netHandStart.smallblind());
1658 		client->GetGame()->startHand();
1659 		client->GetGui().dealHoleCards();
1660 		client->GetGui().refreshGameLabels(GAME_STATE_PREFLOP);
1661 		client->GetGui().refreshPot();
1662 		client->GetGui().waitForGuiUpdateDone();
1663 
1664 		client->GetCallback().SignalNetClientGameInfo(MSG_NET_GAME_CLIENT_HAND_START);
1665 		client->SetState(ClientStateRunHand::Instance());
1666 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_EndOfGameMessage) {
1667 		boost::shared_ptr<Game> curGame = client->GetGame();
1668 		if (curGame) {
1669 			const EndOfGameMessage &netEndOfGame = tmpPacket->GetMsg()->endofgamemessage();
1670 
1671 			boost::shared_ptr<PlayerInterface> tmpPlayer = curGame->getPlayerByUniqueId(netEndOfGame.winnerplayerid());
1672 			if (!tmpPlayer)
1673 				throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1674 			client->GetGui().logPlayerWinGame(tmpPlayer->getMyName(), curGame->getMyGameID());
1675 			// Resubscribe Lobby messages.
1676 			client->ResubscribeLobbyMsg();
1677 			// Show Lobby dialog.
1678 			client->GetCallback().SignalNetClientWaitDialog();
1679 			client->GetCallback().SignalNetClientGameInfo(MSG_NET_GAME_CLIENT_END);
1680 			client->SetState(ClientStateWaitGame::Instance());
1681 		}
1682 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_AfterHandShowCardsMessage) {
1683 		const AfterHandShowCardsMessage &showCards = tmpPacket->GetMsg()->afterhandshowcardsmessage();
1684 		const PlayerResult &r = showCards.playerresult();
1685 
1686 		boost::shared_ptr<PlayerInterface> tmpPlayer = client->GetGame()->getPlayerByUniqueId(r.playerid());
1687 		if (!tmpPlayer)
1688 			throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1689 
1690 		int tmpCards[2];
1691 		int bestHandPos[5];
1692 		tmpCards[0] = static_cast<int>(r.resultcard1());
1693 		tmpCards[1] = static_cast<int>(r.resultcard2());
1694 		tmpPlayer->setMyCards(tmpCards);
1695 		for (int num = 0; num < 5; num++) {
1696 			bestHandPos[num] = r.besthandposition(num);
1697 		}
1698 		if (r.cardsvalue()) {
1699 			tmpPlayer->setMyCardsValueInt(r.cardsvalue());
1700 		}
1701 		tmpPlayer->setMyBestHandPosition(bestHandPos);
1702 		tmpPlayer->setMyCash(r.playermoney());
1703 		tmpPlayer->setLastMoneyWon(r.moneywon());
1704 
1705 		client->GetCallback().SignalNetClientPostRiverShowCards(r.playerid());
1706 		client->GetClientLog()->logHoleCardsHandName(client->GetGame()->getActivePlayerList(), tmpPlayer, true);
1707 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_PlayerIdChangedMessage) {
1708 		boost::shared_ptr<Game> curGame = client->GetGame();
1709 		if (curGame) {
1710 			// Perform Id change.
1711 			const PlayerIdChangedMessage &idChanged = tmpPacket->GetMsg()->playeridchangedmessage();
1712 			boost::shared_ptr<PlayerInterface> tmpPlayer = curGame->getPlayerByUniqueId(idChanged.oldplayerid());
1713 			if (!tmpPlayer)
1714 				throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1715 			tmpPlayer->setMyUniqueID(idChanged.newplayerid());
1716 			// This player is now active again.
1717 			tmpPlayer->setMyStayOnTableStatus(true);
1718 			// Also update the dealer, if necessary.
1719 			curGame->replaceDealer(idChanged.oldplayerid(), idChanged.newplayerid());
1720 			// Update the player name, if necessary.
1721 			PlayerInfo info;
1722 			if (client->GetCachedPlayerInfo(idChanged.newplayerid(), info)) {
1723 				tmpPlayer->setMyName(info.playerName);
1724 			}
1725 		}
1726 	}
1727 }
1728 
1729 //-----------------------------------------------------------------------------
1730 
1731 ClientStateRunHand &
Instance()1732 ClientStateRunHand::Instance()
1733 {
1734 	static ClientStateRunHand state;
1735 	return state;
1736 }
1737 
ClientStateRunHand()1738 ClientStateRunHand::ClientStateRunHand()
1739 {
1740 }
1741 
~ClientStateRunHand()1742 ClientStateRunHand::~ClientStateRunHand()
1743 {
1744 }
1745 
1746 void
Enter(boost::shared_ptr<ClientThread>)1747 ClientStateRunHand::Enter(boost::shared_ptr<ClientThread> /*client*/)
1748 {
1749 }
1750 
1751 void
Exit(boost::shared_ptr<ClientThread>)1752 ClientStateRunHand::Exit(boost::shared_ptr<ClientThread> /*client*/)
1753 {
1754 }
1755 
1756 void
InternalHandlePacket(boost::shared_ptr<ClientThread> client,boost::shared_ptr<NetPacket> tmpPacket)1757 ClientStateRunHand::InternalHandlePacket(boost::shared_ptr<ClientThread> client, boost::shared_ptr<NetPacket> tmpPacket)
1758 {
1759 	boost::shared_ptr<Game> curGame = client->GetGame();
1760 	if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_PlayersActionDoneMessage) {
1761 		const PlayersActionDoneMessage &netActionDone = tmpPacket->GetMsg()->playersactiondonemessage();
1762 
1763 		boost::shared_ptr<PlayerInterface> tmpPlayer = curGame->getPlayerByUniqueId(netActionDone.playerid());
1764 		if (!tmpPlayer)
1765 			throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1766 
1767 		bool isBigBlind = false;
1768 
1769 		if (netActionDone.gamestate() == netStatePreflopSmallBlind) {
1770 			curGame->getCurrentHand()->getCurrentBeRo()->setSmallBlindPositionId(tmpPlayer->getMyUniqueID());
1771 			tmpPlayer->setMyButton(BUTTON_SMALL_BLIND);
1772 		} else if (netActionDone.gamestate() == netStatePreflopBigBlind) {
1773 			curGame->getCurrentHand()->getCurrentBeRo()->setBigBlindPositionId(tmpPlayer->getMyUniqueID());
1774 			tmpPlayer->setMyButton(BUTTON_BIG_BLIND);
1775 			isBigBlind = true;
1776 		} else { // no blind -> log
1777 			if (netActionDone.playeraction()) {
1778 				assert((int)netActionDone.totalplayerbet() >= tmpPlayer->getMySet());
1779 				client->GetGui().logPlayerActionMsg(
1780 					tmpPlayer->getMyName(),
1781 					netActionDone.playeraction(),
1782 					netActionDone.totalplayerbet() - tmpPlayer->getMySet());
1783 				client->GetClientLog()->logPlayerAction(
1784 					tmpPlayer->getMyName(),
1785 					client->GetClientLog()->transformPlayerActionLog(PlayerAction(netActionDone.playeraction())),
1786 					netActionDone.totalplayerbet() - tmpPlayer->getMySet()
1787 				);
1788 				if (tmpPlayer->getMyID() == 0) {
1789 					client->EndPing();
1790 				}
1791 			}
1792 			// Update last players turn only after the blinds.
1793 			curGame->getCurrentHand()->setPreviousPlayerID(tmpPlayer->getMyID());
1794 		}
1795 
1796 		tmpPlayer->setMyAction(PlayerAction(netActionDone.playeraction()));
1797 		tmpPlayer->setMySetAbsolute(netActionDone.totalplayerbet());
1798 		tmpPlayer->setMyCash(netActionDone.playermoney());
1799 		curGame->getCurrentHand()->getCurrentBeRo()->setHighestSet(netActionDone.highestset());
1800 		curGame->getCurrentHand()->getCurrentBeRo()->setMinimumRaise(netActionDone.minimumraise());
1801 		curGame->getCurrentHand()->getBoard()->collectSets();
1802 		curGame->getCurrentHand()->switchRounds();
1803 
1804 		//log blinds sets after setting bigblind-button
1805 		if (isBigBlind) {
1806 			client->GetGui().logNewBlindsSetsMsg(
1807 				curGame->getPlayerByUniqueId(curGame->getCurrentHand()->getCurrentBeRo()->getSmallBlindPositionId())->getMySet(),
1808 				curGame->getPlayerByUniqueId(curGame->getCurrentHand()->getCurrentBeRo()->getBigBlindPositionId())->getMySet(),
1809 				curGame->getPlayerByUniqueId(curGame->getCurrentHand()->getCurrentBeRo()->getSmallBlindPositionId())->getMyName(),
1810 				curGame->getPlayerByUniqueId(curGame->getCurrentHand()->getCurrentBeRo()->getBigBlindPositionId())->getMyName());
1811 			client->GetGui().flushLogAtHand();
1812 			client->GetClientLog()->logNewHandMsg(
1813 				curGame->getCurrentHandID(),
1814 				curGame->getPlayerByUniqueId(curGame->getCurrentHand()->getDealerPosition())->getMyID()+1,
1815 				curGame->getCurrentHand()->getSmallBlind(),
1816 				curGame->getPlayerByUniqueId(curGame->getCurrentHand()->getCurrentBeRo()->getSmallBlindPositionId())->getMyID()+1,
1817 				curGame->getCurrentHand()->getSmallBlind()*2,
1818 				curGame->getPlayerByUniqueId(curGame->getCurrentHand()->getCurrentBeRo()->getBigBlindPositionId())->getMyID()+1,
1819 				curGame->getSeatsList()
1820 			);
1821 		}
1822 
1823 		// Stop the timeout for the player.
1824 		client->GetGui().stopTimeoutAnimation(tmpPlayer->getMyID());
1825 
1826 		// Unmark last player in GUI.
1827 		client->GetGui().refreshGroupbox(tmpPlayer->getMyID(), 3);
1828 
1829 		// Refresh GUI
1830 		if (tmpPlayer->getMyID() == 0)
1831 			client->GetGui().disableMyButtons();
1832 		client->GetGui().refreshAction(tmpPlayer->getMyID(), tmpPlayer->getMyAction());
1833 		client->GetGui().refreshPot();
1834 		client->GetGui().refreshSet();
1835 		client->GetGui().refreshCash();
1836 		client->GetGui().refreshButton();
1837 		client->GetGui().updateMyButtonsState();
1838 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_PlayersTurnMessage) {
1839 		const PlayersTurnMessage &netPlayersTurn = tmpPacket->GetMsg()->playersturnmessage();
1840 
1841 		boost::shared_ptr<PlayerInterface> tmpPlayer = curGame->getPlayerByUniqueId(netPlayersTurn.playerid());
1842 		if (!tmpPlayer)
1843 			throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1844 
1845 		// Set round.
1846 		if (curGame->getCurrentHand()->getCurrentRound() != static_cast<GameState>(netPlayersTurn.gamestate())) {
1847 			ResetPlayerActions(*curGame);
1848 			curGame->getCurrentHand()->setCurrentRound(static_cast<GameState>(netPlayersTurn.gamestate()));
1849 			client->GetClientLog()->setCurrentRound(static_cast<GameState>(netPlayersTurn.gamestate()));
1850 			// Refresh actions.
1851 			client->GetGui().refreshSet();
1852 			client->GetGui().refreshAction();
1853 		}
1854 
1855 		// Next player's turn.
1856 		curGame->getCurrentHand()->getCurrentBeRo()->setCurrentPlayersTurnId(tmpPlayer->getMyID());
1857 
1858 		// Mark current player in GUI.
1859 		int guiStatus = 2;
1860 		if (!tmpPlayer->getMyActiveStatus())
1861 			guiStatus = 0;
1862 		else if (tmpPlayer->getMyAction() == PLAYER_ACTION_FOLD)
1863 			guiStatus = 1;
1864 		client->GetGui().refreshGroupbox(tmpPlayer->getMyID(), guiStatus);
1865 		client->GetGui().refreshAction(tmpPlayer->getMyID(), PLAYER_ACTION_NONE);
1866 
1867 		// Start displaying the timeout for the player.
1868 		client->GetGui().startTimeoutAnimation(tmpPlayer->getMyID(), client->GetGameData().playerActionTimeoutSec);
1869 
1870 		if (tmpPlayer->getMyID() == 0) // Is this the GUI player?
1871 			client->GetGui().meInAction();
1872 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_DealFlopCardsMessage) {
1873 		const DealFlopCardsMessage &netDealFlop = tmpPacket->GetMsg()->dealflopcardsmessage();
1874 
1875 		int tmpCards[5];
1876 		tmpCards[0] = static_cast<int>(netDealFlop.flopcard1());
1877 		tmpCards[1] = static_cast<int>(netDealFlop.flopcard2());
1878 		tmpCards[2] = static_cast<int>(netDealFlop.flopcard3());
1879 		tmpCards[3] = tmpCards[4] = 0;
1880 		curGame->getCurrentHand()->getBoard()->setMyCards(tmpCards);
1881 		curGame->getCurrentHand()->getBoard()->collectPot();
1882 		curGame->getCurrentHand()->setPreviousPlayerID(-1);
1883 
1884 		client->GetGui().logDealBoardCardsMsg(GAME_STATE_FLOP, tmpCards[0], tmpCards[1], tmpCards[2], tmpCards[3], tmpCards[4]);
1885 		client->GetClientLog()->setCurrentRound(GAME_STATE_FLOP);
1886 		client->GetClientLog()->logBoardCards(tmpCards);
1887 		client->GetGui().refreshGameLabels(GAME_STATE_FLOP);
1888 		client->GetGui().refreshPot();
1889 		client->GetGui().refreshSet();
1890 		client->GetGui().dealBeRoCards(1);
1891 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_DealTurnCardMessage) {
1892 		const DealTurnCardMessage &netDealTurn = tmpPacket->GetMsg()->dealturncardmessage();
1893 
1894 		int tmpCards[5];
1895 		curGame->getCurrentHand()->getBoard()->getMyCards(tmpCards);
1896 		tmpCards[3] = static_cast<int>(netDealTurn.turncard());
1897 		curGame->getCurrentHand()->getBoard()->setMyCards(tmpCards);
1898 		curGame->getCurrentHand()->getBoard()->collectPot();
1899 		curGame->getCurrentHand()->setPreviousPlayerID(-1);
1900 
1901 		client->GetGui().logDealBoardCardsMsg(GAME_STATE_TURN, tmpCards[0], tmpCards[1], tmpCards[2], tmpCards[3], tmpCards[4]);
1902 		client->GetClientLog()->setCurrentRound(GAME_STATE_TURN);
1903 		client->GetClientLog()->logBoardCards(tmpCards);
1904 		client->GetGui().refreshGameLabels(GAME_STATE_TURN);
1905 		client->GetGui().refreshPot();
1906 		client->GetGui().refreshSet();
1907 		client->GetGui().dealBeRoCards(2);
1908 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_DealRiverCardMessage) {
1909 		const DealRiverCardMessage &netDealRiver = tmpPacket->GetMsg()->dealrivercardmessage();
1910 
1911 		int tmpCards[5];
1912 		curGame->getCurrentHand()->getBoard()->getMyCards(tmpCards);
1913 		tmpCards[4] = static_cast<int>(netDealRiver.rivercard());
1914 		curGame->getCurrentHand()->getBoard()->setMyCards(tmpCards);
1915 		curGame->getCurrentHand()->getBoard()->collectPot();
1916 		curGame->getCurrentHand()->setPreviousPlayerID(-1);
1917 
1918 		client->GetGui().logDealBoardCardsMsg(GAME_STATE_RIVER, tmpCards[0], tmpCards[1], tmpCards[2], tmpCards[3], tmpCards[4]);
1919 		client->GetClientLog()->setCurrentRound(GAME_STATE_RIVER);
1920 		client->GetClientLog()->logBoardCards(tmpCards);
1921 		client->GetGui().refreshGameLabels(GAME_STATE_RIVER);
1922 		client->GetGui().refreshPot();
1923 		client->GetGui().refreshSet();
1924 		client->GetGui().dealBeRoCards(3);
1925 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_AllInShowCardsMessage) {
1926 		const AllInShowCardsMessage &netAllInShow = tmpPacket->GetMsg()->allinshowcardsmessage();
1927 
1928 		curGame->getCurrentHand()->setAllInCondition(true);
1929 
1930 		// Set player numbers using the game start data slots.
1931 		unsigned numPlayers = netAllInShow.playersallin_size();
1932 		// Request player info for players if needed.
1933 		for (unsigned i = 0; i < numPlayers; i++) {
1934 			const AllInShowCardsMessage::PlayerAllIn &p = netAllInShow.playersallin(i);
1935 
1936 			boost::shared_ptr<PlayerInterface> tmpPlayer = curGame->getPlayerByUniqueId(p.playerid());
1937 			if (!tmpPlayer)
1938 				throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1939 
1940 			int tmpCards[2];
1941 			tmpCards[0] = static_cast<int>(p.allincard1());
1942 			tmpCards[1] = static_cast<int>(p.allincard2());
1943 			tmpPlayer->setMyCards(tmpCards);
1944 		}
1945 		client->GetGui().flipHolecardsAllIn();
1946 		if(curGame->getCurrentHand()->getCurrentRound()<GAME_STATE_RIVER) {
1947 			client->GetClientLog()->logHoleCardsHandName(
1948 				curGame->getActivePlayerList()
1949 			);
1950 		}
1951 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_EndOfHandHideCardsMessage) {
1952 		const EndOfHandHideCardsMessage &hideCards = tmpPacket->GetMsg()->endofhandhidecardsmessage();
1953 		curGame->getCurrentHand()->getBoard()->collectPot();
1954 		// Reset player sets
1955 		ResetPlayerSets(*curGame);
1956 		client->GetGui().refreshPot();
1957 		client->GetGui().refreshSet();
1958 		// Synchronize with GUI.
1959 		client->GetGui().waitForGuiUpdateDone();
1960 
1961 		// End of Hand, but keep cards hidden.
1962 		boost::shared_ptr<PlayerInterface> tmpPlayer = curGame->getPlayerByUniqueId(hideCards.playerid());
1963 		if (!tmpPlayer)
1964 			throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1965 
1966 		tmpPlayer->setMyCash(hideCards.playermoney());
1967 		tmpPlayer->setLastMoneyWon(hideCards.moneywon());
1968 		list<unsigned> winnerList;
1969 		winnerList.push_back(tmpPlayer->getMyUniqueID());
1970 
1971 		curGame->getCurrentHand()->getBoard()->setPot(0);
1972 		curGame->getCurrentHand()->getBoard()->setWinners(winnerList);
1973 
1974 		// logging
1975 		client->GetClientLog()->logHandWinner(curGame->getActivePlayerList(), tmpPlayer->getMyCardsValueInt(), winnerList);
1976 
1977 		client->GetGui().postRiverRunAnimation1();
1978 
1979 		// Wait for next Hand.
1980 		client->GetCallback().SignalNetClientGameInfo(MSG_NET_GAME_SERVER_HAND_END);
1981 		client->SetState(ClientStateWaitHand::Instance());
1982 
1983 		// logging
1984 		client->GetClientLog()->logPlayerSitsOut(curGame->getActivePlayerList());
1985 		client->GetClientLog()->logGameWinner(curGame->getActivePlayerList());
1986 		client->GetClientLog()->logAfterHand();
1987 	} else if (tmpPacket->GetMsg()->messagetype() == PokerTHMessage::Type_EndOfHandShowCardsMessage) {
1988 		const EndOfHandShowCardsMessage &showCards = tmpPacket->GetMsg()->endofhandshowcardsmessage();
1989 
1990 		curGame->getCurrentHand()->getBoard()->collectPot();
1991 		// Reset player sets
1992 		ResetPlayerSets(*curGame);
1993 		client->GetGui().refreshPot();
1994 		client->GetGui().refreshSet();
1995 		// Synchronize with GUI.
1996 		client->GetGui().waitForGuiUpdateDone();
1997 
1998 		// End of Hand, show cards.
1999 		list<unsigned> winnerList;
2000 		list<unsigned> showList;
2001 		int highestValueOfCards = 0;
2002 		unsigned numResults = showCards.playerresults_size();
2003 		// Request player info for players if needed.
2004 		for (unsigned i = 0; i < numResults; i++) {
2005 			const PlayerResult &r = showCards.playerresults(i);
2006 
2007 			boost::shared_ptr<PlayerInterface> tmpPlayer = curGame->getPlayerByUniqueId(r.playerid());
2008 			if (!tmpPlayer)
2009 				throw ClientException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
2010 
2011 			int tmpCards[2];
2012 			int bestHandPos[5];
2013 			tmpCards[0] = static_cast<int>(r.resultcard1());
2014 			tmpCards[1] = static_cast<int>(r.resultcard2());
2015 			tmpPlayer->setMyCards(tmpCards);
2016 			for (int num = 0; num < 5; num++) {
2017 				bestHandPos[num] = r.besthandposition(num);
2018 			}
2019 			if (r.has_cardsvalue()) {
2020 				tmpPlayer->setMyCardsValueInt(r.cardsvalue());
2021 			}
2022 			tmpPlayer->setMyBestHandPosition(bestHandPos);
2023 			if (tmpPlayer->getMyCardsValueInt() > highestValueOfCards)
2024 				highestValueOfCards = tmpPlayer->getMyCardsValueInt();
2025 			tmpPlayer->setMyCash(r.playermoney());
2026 			tmpPlayer->setLastMoneyWon(r.moneywon());
2027 			if (r.moneywon())
2028 				winnerList.push_back(r.playerid());
2029 			showList.push_back(r.playerid());
2030 		}
2031 
2032 		curGame->getCurrentHand()->setCurrentRound(GAME_STATE_POST_RIVER);
2033 		client->GetClientLog()->setCurrentRound(GAME_STATE_POST_RIVER);
2034 		curGame->getCurrentHand()->getCurrentBeRo()->setHighestCardsValue(highestValueOfCards);
2035 		curGame->getCurrentHand()->getBoard()->setPot(0);
2036 		curGame->getCurrentHand()->getBoard()->setWinners(winnerList);
2037 		curGame->getCurrentHand()->getBoard()->setPlayerNeedToShowCards(showList);
2038 
2039 		// logging
2040 		client->GetClientLog()->logHoleCardsHandName(curGame->getActivePlayerList());
2041 		client->GetClientLog()->logHandWinner(curGame->getActivePlayerList(), highestValueOfCards, winnerList);
2042 
2043 		client->GetGui().postRiverRunAnimation1();
2044 
2045 		// Wait for next Hand.
2046 		client->GetCallback().SignalNetClientGameInfo(MSG_NET_GAME_CLIENT_HAND_END);
2047 		client->SetState(ClientStateWaitHand::Instance());
2048 
2049 		// logging
2050 		client->GetClientLog()->logPlayerSitsOut(curGame->getActivePlayerList());
2051 		client->GetClientLog()->logGameWinner(curGame->getActivePlayerList());
2052 		client->GetClientLog()->logAfterHand();
2053 	}
2054 
2055 	// Synchronize with GUI.
2056 	client->GetGui().waitForGuiUpdateDone();
2057 }
2058 
2059 void
ResetPlayerActions(Game & curGame)2060 ClientStateRunHand::ResetPlayerActions(Game &curGame)
2061 {
2062 	// Reset player actions
2063 	PlayerListIterator i = curGame.getSeatsList()->begin();
2064 	PlayerListIterator end = curGame.getSeatsList()->end();
2065 
2066 	while (i != end) {
2067 		int action = (*i)->getMyAction();
2068 		if (action != 1 && action != 6)
2069 			(*i)->setMyAction(PLAYER_ACTION_NONE);
2070 		(*i)->setMySetNull();
2071 		++i;
2072 	}
2073 }
2074 
2075 void
ResetPlayerSets(Game & curGame)2076 ClientStateRunHand::ResetPlayerSets(Game &curGame)
2077 {
2078 	PlayerListIterator i = curGame.getSeatsList()->begin();
2079 	PlayerListIterator end = curGame.getSeatsList()->end();
2080 	while (i != end) {
2081 		(*i)->setMySetNull();
2082 		++i;
2083 	}
2084 }
2085 
2086 //-----------------------------------------------------------------------------
2087 
2088 ClientStateFinal &
Instance()2089 ClientStateFinal::Instance()
2090 {
2091 	static ClientStateFinal state;
2092 	return state;
2093 }
2094