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/servergamestate.h>
33 #include <net/servergame.h>
34 #include <net/serverlobbythread.h>
35 #include <net/senderhelper.h>
36 #include <net/netpacket.h>
37 #include <net/socket_msg.h>
38 #include <net/serverexception.h>
39 #include <net/net_helper.h>
40 #include <net/chatcleanermanager.h>
41 #include <db/serverdbinterface.h>
42 #include <core/loghelper.h>
43 #include <core/avatarmanager.h>
44 #include <gamedata.h>
45 #include <game.h>
46 #include <playerinterface.h>
47 #include <handinterface.h>
48 
49 #include <boost/bind.hpp>
50 
51 #include <sstream>
52 
53 using namespace std;
54 
55 #ifdef BOOST_ASIO_HAS_STD_CHRONO
56 using namespace std::chrono;
57 #else
58 using namespace boost::chrono;
59 #endif
60 
61 //#define POKERTH_SERVER_TEST
62 
63 #ifdef POKERTH_SERVER_TEST
64 #define SERVER_DELAY_NEXT_GAME_SEC				0
65 #define SERVER_DEAL_FLOP_CARDS_DELAY_SEC		0
66 #define SERVER_DEAL_TURN_CARD_DELAY_SEC			0
67 #define SERVER_DEAL_RIVER_CARD_DELAY_SEC		0
68 #define SERVER_DEAL_ADD_ALL_IN_DELAY_SEC		0
69 #define SERVER_SHOW_CARDS_DELAY_SEC				0
70 #define SERVER_COMPUTER_ACTION_DELAY_SEC		0
71 #define SERVER_PLAYER_TIMEOUT_ADD_DELAY_SEC		1
72 #else
73 #define SERVER_DELAY_NEXT_GAME_SEC				10
74 #define SERVER_DEAL_FLOP_CARDS_DELAY_SEC		5
75 #define SERVER_DEAL_TURN_CARD_DELAY_SEC			2
76 #define SERVER_DEAL_RIVER_CARD_DELAY_SEC		2
77 #define SERVER_DEAL_ADD_ALL_IN_DELAY_SEC		2
78 #define SERVER_SHOW_CARDS_DELAY_SEC				2
79 #define SERVER_COMPUTER_ACTION_DELAY_SEC		2
80 #define SERVER_PLAYER_TIMEOUT_ADD_DELAY_SEC		2
81 #endif
82 
83 #define SERVER_START_GAME_TIMEOUT_SEC				10
84 #define SERVER_AUTOSTART_GAME_DELAY_SEC				6
85 #define SERVER_GAME_ADMIN_WARNING_REMAINING_SEC		60
86 #define SERVER_GAME_ADMIN_TIMEOUT_SEC				300		// 5 min, MUST be > SERVER_GAME_ADMIN_WARNING_REMAINING_SEC
87 #define SERVER_GAME_AUTOFOLD_TIMEOUT_FACTOR			30
88 #define SERVER_GAME_FORCED_TIMEOUT_FACTOR			60
89 #define SERVER_VOTE_KICK_TIMEOUT_SEC				30
90 #define SERVER_LOOP_DELAY_MSEC						50
91 #define SERVER_MAX_NUM_SPECTATORS_PER_GAME			100
92 
93 #define GAME_MAX_NUM_JOINS_PER_PLAYER				6
94 
95 // Helper functions
96 
SendPlayerAction(ServerGame & server,boost::shared_ptr<PlayerInterface> player)97 static void SendPlayerAction(ServerGame &server, boost::shared_ptr<PlayerInterface> player)
98 {
99 	if (!player.get())
100 		throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0);
101 
102 	boost::shared_ptr<NetPacket> packet(new NetPacket);
103 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_PlayersActionDoneMessage);
104 	PlayersActionDoneMessage *netActionDone = packet->GetMsg()->mutable_playersactiondonemessage();
105 
106 	netActionDone->set_gameid(server.GetId());
107 	netActionDone->set_gamestate(static_cast<NetGameState>(server.GetCurRound()));
108 	netActionDone->set_highestset(server.GetGame().getCurrentHand()->getCurrentBeRo()->getHighestSet());
109 	netActionDone->set_minimumraise(server.GetGame().getCurrentHand()->getCurrentBeRo()->getMinimumRaise());
110 	netActionDone->set_playeraction(static_cast<NetPlayerAction>(player->getMyAction()));
111 	netActionDone->set_playerid(player->getMyUniqueID());
112 	netActionDone->set_playermoney(player->getMyCash());
113 	netActionDone->set_totalplayerbet(player->getMySet());
114 	server.SendToAllPlayers(packet, SessionData::Game | SessionData::Spectating);
115 }
116 
SendNewRoundCards(ServerGame & server,Game & curGame,int state)117 static void SendNewRoundCards(ServerGame &server, Game &curGame, int state)
118 {
119 	int cards[5];
120 	curGame.getCurrentHand()->getBoard()->getMyCards(cards);
121 	switch(state) {
122 	case GAME_STATE_PREFLOP: {
123 		// nothing to do
124 	} break;
125 	case GAME_STATE_FLOP: {
126 		// deal flop cards
127 		boost::shared_ptr<NetPacket> packet(new NetPacket);
128 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_DealFlopCardsMessage);
129 		DealFlopCardsMessage *netDealFlop = packet->GetMsg()->mutable_dealflopcardsmessage();
130 		netDealFlop->set_gameid(server.GetId());
131 		netDealFlop->set_flopcard1(cards[0]);
132 		netDealFlop->set_flopcard2(cards[1]);
133 		netDealFlop->set_flopcard3(cards[2]);
134 		server.SendToAllPlayers(packet, SessionData::Game | SessionData::Spectating);
135 	}
136 	break;
137 	case GAME_STATE_TURN: {
138 		// deal turn card
139 		boost::shared_ptr<NetPacket> packet(new NetPacket);
140 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_DealTurnCardMessage);
141 		DealTurnCardMessage *netDealTurn = packet->GetMsg()->mutable_dealturncardmessage();
142 		netDealTurn->set_gameid(server.GetId());
143 		netDealTurn->set_turncard(cards[3]);
144 		server.SendToAllPlayers(packet, SessionData::Game | SessionData::Spectating);
145 	}
146 	break;
147 	case GAME_STATE_RIVER: {
148 		// deal river card
149 		boost::shared_ptr<NetPacket> packet(new NetPacket);
150 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_DealRiverCardMessage);
151 		DealRiverCardMessage *netDealRiver = packet->GetMsg()->mutable_dealrivercardmessage();
152 		netDealRiver->set_gameid(server.GetId());
153 		netDealRiver->set_rivercard(cards[4]);
154 		server.SendToAllPlayers(packet, SessionData::Game | SessionData::Spectating);
155 	}
156 	break;
157 	default: {
158 		//
159 	}
160 	}
161 }
162 
PerformPlayerAction(ServerGame & server,boost::shared_ptr<PlayerInterface> player,PlayerAction action,int bet)163 static void PerformPlayerAction(ServerGame &server, boost::shared_ptr<PlayerInterface> player, PlayerAction action, int bet)
164 {
165 	Game &curGame = server.GetGame();
166 	if (!player.get())
167 		throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0);
168 	player->setMyAction(action);
169 	// Only change the player bet if action is not fold/check
170 	if (action != PLAYER_ACTION_FOLD && action != PLAYER_ACTION_CHECK) {
171 
172 		player->setMySet(bet);
173 
174 		// update minimumRaise and lastActionPlayer
175 		switch(action) {
176 		case PLAYER_ACTION_BET: {
177 			curGame.getCurrentHand()->getCurrentBeRo()->setMinimumRaise(bet);
178 			curGame.getCurrentHand()->setLastActionPlayerID(player->getMyUniqueID());
179 		}
180 		break;
181 		case PLAYER_ACTION_RAISE: {
182 			curGame.getCurrentHand()->getCurrentBeRo()->setMinimumRaise(player->getMySet() - curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet());
183 			curGame.getCurrentHand()->setLastActionPlayerID(player->getMyUniqueID());
184 		}
185 		break;
186 		case PLAYER_ACTION_ALLIN: {
187 			if(player->getMySet() - curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet() > curGame.getCurrentHand()->getCurrentBeRo()->getMinimumRaise()) {
188 				curGame.getCurrentHand()->getCurrentBeRo()->setMinimumRaise(player->getMySet() - curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet());
189 			}
190 			if(player->getMySet() - curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet() > 0) {
191 				curGame.getCurrentHand()->setLastActionPlayerID(player->getMyUniqueID());
192 			}
193 		}
194 		break;
195 		default: {
196 		}
197 		}
198 
199 		// update highestSet
200 		if (player->getMySet() > curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet())
201 			curGame.getCurrentHand()->getCurrentBeRo()->setHighestSet(player->getMySet());
202 		// Update total sets.
203 		curGame.getCurrentHand()->getBoard()->collectSets();
204 	}
205 
206 	SendPlayerAction(server, player);
207 }
208 
209 static void
SetPlayerResult(PlayerResult & playerResult,boost::shared_ptr<PlayerInterface> tmpPlayer,GameState roundBeforePostRiver)210 SetPlayerResult(PlayerResult &playerResult, boost::shared_ptr<PlayerInterface> tmpPlayer, GameState roundBeforePostRiver)
211 {
212 	playerResult.set_playerid(tmpPlayer->getMyUniqueID());
213 	int tmpCards[2];
214 	int bestHandPos[5];
215 	tmpPlayer->getMyCards(tmpCards);
216 	playerResult.set_resultcard1(tmpCards[0]);
217 	playerResult.set_resultcard2(tmpCards[1]);
218 	tmpPlayer->getMyBestHandPosition(bestHandPos);
219 	for (int num = 0; num < 5; num++) {
220 		playerResult.add_besthandposition(bestHandPos[num]);
221 	}
222 
223 	if (roundBeforePostRiver == GAME_STATE_RIVER) {
224 		playerResult.set_cardsvalue(tmpPlayer->getMyCardsValueInt());
225 	}
226 	playerResult.set_moneywon(tmpPlayer->getLastMoneyWon());
227 	playerResult.set_playermoney(tmpPlayer->getMyCash());
228 }
229 
230 //-----------------------------------------------------------------------------
231 
~ServerGameState()232 ServerGameState::~ServerGameState()
233 {
234 }
235 
236 //-----------------------------------------------------------------------------
237 
~AbstractServerGameStateReceiving()238 AbstractServerGameStateReceiving::~AbstractServerGameStateReceiving()
239 {
240 }
241 
242 void
ProcessPacket(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session,boost::shared_ptr<NetPacket> packet)243 AbstractServerGameStateReceiving::ProcessPacket(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
244 {
245 	if (packet->IsClientActivity()) {
246 		session->ResetActivityTimer();
247 	}
248 	if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_PlayerInfoRequestMessage) {
249 		// Delegate to Lobby.
250 		server->GetLobbyThread().HandleGameRetrievePlayerInfo(session, packet->GetMsg()->playerinforequestmessage());
251 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AvatarRequestMessage) {
252 		// Delegate to Lobby.
253 		server->GetLobbyThread().HandleGameRetrieveAvatar(session, packet->GetMsg()->avatarrequestmessage());
254 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_LeaveGameRequestMessage) {
255 		server->MoveSessionToLobby(session, NTF_NET_REMOVED_ON_REQUEST);
256 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_KickPlayerRequestMessage) {
257 		// Only admins are allowed to kick, and only in the lobby.
258 		// After leaving the lobby, a vote needs to be initiated to kick.
259 		const KickPlayerRequestMessage &netKickRequest = packet->GetMsg()->kickplayerrequestmessage();
260 		if (session->GetPlayerData()->IsGameAdmin() && !server->IsRunning()
261 				&& netKickRequest.gameid() == server->GetId() && server->GetGameData().gameType != GAME_TYPE_RANKING) {
262 			server->KickPlayer(netKickRequest.playerid());
263 		}
264 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AskKickPlayerMessage) {
265 		if (server->GetGameData().gameType != GAME_TYPE_RANKING) {
266 			const AskKickPlayerMessage &netAskKick = packet->GetMsg()->askkickplayermessage();
267 			server->InternalAskVoteKick(session, netAskKick.playerid(), SERVER_VOTE_KICK_TIMEOUT_SEC);
268 		}
269 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_VoteKickRequestMessage) {
270 		const VoteKickRequestMessage &netVoteKick = packet->GetMsg()->votekickrequestmessage();
271 		server->InternalVoteKick(session, netVoteKick.petitionid(), netVoteKick.votekick() ? KICK_VOTE_IN_FAVOUR : KICK_VOTE_AGAINST);
272 	}
273 	// Chat text is always allowed.
274 	else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ChatRequestMessage) {
275 		bool chatSent = false;
276 		const ChatRequestMessage &netChatRequest = packet->GetMsg()->chatrequestmessage();
277 		// Only forward if this player is known and not a guest.
278 		if (session->GetPlayerData()->GetRights() != PLAYER_RIGHTS_GUEST) {
279 			// Forward chat text to all players.
280 			// TODO: Some limitation needed.
281 			if (!netChatRequest.has_targetgameid()) {
282 				if (!server->IsRunning()) {
283 					server->GetLobbyThread().HandleChatRequest(session, netChatRequest);
284 					chatSent = true;
285 				}
286 			} else {
287 				boost::shared_ptr<PlayerInterface> tmpPlayer (server->GetPlayerInterfaceFromGame(session->GetPlayerData()->GetUniqueId()));
288 				// If we did not find the player, then the game did not start yet. Allow chat for now.
289 				// Otherwise, check whether the player is muted.
290 				if (!tmpPlayer || !tmpPlayer->isMuted()) {
291 					boost::shared_ptr<NetPacket> packet(new NetPacket);
292 					packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatMessage);
293 					ChatMessage *netChat = packet->GetMsg()->mutable_chatmessage();
294 					netChat->set_gameid(server->GetId());
295 					netChat->set_playerid(session->GetPlayerData()->GetUniqueId());
296 					netChat->set_chattype(ChatMessage::chatTypeGame);
297 					netChat->set_chattext(netChatRequest.chattext());
298 					server->SendToAllPlayers(packet, SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
299 					chatSent = true;
300 
301 					// Send the message to the chat cleaner bot for ranking games.
302 					//if (server->GetGameData().gameType == GAME_TYPE_RANKING)
303 					//{
304 					server->GetLobbyThread().GetChatCleaner().HandleGameChatText(
305 						server->GetId(),
306 						session->GetPlayerData()->GetUniqueId(),
307 						session->GetPlayerData()->GetName(),
308 						netChatRequest.chattext());
309 					//}
310 				}
311 			}
312 		}
313 		// Reject chat otherwise.
314 		if (!chatSent) {
315 			boost::shared_ptr<NetPacket> packet(new NetPacket);
316 			packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ChatRejectMessage);
317 			ChatRejectMessage *netReject = packet->GetMsg()->mutable_chatrejectmessage();
318 			netReject->set_chattext(netChatRequest.chattext());
319 			server->GetLobbyThread().GetSender().Send(session, packet);
320 		}
321 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_SubscriptionRequestMessage) {
322 		const SubscriptionRequestMessage &netSubscription = packet->GetMsg()->subscriptionrequestmessage();
323 		if (netSubscription.subscriptionaction() == SubscriptionRequestMessage::resubscribeGameList) {
324 			if (!session->WantsLobbyMsg())
325 				server->GetLobbyThread().ResubscribeLobbyMsg(session);
326 		} else
327 			session->ResetWantsLobbyMsg();
328 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ReportAvatarMessage) {
329 		const ReportAvatarMessage &netReport = packet->GetMsg()->reportavatarmessage();
330 		boost::shared_ptr<PlayerData> tmpPlayer = server->GetPlayerDataByUniqueId(netReport.reportedplayerid());
331 		MD5Buf tmpMD5;
332 		memcpy(tmpMD5.GetData(), netReport.reportedavatarhash().data(), MD5_DATA_SIZE);
333 		if (tmpPlayer && tmpPlayer->GetDBId() && !tmpMD5.IsZero() && tmpPlayer->GetAvatarMD5() == tmpMD5) {
334 			if (!server->IsAvatarReported(tmpPlayer->GetUniqueId())) {
335 				// Temporarily note that this avatar was reported.
336 				// This prevents spamming of the avatar report.
337 				server->AddReportedAvatar(tmpPlayer->GetUniqueId());
338 				DB_id myDBid = session->GetPlayerData()->GetDBId();
339 				// Do not use the "game" database object, but the global one.
340 				// The entry should be created even if we are not running a
341 				// ranking game.
342 
343 				string tmpAvatarType;
344 				tmpAvatarType = AvatarManager::GetAvatarFileExtension(AvatarManager::GetAvatarFileType(tmpPlayer->GetAvatarFile()));
345 				if (!tmpAvatarType.empty())
346 					tmpAvatarType.erase(0, 1); // Only store extension without the "."
347 
348 				server->GetLobbyThread().GetDatabase()->AsyncReportAvatar(
349 					session->GetPlayerData()->GetUniqueId(),
350 					tmpPlayer->GetUniqueId(),
351 					tmpPlayer->GetDBId(),
352 					tmpPlayer->GetAvatarMD5().ToString(),
353 					tmpAvatarType,
354 					myDBid != 0 ? &myDBid : NULL
355 				);
356 			} else {
357 				boost::shared_ptr<NetPacket> packet(new NetPacket);
358 				packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ReportAvatarAckMessage);
359 				ReportAvatarAckMessage *netReportAck = packet->GetMsg()->mutable_reportavatarackmessage();
360 				netReportAck->set_reportedplayerid(netReport.reportedplayerid());
361 				netReportAck->set_reportavatarresult(ReportAvatarAckMessage::avatarReportDuplicate);
362 				server->GetLobbyThread().GetSender().Send(session, packet);
363 			}
364 		} else {
365 			boost::shared_ptr<NetPacket> packet(new NetPacket);
366 			packet->GetMsg()->set_messagetype(PokerTHMessage::Type_ReportAvatarAckMessage);
367 			ReportAvatarAckMessage *netReportAck = packet->GetMsg()->mutable_reportavatarackmessage();
368 			netReportAck->set_reportedplayerid(netReport.reportedplayerid());
369 			netReportAck->set_reportavatarresult(ReportAvatarAckMessage::avatarReportInvalid);
370 			server->GetLobbyThread().GetSender().Send(session, packet);
371 		}
372 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ReportGameMessage) {
373 		// Delegate to Lobby.
374 		server->GetLobbyThread().HandleGameReportGame(session, packet->GetMsg()->reportgamemessage());
375 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_JoinExistingGameMessage) {
376 		// Ignore join game requests in this state (may be multiple clicks from the lobby).
377 	} else {
378 		// Packet processing in subclass.
379 		InternalProcessPacket(server, session, packet);
380 	}
381 }
382 
383 boost::shared_ptr<NetPacket>
CreateNetPacketPlayerJoined(unsigned gameId,const PlayerData & playerData)384 AbstractServerGameStateReceiving::CreateNetPacketPlayerJoined(unsigned gameId, const PlayerData &playerData)
385 {
386 	boost::shared_ptr<NetPacket> packet(new NetPacket);
387 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GamePlayerJoinedMessage);
388 	GamePlayerJoinedMessage *netGamePlayer = packet->GetMsg()->mutable_gameplayerjoinedmessage();
389 	netGamePlayer->set_gameid(gameId);
390 	netGamePlayer->set_playerid(playerData.GetUniqueId());
391 	netGamePlayer->set_isgameadmin(playerData.IsGameAdmin());
392 	return packet;
393 }
394 
395 boost::shared_ptr<NetPacket>
CreateNetPacketSpectatorJoined(unsigned gameId,const PlayerData & playerData)396 AbstractServerGameStateReceiving::CreateNetPacketSpectatorJoined(unsigned gameId, const PlayerData &playerData)
397 {
398 	boost::shared_ptr<NetPacket> packet(new NetPacket);
399 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameSpectatorJoinedMessage);
400 	GameSpectatorJoinedMessage *netGameSpectator = packet->GetMsg()->mutable_gamespectatorjoinedmessage();
401 	netGameSpectator->set_gameid(gameId);
402 	netGameSpectator->set_playerid(playerData.GetUniqueId());
403 	return packet;
404 }
405 
406 boost::shared_ptr<NetPacket>
CreateNetPacketJoinGameAck(const ServerGame & server,const PlayerData & playerData,bool spectateOnly)407 AbstractServerGameStateReceiving::CreateNetPacketJoinGameAck(const ServerGame &server, const PlayerData &playerData, bool spectateOnly)
408 {
409 	boost::shared_ptr<NetPacket> packet(new NetPacket);
410 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_JoinGameAckMessage);
411 	JoinGameAckMessage *netJoinReply = packet->GetMsg()->mutable_joingameackmessage();
412 	netJoinReply->set_gameid(server.GetId());
413 	netJoinReply->set_areyougameadmin(playerData.IsGameAdmin());
414 	netJoinReply->set_spectateonly(spectateOnly);
415 
416 	NetGameInfo *gameInfo = netJoinReply->mutable_gameinfo();
417 	NetPacket::SetGameData(server.GetGameData(), *gameInfo);
418 	gameInfo->set_gamename(server.GetName());
419 	return packet;
420 }
421 
422 boost::shared_ptr<NetPacket>
CreateNetPacketHandStart(const ServerGame & server)423 AbstractServerGameStateReceiving::CreateNetPacketHandStart(const ServerGame &server)
424 {
425 	const Game &curGame = server.GetGame();
426 
427 	boost::shared_ptr<NetPacket> notifyCards(new NetPacket);
428 	notifyCards->GetMsg()->set_messagetype(PokerTHMessage::Type_HandStartMessage);
429 	HandStartMessage *netHandStart = notifyCards->GetMsg()->mutable_handstartmessage();
430 	netHandStart->set_gameid(server.GetId());
431 
432 	PlayerListIterator player_i = curGame.getSeatsList()->begin();
433 	PlayerListIterator player_end = curGame.getSeatsList()->end();
434 	int playerCounter = 0;
435 	while (player_i != player_end && playerCounter < server.GetStartData().numberOfPlayers) {
436 		NetPlayerState seatState;
437 		if (!(*player_i)->getMyActiveStatus()) {
438 			seatState = netPlayerStateNoMoney;
439 		} else if (!(*player_i)->isSessionActive()) {
440 			seatState = netPlayerStateSessionInactive;
441 		} else {
442 			seatState = netPlayerStateNormal;
443 		}
444 		netHandStart->add_seatstates(seatState);
445 		++player_i;
446 		++playerCounter;
447 	}
448 
449 	netHandStart->set_smallblind(curGame.getCurrentHand()->getSmallBlind());
450 	netHandStart->set_dealerplayerid(curGame.getCurrentHand()->getDealerPosition());
451 	return notifyCards;
452 }
453 
454 void
AcceptNewSession(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session,bool spectateOnly)455 AbstractServerGameStateReceiving::AcceptNewSession(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session, bool spectateOnly)
456 {
457 	// Set game admin, if applicable.
458 	session->GetPlayerData()->SetGameAdmin(session->GetPlayerData()->GetUniqueId() == server->GetAdminPlayerId());
459 
460 	// Send ack to client.
461 	server->GetLobbyThread().GetSender().Send(session, CreateNetPacketJoinGameAck(*server, *session->GetPlayerData(), spectateOnly));
462 
463 	// Send notifications for connected players to client.
464 	PlayerDataList tmpPlayerList(server->GetFullPlayerDataList());
465 	PlayerDataList::iterator player_i = tmpPlayerList.begin();
466 	PlayerDataList::iterator player_end = tmpPlayerList.end();
467 	while (player_i != player_end) {
468 		server->GetLobbyThread().GetSender().Send(session, CreateNetPacketPlayerJoined(server->GetId(), *(*player_i)));
469 		++player_i;
470 	}
471 
472 	// Send notifications for connected spectators to client.
473 	PlayerDataList tmpSpectatorList(server->GetSessionManager().GetSpectatorDataList());
474 	PlayerDataList::iterator spectator_i = tmpSpectatorList.begin();
475 	PlayerDataList::iterator spectator_end = tmpSpectatorList.end();
476 	while (spectator_i != spectator_end) {
477 		server->GetLobbyThread().GetSender().Send(session, CreateNetPacketSpectatorJoined(server->GetId(), *(*spectator_i)));
478 		++spectator_i;
479 	}
480 
481 	// Send "Player Joined"/"Spectator Joined" to other fully connected clients.
482 	if (spectateOnly) {
483 		server->SendToAllPlayers(CreateNetPacketSpectatorJoined(server->GetId(), *session->GetPlayerData()), SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
484 	} else {
485 		server->SendToAllPlayers(CreateNetPacketPlayerJoined(server->GetId(), *session->GetPlayerData()), SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
486 	}
487 
488 	// Accept session.
489 	server->GetSessionManager().AddSession(session);
490 
491 	// Notify lobby.
492 	if (spectateOnly) {
493 		server->GetLobbyThread().NotifySpectatorJoinedGame(server->GetId(), session->GetPlayerData()->GetUniqueId());
494 	} else {
495 		server->GetLobbyThread().NotifyPlayerJoinedGame(server->GetId(), session->GetPlayerData()->GetUniqueId());
496 	}
497 }
498 
499 //-----------------------------------------------------------------------------
500 
501 ServerGameStateInit ServerGameStateInit::s_state;
502 
503 ServerGameStateInit &
Instance()504 ServerGameStateInit::Instance()
505 {
506 	return s_state;
507 }
508 
ServerGameStateInit()509 ServerGameStateInit::ServerGameStateInit()
510 {
511 }
512 
~ServerGameStateInit()513 ServerGameStateInit::~ServerGameStateInit()
514 {
515 }
516 
517 void
Enter(boost::shared_ptr<ServerGame> server)518 ServerGameStateInit::Enter(boost::shared_ptr<ServerGame> server)
519 {
520 	RegisterAdminTimer(server);
521 }
522 
523 void
Exit(boost::shared_ptr<ServerGame> server)524 ServerGameStateInit::Exit(boost::shared_ptr<ServerGame> server)
525 {
526 	UnregisterAdminTimer(server);
527 	UnregisterAutoStartTimer(server);
528 }
529 
530 void
NotifyGameAdminChanged(boost::shared_ptr<ServerGame> server)531 ServerGameStateInit::NotifyGameAdminChanged(boost::shared_ptr<ServerGame> server)
532 {
533 	UnregisterAdminTimer(server);
534 	RegisterAdminTimer(server);
535 }
536 
537 void
NotifySessionRemoved(boost::shared_ptr<ServerGame> server)538 ServerGameStateInit::NotifySessionRemoved(boost::shared_ptr<ServerGame> server)
539 {
540 	UnregisterAutoStartTimer(server);
541 }
542 
543 void
HandleNewPlayer(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session)544 ServerGameStateInit::HandleNewPlayer(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session)
545 {
546 	if (session && session->GetPlayerData()) {
547 		const GameData &tmpGameData = server->GetGameData();
548 
549 		// Check the number of players and number of joins per player
550 		if (server->GetCurNumberOfPlayers() >= tmpGameData.maxNumberOfPlayers || server->GetNumJoinsPerPlayer(session->GetPlayerData()->GetName()) > GAME_MAX_NUM_JOINS_PER_PLAYER) {
551 			server->MoveSessionToLobby(session, NTF_NET_REMOVED_GAME_FULL);
552 		} else {
553 			// add player to NumJoinsPerPlayerMap
554 			server->AddPlayerToNumJoinsPerPlayer(session->GetPlayerData()->GetName());
555 
556 			AcceptNewSession(server, session, false);
557 
558 			if (server->GetCurNumberOfPlayers() == tmpGameData.maxNumberOfPlayers) {
559 				// Automatically start the game if it is full.
560 				RegisterAutoStartTimer(server);
561 			}
562 		}
563 	}
564 }
565 
566 void
HandleNewSpectator(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session)567 ServerGameStateInit::HandleNewSpectator(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session)
568 {
569 	if (session && session->GetPlayerData()) {
570 		if (server->GetSpectatorIdList().size() >= SERVER_MAX_NUM_SPECTATORS_PER_GAME) {
571 			server->MoveSessionToLobby(session, NTF_NET_REMOVED_GAME_FULL);
572 		} else {
573 			AcceptNewSession(server, session, true);
574 		}
575 	}
576 }
577 
578 void
RegisterAdminTimer(boost::shared_ptr<ServerGame> server)579 ServerGameStateInit::RegisterAdminTimer(boost::shared_ptr<ServerGame> server)
580 {
581 	// No admin timeout in LAN or ranking games.
582 	if (server->GetLobbyThread().GetServerMode() != SERVER_MODE_LAN && server->GetGameData().gameType != GAME_TYPE_RANKING) {
583 		server->GetStateTimer1().expires_from_now(
584 			seconds(SERVER_GAME_ADMIN_TIMEOUT_SEC - SERVER_GAME_ADMIN_WARNING_REMAINING_SEC));
585 		server->GetStateTimer1().async_wait(
586 			boost::bind(
587 				&ServerGameStateInit::TimerAdminWarning, this, boost::asio::placeholders::error, server));
588 	}
589 }
590 
591 void
UnregisterAdminTimer(boost::shared_ptr<ServerGame> server)592 ServerGameStateInit::UnregisterAdminTimer(boost::shared_ptr<ServerGame> server)
593 {
594 	server->GetStateTimer1().cancel();
595 }
596 
597 void
RegisterAutoStartTimer(boost::shared_ptr<ServerGame> server)598 ServerGameStateInit::RegisterAutoStartTimer(boost::shared_ptr<ServerGame> server)
599 {
600 	// No autostart in LAN games.
601 	if (server->GetLobbyThread().GetServerMode() != SERVER_MODE_LAN) {
602 		server->GetStateTimer2().expires_from_now(
603 			seconds(SERVER_AUTOSTART_GAME_DELAY_SEC));
604 		server->GetStateTimer2().async_wait(
605 			boost::bind(
606 				&ServerGameStateInit::TimerAutoStart, this, boost::asio::placeholders::error, server));
607 	}
608 }
609 
610 void
UnregisterAutoStartTimer(boost::shared_ptr<ServerGame> server)611 ServerGameStateInit::UnregisterAutoStartTimer(boost::shared_ptr<ServerGame> server)
612 {
613 	server->GetStateTimer2().cancel();
614 }
615 
616 void
TimerAutoStart(const boost::system::error_code & ec,boost::shared_ptr<ServerGame> server)617 ServerGameStateInit::TimerAutoStart(const boost::system::error_code &ec, boost::shared_ptr<ServerGame> server)
618 {
619 	if (!ec && &server->GetState() == this) {
620 		SendStartEvent(*server, false);
621 	}
622 }
623 
624 void
TimerAdminWarning(const boost::system::error_code & ec,boost::shared_ptr<ServerGame> server)625 ServerGameStateInit::TimerAdminWarning(const boost::system::error_code &ec, boost::shared_ptr<ServerGame> server)
626 {
627 	if (!ec && &server->GetState() == this) {
628 		// Find game admin.
629 		boost::shared_ptr<SessionData> session = server->GetSessionManager().GetSessionByUniquePlayerId(server->GetAdminPlayerId());
630 		if (session) {
631 			// Send him a warning.
632 			boost::shared_ptr<NetPacket> packet(new NetPacket);
633 			packet->GetMsg()->set_messagetype(PokerTHMessage::Type_TimeoutWarningMessage);
634 			TimeoutWarningMessage *netWarning = packet->GetMsg()->mutable_timeoutwarningmessage();
635 			netWarning->set_timeoutreason(TimeoutWarningMessage::timeoutInactiveGame);
636 			netWarning->set_remainingseconds(SERVER_GAME_ADMIN_WARNING_REMAINING_SEC);
637 			server->GetLobbyThread().GetSender().Send(session, packet);
638 		}
639 		// Start timeout timer.
640 		server->GetStateTimer1().expires_from_now(
641 			seconds(SERVER_GAME_ADMIN_WARNING_REMAINING_SEC));
642 		server->GetStateTimer1().async_wait(
643 			boost::bind(
644 				&ServerGameStateInit::TimerAdminTimeout, this, boost::asio::placeholders::error, server));
645 	}
646 }
647 
648 void
TimerAdminTimeout(const boost::system::error_code & ec,boost::shared_ptr<ServerGame> server)649 ServerGameStateInit::TimerAdminTimeout(const boost::system::error_code &ec, boost::shared_ptr<ServerGame> server)
650 {
651 	if (!ec && &server->GetState() == this) {
652 		// Find game admin.
653 		boost::shared_ptr<SessionData> session = server->GetSessionManager().GetSessionByUniquePlayerId(server->GetAdminPlayerId());
654 		if (session) {
655 			// Remove him from the game.
656 			server->MoveSessionToLobby(session, NTF_NET_REMOVED_TIMEOUT);
657 		}
658 	}
659 }
660 
661 void
SendStartEvent(ServerGame & server,bool fillWithComputerPlayers)662 ServerGameStateInit::SendStartEvent(ServerGame &server, bool fillWithComputerPlayers)
663 {
664 	if (fillWithComputerPlayers) {
665 		int remainingSlots = server.GetGameData().maxNumberOfPlayers - server.GetCurNumberOfPlayers();
666 		for (int i = 1; i <= remainingSlots; i++) {
667 			boost::shared_ptr<PlayerData> tmpPlayerData(
668 				new PlayerData(server.GetLobbyThread().GetNextUniquePlayerId(), 0, PLAYER_TYPE_COMPUTER, PLAYER_RIGHTS_NORMAL, false));
669 
670 			ostringstream name;
671 			name << SERVER_COMPUTER_PLAYER_NAME << i;
672 			tmpPlayerData->SetName(name.str());
673 			server.AddComputerPlayer(tmpPlayerData);
674 
675 			// Send "Player Joined" to other fully connected clients.
676 			server.SendToAllPlayers(CreateNetPacketPlayerJoined(server.GetId(), *tmpPlayerData), SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
677 
678 			// Notify lobby.
679 			server.GetLobbyThread().NotifyPlayerJoinedGame(server.GetId(), tmpPlayerData->GetUniqueId());
680 		}
681 	}
682 	boost::shared_ptr<NetPacket> packet(new NetPacket);
683 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_StartEventMessage);
684 	StartEventMessage *netStartEvent = packet->GetMsg()->mutable_starteventmessage();
685 	netStartEvent->set_starteventtype(StartEventMessage::startEvent);
686 	netStartEvent->set_gameid(server.GetId());
687 	netStartEvent->set_fillwithcomputerplayers(fillWithComputerPlayers);
688 
689 	// Wait for all players to confirm start of game.
690 	server.SendToAllPlayers(packet, SessionData::Game);
691 	// Notify lobby that this game is running.
692 	server.GetLobbyThread().NotifyStartingGame(server.GetId());
693 
694 	server.SetState(ServerGameStateStartGame::Instance());
695 }
696 
697 void
InternalProcessPacket(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session,boost::shared_ptr<NetPacket> packet)698 ServerGameStateInit::InternalProcessPacket(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
699 {
700 	if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_StartEventMessage) {
701 		const StartEventMessage &netStartEvent = packet->GetMsg()->starteventmessage();
702 		// Only admins are allowed to start the game.
703 		if (session->GetPlayerData()->IsGameAdmin()
704 				&& netStartEvent.gameid() == server->GetId()
705 				&& netStartEvent.starteventtype() == StartEventMessage::startEvent
706 				&& (server->GetGameData().gameType != GAME_TYPE_RANKING // ranking games need to be full
707 					|| server->GetGameData().maxNumberOfPlayers == server->GetCurNumberOfPlayers())) {
708 			SendStartEvent(*server, netStartEvent.fillwithcomputerplayers());
709 		} else { // kick players who try to start but are not allowed to
710 			server->MoveSessionToLobby(session, NTF_NET_REMOVED_START_FAILED);
711 		}
712 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_InvitePlayerToGameMessage) {
713 		const InvitePlayerToGameMessage &netInvite = packet->GetMsg()->inviteplayertogamemessage();
714 
715 		// Only invite players which are not already within the group.
716 		if (netInvite.gameid() == server->GetId() && !server->IsPlayerConnected(netInvite.playerid())) {
717 			boost::shared_ptr<NetPacket> packet(new NetPacket);
718 			packet->GetMsg()->set_messagetype(PokerTHMessage::Type_InviteNotifyMessage);
719 			InviteNotifyMessage *netInvNotif = packet->GetMsg()->mutable_invitenotifymessage();
720 			netInvNotif->set_gameid(netInvite.gameid());
721 			netInvNotif->set_playeridbywhom(session->GetPlayerData()->GetUniqueId());
722 			netInvNotif->set_playeridwho(netInvite.playerid());
723 
724 			bool requestSent = server->GetLobbyThread().SendToLobbyPlayer(netInvite.playerid(), packet);
725 			server->SendToAllPlayers(packet, SessionData::Game);
726 			if (requestSent) {
727 				// This player has been invited.
728 				server->AddPlayerInvitation(netInvite.playerid());
729 			} else {
730 				// Player is not in lobby - send reject message.
731 				boost::shared_ptr<NetPacket> p2(new NetPacket);
732 				p2->GetMsg()->set_messagetype(PokerTHMessage::Type_RejectInvNotifyMessage);
733 				RejectInvNotifyMessage *netRejNotif = p2->GetMsg()->mutable_rejectinvnotifymessage();
734 				netRejNotif->set_gameid(netInvite.gameid());
735 				netRejNotif->set_playerid(netInvite.playerid());
736 				netRejNotif->set_playerrejectreason(RejectGameInvitationMessage::rejectReasonBusy);
737 
738 				server->SendToAllPlayers(p2, SessionData::Game);
739 			}
740 		}
741 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ResetTimeoutMessage) {
742 		if (session->GetPlayerData()->IsGameAdmin()) {
743 			RegisterAdminTimer(server);
744 		}
745 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AdminRemoveGameMessage) {
746 		server->GetLobbyThread().HandleAdminRemoveGame(session, packet->GetMsg()->adminremovegamemessage());
747 	} else if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_AdminBanPlayerMessage) {
748 		server->GetLobbyThread().HandleAdminBanPlayer(session, packet->GetMsg()->adminbanplayermessage());
749 	} else {
750 		server->SessionError(session, ERR_SOCK_INVALID_PACKET);
751 	}
752 }
753 
754 //-----------------------------------------------------------------------------
755 
756 ServerGameStateStartGame ServerGameStateStartGame::s_state;
757 
758 ServerGameStateStartGame &
Instance()759 ServerGameStateStartGame::Instance()
760 {
761 	return s_state;
762 }
763 
ServerGameStateStartGame()764 ServerGameStateStartGame::ServerGameStateStartGame()
765 {
766 }
767 
~ServerGameStateStartGame()768 ServerGameStateStartGame::~ServerGameStateStartGame()
769 {
770 }
771 
772 void
Enter(boost::shared_ptr<ServerGame> server)773 ServerGameStateStartGame::Enter(boost::shared_ptr<ServerGame> server)
774 {
775 	server->GetStateTimer1().expires_from_now(
776 		seconds(SERVER_START_GAME_TIMEOUT_SEC));
777 	server->GetStateTimer1().async_wait(
778 		boost::bind(
779 			&ServerGameStateStartGame::TimerTimeout, this, boost::asio::placeholders::error, server));
780 }
781 
782 void
Exit(boost::shared_ptr<ServerGame> server)783 ServerGameStateStartGame::Exit(boost::shared_ptr<ServerGame> server)
784 {
785 	server->GetStateTimer1().cancel();
786 }
787 
788 void
HandleNewPlayer(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session)789 ServerGameStateStartGame::HandleNewPlayer(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session)
790 {
791 	// Do not accept new sessions in this state.
792 	server->MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING);
793 }
794 
795 void
HandleNewSpectator(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session)796 ServerGameStateStartGame::HandleNewSpectator(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session)
797 {
798 	if (session && session->GetPlayerData()) {
799 		AcceptNewSession(server, session, true);
800 	}
801 }
802 
803 void
InternalProcessPacket(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session,boost::shared_ptr<NetPacket> packet)804 ServerGameStateStartGame::InternalProcessPacket(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
805 {
806 	if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_StartEventAckMessage) {
807 		session->SetReadyFlag();
808 		if (server->GetSessionManager().CountReadySessions() == server->GetSessionManager().GetSessionCountWithState(SessionData::Game)) {
809 			// Everyone is ready.
810 			server->GetSessionManager().ResetAllReadyFlags();
811 			DoStart(server);
812 		}
813 	}
814 }
815 
816 void
TimerTimeout(const boost::system::error_code & ec,boost::shared_ptr<ServerGame> server)817 ServerGameStateStartGame::TimerTimeout(const boost::system::error_code &ec, boost::shared_ptr<ServerGame> server)
818 {
819 	if (!ec && &server->GetState() == this) {
820 		// On timeout: start anyway.
821 		server->GetSessionManager().ResetAllReadyFlags();
822 		// TODO report successful start! -> new callback?!
823 		//retVal = MSG_SOCK_INIT_DONE;
824 		DoStart(server);
825 	}
826 }
827 
828 void
DoStart(boost::shared_ptr<ServerGame> server)829 ServerGameStateStartGame::DoStart(boost::shared_ptr<ServerGame> server)
830 {
831 	PlayerDataList tmpPlayerList(server->InternalStartGame());
832 	if (tmpPlayerList.size() <= 1) {
833 		if (!tmpPlayerList.empty()) {
834 			boost::shared_ptr<PlayerData> tmpPlayer(tmpPlayerList.front());
835 			boost::shared_ptr<SessionData> tmpSession = server->GetSessionManager().GetSessionByUniquePlayerId(tmpPlayer->GetUniqueId());
836 			if (tmpSession)
837 				server->MoveSessionToLobby(tmpSession, NTF_NET_REMOVED_START_FAILED);
838 		}
839 	} else {
840 		boost::shared_ptr<NetPacket> packet(new NetPacket);
841 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameStartInitialMessage);
842 		GameStartInitialMessage *netGameStart = packet->GetMsg()->mutable_gamestartinitialmessage();
843 		netGameStart->set_gameid(server->GetId());
844 		netGameStart->set_startdealerplayerid(server->GetStartData().startDealerPlayerId);
845 
846 		// Send player order to clients.
847 		// Assume player list is sorted by number.
848 		PlayerDataList::iterator player_i = tmpPlayerList.begin();
849 		PlayerDataList::iterator player_end = tmpPlayerList.end();
850 		while (player_i != player_end) {
851 			netGameStart->add_playerseats((*player_i)->GetUniqueId());
852 			++player_i;
853 		}
854 
855 		server->SendToAllPlayers(packet, SessionData::Game | SessionData::Spectating);
856 
857 		// Start the first hand.
858 		ServerGameStateHand::StartNewHand(server);
859 		server->SetState(ServerGameStateHand::Instance());
860 	}
861 }
862 
863 //-----------------------------------------------------------------------------
864 
~AbstractServerGameStateRunning()865 AbstractServerGameStateRunning::~AbstractServerGameStateRunning()
866 {
867 }
868 
869 void
HandleNewPlayer(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session)870 AbstractServerGameStateRunning::HandleNewPlayer(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session)
871 {
872 
873 	// Verify that the user is allowed to rejoin.
874 	if (session && session->GetPlayerData()) {
875 		boost::shared_ptr<PlayerInterface> tmpPlayer = server->GetPlayerInterfaceFromGame(session->GetPlayerData()->GetName());
876 		if (tmpPlayer && tmpPlayer->getMyGuid() == session->GetPlayerData()->GetOldGuid()) {
877 			// The player wants to rejoin.
878 			AcceptNewSession(server, session, false);
879 			// Remember: We need to initiate a rejoin when starting the next hand.
880 			server->AddRejoinPlayer(session->GetPlayerData()->GetUniqueId());
881 
882 			// Send start event right away.
883 			boost::shared_ptr<NetPacket> packet(new NetPacket);
884 			packet->GetMsg()->set_messagetype(PokerTHMessage::Type_StartEventMessage);
885 			StartEventMessage *netStartEvent = packet->GetMsg()->mutable_starteventmessage();
886 			netStartEvent->set_starteventtype(StartEventMessage::rejoinEvent);
887 			netStartEvent->set_gameid(server->GetId());
888 
889 			// Wait for rejoining player to confirm start of game.
890 			server->GetLobbyThread().GetSender().Send(session, packet);
891 		} else {
892 			// Do not accept "new" sessions in this state, only rejoin is allowed.
893 			server->MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING);
894 		}
895 	}
896 }
897 
898 void
HandleNewSpectator(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session)899 AbstractServerGameStateRunning::HandleNewSpectator(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session)
900 {
901 	if (session && session->GetPlayerData()) {
902 		AcceptNewSession(server, session, true);
903 		session->SetState(SessionData::SpectatorWaiting);
904 		server->AddNewSpectator(session->GetPlayerData()->GetUniqueId());
905 	}
906 }
907 
908 void
InternalProcessPacket(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session,boost::shared_ptr<NetPacket> packet)909 AbstractServerGameStateRunning::InternalProcessPacket(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
910 {
911 	if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ResetTimeoutMessage) {
912 		// Reactivate session.
913 		server->AddReactivatePlayer(session->GetPlayerData()->GetUniqueId());
914 	}
915 }
916 
917 //-----------------------------------------------------------------------------
918 
919 ServerGameStateHand ServerGameStateHand::s_state;
920 
921 ServerGameStateHand &
Instance()922 ServerGameStateHand::Instance()
923 {
924 	return s_state;
925 }
926 
ServerGameStateHand()927 ServerGameStateHand::ServerGameStateHand()
928 {
929 }
930 
~ServerGameStateHand()931 ServerGameStateHand::~ServerGameStateHand()
932 {
933 }
934 
935 void
Enter(boost::shared_ptr<ServerGame> server)936 ServerGameStateHand::Enter(boost::shared_ptr<ServerGame> server)
937 {
938 	server->GetStateTimer1().expires_from_now(
939 		milliseconds(SERVER_LOOP_DELAY_MSEC));
940 	server->GetStateTimer1().async_wait(
941 		boost::bind(
942 			&ServerGameStateHand::TimerLoop, this, boost::asio::placeholders::error, server));
943 }
944 
945 void
Exit(boost::shared_ptr<ServerGame> server)946 ServerGameStateHand::Exit(boost::shared_ptr<ServerGame> server)
947 {
948 	server->GetStateTimer1().cancel();
949 }
950 
951 void
InternalProcessPacket(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session,boost::shared_ptr<NetPacket> packet)952 ServerGameStateHand::InternalProcessPacket(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
953 {
954 	AbstractServerGameStateRunning::InternalProcessPacket(server, session, packet);
955 }
956 
957 void
TimerLoop(const boost::system::error_code & ec,boost::shared_ptr<ServerGame> server)958 ServerGameStateHand::TimerLoop(const boost::system::error_code &ec, boost::shared_ptr<ServerGame> server)
959 {
960 	if (!ec && &server->GetState() == this) {
961 		try {
962 			EngineLoop(server);
963 		} catch (const PokerTHException &e) {
964 			LOG_ERROR("Game " << server->GetId() << " - Engine exception: " << e.what());
965 			server->RemoveAllSessions(); // Close this game on error.
966 		}
967 	}
968 }
969 
970 void
EngineLoop(boost::shared_ptr<ServerGame> server)971 ServerGameStateHand::EngineLoop(boost::shared_ptr<ServerGame> server)
972 {
973 	Game &curGame = server->GetGame();
974 
975 	// Main game loop.
976 	int curRound = curGame.getCurrentHand()->getCurrentRound();
977 	curGame.getCurrentHand()->switchRounds();
978 	if (!curGame.getCurrentHand()->getAllInCondition())
979 		curGame.getCurrentHand()->getCurrentBeRo()->run();
980 	int newRound = curGame.getCurrentHand()->getCurrentRound();
981 
982 	// If round changes, deal cards if needed.
983 	if (newRound != curRound && newRound != GAME_STATE_POST_RIVER) {
984 		if (newRound <= curRound)
985 			throw ServerException(__FILE__, __LINE__, ERR_NET_INVALID_GAME_ROUND, 0);
986 
987 		// Retrieve non-fold players. If only one player is left, no cards are shown.
988 		list<boost::shared_ptr<PlayerInterface> > nonFoldPlayers = *curGame.getActivePlayerList();
989 		nonFoldPlayers.remove_if(boost::bind(&PlayerInterface::getMyAction, _1) == PLAYER_ACTION_FOLD);
990 
991 		if (curGame.getCurrentHand()->getAllInCondition()
992 				&& !curGame.getCurrentHand()->getCardsShown()
993 				&& nonFoldPlayers.size() > 1) {
994 			// Send cards of all active players to all players (all in).
995 			boost::shared_ptr<NetPacket> allIn(new NetPacket);
996 			allIn->GetMsg()->set_messagetype(PokerTHMessage::Type_AllInShowCardsMessage);
997 			AllInShowCardsMessage *netAllInShow = allIn->GetMsg()->mutable_allinshowcardsmessage();
998 			netAllInShow->set_gameid(server->GetId());
999 
1000 			PlayerListConstIterator i = nonFoldPlayers.begin();
1001 			PlayerListConstIterator end = nonFoldPlayers.end();
1002 
1003 			while (i != end) {
1004 				AllInShowCardsMessage::PlayerAllIn *playerAllIn = netAllInShow->add_playersallin();
1005 				playerAllIn->set_playerid((*i)->getMyUniqueID());
1006 				int tmpCards[2];
1007 				(*i)->getMyCards(tmpCards);
1008 				playerAllIn->set_allincard1(tmpCards[0]);
1009 				playerAllIn->set_allincard2(tmpCards[1]);
1010 				++i;
1011 			}
1012 			server->SendToAllPlayers(allIn, SessionData::Game | SessionData::Spectating);
1013 			curGame.getCurrentHand()->setCardsShown(true);
1014 
1015 			server->GetStateTimer1().expires_from_now(
1016 				seconds(SERVER_SHOW_CARDS_DELAY_SEC));
1017 			server->GetStateTimer1().async_wait(
1018 				boost::bind(
1019 					&ServerGameStateHand::TimerShowCards, this, boost::asio::placeholders::error, server));
1020 		} else {
1021 			SendNewRoundCards(*server, curGame, newRound);
1022 
1023 			server->GetStateTimer1().expires_from_now(
1024 				seconds(GetDealCardsDelaySec(*server)));
1025 			server->GetStateTimer1().async_wait(
1026 				boost::bind(
1027 					&ServerGameStateHand::TimerLoop, this, boost::asio::placeholders::error, server));
1028 		}
1029 	} else {
1030 		if (newRound != GAME_STATE_POST_RIVER) { // continue hand
1031 			if (curGame.getCurrentHand()->getAllInCondition())
1032 				throw ServerException(__FILE__, __LINE__, ERR_NET_INTERNAL_GAME_ERROR, 0);
1033 
1034 			// Retrieve current player.
1035 			boost::shared_ptr<PlayerInterface> curPlayer = curGame.getCurrentPlayer();
1036 			if (!curPlayer.get())
1037 				throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0);
1038 			if (!curPlayer->getMyActiveStatus())
1039 				throw ServerException(__FILE__, __LINE__, ERR_NET_PLAYER_NOT_ACTIVE, 0);
1040 
1041 			boost::shared_ptr<NetPacket> notification(new NetPacket);
1042 			notification->GetMsg()->set_messagetype(PokerTHMessage::Type_PlayersTurnMessage);
1043 			PlayersTurnMessage *netPlayersTurn = notification->GetMsg()->mutable_playersturnmessage();
1044 			netPlayersTurn->set_gameid(server->GetId());
1045 			netPlayersTurn->set_gamestate(static_cast<NetGameState>(curGame.getCurrentHand()->getCurrentRound()));
1046 			netPlayersTurn->set_playerid(curPlayer->getMyUniqueID());
1047 			server->SendToAllPlayers(notification, SessionData::Game | SessionData::Spectating);
1048 
1049 			// If the player is computer controlled, let the engine act.
1050 			if (curPlayer->getMyType() == PLAYER_TYPE_COMPUTER) {
1051 				server->GetStateTimer1().expires_from_now(
1052 					seconds(SERVER_COMPUTER_ACTION_DELAY_SEC));
1053 				server->GetStateTimer1().async_wait(
1054 					boost::bind(
1055 						&ServerGameStateHand::TimerComputerAction, this, boost::asio::placeholders::error, server));
1056 			} else {
1057 				// If the player we are waiting for left, continue without him.
1058 				if (!server->GetSessionManager().IsPlayerConnected(curPlayer->getMyUniqueID())
1059 						|| !curPlayer->isSessionActive()) {
1060 					PerformPlayerAction(*server, curPlayer, PLAYER_ACTION_FOLD, 0);
1061 
1062 					server->GetStateTimer1().expires_from_now(
1063 						milliseconds(SERVER_LOOP_DELAY_MSEC));
1064 					server->GetStateTimer1().async_wait(
1065 						boost::bind(
1066 							&ServerGameStateHand::TimerLoop, this, boost::asio::placeholders::error, server));
1067 				} else {
1068 					server->SetState(ServerGameStateWaitPlayerAction::Instance());
1069 				}
1070 			}
1071 		} else { // hand is over
1072 			// Engine will find out who won.
1073 			curGame.getCurrentHand()->getCurrentBeRo()->postRiverRun();
1074 
1075 			// Retrieve non-fold players. If only one player is left, no cards are shown.
1076 			list<boost::shared_ptr<PlayerInterface> > nonFoldPlayers = *curGame.getActivePlayerList();
1077 			nonFoldPlayers.remove_if(boost::bind(&PlayerInterface::getMyAction, _1) == PLAYER_ACTION_FOLD);
1078 
1079 			if (nonFoldPlayers.size() == 1) {
1080 				// End of Hand, but keep cards hidden.
1081 				boost::shared_ptr<PlayerInterface> player = nonFoldPlayers.front();
1082 				boost::shared_ptr<NetPacket> endHand(new NetPacket);
1083 				endHand->GetMsg()->set_messagetype(PokerTHMessage::Type_EndOfHandHideCardsMessage);
1084 				EndOfHandHideCardsMessage *netEndHand = endHand->GetMsg()->mutable_endofhandhidecardsmessage();
1085 				netEndHand->set_gameid(server->GetId());
1086 				netEndHand->set_playerid(player->getMyUniqueID());
1087 				netEndHand->set_moneywon(player->getLastMoneyWon());
1088 				netEndHand->set_playermoney(player->getMyCash());
1089 				server->SendToAllPlayers(endHand, SessionData::Game | SessionData::Spectating);
1090 			} else {
1091 				// End of Hand - show cards.
1092 				const PlayerIdList showList(curGame.getCurrentHand()->getBoard()->getPlayerNeedToShowCards());
1093 				boost::shared_ptr<NetPacket> endHand(new NetPacket);
1094 				endHand->GetMsg()->set_messagetype(PokerTHMessage::Type_EndOfHandShowCardsMessage);
1095 				EndOfHandShowCardsMessage *netEndHand = endHand->GetMsg()->mutable_endofhandshowcardsmessage();
1096 				netEndHand->set_gameid(server->GetId());
1097 
1098 				PlayerIdList::const_iterator i = showList.begin();
1099 				PlayerIdList::const_iterator end = showList.end();
1100 
1101 				while (i != end) {
1102 					boost::shared_ptr<PlayerInterface> tmpPlayer(curGame.getPlayerByUniqueId(*i));
1103 					if (tmpPlayer) {
1104 						PlayerResult *playerResult = netEndHand->add_playerresults();
1105 						SetPlayerResult(*playerResult, tmpPlayer, GAME_STATE_RIVER);
1106 					}
1107 					++i;
1108 				}
1109 				server->SendToAllPlayers(endHand, SessionData::Game | SessionData::Spectating);
1110 			}
1111 
1112 			// Remove disconnected players. This is the one and only place to do this.
1113 			server->RemoveDisconnectedPlayers();
1114 
1115 			// Update rankings of all remaining players
1116 			server->UpdateRankingMap();
1117 
1118 			// Start next hand - if enough players are left.
1119 			list<boost::shared_ptr<PlayerInterface> > playersWithCash = *curGame.getActivePlayerList();
1120 			playersWithCash.remove_if(boost::bind(&PlayerInterface::getMyCash, _1) < 1);
1121 
1122 			if (playersWithCash.empty()) {
1123 				// No more players left - restart.
1124 				server->SetState(SERVER_INITIAL_STATE::Instance());
1125 			} else if (playersWithCash.size() == 1) {
1126 				boost::shared_ptr<PlayerInterface> winnerPlayer = *(playersWithCash.begin());
1127 				server->InternalEndGame();
1128 
1129 				// View a dialog for a new game - delayed.
1130 				server->GetStateTimer1().expires_from_now(
1131 					seconds(SERVER_DELAY_NEXT_GAME_SEC));
1132 				server->GetStateTimer1().async_wait(
1133 					boost::bind(
1134 						&ServerGameStateHand::TimerNextGame, this, boost::asio::placeholders::error, server, winnerPlayer->getMyUniqueID()));
1135 			} else {
1136 				server->SetState(ServerGameStateWaitNextHand::Instance());
1137 			}
1138 		}
1139 	}
1140 }
1141 
1142 void
TimerShowCards(const boost::system::error_code & ec,boost::shared_ptr<ServerGame> server)1143 ServerGameStateHand::TimerShowCards(const boost::system::error_code &ec, boost::shared_ptr<ServerGame> server)
1144 {
1145 	if (!ec && &server->GetState() == this) {
1146 		Game &curGame = server->GetGame();
1147 		SendNewRoundCards(*server, curGame, curGame.getCurrentHand()->getCurrentRound());
1148 
1149 		server->GetStateTimer1().expires_from_now(
1150 			seconds(GetDealCardsDelaySec(*server)));
1151 		server->GetStateTimer1().async_wait(
1152 			boost::bind(
1153 				&ServerGameStateHand::TimerLoop, this, boost::asio::placeholders::error, server));
1154 	}
1155 }
1156 
1157 void
TimerComputerAction(const boost::system::error_code & ec,boost::shared_ptr<ServerGame> server)1158 ServerGameStateHand::TimerComputerAction(const boost::system::error_code &ec, boost::shared_ptr<ServerGame> server)
1159 {
1160 	if (!ec && &server->GetState() == this) {
1161 		try {
1162 			boost::shared_ptr<PlayerInterface> curPlayer = server->GetGame().getCurrentPlayer();
1163 			if (!curPlayer)
1164 				throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0);
1165 
1166 			curPlayer->action();
1167 			SendPlayerAction(*server, curPlayer);
1168 			EngineLoop(server);
1169 		} catch (const PokerTHException &e) {
1170 			LOG_ERROR("Game " << server->GetId() << " - Computer timer exception: " << e.what());
1171 			server->RemoveAllSessions(); // Close this game on error.
1172 		}
1173 	}
1174 }
1175 
1176 void
TimerNextHand(const boost::system::error_code & ec,boost::shared_ptr<ServerGame> server)1177 ServerGameStateHand::TimerNextHand(const boost::system::error_code &ec, boost::shared_ptr<ServerGame> server)
1178 {
1179 	if (!ec && &server->GetState() == this) {
1180 		StartNewHand(server);
1181 		TimerLoop(ec, server);
1182 	}
1183 }
1184 
1185 void
TimerNextGame(const boost::system::error_code & ec,boost::shared_ptr<ServerGame> server,unsigned winnerPlayerId)1186 ServerGameStateHand::TimerNextGame(const boost::system::error_code &ec, boost::shared_ptr<ServerGame> server, unsigned winnerPlayerId)
1187 {
1188 	if (!ec && &server->GetState() == this) {
1189 		boost::shared_ptr<NetPacket> endGame(new NetPacket);
1190 		endGame->GetMsg()->set_messagetype(PokerTHMessage::Type_EndOfGameMessage);
1191 		EndOfGameMessage *netEndGame = endGame->GetMsg()->mutable_endofgamemessage();
1192 		netEndGame->set_gameid(server->GetId());
1193 		netEndGame->set_winnerplayerid(winnerPlayerId);
1194 		server->SendToAllPlayers(endGame, SessionData::Game | SessionData::Spectating);
1195 
1196 		// Wait for the start of a new game.
1197 		server->RemoveAutoLeavePlayers();
1198 		server->ResetComputerPlayerList();
1199 		server->GetLobbyThread().NotifyReopeningGame(server->GetId());
1200 		server->SetState(ServerGameStateInit::Instance());
1201 	}
1202 }
1203 
1204 int
GetDealCardsDelaySec(ServerGame & server)1205 ServerGameStateHand::GetDealCardsDelaySec(ServerGame &server)
1206 {
1207 	Game &curGame = server.GetGame();
1208 	int allInDelay = curGame.getCurrentHand()->getAllInCondition() ? SERVER_DEAL_ADD_ALL_IN_DELAY_SEC : 0;
1209 	int delay = 0;
1210 
1211 	switch(curGame.getCurrentHand()->getCurrentRound()) {
1212 	case GAME_STATE_FLOP:
1213 		delay = SERVER_DEAL_FLOP_CARDS_DELAY_SEC;
1214 		break;
1215 	case GAME_STATE_TURN:
1216 		delay = SERVER_DEAL_TURN_CARD_DELAY_SEC + allInDelay;
1217 		break;
1218 	case GAME_STATE_RIVER:
1219 		delay = SERVER_DEAL_RIVER_CARD_DELAY_SEC;
1220 		break;
1221 	default:
1222 		break;
1223 	}
1224 	return delay;
1225 }
1226 
1227 void
StartNewHand(boost::shared_ptr<ServerGame> server)1228 ServerGameStateHand::StartNewHand(boost::shared_ptr<ServerGame> server)
1229 {
1230 	Game &curGame = server->GetGame();
1231 
1232 	// Reactivate players which were previously inactive.
1233 	ReactivatePlayers(server);
1234 
1235 	// Initialize rejoining players.
1236 	// This has to be done before initialising the new hand, because there are side effects.
1237 	InitRejoiningPlayers(server);
1238 
1239 	// Initialize new spectators.
1240 	InitNewSpectators(server);
1241 
1242 	// Kick inactive players.
1243 	CheckPlayerTimeouts(server);
1244 
1245 	// Initialize hand.
1246 	curGame.initHand();
1247 
1248 	// HACK: Skip GUI notification run
1249 	curGame.getCurrentHand()->getFlop()->skipFirstRunGui();
1250 	curGame.getCurrentHand()->getTurn()->skipFirstRunGui();
1251 	curGame.getCurrentHand()->getRiver()->skipFirstRunGui();
1252 
1253 	// Consider all players, even inactive.
1254 	PlayerListIterator i = curGame.getSeatsList()->begin();
1255 	PlayerListIterator end = curGame.getSeatsList()->end();
1256 
1257 	// Send cards to all players.
1258 	while (i != end) {
1259 		// Also send to inactive players.
1260 		boost::shared_ptr<PlayerInterface> tmpPlayer = *i;
1261 		boost::shared_ptr<SessionData> tmpSession = server->GetSessionManager().GetSessionByUniquePlayerId(tmpPlayer->getMyUniqueID());
1262 		if (tmpSession) {
1263 			int cards[2];
1264 			bool errorFlag = false;
1265 			tmpPlayer->getMyCards(cards);
1266 
1267 			boost::shared_ptr<NetPacket> notifyCards = CreateNetPacketHandStart(*server);
1268 			HandStartMessage *netHandStart = notifyCards->GetMsg()->mutable_handstartmessage();
1269 			string tmpPassword(tmpSession->AuthGetPassword());
1270 			if (tmpPassword.empty()) { // encrypt only if password is present
1271 				HandStartMessage::PlainCards *plainCards = netHandStart->mutable_plaincards();
1272 				plainCards->set_plaincard1(cards[0]);
1273 				plainCards->set_plaincard2(cards[1]);
1274 			} else {
1275 				ostringstream cardDataStream;
1276 				vector<unsigned char> tmpCipher;
1277 				cardDataStream
1278 						<< tmpPlayer->getMyUniqueID() << " "
1279 						<< server->GetId() << " "
1280 						<< curGame.getCurrentHandID() << " "
1281 						<< cards[0] << " "
1282 						<< cards[1];
1283 				if (CryptHelper::AES128Encrypt((const unsigned char *)tmpPassword.c_str(),
1284 											   (unsigned)tmpPassword.size(),
1285 											   cardDataStream.str(),
1286 											   tmpCipher)
1287 						&& !tmpCipher.empty()) {
1288 					netHandStart->set_encryptedcards((const char *)&tmpCipher[0], tmpCipher.size());
1289 				} else {
1290 					server->RemovePlayer(tmpPlayer->getMyUniqueID(), ERR_SOCK_INVALID_STATE);
1291 					errorFlag = true;
1292 				}
1293 			}
1294 
1295 			if (!errorFlag) {
1296 				server->GetLobbyThread().GetSender().Send(tmpSession, notifyCards);
1297 			}
1298 		}
1299 		++i;
1300 	}
1301 	server->SendToAllPlayers(CreateNetPacketHandStart(*server), SessionData::Spectating);
1302 
1303 	// Start hand.
1304 	curGame.startHand();
1305 
1306 	// Auto small blind / big blind at the beginning of hand.
1307 	i = curGame.getActivePlayerList()->begin();
1308 	end = curGame.getActivePlayerList()->end();
1309 
1310 	while (i != end) {
1311 		boost::shared_ptr<PlayerInterface> tmpPlayer = *i;
1312 		if (tmpPlayer->getMyButton() == BUTTON_SMALL_BLIND) {
1313 			boost::shared_ptr<NetPacket> notifySmallBlind(new NetPacket);
1314 			notifySmallBlind->GetMsg()->set_messagetype(PokerTHMessage::Type_PlayersActionDoneMessage);
1315 			PlayersActionDoneMessage *netSmallBlind = notifySmallBlind->GetMsg()->mutable_playersactiondonemessage();
1316 			netSmallBlind->set_gameid(server->GetId());
1317 			netSmallBlind->set_gamestate(netStatePreflopSmallBlind);
1318 			netSmallBlind->set_playerid(tmpPlayer->getMyUniqueID());
1319 			netSmallBlind->set_playeraction(static_cast<NetPlayerAction>(tmpPlayer->getMyAction()));
1320 			netSmallBlind->set_totalplayerbet(tmpPlayer->getMySet());
1321 			netSmallBlind->set_playermoney(tmpPlayer->getMyCash());
1322 			netSmallBlind->set_highestset(server->GetGame().getCurrentHand()->getCurrentBeRo()->getHighestSet());
1323 			netSmallBlind->set_minimumraise(server->GetGame().getCurrentHand()->getCurrentBeRo()->getMinimumRaise());
1324 			server->SendToAllPlayers(notifySmallBlind, SessionData::Game | SessionData::Spectating);
1325 			break;
1326 		}
1327 		++i;
1328 	}
1329 
1330 	i = curGame.getActivePlayerList()->begin();
1331 	end = curGame.getActivePlayerList()->end();
1332 	while (i != end) {
1333 		boost::shared_ptr<PlayerInterface> tmpPlayer = *i;
1334 		if (tmpPlayer->getMyButton() == BUTTON_BIG_BLIND) {
1335 			boost::shared_ptr<NetPacket> notifyBigBlind(new NetPacket);
1336 			notifyBigBlind->GetMsg()->set_messagetype(PokerTHMessage::Type_PlayersActionDoneMessage);
1337 			PlayersActionDoneMessage *netBigBlind = notifyBigBlind->GetMsg()->mutable_playersactiondonemessage();
1338 			netBigBlind->set_gameid(server->GetId());
1339 			netBigBlind->set_gamestate(netStatePreflopBigBlind);
1340 			netBigBlind->set_playerid(tmpPlayer->getMyUniqueID());
1341 			netBigBlind->set_playeraction(static_cast<NetPlayerAction>(tmpPlayer->getMyAction()));
1342 			netBigBlind->set_totalplayerbet(tmpPlayer->getMySet());
1343 			netBigBlind->set_playermoney(tmpPlayer->getMyCash());
1344 			netBigBlind->set_highestset(server->GetGame().getCurrentHand()->getCurrentBeRo()->getHighestSet());
1345 			netBigBlind->set_minimumraise(server->GetGame().getCurrentHand()->getCurrentBeRo()->getMinimumRaise());
1346 			server->SendToAllPlayers(notifyBigBlind, SessionData::Game | SessionData::Spectating);
1347 			break;
1348 		}
1349 		++i;
1350 	}
1351 }
1352 
1353 void
CheckPlayerTimeouts(boost::shared_ptr<ServerGame> server)1354 ServerGameStateHand::CheckPlayerTimeouts(boost::shared_ptr<ServerGame> server)
1355 {
1356 	// Check timeout.
1357 	int actionTimeout = server->GetGameData().playerActionTimeoutSec;
1358 	if (actionTimeout) {
1359 		// Consider all active players.
1360 		PlayerListIterator i = server->GetGame().getActivePlayerList()->begin();
1361 		PlayerListIterator end = server->GetGame().getActivePlayerList()->end();
1362 
1363 		// Check timeouts of players.
1364 		while (i != end) {
1365 			boost::shared_ptr<PlayerInterface> tmpPlayer = *i;
1366 			if (tmpPlayer->getMyType() == PLAYER_TYPE_HUMAN
1367 					&& (int)tmpPlayer->getTimeSecSinceLastRemoteAction() >= actionTimeout * SERVER_GAME_AUTOFOLD_TIMEOUT_FACTOR) {
1368 				if (tmpPlayer->isSessionActive()) {
1369 					tmpPlayer->setIsSessionActive(false);
1370 					boost::shared_ptr<SessionData> session = server->GetSessionManager().GetSessionByUniquePlayerId(tmpPlayer->getMyUniqueID());
1371 					if (session) {
1372 						boost::shared_ptr<NetPacket> packet(new NetPacket);
1373 						packet->GetMsg()->set_messagetype(PokerTHMessage::Type_TimeoutWarningMessage);
1374 						TimeoutWarningMessage *netWarning = packet->GetMsg()->mutable_timeoutwarningmessage();
1375 						netWarning->set_timeoutreason(TimeoutWarningMessage::timeoutKickAfterAutofold);
1376 						netWarning->set_remainingseconds(actionTimeout * SERVER_GAME_FORCED_TIMEOUT_FACTOR - tmpPlayer->getTimeSecSinceLastRemoteAction());
1377 						server->GetLobbyThread().GetSender().Send(session, packet);
1378 					}
1379 				}
1380 				if ((int)tmpPlayer->getTimeSecSinceLastRemoteAction() >= actionTimeout * SERVER_GAME_FORCED_TIMEOUT_FACTOR) {
1381 					server->KickPlayer(tmpPlayer->getMyUniqueID());
1382 				}
1383 			}
1384 			++i;
1385 		}
1386 	}
1387 }
1388 
1389 void
ReactivatePlayers(boost::shared_ptr<ServerGame> server)1390 ServerGameStateHand::ReactivatePlayers(boost::shared_ptr<ServerGame> server)
1391 {
1392 	PlayerIdList reactivateIdList(server->GetAndResetReactivatePlayers());
1393 	PlayerIdList::iterator i = reactivateIdList.begin();
1394 	PlayerIdList::iterator end = reactivateIdList.end();
1395 	while (i != end) {
1396 		boost::shared_ptr<PlayerInterface> tmpPlayer(server->GetGame().getPlayerByUniqueId(*i));
1397 		if (tmpPlayer) {
1398 			tmpPlayer->markRemoteAction();
1399 			tmpPlayer->setIsSessionActive(true);
1400 		}
1401 		++i;
1402 	}
1403 }
1404 
1405 void
InitRejoiningPlayers(boost::shared_ptr<ServerGame> server)1406 ServerGameStateHand::InitRejoiningPlayers(boost::shared_ptr<ServerGame> server)
1407 {
1408 	PlayerIdList rejoinIdList(server->GetAndResetRejoinPlayers());
1409 	PlayerIdList::iterator i = rejoinIdList.begin();
1410 	PlayerIdList::iterator end = rejoinIdList.end();
1411 	while (i != end) {
1412 		boost::shared_ptr<SessionData> session(server->GetSessionManager().GetSessionByUniquePlayerId(*i));
1413 		if (session && session->GetPlayerData()) {
1414 			PerformRejoin(server, session);
1415 		}
1416 		++i;
1417 	}
1418 }
1419 
1420 void
InitNewSpectators(boost::shared_ptr<ServerGame> server)1421 ServerGameStateHand::InitNewSpectators(boost::shared_ptr<ServerGame> server)
1422 {
1423 	PlayerIdList spectatorIdList(server->GetAndResetNewSpectators());
1424 	PlayerIdList::iterator i = spectatorIdList.begin();
1425 	PlayerIdList::iterator end = spectatorIdList.end();
1426 	while (i != end) {
1427 		boost::shared_ptr<SessionData> session(server->GetSessionManager().GetSessionByUniquePlayerId(*i));
1428 		if (session && session->GetPlayerData()) {
1429 			session->SetState(SessionData::Spectating);
1430 			SendGameData(server, session);
1431 		}
1432 		++i;
1433 	}
1434 }
1435 
1436 void
PerformRejoin(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session)1437 ServerGameStateHand::PerformRejoin(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session)
1438 {
1439 	Game &curGame = server->GetGame();
1440 	// Set new player id.
1441 	boost::shared_ptr<PlayerInterface> rejoinPlayer = curGame.getPlayerByName(session->GetPlayerData()->GetName());
1442 	if (rejoinPlayer) {
1443 		// Notify other clients about id change.
1444 		boost::shared_ptr<NetPacket> packet(new NetPacket);
1445 		packet->GetMsg()->set_messagetype(PokerTHMessage::Type_PlayerIdChangedMessage);
1446 		PlayerIdChangedMessage *netIdChanged = packet->GetMsg()->mutable_playeridchangedmessage();
1447 		netIdChanged->set_oldplayerid(rejoinPlayer->getMyUniqueID());
1448 		netIdChanged->set_newplayerid(session->GetPlayerData()->GetUniqueId());
1449 		server->SendToAllButOnePlayers(packet, session->GetId(), SessionData::Game | SessionData::Spectating | SessionData::SpectatorWaiting);
1450 
1451 		// Update the dealer, if necessary.
1452 		curGame.replaceDealer(rejoinPlayer->getMyUniqueID(), session->GetPlayerData()->GetUniqueId());
1453 		// Update the ranking map.
1454 		server->ReplaceRankingPlayer(rejoinPlayer->getMyUniqueID(), session->GetPlayerData()->GetUniqueId());
1455 		// Change the Id in the poker engine.
1456 		rejoinPlayer->setMyUniqueID(session->GetPlayerData()->GetUniqueId());
1457 		rejoinPlayer->setMyGuid(session->GetPlayerData()->GetGuid());
1458 		rejoinPlayer->markRemoteAction();
1459 		rejoinPlayer->setIsSessionActive(true);
1460 		SendGameData(server, session);
1461 	} else {
1462 		server->SessionError(session, ERR_SOCK_INVALID_STATE);
1463 	}
1464 }
1465 
1466 void
SendGameData(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session)1467 ServerGameStateHand::SendGameData(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session)
1468 {
1469 	Game &curGame = server->GetGame();
1470 	// Send game start notification to rejoining client.
1471 	boost::shared_ptr<NetPacket> packet(new NetPacket);
1472 	packet->GetMsg()->set_messagetype(PokerTHMessage::Type_GameStartRejoinMessage);
1473 	GameStartRejoinMessage *netGameStart = packet->GetMsg()->mutable_gamestartrejoinmessage();
1474 	netGameStart->set_gameid(server->GetId());
1475 	netGameStart->set_startdealerplayerid(curGame.getDealerPosition());
1476 	netGameStart->set_handnum(curGame.getCurrentHandID());
1477 	PlayerListIterator player_i = curGame.getSeatsList()->begin();
1478 	PlayerListIterator player_end = curGame.getSeatsList()->end();
1479 	int player_count = 0;
1480 	while (player_i != player_end && player_count < server->GetStartData().numberOfPlayers) {
1481 		boost::shared_ptr<PlayerInterface> tmpPlayer = *player_i;
1482 		GameStartRejoinMessage::RejoinPlayerData *playerSlot = netGameStart->add_rejoinplayerdata();
1483 		playerSlot->set_playerid(tmpPlayer->getMyUniqueID());
1484 		playerSlot->set_playermoney(tmpPlayer->getMyCash());
1485 		++player_i;
1486 		++player_count;
1487 	}
1488 
1489 	server->GetLobbyThread().GetSender().Send(session, packet);
1490 }
1491 
1492 //-----------------------------------------------------------------------------
1493 
1494 
1495 ServerGameStateWaitPlayerAction ServerGameStateWaitPlayerAction::s_state;
1496 
1497 ServerGameStateWaitPlayerAction &
Instance()1498 ServerGameStateWaitPlayerAction::Instance()
1499 {
1500 	return s_state;
1501 }
1502 
ServerGameStateWaitPlayerAction()1503 ServerGameStateWaitPlayerAction::ServerGameStateWaitPlayerAction()
1504 {
1505 }
1506 
~ServerGameStateWaitPlayerAction()1507 ServerGameStateWaitPlayerAction::~ServerGameStateWaitPlayerAction()
1508 {
1509 }
1510 
1511 void
Enter(boost::shared_ptr<ServerGame> server)1512 ServerGameStateWaitPlayerAction::Enter(boost::shared_ptr<ServerGame> server)
1513 {
1514 	if (server->GetGameData().playerActionTimeoutSec > 0) { // zero means unlimited thinking time
1515 #ifdef POKERTH_SERVER_TEST
1516 		int timeoutSec = SERVER_PLAYER_TIMEOUT_ADD_DELAY_SEC;
1517 #else
1518 		int timeoutSec = server->GetGameData().playerActionTimeoutSec + SERVER_PLAYER_TIMEOUT_ADD_DELAY_SEC;
1519 #endif
1520 
1521 		server->GetStateTimer1().expires_from_now(seconds(timeoutSec));
1522 		server->GetStateTimer1().async_wait(
1523 			boost::bind(
1524 				&ServerGameStateWaitPlayerAction::TimerTimeout, this, boost::asio::placeholders::error, server));
1525 	}
1526 }
1527 
1528 void
Exit(boost::shared_ptr<ServerGame> server)1529 ServerGameStateWaitPlayerAction::Exit(boost::shared_ptr<ServerGame> server)
1530 {
1531 	server->GetStateTimer1().cancel();
1532 }
1533 
1534 void
InternalProcessPacket(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session,boost::shared_ptr<NetPacket> packet)1535 ServerGameStateWaitPlayerAction::InternalProcessPacket(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
1536 {
1537 	AbstractServerGameStateRunning::InternalProcessPacket(server, session, packet);
1538 
1539 	if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_MyActionRequestMessage) {
1540 		MyActionRequestMessage *netMyAction = packet->GetMsg()->mutable_myactionrequestmessage();
1541 
1542 		// TODO consider game id.
1543 		Game &curGame = server->GetGame();
1544 		boost::shared_ptr<PlayerInterface> tmpPlayer = curGame.getPlayerByUniqueId(session->GetPlayerData()->GetUniqueId());
1545 		if (!tmpPlayer)
1546 			throw ServerException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0);
1547 
1548 		// Check whether this is the correct round.
1549 		PlayerActionCode code = ACTION_CODE_VALID;
1550 		if (curGame.getCurrentHand()->getCurrentRound() != static_cast<GameState>(netMyAction->gamestate()))
1551 			code = ACTION_CODE_INVALID_STATE;
1552 
1553 		// Check whether this is the correct player.
1554 		boost::shared_ptr<PlayerInterface> curPlayer = server->GetGame().getCurrentPlayer();
1555 		if (code == ACTION_CODE_VALID
1556 				&& (curPlayer->getMyUniqueID() != tmpPlayer->getMyUniqueID())) {
1557 			code = ACTION_CODE_NOT_YOUR_TURN;
1558 		}
1559 
1560 		// If the client omitted some values, fill them in.
1561 		if (netMyAction->myaction() == netActionCall && netMyAction->myrelativebet() == 0) {
1562 			if (curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet() >= tmpPlayer->getMySet() + tmpPlayer->getMyCash())
1563 				netMyAction->set_myaction(netActionAllIn);
1564 			else
1565 				netMyAction->set_myrelativebet(curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet() - tmpPlayer->getMySet());
1566 		}
1567 		if (netMyAction->myaction() == netActionAllIn && netMyAction->myrelativebet() == 0)
1568 			netMyAction->set_myrelativebet(tmpPlayer->getMyCash());
1569 
1570 		// Check whether the action is valid.
1571 		if (code == ACTION_CODE_VALID
1572 				&& (tmpPlayer->checkMyAction(
1573 						netMyAction->myaction(),
1574 						netMyAction->myrelativebet(),
1575 						curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet(),
1576 						curGame.getCurrentHand()->getCurrentBeRo()->getMinimumRaise(),
1577 						curGame.getCurrentHand()->getSmallBlind()) != 0)) {
1578 			code = ACTION_CODE_NOT_ALLOWED;
1579 		}
1580 
1581 		if (code == ACTION_CODE_VALID) {
1582 			tmpPlayer->setIsSessionActive(true);
1583 			tmpPlayer->markRemoteAction();
1584 			PerformPlayerAction(*server, tmpPlayer, static_cast<PlayerAction>(netMyAction->myaction()), netMyAction->myrelativebet());
1585 			server->SetState(ServerGameStateHand::Instance());
1586 		} else {
1587 			// Send reject message.
1588 			boost::shared_ptr<NetPacket> reject(new NetPacket);
1589 			reject->GetMsg()->set_messagetype(PokerTHMessage::Type_YourActionRejectedMessage);
1590 			YourActionRejectedMessage *netActionRejected = reject->GetMsg()->mutable_youractionrejectedmessage();
1591 			netActionRejected->set_gameid(server->GetId());
1592 			netActionRejected->set_gamestate(netMyAction->gamestate());
1593 			netActionRejected->set_youraction(netMyAction->myaction());
1594 			netActionRejected->set_yourrelativebet(netMyAction->myrelativebet());
1595 			netActionRejected->set_rejectionreason(static_cast<YourActionRejectedMessage::RejectionReason>(code));
1596 			server->GetLobbyThread().GetSender().Send(session, reject);
1597 		}
1598 	}
1599 }
1600 
1601 void
TimerTimeout(const boost::system::error_code & ec,boost::shared_ptr<ServerGame> server)1602 ServerGameStateWaitPlayerAction::TimerTimeout(const boost::system::error_code &ec, boost::shared_ptr<ServerGame> server)
1603 {
1604 	if (!ec && &server->GetState() == this) {
1605 		try {
1606 			Game &curGame = server->GetGame();
1607 			// Retrieve current player.
1608 			boost::shared_ptr<PlayerInterface> curPlayer = curGame.getCurrentPlayer();
1609 			if (!curPlayer)
1610 				throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0);
1611 
1612 			// Player did not act fast enough. Act for him.
1613 			if (curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet() == curPlayer->getMySet())
1614 				PerformPlayerAction(*server, curPlayer, PLAYER_ACTION_CHECK, 0);
1615 			else
1616 				PerformPlayerAction(*server, curPlayer, PLAYER_ACTION_FOLD, 0);
1617 
1618 			server->SetState(ServerGameStateHand::Instance());
1619 		} catch (const PokerTHException &e) {
1620 			LOG_ERROR("Game " << server->GetId() << " - Player timer exception: " << e.what());
1621 			server->RemoveAllSessions(); // Close this game on error.
1622 		}
1623 	}
1624 }
1625 
1626 //-----------------------------------------------------------------------------
1627 
1628 
1629 ServerGameStateWaitNextHand ServerGameStateWaitNextHand::s_state;
1630 
1631 ServerGameStateWaitNextHand &
Instance()1632 ServerGameStateWaitNextHand::Instance()
1633 {
1634 	return s_state;
1635 }
1636 
ServerGameStateWaitNextHand()1637 ServerGameStateWaitNextHand::ServerGameStateWaitNextHand()
1638 {
1639 }
1640 
~ServerGameStateWaitNextHand()1641 ServerGameStateWaitNextHand::~ServerGameStateWaitNextHand()
1642 {
1643 }
1644 
1645 void
Enter(boost::shared_ptr<ServerGame> server)1646 ServerGameStateWaitNextHand::Enter(boost::shared_ptr<ServerGame> server)
1647 {
1648 #ifdef POKERTH_SERVER_TEST
1649 	int timeoutSec = 0;
1650 #else
1651 	int timeoutSec = server->GetGameData().delayBetweenHandsSec;
1652 #endif
1653 
1654 	server->GetStateTimer1().expires_from_now(
1655 		seconds(timeoutSec));
1656 
1657 	server->GetStateTimer1().async_wait(
1658 		boost::bind(
1659 			&ServerGameStateWaitNextHand::TimerTimeout, this, boost::asio::placeholders::error, server));
1660 }
1661 
1662 void
Exit(boost::shared_ptr<ServerGame> server)1663 ServerGameStateWaitNextHand::Exit(boost::shared_ptr<ServerGame> server)
1664 {
1665 	server->GetStateTimer1().cancel();
1666 }
1667 
1668 void
InternalProcessPacket(boost::shared_ptr<ServerGame> server,boost::shared_ptr<SessionData> session,boost::shared_ptr<NetPacket> packet)1669 ServerGameStateWaitNextHand::InternalProcessPacket(boost::shared_ptr<ServerGame> server, boost::shared_ptr<SessionData> session, boost::shared_ptr<NetPacket> packet)
1670 {
1671 	AbstractServerGameStateRunning::InternalProcessPacket(server, session, packet);
1672 
1673 	if (packet->GetMsg()->messagetype() == PokerTHMessage::Type_ShowMyCardsRequestMessage) {
1674 		Game &curGame = server->GetGame();
1675 		boost::shared_ptr<NetPacket> show(new NetPacket);
1676 		show->GetMsg()->set_messagetype(PokerTHMessage::Type_AfterHandShowCardsMessage);
1677 
1678 		AfterHandShowCardsMessage *netShowCards = show->GetMsg()->mutable_afterhandshowcardsmessage();
1679 		boost::shared_ptr<PlayerInterface> tmpPlayer(curGame.getPlayerByUniqueId(session->GetPlayerData()->GetUniqueId()));
1680 		if (tmpPlayer) {
1681 			SetPlayerResult(*netShowCards->mutable_playerresult(), tmpPlayer, curGame.getCurrentHand()->getRoundBeforePostRiver());
1682 			server->SendToAllPlayers(show, SessionData::Game | SessionData::Spectating);
1683 		}
1684 	}
1685 }
1686 
1687 void
TimerTimeout(const boost::system::error_code & ec,boost::shared_ptr<ServerGame> server)1688 ServerGameStateWaitNextHand::TimerTimeout(const boost::system::error_code &ec, boost::shared_ptr<ServerGame> server)
1689 {
1690 	if (!ec && &server->GetState() == this) {
1691 		ServerGameStateHand::StartNewHand(server);
1692 		server->SetState(ServerGameStateHand::Instance());
1693 	}
1694 }
1695 
1696 //-----------------------------------------------------------------------------
1697 
1698 ServerGameStateFinal ServerGameStateFinal::s_state;
1699 
1700 ServerGameStateFinal &
Instance()1701 ServerGameStateFinal::Instance()
1702 {
1703 	return s_state;
1704 }
1705 
1706 //-----------------------------------------------------------------------------
1707 
1708