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