From 08ed5e04965940167e3b1a2e91baa9ca675ffac4 Mon Sep 17 00:00:00 2001 From: lotodore Date: Sat, 30 May 2009 22:20:40 +0000 Subject: [PATCH] Almost complete rewrite of server game state due to porting the network engine to asio. Server is still broken in a lot of ways. A game is no longer run in a separate thread (but this change is still incomplete). --- src/net/common/servergamestate.cpp | 1489 +++++++++++--------------- src/net/common/servergamethread.cpp | 119 +- src/net/common/serverlobbythread.cpp | 46 +- src/net/servergamestate.h | 339 +----- src/net/servergamethread.h | 36 +- src/net/serverlobbythread.h | 1 + 6 files changed, 759 insertions(+), 1271 deletions(-) diff --git a/src/net/common/servergamestate.cpp b/src/net/common/servergamestate.cpp index 6542b12f..731192a4 100644 --- a/src/net/common/servergamestate.cpp +++ b/src/net/common/servergamestate.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2007 by Lothar May * + * Copyright (C) 2007-2009 by Lothar May * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -64,6 +64,7 @@ using namespace std; #define SERVER_GAME_ADMIN_WARNING_REMAINING_SEC 60 #define SERVER_GAME_ADMIN_TIMEOUT_SEC 300 // 5 min, MUST be > SERVER_GAME_ADMIN_WARNING_REMAINING_SEC #define SERVER_VOTE_KICK_TIMEOUT_SEC 30 +#define SERVER_LOOP_DELAY_MSEC 20 // Helper functions // TODO: these are hacks. @@ -129,490 +130,7 @@ static void SendNewRoundCards(ServerGameThread &server, Game &curGame, int state } } -//----------------------------------------------------------------------------- - -ServerGameState::~ServerGameState() -{ -} - -//----------------------------------------------------------------------------- - -AbstractServerGameStateReceiving::AbstractServerGameStateReceiving() -{ -} - -AbstractServerGameStateReceiving::~AbstractServerGameStateReceiving() -{ -} - -int -AbstractServerGameStateReceiving::Process(ServerGameThread &server) -{ - // This is the receive loop for the server. - int retVal = MSG_SOCK_INTERNAL_PENDING; - SessionWrapper session = server.GetSessionManager().Select(RECV_TIMEOUT_MSEC); - - if (session.sessionData) - { - boost::shared_ptr packet; - try - { - // Receive the packet. - packet = server.GetReceiver().Recv(session.sessionData->GetSocket(), session.sessionData->GetReceiveBuffer()); - } catch (const NetException &) - { - server.ErrorRemoveSession(session); - return retVal; - } - - // Process packet if one was received. - if (packet) - { - if (packet->IsClientActivity()) - session.sessionData->ResetActivityTimer(); - if (packet->ToNetPacketRetrievePlayerInfo()) - { - // Delegate to Lobby. - server.GetLobbyThread().HandleGameRetrievePlayerInfo(session, *packet->ToNetPacketRetrievePlayerInfo()); - } - else if (packet->ToNetPacketRetrieveAvatar()) - { - // Delegate to Lobby. - server.GetLobbyThread().HandleGameRetrieveAvatar(session, *packet->ToNetPacketRetrieveAvatar()); - } - else if (packet->ToNetPacketLeaveCurrentGame()) - { - server.MoveSessionToLobby(session, NTF_NET_REMOVED_ON_REQUEST); - } - else if (packet->ToNetPacketKickPlayer()) - { - // Only admins are allowed to kick, and only in the lobby. - // After leaving the lobby, a vote needs to be initiated to kick. - if (session.playerData->GetRights() == PLAYER_RIGHTS_ADMIN && !server.IsRunning()) - { - NetPacketKickPlayer::Data kickPlayerData; - packet->ToNetPacketKickPlayer()->GetData(kickPlayerData); - - server.InternalKickPlayer(kickPlayerData.playerId); - } - } - else if (packet->ToNetPacketAskKickPlayer()) - { - if (session.playerData) - { - NetPacketAskKickPlayer::Data askKickData; - packet->ToNetPacketAskKickPlayer()->GetData(askKickData); - - server.InternalAskVoteKick(session, askKickData.playerId, SERVER_VOTE_KICK_TIMEOUT_SEC); - } - } - else if (packet->ToNetPacketVoteKickPlayer()) - { - if (session.playerData) - { - NetPacketVoteKickPlayer::Data voteData; - packet->ToNetPacketVoteKickPlayer()->GetData(voteData); - - server.InternalVoteKick(session, voteData.petitionId, voteData.vote); - } - } - // Chat text is always allowed. - else if (packet->ToNetPacketSendChatText()) - { - if (session.playerData.get()) // Only forward if this player is known. - { - // Forward chat text to all players. - // TODO: Some limitation needed. - NetPacketSendChatText::Data inChatData; - packet->ToNetPacketSendChatText()->GetData(inChatData); - - boost::shared_ptr outChat(new NetPacketChatText); - NetPacketChatText::Data outChatData; - outChatData.playerId = session.playerData->GetUniqueId(); - outChatData.text = inChatData.text; - static_cast(outChat.get())->SetData(outChatData); - server.SendToAllPlayers(outChat, SessionData::Game); - } - } - else if (packet->ToNetPacketUnsubscribeGameList()) - { - // We can do this directly in this thread. - session.sessionData->ResetWantsLobbyMsg(); - } - else if (packet->ToNetPacketResubscribeGameList()) - { - // This needs to be performed in the lobby thread, - // because a new game list needs to be sent. - if (!session.sessionData->WantsLobbyMsg()) - server.GetLobbyThread().ResubscribeLobbyMsg(session); - } - else - { - // Packet processing in subclass. - retVal = InternalProcess(server, session, packet); - } - } - } - return retVal; -} - -//----------------------------------------------------------------------------- - -AbstractServerGameStateTimer::~AbstractServerGameStateTimer() -{ -} - -void -AbstractServerGameStateTimer::Init(ServerGameThread & server) -{ - // Reset timer. - server.GetStateTimer().reset(); - server.GetStateTimer().start(); - server.SetStateTimerFlag(0); -} - -//----------------------------------------------------------------------------- - -ServerGameStateInit ServerGameStateInit::m_state; - -ServerGameStateInit & -ServerGameStateInit::Instance() -{ - return m_state; -} - -ServerGameStateInit::ServerGameStateInit() -{ -} - -ServerGameStateInit::~ServerGameStateInit() -{ -} - -void -ServerGameStateInit::NotifyGameAdminChanged(ServerGameThread &server) -{ - // Reset admin timer. - AbstractServerGameStateTimer::Init(server); -} - -void -ServerGameStateInit::HandleNewSession(ServerGameThread &server, SessionWrapper session) -{ - if (session.sessionData.get() && session.playerData.get()) - { - size_t curNumPlayers = server.GetCurNumberOfPlayers(); - - // Check the number of players. - if (curNumPlayers >= (size_t)server.GetGameData().maxNumberOfPlayers) - { - server.MoveSessionToLobby(session, NTF_NET_REMOVED_GAME_FULL); - } - // Check whether the client supports the current game. - else if ((size_t)server.GetGameData().maxNumberOfPlayers > session.sessionData->GetMaxNumPlayers()) - { - server.MoveSessionToLobby(session, NTF_NET_REMOVED_GAME_FULL); - } - else - { - if (session.playerData->GetUniqueId() == server.GetAdminPlayerId()) - { - // This is the admin player. - session.playerData->SetRights(PLAYER_RIGHTS_ADMIN); - } - - // Send ack to client. - boost::shared_ptr joinGameAck(new NetPacketJoinGameAck); - NetPacketJoinGameAck::Data joinGameAckData; - joinGameAckData.gameId = server.GetId(); - joinGameAckData.prights = session.playerData->GetRights(); - joinGameAckData.gameData = server.GetGameData(); - static_cast(joinGameAck.get())->SetData(joinGameAckData); - session.sessionData->GetSender().Send(session.sessionData, joinGameAck); - - // Send notifications for connected players to client. - PlayerDataList tmpPlayerList = server.GetFullPlayerDataList(); - PlayerDataList::iterator player_i = tmpPlayerList.begin(); - PlayerDataList::iterator player_end = tmpPlayerList.end(); - while (player_i != player_end) - { - session.sessionData->GetSender().Send(session.sessionData, CreateNetPacketPlayerJoined(*(*player_i))); - ++player_i; - } - - // Send "Player Joined" to other fully connected clients. - server.SendToAllPlayers(CreateNetPacketPlayerJoined(*session.playerData), SessionData::Game); - - // Accept session. - server.GetSessionManager().AddSession(session); - - // Notify lobby. - server.GetLobbyThread().NotifyPlayerJoinedGame(server.GetId(), session.playerData->GetUniqueId()); - } - } -} - -int -ServerGameStateInit::Process(ServerGameThread &server) -{ - if (server.GetStateTimer().elapsed().total_seconds() >= SERVER_GAME_ADMIN_TIMEOUT_SEC - SERVER_GAME_ADMIN_WARNING_REMAINING_SEC - && !server.GetStateTimerFlag()) - { - // Admin timeout - notify game admin. - server.SetStateTimerFlag(1); // Only once. - // Find game admin. - SessionWrapper session = server.GetSessionManager().GetSessionByUniquePlayerId(server.GetAdminPlayerId()); - if (session.sessionData.get()) - { - boost::shared_ptr warning(new NetPacketTimeoutWarning); - NetPacketTimeoutWarning::Data warningData; - warningData.timeoutReason = NETWORK_TIMEOUT_GAME_ADMIN_IDLE; - warningData.remainingSeconds = SERVER_GAME_ADMIN_WARNING_REMAINING_SEC; - static_cast(warning.get())->SetData(warningData); - session.sessionData->GetSender().Send(session.sessionData, warning); - } - } - else if (server.GetStateTimer().elapsed().total_seconds() >= SERVER_GAME_ADMIN_TIMEOUT_SEC) - { - // Find game admin. - SessionWrapper session = server.GetSessionManager().GetSessionByUniquePlayerId(server.GetAdminPlayerId()); - if (session.sessionData.get()) - { - server.MoveSessionToLobby(session, NTF_NET_REMOVED_TIMEOUT); - } - } - return AbstractServerGameStateReceiving::Process(server); -} - -int -ServerGameStateInit::InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet) -{ - int retVal = MSG_SOCK_INTERNAL_PENDING; - - if (packet->ToNetPacketStartEvent()) - { - // Only admins are allowed to start the game. - if (session.playerData->GetRights() == PLAYER_RIGHTS_ADMIN) - { - NetPacketStartEvent::Data startData; - packet->ToNetPacketStartEvent()->GetData(startData); - - // Fill up with computer players. - server.ResetComputerPlayerList(); - - if (startData.fillUpWithCpuPlayers) - { - int remainingSlots = server.GetGameData().maxNumberOfPlayers - server.GetCurNumberOfPlayers(); - for (int i = 1; i <= remainingSlots; i++) - { - boost::shared_ptr tmpPlayerData( - new PlayerData(server.GetLobbyThread().GetNextUniquePlayerId(), 0, PLAYER_TYPE_COMPUTER, PLAYER_RIGHTS_NORMAL)); - - ostringstream name; - name << SERVER_COMPUTER_PLAYER_NAME << i; - tmpPlayerData->SetName(name.str()); - server.AddComputerPlayer(tmpPlayerData); - - // Send "Player Joined" to other fully connected clients. - server.SendToAllPlayers(CreateNetPacketPlayerJoined(*tmpPlayerData), SessionData::Game); - - // Notify lobby. - server.GetLobbyThread().NotifyPlayerJoinedGame(server.GetId(), tmpPlayerData->GetUniqueId()); - } - } - // Wait for all players to confirm start of game. - server.SendToAllPlayers(boost::shared_ptr(packet->Clone()), SessionData::Game); - - server.SetState(ServerGameStateWaitAck::Instance()); - } - } - else if (packet->ToNetPacketResetTimeout()) - { - if (session.playerData->GetRights() == PLAYER_RIGHTS_ADMIN) - { - // Reset admin timer. - AbstractServerGameStateTimer::Init(server); - } - } - else - { - server.SessionError(session, ERR_SOCK_INVALID_PACKET); - } - - return retVal; -} - -boost::shared_ptr -ServerGameStateInit::CreateNetPacketPlayerJoined(const PlayerData &playerData) -{ - boost::shared_ptr thisPlayerJoined(new NetPacketPlayerJoined); - NetPacketPlayerJoined::Data thisPlayerJoinedData; - thisPlayerJoinedData.playerId = playerData.GetUniqueId(); - thisPlayerJoinedData.prights = playerData.GetRights(); - static_cast(thisPlayerJoined.get())->SetData(thisPlayerJoinedData); - return thisPlayerJoined; -} - -//----------------------------------------------------------------------------- - -AbstractServerGameStateRunning::AbstractServerGameStateRunning() -{ -} - -AbstractServerGameStateRunning::~AbstractServerGameStateRunning() -{ -} - -void -AbstractServerGameStateRunning::HandleNewSession(ServerGameThread &server, SessionWrapper session) -{ - // Do not accept new sessions in this state. - server.MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING); -} - -//----------------------------------------------------------------------------- - -ServerGameStateWaitAck ServerGameStateWaitAck::m_state; - -ServerGameStateWaitAck & -ServerGameStateWaitAck::Instance() -{ - return m_state; -} - -ServerGameStateWaitAck::ServerGameStateWaitAck() -{ -} - -ServerGameStateWaitAck::~ServerGameStateWaitAck() -{ -} - -int -ServerGameStateWaitAck::Process(ServerGameThread &server) -{ - int retVal; - - if (server.GetStateTimer().elapsed().total_seconds() >= SERVER_START_GAME_TIMEOUT_SEC) - { - // On timeout: start anyway. - server.GetSessionManager().ResetAllReadyFlags(); - server.SetState(SERVER_START_GAME_STATE::Instance()); - retVal = MSG_SOCK_INIT_DONE; - } - else - retVal = AbstractServerGameStateReceiving::Process(server); - - return retVal; -} - -int -ServerGameStateWaitAck::InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet) -{ - int retVal = MSG_SOCK_INTERNAL_PENDING; - - if (packet->ToNetPacketStartEventAck()) - { - session.sessionData->SetReadyFlag(); - if (server.GetSessionManager().CountReadySessions() == server.GetSessionManager().GetRawSessionCount()) - { - // Everyone is ready. - server.GetSessionManager().ResetAllReadyFlags(); - server.SetState(SERVER_START_GAME_STATE::Instance()); - retVal = MSG_SOCK_INIT_DONE; - } - retVal = MSG_SOCK_INIT_DONE; - } - - return retVal; -} - -//----------------------------------------------------------------------------- - -ServerGameStateStartGame ServerGameStateStartGame::m_state; - -ServerGameStateStartGame & -ServerGameStateStartGame::Instance() -{ - return m_state; -} - -ServerGameStateStartGame::ServerGameStateStartGame() -{ -} - -ServerGameStateStartGame::~ServerGameStateStartGame() -{ -} - -int -ServerGameStateStartGame::Process(ServerGameThread &server) -{ - int retVal = MSG_SOCK_INTERNAL_PENDING; - - PlayerDataList tmpPlayerList = server.GetFullPlayerDataList(); - if (tmpPlayerList.size() <= 1) - { - if (!tmpPlayerList.empty()) - { - boost::shared_ptr tmpPlayer(tmpPlayerList.front()); - SessionWrapper tmpSession = server.GetSessionManager().GetSessionByUniquePlayerId(tmpPlayer->GetUniqueId()); - if (tmpSession.sessionData.get()) - server.MoveSessionToLobby(tmpSession, NTF_NET_REMOVED_START_FAILED); - } - } - else - { - server.InternalStartGame(); - - boost::shared_ptr answer(new NetPacketGameStart); - - NetPacketGameStart::Data gameStartData; - gameStartData.startData = server.GetStartData(); - - // Send player order to clients. - // Assume player list is sorted by number. - PlayerDataList::iterator player_i = tmpPlayerList.begin(); - PlayerDataList::iterator player_end = tmpPlayerList.end(); - while (player_i != player_end) - { - NetPacketGameStart::PlayerSlot tmpPlayerSlot; - tmpPlayerSlot.playerId = (*player_i)->GetUniqueId(); - gameStartData.playerSlots.push_back(tmpPlayerSlot); - - ++player_i; - } - - static_cast(answer.get())->SetData(gameStartData); - - server.SendToAllPlayers(answer, SessionData::Game); - server.SetState(ServerGameStateStartHand::Instance()); - retVal = MSG_NET_GAME_SERVER_START; - } - - return retVal; -} - -//----------------------------------------------------------------------------- - -ServerGameStateStartHand ServerGameStateStartHand::m_state; - -ServerGameStateStartHand & -ServerGameStateStartHand::Instance() -{ - return m_state; -} - -ServerGameStateStartHand::ServerGameStateStartHand() -{ -} - -ServerGameStateStartHand::~ServerGameStateStartHand() -{ -} - -int -ServerGameStateStartHand::Process(ServerGameThread &server) +static void StartNewHand(ServerGameThread &server) { // Initialize hand. Game &curGame = server.GetGame(); @@ -698,35 +216,491 @@ ServerGameStateStartHand::Process(ServerGameThread &server) } ++i; } +} - server.SetState(ServerGameStateStartRound::Instance()); +static void PerformPlayerAction(ServerGameThread &server, boost::shared_ptr player, PlayerAction action, int bet) +{ + Game &curGame = server.GetGame(); + if (!player.get()) + throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0); + player->setMyAction(action); + // Only change the player bet if action is not fold/check + if (action != PLAYER_ACTION_FOLD && action != PLAYER_ACTION_CHECK) + { - return MSG_NET_GAME_SERVER_HAND_START; + player->setMySet(bet); + + // update minimumRaise + switch(action) { + case PLAYER_ACTION_BET: { + curGame.getCurrentHand()->getCurrentBeRo()->setMinimumRaise(bet); + } break; + case PLAYER_ACTION_RAISE: { + curGame.getCurrentHand()->getCurrentBeRo()->setMinimumRaise(player->getMySet() - curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet()); + } break; + case PLAYER_ACTION_ALLIN: { + if(player->getMySet() - curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet() > curGame.getCurrentHand()->getCurrentBeRo()->getMinimumRaise()) { + curGame.getCurrentHand()->getCurrentBeRo()->setMinimumRaise(player->getMySet() - curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet()); + } + } break; + default: { + } + } + + // update highestSet + if (player->getMySet() > curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet()) + curGame.getCurrentHand()->getCurrentBeRo()->setHighestSet(player->getMySet()); + // Update total sets. + curGame.getCurrentHand()->getBoard()->collectSets(); + } + + + SendPlayerAction(server, player); } //----------------------------------------------------------------------------- -ServerGameStateStartRound ServerGameStateStartRound::m_state; - -ServerGameStateStartRound & -ServerGameStateStartRound::Instance() -{ - return m_state; -} - -ServerGameStateStartRound::ServerGameStateStartRound() +ServerGameState::~ServerGameState() { } -ServerGameStateStartRound::~ServerGameStateStartRound() +//----------------------------------------------------------------------------- + +AbstractServerGameStateReceiving::AbstractServerGameStateReceiving(ServerGameThread &serverGame) +: ServerGameState(serverGame) +{ +} + +AbstractServerGameStateReceiving::~AbstractServerGameStateReceiving() { } int -ServerGameStateStartRound::Process(ServerGameThread &server) +AbstractServerGameStateReceiving::ProcessPacket(SessionWrapper session, boost::shared_ptr packet) +{ + // This is the receive loop for the server. + int retVal = MSG_SOCK_INTERNAL_PENDING; + + if (packet->IsClientActivity()) + session.sessionData->ResetActivityTimer(); + if (packet->ToNetPacketRetrievePlayerInfo()) + { + // Delegate to Lobby. + GetServerGame().GetLobbyThread().HandleGameRetrievePlayerInfo(session, *packet->ToNetPacketRetrievePlayerInfo()); + } + else if (packet->ToNetPacketRetrieveAvatar()) + { + // Delegate to Lobby. + GetServerGame().GetLobbyThread().HandleGameRetrieveAvatar(session, *packet->ToNetPacketRetrieveAvatar()); + } + else if (packet->ToNetPacketLeaveCurrentGame()) + { + GetServerGame().MoveSessionToLobby(session, NTF_NET_REMOVED_ON_REQUEST); + } + else if (packet->ToNetPacketKickPlayer()) + { + // Only admins are allowed to kick, and only in the lobby. + // After leaving the lobby, a vote needs to be initiated to kick. + if (session.playerData->GetRights() == PLAYER_RIGHTS_ADMIN && !GetServerGame().IsRunning()) + { + NetPacketKickPlayer::Data kickPlayerData; + packet->ToNetPacketKickPlayer()->GetData(kickPlayerData); + + GetServerGame().InternalKickPlayer(kickPlayerData.playerId); + } + } + else if (packet->ToNetPacketAskKickPlayer()) + { + if (session.playerData) + { + NetPacketAskKickPlayer::Data askKickData; + packet->ToNetPacketAskKickPlayer()->GetData(askKickData); + + GetServerGame().InternalAskVoteKick(session, askKickData.playerId, SERVER_VOTE_KICK_TIMEOUT_SEC); + } + } + else if (packet->ToNetPacketVoteKickPlayer()) + { + if (session.playerData) + { + NetPacketVoteKickPlayer::Data voteData; + packet->ToNetPacketVoteKickPlayer()->GetData(voteData); + + GetServerGame().InternalVoteKick(session, voteData.petitionId, voteData.vote); + } + } + // Chat text is always allowed. + else if (packet->ToNetPacketSendChatText()) + { + if (session.playerData) // Only forward if this player is known. + { + // Forward chat text to all players. + // TODO: Some limitation needed. + NetPacketSendChatText::Data inChatData; + packet->ToNetPacketSendChatText()->GetData(inChatData); + + boost::shared_ptr outChat(new NetPacketChatText); + NetPacketChatText::Data outChatData; + outChatData.playerId = session.playerData->GetUniqueId(); + outChatData.text = inChatData.text; + static_cast(outChat.get())->SetData(outChatData); + GetServerGame().SendToAllPlayers(outChat, SessionData::Game); + } + } + else if (packet->ToNetPacketUnsubscribeGameList()) + { + // We can do this directly in this thread. + session.sessionData->ResetWantsLobbyMsg(); + } + else if (packet->ToNetPacketResubscribeGameList()) + { + // This needs to be performed in the lobby thread, + // because a new game list needs to be sent. + if (!session.sessionData->WantsLobbyMsg()) + GetServerGame().GetLobbyThread().ResubscribeLobbyMsg(session); + } + else + { + // Packet processing in subclass. + retVal = InternalProcessPacket(session, packet); + } + + return retVal; +} + +//----------------------------------------------------------------------------- + +ServerGameStateInit::ServerGameStateInit(ServerGameThread &serverGame) +: AbstractServerGameStateReceiving(serverGame), m_timerRegistered(false) +{ + RegisterAdminTimers(); +} + +ServerGameStateInit::~ServerGameStateInit() +{ + UnregisterAdminTimers(); +} + +void +ServerGameStateInit::NotifyGameAdminChanged() +{ + RegisterAdminTimers(); +} + +void +ServerGameStateInit::HandleNewSession(SessionWrapper session) +{ + if (session.sessionData.get() && session.playerData.get()) + { + size_t curNumPlayers = GetServerGame().GetCurNumberOfPlayers(); + + // Check the number of players. + if (curNumPlayers >= (size_t)GetServerGame().GetGameData().maxNumberOfPlayers) + { + GetServerGame().MoveSessionToLobby(session, NTF_NET_REMOVED_GAME_FULL); + } + // Check whether the client supports the current game. + else if ((size_t)GetServerGame().GetGameData().maxNumberOfPlayers > session.sessionData->GetMaxNumPlayers()) + { + GetServerGame().MoveSessionToLobby(session, NTF_NET_REMOVED_GAME_FULL); + } + else + { + if (session.playerData->GetUniqueId() == GetServerGame().GetAdminPlayerId()) + { + // This is the admin player. + session.playerData->SetRights(PLAYER_RIGHTS_ADMIN); + } + + // Send ack to client. + boost::shared_ptr joinGameAck(new NetPacketJoinGameAck); + NetPacketJoinGameAck::Data joinGameAckData; + joinGameAckData.gameId = GetServerGame().GetId(); + joinGameAckData.prights = session.playerData->GetRights(); + joinGameAckData.gameData = GetServerGame().GetGameData(); + static_cast(joinGameAck.get())->SetData(joinGameAckData); + session.sessionData->GetSender().Send(session.sessionData, joinGameAck); + + // Send notifications for connected players to client. + PlayerDataList tmpPlayerList = GetServerGame().GetFullPlayerDataList(); + PlayerDataList::iterator player_i = tmpPlayerList.begin(); + PlayerDataList::iterator player_end = tmpPlayerList.end(); + while (player_i != player_end) + { + session.sessionData->GetSender().Send(session.sessionData, CreateNetPacketPlayerJoined(*(*player_i))); + ++player_i; + } + + // Send "Player Joined" to other fully connected clients. + GetServerGame().SendToAllPlayers(CreateNetPacketPlayerJoined(*session.playerData), SessionData::Game); + + // Accept session. + GetServerGame().GetSessionManager().AddSession(session); + + // Notify lobby. + GetServerGame().GetLobbyThread().NotifyPlayerJoinedGame(GetServerGame().GetId(), session.playerData->GetUniqueId()); + } + } +} + +void +ServerGameStateInit::RegisterAdminTimers() +{ + if (m_timerRegistered) + UnregisterAdminTimers(); + + m_adminWarningTimerId = GetServerGame().GetLobbyThread().GetTimerManager().RegisterTimer( + (SERVER_GAME_ADMIN_TIMEOUT_SEC - SERVER_GAME_ADMIN_WARNING_REMAINING_SEC) * 1000, + boost::bind(&ServerGameStateInit::TimerAdminWarning, this)); + m_adminTimeoutTimerId = GetServerGame().GetLobbyThread().GetTimerManager().RegisterTimer( + SERVER_GAME_ADMIN_TIMEOUT_SEC * 1000, + boost::bind(&ServerGameStateInit::TimerAdminTimeout, this)); + m_timerRegistered = true; +} + +void +ServerGameStateInit::UnregisterAdminTimers() +{ + if (m_timerRegistered) + { + GetServerGame().GetLobbyThread().GetTimerManager().UnregisterTimer(m_adminWarningTimerId); + GetServerGame().GetLobbyThread().GetTimerManager().UnregisterTimer(m_adminTimeoutTimerId); + m_timerRegistered = false; + } +} + +void +ServerGameStateInit::TimerAdminWarning() +{ + // Find game admin. + SessionWrapper session = GetServerGame().GetSessionManager().GetSessionByUniquePlayerId(GetServerGame().GetAdminPlayerId()); + if (session.sessionData.get()) + { + // Send him a warning. + boost::shared_ptr warning(new NetPacketTimeoutWarning); + NetPacketTimeoutWarning::Data warningData; + warningData.timeoutReason = NETWORK_TIMEOUT_GAME_ADMIN_IDLE; + warningData.remainingSeconds = SERVER_GAME_ADMIN_WARNING_REMAINING_SEC; + static_cast(warning.get())->SetData(warningData); + session.sessionData->GetSender().Send(session.sessionData, warning); + } +} + +void +ServerGameStateInit::TimerAdminTimeout() +{ + // Find game admin. + SessionWrapper session = GetServerGame().GetSessionManager().GetSessionByUniquePlayerId(GetServerGame().GetAdminPlayerId()); + if (session.sessionData.get()) + { + // Remove him from the game. + GetServerGame().MoveSessionToLobby(session, NTF_NET_REMOVED_TIMEOUT); + } +} + +int +ServerGameStateInit::InternalProcessPacket(SessionWrapper session, boost::shared_ptr packet) { int retVal = MSG_SOCK_INTERNAL_PENDING; - Game &curGame = server.GetGame(); + + if (packet->ToNetPacketStartEvent()) + { + // Only admins are allowed to start the game. + if (session.playerData->GetRights() == PLAYER_RIGHTS_ADMIN) + { + NetPacketStartEvent::Data startData; + packet->ToNetPacketStartEvent()->GetData(startData); + + // Fill up with computer players. + GetServerGame().ResetComputerPlayerList(); + + if (startData.fillUpWithCpuPlayers) + { + int remainingSlots = GetServerGame().GetGameData().maxNumberOfPlayers - GetServerGame().GetCurNumberOfPlayers(); + for (int i = 1; i <= remainingSlots; i++) + { + boost::shared_ptr tmpPlayerData( + new PlayerData(GetServerGame().GetLobbyThread().GetNextUniquePlayerId(), 0, PLAYER_TYPE_COMPUTER, PLAYER_RIGHTS_NORMAL)); + + ostringstream name; + name << SERVER_COMPUTER_PLAYER_NAME << i; + tmpPlayerData->SetName(name.str()); + GetServerGame().AddComputerPlayer(tmpPlayerData); + + // Send "Player Joined" to other fully connected clients. + GetServerGame().SendToAllPlayers(CreateNetPacketPlayerJoined(*tmpPlayerData), SessionData::Game); + + // Notify lobby. + GetServerGame().GetLobbyThread().NotifyPlayerJoinedGame(GetServerGame().GetId(), tmpPlayerData->GetUniqueId()); + } + } + // Wait for all players to confirm start of game. + GetServerGame().SendToAllPlayers(boost::shared_ptr(packet->Clone()), SessionData::Game); + + GetServerGame().SetState(boost::shared_ptr(new ServerGameStateStartGame(GetServerGame()))); + } + } + else if (packet->ToNetPacketResetTimeout()) + { + if (session.playerData->GetRights() == PLAYER_RIGHTS_ADMIN) + { + RegisterAdminTimers(); + } + } + else + { + GetServerGame().SessionError(session, ERR_SOCK_INVALID_PACKET); + } + + return retVal; +} + +boost::shared_ptr +ServerGameStateInit::CreateNetPacketPlayerJoined(const PlayerData &playerData) +{ + boost::shared_ptr thisPlayerJoined(new NetPacketPlayerJoined); + NetPacketPlayerJoined::Data thisPlayerJoinedData; + thisPlayerJoinedData.playerId = playerData.GetUniqueId(); + thisPlayerJoinedData.prights = playerData.GetRights(); + static_cast(thisPlayerJoined.get())->SetData(thisPlayerJoinedData); + return thisPlayerJoined; +} + +//----------------------------------------------------------------------------- + +ServerGameStateStartGame::ServerGameStateStartGame(ServerGameThread &serverGame) +: AbstractServerGameStateReceiving(serverGame) +{ + m_timeoutTimerId = GetServerGame().GetLobbyThread().GetTimerManager().RegisterTimer( + SERVER_START_GAME_TIMEOUT_SEC * 1000, + boost::bind(&ServerGameStateStartGame::TimerTimeout, this)); +} + +ServerGameStateStartGame::~ServerGameStateStartGame() +{ + // The Id might be invalid, but this is not a problem. + GetServerGame().GetLobbyThread().GetTimerManager().UnregisterTimer(m_timeoutTimerId); +} + +void +ServerGameStateStartGame::HandleNewSession(SessionWrapper session) +{ + // Do not accept new sessions in this state. + GetServerGame().MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING); +} + +int +ServerGameStateStartGame::InternalProcessPacket(SessionWrapper session, boost::shared_ptr packet) +{ + int retVal = MSG_SOCK_INTERNAL_PENDING; + + if (packet->ToNetPacketStartEventAck()) + { + session.sessionData->SetReadyFlag(); + if (GetServerGame().GetSessionManager().CountReadySessions() == GetServerGame().GetSessionManager().GetRawSessionCount()) + { + // Everyone is ready. + GetServerGame().GetSessionManager().ResetAllReadyFlags(); + DoStart(); + GetServerGame().SetState(boost::shared_ptr(new SERVER_START_GAME_STATE(GetServerGame()))); + retVal = MSG_SOCK_INIT_DONE; + } + } + + return retVal; +} + +void +ServerGameStateStartGame::TimerTimeout() +{ + // On timeout: start anyway. + GetServerGame().GetSessionManager().ResetAllReadyFlags(); + // TODO report successful start! -> new callback?! + //retVal = MSG_SOCK_INIT_DONE; + DoStart(); + GetServerGame().SetState(boost::shared_ptr(new SERVER_START_GAME_STATE(GetServerGame()))); +} + +void +ServerGameStateStartGame::DoStart() +{ + PlayerDataList tmpPlayerList = GetServerGame().GetFullPlayerDataList(); + if (tmpPlayerList.size() <= 1) + { + if (!tmpPlayerList.empty()) + { + boost::shared_ptr tmpPlayer(tmpPlayerList.front()); + SessionWrapper tmpSession = GetServerGame().GetSessionManager().GetSessionByUniquePlayerId(tmpPlayer->GetUniqueId()); + if (tmpSession.sessionData.get()) + GetServerGame().MoveSessionToLobby(tmpSession, NTF_NET_REMOVED_START_FAILED); + } + } + else + { + GetServerGame().InternalStartGame(); + + boost::shared_ptr answer(new NetPacketGameStart); + + NetPacketGameStart::Data gameStartData; + gameStartData.startData = GetServerGame().GetStartData(); + + // Send player order to clients. + // Assume player list is sorted by number. + PlayerDataList::iterator player_i = tmpPlayerList.begin(); + PlayerDataList::iterator player_end = tmpPlayerList.end(); + while (player_i != player_end) + { + NetPacketGameStart::PlayerSlot tmpPlayerSlot; + tmpPlayerSlot.playerId = (*player_i)->GetUniqueId(); + gameStartData.playerSlots.push_back(tmpPlayerSlot); + + ++player_i; + } + + static_cast(answer.get())->SetData(gameStartData); + GetServerGame().SendToAllPlayers(answer, SessionData::Game); + + // Start the first hand. + StartNewHand(GetServerGame()); + GetServerGame().SetState(boost::shared_ptr(new ServerGameStateHand(GetServerGame()))); + } +} + +//----------------------------------------------------------------------------- + +ServerGameStateHand::ServerGameStateHand(ServerGameThread &serverGame) +: AbstractServerGameStateReceiving(serverGame) +{ + m_loopTimerId = GetServerGame().GetLobbyThread().GetTimerManager().RegisterTimer( + SERVER_LOOP_DELAY_MSEC, + boost::bind(&ServerGameStateHand::TimerLoop, this)); +} + +ServerGameStateHand::~ServerGameStateHand() +{ + // The Id might be invalid, but this is not a problem. + GetServerGame().GetLobbyThread().GetTimerManager().UnregisterTimer(m_loopTimerId); +} + +void +ServerGameStateHand::HandleNewSession(SessionWrapper session) +{ + // Do not accept new sessions in this state. + GetServerGame().MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING); +} + +int +ServerGameStateHand::InternalProcessPacket(SessionWrapper session, boost::shared_ptr packet) +{ + // TODO: maybe reject packet. + return MSG_SOCK_INTERNAL_PENDING; +} + +void +ServerGameStateHand::TimerLoop() +{ + bool newTimerSet = false; + Game &curGame = GetServerGame().GetGame(); // Main game loop. int curRound = curGame.getCurrentHand()->getCurrentRound(); @@ -770,18 +744,22 @@ ServerGameStateStartRound::Process(ServerGameThread &server) ++i; } static_cast(allIn.get())->SetData(allInData); - server.SendToAllPlayers(allIn, SessionData::Game); + GetServerGame().SendToAllPlayers(allIn, SessionData::Game); curGame.getCurrentHand()->setCardsShown(true); - server.SetState(ServerGameStateShowCardsDelay::Instance()); - retVal = MSG_NET_GAME_SERVER_CARDS_DELAY; + m_loopTimerId = GetServerGame().GetLobbyThread().GetTimerManager().RegisterTimer( + SERVER_SHOW_CARDS_DELAY_SEC * 1000, + boost::bind(&ServerGameStateHand::TimerLoop, this)); + newTimerSet = true; } else { - SendNewRoundCards(server, curGame, newRound); + SendNewRoundCards(GetServerGame(), curGame, newRound); - server.SetState(ServerGameStateDealCardsDelay::Instance()); - retVal = MSG_NET_GAME_SERVER_CARDS_DELAY; + m_loopTimerId = GetServerGame().GetLobbyThread().GetTimerManager().RegisterTimer( + GetDealCardsDelaySec() * 1000, + boost::bind(&ServerGameStateHand::TimerLoop, this)); + newTimerSet = true; } } else @@ -804,11 +782,26 @@ ServerGameStateStartRound::Process(ServerGameThread &server) playersTurnData.playerId = curPlayer->getMyUniqueID(); static_cast(notification.get())->SetData(playersTurnData); - server.SendToAllPlayers(notification, SessionData::Game); + GetServerGame().SendToAllPlayers(notification, SessionData::Game); - server.SetState(ServerGameStateWaitPlayerAction::Instance()); - - retVal = MSG_NET_GAME_SERVER_ROUND; + // If the player is computer controlled, let the engine act. + if (curPlayer->getMyType() == PLAYER_TYPE_COMPUTER) + { + m_loopTimerId = GetServerGame().GetLobbyThread().GetTimerManager().RegisterTimer( + SERVER_COMPUTER_ACTION_DELAY_SEC * 1000, + boost::bind(&ServerGameStateHand::TimerComputerAction, this)); + newTimerSet = true; + } + // If the player we are waiting for left, continue without him. + else if (!GetServerGame().GetSessionManager().IsPlayerConnected(curPlayer->getMyName())) + { + PerformPlayerAction(GetServerGame(), curPlayer, PLAYER_ACTION_FOLD, 0); + } + else + { + //wrong!!! + GetServerGame().SetState(boost::shared_ptr(new ServerGameStateWaitPlayerAction(GetServerGame()))); + } } else // hand is over { @@ -830,7 +823,7 @@ ServerGameStateStartRound::Process(ServerGameThread &server) endHandData.playerMoney = player->getMyCash(); static_cast(endHand.get())->SetData(endHandData); - server.SendToAllPlayers(endHand, SessionData::Game); + GetServerGame().SendToAllPlayers(endHand, SessionData::Game); } else { @@ -865,11 +858,11 @@ ServerGameStateStartRound::Process(ServerGameThread &server) } static_cast(endHand.get())->SetData(endHandData); - server.SendToAllPlayers(endHand, SessionData::Game); + GetServerGame().SendToAllPlayers(endHand, SessionData::Game); } // Remove disconnected players. This is the one and only place to do this. - server.RemoveDisconnectedPlayers(); + GetServerGame().RemoveDisconnectedPlayers(); // Start next hand - if enough players are left. list > playersWithCash = *curGame.getActivePlayerList(); @@ -878,89 +871,139 @@ ServerGameStateStartRound::Process(ServerGameThread &server) if (playersWithCash.empty()) { // No more players left - restart. - server.SetState(SERVER_INITIAL_STATE::Instance()); - retVal = MSG_NET_GAME_SERVER_END; + //wrong!!! + GetServerGame().SetState(boost::shared_ptr(new SERVER_INITIAL_STATE(GetServerGame()))); } else if (playersWithCash.size() == 1) { // View a dialog for a new game - delayed. - server.SetState(ServerGameStateNextGameDelay::Instance()); - retVal = MSG_NET_GAME_SERVER_END; + m_loopTimerId = GetServerGame().GetLobbyThread().GetTimerManager().RegisterTimer( + SERVER_DELAY_NEXT_GAME_SEC * 1000, + boost::bind(&ServerGameStateHand::TimerNextGame, this)); + newTimerSet = true; } else { - server.SetState(ServerGameStateNextHandDelay::Instance()); - retVal = MSG_NET_GAME_SERVER_HAND_END; + m_loopTimerId = GetServerGame().GetLobbyThread().GetTimerManager().RegisterTimer( + SERVER_DELAY_NEXT_HAND_SEC * 1000, + boost::bind(&ServerGameStateHand::TimerNextHand, this)); + newTimerSet = true; } } } - return retVal; +} + +void +ServerGameStateHand::TimerShowCards() +{ + Game &curGame = GetServerGame().GetGame(); + SendNewRoundCards(GetServerGame(), curGame, curGame.getCurrentHand()->getCurrentRound()); + + m_loopTimerId = GetServerGame().GetLobbyThread().GetTimerManager().RegisterTimer( + GetDealCardsDelaySec() * 1000, + boost::bind(&ServerGameStateHand::TimerLoop, this)); +} + +void +ServerGameStateHand::TimerComputerAction() +{ + boost::shared_ptr tmpPlayer = GetServerGame().GetGame().getCurrentPlayer(); + + tmpPlayer->action(); + SendPlayerAction(GetServerGame(), tmpPlayer); + TimerLoop(); +} + +void +ServerGameStateHand::TimerNextHand() +{ + StartNewHand(GetServerGame()); + TimerLoop(); +} + +void +ServerGameStateHand::TimerNextGame() +{ + Game &curGame = GetServerGame().GetGame(); + // The game has ended. Notify all clients. + boost::shared_ptr winnerPlayer; + PlayerListIterator i = curGame.getActivePlayerList()->begin(); + PlayerListIterator end = curGame.getActivePlayerList()->end(); + while (i != end) + { + winnerPlayer = *i; + if (winnerPlayer->getMyCash() > 0) + break; + ++i; + } + + boost::shared_ptr endGame(new NetPacketEndOfGame); + NetPacketEndOfGame::Data endGameData; + endGameData.winnerPlayerId = winnerPlayer->getMyUniqueID(); + static_cast(endGame.get())->SetData(endGameData); + + GetServerGame().SendToAllPlayers(endGame, SessionData::Game); + + // Wait for the start of a new game. + GetServerGame().ResetComputerPlayerList(); + GetServerGame().ResetGame(); + GetServerGame().GetLobbyThread().NotifyReopeningGame(GetServerGame().GetId()); + GetServerGame().SetState(boost::shared_ptr(new ServerGameStateInit(GetServerGame()))); +} + +int +ServerGameStateHand::GetDealCardsDelaySec() +{ + Game &curGame = GetServerGame().GetGame(); + int allInDelay = curGame.getCurrentHand()->getAllInCondition() ? SERVER_DEAL_ADD_ALL_IN_DELAY_SEC : 0; + int delay = 0; + + switch(curGame.getCurrentHand()->getCurrentRound()) + { + case GAME_STATE_FLOP: + delay = SERVER_DEAL_FLOP_CARDS_DELAY_SEC; + break; + case GAME_STATE_TURN: + delay = SERVER_DEAL_TURN_CARD_DELAY_SEC + allInDelay; + break; + case GAME_STATE_RIVER: + delay = SERVER_DEAL_RIVER_CARD_DELAY_SEC; + break; + } + return delay; } //----------------------------------------------------------------------------- -ServerGameStateWaitPlayerAction ServerGameStateWaitPlayerAction::m_state; - -ServerGameStateWaitPlayerAction & -ServerGameStateWaitPlayerAction::Instance() +ServerGameStateWaitPlayerAction::ServerGameStateWaitPlayerAction(ServerGameThread &serverGame) +: AbstractServerGameStateReceiving(serverGame) { - return m_state; -} +#ifdef SERVER_TEST + int timeoutSec = 0; +#else + int timeoutSec = GetServerGame().GetGameData().playerActionTimeoutSec + SERVER_PLAYER_TIMEOUT_ADD_DELAY_SEC; +#endif -ServerGameStateWaitPlayerAction::ServerGameStateWaitPlayerAction() -{ + m_timeoutTimerId = GetServerGame().GetLobbyThread().GetTimerManager().RegisterTimer( + timeoutSec * 1000, + boost::bind(&ServerGameStateWaitPlayerAction::TimerTimeout, this)); } ServerGameStateWaitPlayerAction::~ServerGameStateWaitPlayerAction() { + // The Id might be invalid, but this is not a problem. + GetServerGame().GetLobbyThread().GetTimerManager().UnregisterTimer(m_timeoutTimerId); } -int -ServerGameStateWaitPlayerAction::Process(ServerGameThread &server) +void +ServerGameStateWaitPlayerAction::HandleNewSession(SessionWrapper session) { - int retVal; - - boost::shared_ptr curPlayer = server.GetGame().getCurrentPlayer(); - if (!curPlayer.get()) - throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0); - - // If the player is computer controlled, let the engine act. - if (curPlayer->getMyType() == PLAYER_TYPE_COMPUTER) - { - server.SetState(ServerGameStateComputerAction::Instance()); - retVal = MSG_SOCK_INTERNAL_PENDING; - } - // If the player we are waiting for left, continue without him. - else if (!server.GetSessionManager().IsPlayerConnected(curPlayer->getMyName())) - { - PerformPlayerAction(server, curPlayer, PLAYER_ACTION_FOLD, 0); - - server.SetState(ServerGameStateStartRound::Instance()); - retVal = MSG_NET_GAME_SERVER_ACTION; - } -#ifdef SERVER_TEST - else if (true) -#else - else if (server.GetStateTimer().elapsed().total_seconds() >= server.GetGameData().playerActionTimeoutSec + SERVER_PLAYER_TIMEOUT_ADD_DELAY_SEC) -#endif - { - // Player did not act fast enough. Act for him. - if (server.GetGame().getCurrentHand()->getCurrentBeRo()->getHighestSet() == curPlayer->getMySet()) - PerformPlayerAction(server, curPlayer, PLAYER_ACTION_CHECK, 0); - else - PerformPlayerAction(server, curPlayer, PLAYER_ACTION_FOLD, 0); - - server.SetState(ServerGameStateStartRound::Instance()); - retVal = MSG_NET_GAME_SERVER_ACTION; - } - else - retVal = AbstractServerGameStateReceiving::Process(server); - - return retVal; + // Do not accept new sessions in this state. + GetServerGame().MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING); } int -ServerGameStateWaitPlayerAction::InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet) +ServerGameStateWaitPlayerAction::InternalProcessPacket(SessionWrapper session, boost::shared_ptr packet) { int retVal = MSG_SOCK_INTERNAL_PENDING; @@ -969,7 +1012,7 @@ ServerGameStateWaitPlayerAction::InternalProcess(ServerGameThread &server, Sessi NetPacketPlayersAction::Data actionData; packet->ToNetPacketPlayersAction()->GetData(actionData); - Game &curGame = server.GetGame(); + Game &curGame = GetServerGame().GetGame(); boost::shared_ptr tmpPlayer = curGame.getPlayerByUniqueId(session.playerData->GetUniqueId()); if (!tmpPlayer.get()) throw ServerException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0); @@ -980,7 +1023,7 @@ ServerGameStateWaitPlayerAction::InternalProcess(ServerGameThread &server, Sessi code = ACTION_CODE_INVALID_STATE; // Check whether this is the correct player. - boost::shared_ptr curPlayer = server.GetGame().getCurrentPlayer(); + boost::shared_ptr curPlayer = GetServerGame().GetGame().getCurrentPlayer(); if (code == ACTION_CODE_VALID && (curPlayer->getMyUniqueID() != tmpPlayer->getMyUniqueID())) { @@ -1012,8 +1055,8 @@ ServerGameStateWaitPlayerAction::InternalProcess(ServerGameThread &server, Sessi if (code == ACTION_CODE_VALID) { - PerformPlayerAction(server, tmpPlayer, actionData.playerAction, actionData.playerBet); - server.SetState(ServerGameStateStartRound::Instance()); + PerformPlayerAction(GetServerGame(), tmpPlayer, actionData.playerAction, actionData.playerBet); + GetServerGame().SetState(boost::shared_ptr(new ServerGameStateHand(GetServerGame()))); retVal = MSG_NET_GAME_SERVER_ACTION; } else @@ -1034,300 +1077,22 @@ ServerGameStateWaitPlayerAction::InternalProcess(ServerGameThread &server, Sessi } void -ServerGameStateWaitPlayerAction::PerformPlayerAction(ServerGameThread &server, boost::shared_ptr player, PlayerAction action, int bet) +ServerGameStateWaitPlayerAction::TimerTimeout() { - Game &curGame = server.GetGame(); - if (!player.get()) + Game &curGame = GetServerGame().GetGame(); + // Retrieve current player. + boost::shared_ptr curPlayer = curGame.getCurrentPlayer(); + if (!curPlayer.get()) throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0); - player->setMyAction(action); - // Only change the player bet if action is not fold/check - if (action != PLAYER_ACTION_FOLD && action != PLAYER_ACTION_CHECK) - { - player->setMySet(bet); + // Player did not act fast enough. Act for him. + if (curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet() == curPlayer->getMySet()) + PerformPlayerAction(GetServerGame(), curPlayer, PLAYER_ACTION_CHECK, 0); + else + PerformPlayerAction(GetServerGame(), curPlayer, PLAYER_ACTION_FOLD, 0); - // update minimumRaise - switch(action) { - case PLAYER_ACTION_BET: { - curGame.getCurrentHand()->getCurrentBeRo()->setMinimumRaise(bet); - } break; - case PLAYER_ACTION_RAISE: { - curGame.getCurrentHand()->getCurrentBeRo()->setMinimumRaise(player->getMySet() - curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet()); - } break; - case PLAYER_ACTION_ALLIN: { - if(player->getMySet() - curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet() > curGame.getCurrentHand()->getCurrentBeRo()->getMinimumRaise()) { - curGame.getCurrentHand()->getCurrentBeRo()->setMinimumRaise(player->getMySet() - curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet()); - } - } break; - default: { - } - } - - // update highestSet - if (player->getMySet() > curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet()) - curGame.getCurrentHand()->getCurrentBeRo()->setHighestSet(player->getMySet()); - // Update total sets. - curGame.getCurrentHand()->getBoard()->collectSets(); - } - - - SendPlayerAction(server, player); + GetServerGame().SetState(boost::shared_ptr(new ServerGameStateHand(GetServerGame()))); } //----------------------------------------------------------------------------- -ServerGameStateComputerAction ServerGameStateComputerAction::m_state; - -ServerGameStateComputerAction & -ServerGameStateComputerAction::Instance() -{ - return m_state; -} - -ServerGameStateComputerAction::ServerGameStateComputerAction() -{ -} - -ServerGameStateComputerAction::~ServerGameStateComputerAction() -{ -} - -int -ServerGameStateComputerAction::Process(ServerGameThread &server) -{ - int retVal = AbstractServerGameStateReceiving::Process(server); - - if (server.GetStateTimer().elapsed().total_seconds() >= SERVER_COMPUTER_ACTION_DELAY_SEC) - { - boost::shared_ptr tmpPlayer = server.GetGame().getCurrentPlayer(); - - tmpPlayer->action(); - SendPlayerAction(server, tmpPlayer); - - server.SetState(ServerGameStateStartRound::Instance()); - retVal = MSG_NET_GAME_SERVER_ACTION; - } - - return retVal; -} - -int -ServerGameStateComputerAction::InternalProcess(ServerGameThread &/*server*/, SessionWrapper /*session*/, boost::shared_ptr /*packet*/) -{ - return MSG_SOCK_INTERNAL_PENDING; -} - -//----------------------------------------------------------------------------- - -ServerGameStateDealCardsDelay ServerGameStateDealCardsDelay::m_state; - -ServerGameStateDealCardsDelay & -ServerGameStateDealCardsDelay::Instance() -{ - return m_state; -} - -ServerGameStateDealCardsDelay::ServerGameStateDealCardsDelay() -{ -} - -ServerGameStateDealCardsDelay::~ServerGameStateDealCardsDelay() -{ -} - -int -ServerGameStateDealCardsDelay::Process(ServerGameThread &server) -{ - int retVal = AbstractServerGameStateReceiving::Process(server); - - Game &curGame = server.GetGame(); - - int allInDelay = curGame.getCurrentHand()->getAllInCondition() ? SERVER_DEAL_ADD_ALL_IN_DELAY_SEC : 0; - int delay = 0; - - switch(curGame.getCurrentHand()->getCurrentRound()) - { - case GAME_STATE_FLOP: - delay = SERVER_DEAL_FLOP_CARDS_DELAY_SEC; - break; - case GAME_STATE_TURN: - delay = SERVER_DEAL_TURN_CARD_DELAY_SEC + allInDelay; - break; - case GAME_STATE_RIVER: - delay = SERVER_DEAL_RIVER_CARD_DELAY_SEC; - break; - } - if (!delay || server.GetStateTimer().elapsed().total_seconds() >= delay) - server.SetState(ServerGameStateStartRound::Instance()); - - return retVal; -} - -int -ServerGameStateDealCardsDelay::InternalProcess(ServerGameThread &/*server*/, SessionWrapper /*session*/, boost::shared_ptr /*packet*/) -{ - return MSG_SOCK_INTERNAL_PENDING; -} - -//----------------------------------------------------------------------------- - -ServerGameStateShowCardsDelay ServerGameStateShowCardsDelay::m_state; - -ServerGameStateShowCardsDelay & -ServerGameStateShowCardsDelay::Instance() -{ - return m_state; -} - -ServerGameStateShowCardsDelay::ServerGameStateShowCardsDelay() -{ -} - -ServerGameStateShowCardsDelay::~ServerGameStateShowCardsDelay() -{ -} - -int -ServerGameStateShowCardsDelay::Process(ServerGameThread &server) -{ - int retVal = AbstractServerGameStateReceiving::Process(server); - - if (server.GetStateTimer().elapsed().total_seconds() >= SERVER_SHOW_CARDS_DELAY_SEC) - { - Game &curGame = server.GetGame(); - SendNewRoundCards(server, curGame, curGame.getCurrentHand()->getCurrentRound()); - - server.SetState(ServerGameStateDealCardsDelay::Instance()); - retVal = MSG_NET_GAME_SERVER_CARDS_DELAY; - } - - return retVal; -} - -int -ServerGameStateShowCardsDelay::InternalProcess(ServerGameThread &/*server*/, SessionWrapper /*session*/, boost::shared_ptr /*packet*/) -{ - return MSG_SOCK_INTERNAL_PENDING; -} - -//----------------------------------------------------------------------------- - -ServerGameStateNextHandDelay ServerGameStateNextHandDelay::m_state; - -ServerGameStateNextHandDelay & -ServerGameStateNextHandDelay::Instance() -{ - return m_state; -} - -ServerGameStateNextHandDelay::ServerGameStateNextHandDelay() -{ -} - -ServerGameStateNextHandDelay::~ServerGameStateNextHandDelay() -{ -} - -int -ServerGameStateNextHandDelay::Process(ServerGameThread &server) -{ - int retVal = AbstractServerGameStateReceiving::Process(server); - - if (server.GetStateTimer().elapsed().total_seconds() >= SERVER_DELAY_NEXT_HAND_SEC) - server.SetState(ServerGameStateStartHand::Instance()); - - return retVal; -} - -int -ServerGameStateNextHandDelay::InternalProcess(ServerGameThread &/*server*/, SessionWrapper /*session*/, boost::shared_ptr /*packet*/) -{ - return MSG_SOCK_INTERNAL_PENDING; -} - -//----------------------------------------------------------------------------- - -ServerGameStateNextGameDelay ServerGameStateNextGameDelay::m_state; - -ServerGameStateNextGameDelay & -ServerGameStateNextGameDelay::Instance() -{ - return m_state; -} - -ServerGameStateNextGameDelay::ServerGameStateNextGameDelay() -{ -} - -ServerGameStateNextGameDelay::~ServerGameStateNextGameDelay() -{ -} - -int -ServerGameStateNextGameDelay::Process(ServerGameThread &server) -{ - int retVal = AbstractServerGameStateReceiving::Process(server); - - if (server.GetStateTimer().elapsed().total_seconds() >= SERVER_DELAY_NEXT_GAME_SEC) - { - Game &curGame = server.GetGame(); - // The game has ended. Notify all clients. - boost::shared_ptr winnerPlayer; - PlayerListIterator i = curGame.getActivePlayerList()->begin(); - PlayerListIterator end = curGame.getActivePlayerList()->end(); - while (i != end) - { - winnerPlayer = *i; - if (winnerPlayer->getMyCash() > 0) - break; - ++i; - } - - boost::shared_ptr endGame(new NetPacketEndOfGame); - NetPacketEndOfGame::Data endGameData; - endGameData.winnerPlayerId = winnerPlayer->getMyUniqueID(); - static_cast(endGame.get())->SetData(endGameData); - - server.SendToAllPlayers(endGame, SessionData::Game); - - // Wait for the start of a new game. - server.ResetComputerPlayerList(); - server.ResetGame(); - server.SetState(ServerGameStateInit::Instance()); - server.GetLobbyThread().NotifyReopeningGame(server.GetId()); - } - - return retVal; -} - -int -ServerGameStateNextGameDelay::InternalProcess(ServerGameThread &/*server*/, SessionWrapper /*session*/, boost::shared_ptr /*packet*/) -{ - return MSG_SOCK_INTERNAL_PENDING; -} - -//----------------------------------------------------------------------------- - -ServerGameStateFinal ServerGameStateFinal::m_state; - -ServerGameStateFinal & -ServerGameStateFinal::Instance() -{ - return m_state; -} - -ServerGameStateFinal::ServerGameStateFinal() -{ -} - -ServerGameStateFinal::~ServerGameStateFinal() -{ -} - -int -ServerGameStateFinal::InternalProcess(ServerGameThread &/*server*/, SessionWrapper /*session*/, boost::shared_ptr /*packet*/) -{ - return MSG_SOCK_INTERNAL_PENDING; -} - -//----------------------------------------------------------------------------- diff --git a/src/net/common/servergamethread.cpp b/src/net/common/servergamethread.cpp index e2acf518..d56877df 100644 --- a/src/net/common/servergamethread.cpp +++ b/src/net/common/servergamethread.cpp @@ -33,6 +33,7 @@ #define SERVER_CHECK_VOTE_KICK_INTERVAL_MSEC 500 +#define SERVER_REMOVE_PLAYER_INTERVAL_MSEC 1000 #define SERVER_KICK_TIMEOUT_ADD_DELAY_SEC 2 using namespace std; @@ -41,17 +42,29 @@ using namespace std; ServerGameThread::ServerGameThread(ServerLobbyThread &lobbyThread, u_int32_t id, const string &name, const string &pwd, const GameData &gameData, unsigned adminPlayerId, GuiInterface &gui, ConfigFile *playerConfig) : m_adminPlayerId(adminPlayerId), m_lobbyThread(lobbyThread), m_gui(gui), m_gameData(gameData), m_id(id), m_name(name), m_password(pwd), m_playerConfig(playerConfig), - m_curState(NULL), m_gameNum(1), m_curPetitionId(1), - m_stateTimer(boost::posix_time::time_duration(0, 0, 0), boost::timers::portable::microsec_timer::manual_start), - m_stateTimerFlag(0) + m_gameNum(1), m_curPetitionId(1) { LOG_VERBOSE("Game object " << GetId() << " created."); m_receiver.reset(new ReceiverHelper); + + m_removePlayerTimerId = GetLobbyThread().GetTimerManager().RegisterTimer( + SERVER_REMOVE_PLAYER_INTERVAL_MSEC, + boost::bind(&ServerGameThread::TimerRemovePlayer, this), + true); + m_voteKickTimerId = GetLobbyThread().GetTimerManager().RegisterTimer( + SERVER_CHECK_VOTE_KICK_INTERVAL_MSEC, + boost::bind(&ServerGameThread::TimerVoteKick, this), + true); + + SetState(boost::shared_ptr(new SERVER_INITIAL_STATE(*this))); } ServerGameThread::~ServerGameThread() { + GetLobbyThread().GetTimerManager().UnregisterTimer(m_removePlayerTimerId); + GetLobbyThread().GetTimerManager().UnregisterTimer(m_voteKickTimerId); + LOG_VERBOSE("Game object " << GetId() << " destructed."); } @@ -70,9 +83,8 @@ ServerGameThread::GetName() const void ServerGameThread::AddSession(SessionWrapper session) { - // Must be thread safe. - boost::mutex::scoped_lock lock(m_sessionQueueMutex); - m_sessionQueue.push_back(session); + if (session.sessionData) + GetState().HandleNewSession(session); } void @@ -82,6 +94,13 @@ ServerGameThread::RemovePlayer(unsigned playerId, unsigned errorCode) m_removePlayerList.push_back(RemovePlayerList::value_type(playerId, errorCode)); } +void +ServerGameThread::HandlePacket(SessionWrapper session, boost::shared_ptr packet) +{ + if (session.sessionData && packet) + GetState().ProcessPacket(session, packet); +} + GameState ServerGameThread::GetCurRound() const { @@ -111,50 +130,15 @@ ServerGameThread::RemoveAllSessions() GetSessionManager().ForEach(boost::bind(&ServerLobbyThread::RemoveSessionFromGame, boost::ref(lobbyThread), _1)); } -void -ServerGameThread::Main() -{ - LOG_VERBOSE("Game thread " << GetId() << " started."); +// TODO terminate game. +// TODO Handle game termination! +// ResetComputerPlayerList(); +// GetLobbyThread().RemoveGame(GetId()); - SetState(SERVER_INITIAL_STATE::Instance()); - - try - { - do - { - { - // Handle one new session at a time. - SessionWrapper tmpSession; - { - boost::mutex::scoped_lock lock(m_sessionQueueMutex); - if (!m_sessionQueue.empty()) - { - tmpSession = m_sessionQueue.front(); - m_sessionQueue.pop_front(); - } - } - if (tmpSession.sessionData.get()) - GetState().HandleNewSession(*this, tmpSession); - } - // Process current state. - GetState().Process(*this); - RemovePlayerLoop(); - VoteKickLoop(); - } while (!ShouldTerminate() && GetSessionManager().HasSessions()); - } catch (const PokerTHException &e) - { - GetCallback().SignalNetServerError(e.GetErrorId(), e.GetOsErrorCode()); - LOG_ERROR(e.what()); - } - - ResetComputerPlayerList(); - GetLobbyThread().RemoveGame(GetId()); - - LOG_VERBOSE("Game thread " << GetId() << " terminating."); -} +// LOG_VERBOSE("Game thread " << GetId() << " terminating."); void -ServerGameThread::RemovePlayerLoop() +ServerGameThread::TimerRemovePlayer() { boost::mutex::scoped_lock lock(m_removePlayerListMutex); @@ -173,16 +157,7 @@ ServerGameThread::RemovePlayerLoop() } void -ServerGameThread::VoteKickLoop() -{ - // Do not call the vote kick action all the time. - // Check the timer. - if (m_voteKickActionTimer.elapsed().total_milliseconds() >= SERVER_CHECK_VOTE_KICK_INTERVAL_MSEC) - VoteKickAction(); -} - -void -ServerGameThread::VoteKickAction() +ServerGameThread::TimerVoteKick() { // Check whether someone should be kicked, or whether a vote kick should be aborted. // Only one vote kick can be active at a time. @@ -605,7 +580,7 @@ ServerGameThread::RemovePlayerData(boost::shared_ptr player, int rea SetAdminPlayerId(newAdmin->GetUniqueId()); newAdmin->SetRights(PLAYER_RIGHTS_ADMIN); // Notify game state on admin change - GetState().NotifyGameAdminChanged(*this); + GetState().NotifyGameAdminChanged(); // Send "Game Admin Changed" to clients. boost::shared_ptr adminChanged(new NetPacketGameAdminChanged); NetPacketGameAdminChanged::Data adminChangedData; @@ -743,33 +718,9 @@ ServerGameThread::GetState() } void -ServerGameThread::SetState(ServerGameState &newState) +ServerGameThread::SetState(boost::shared_ptr newState) { - newState.Init(*this); - m_curState = &newState; -} - -const boost::timers::portable::microsec_timer & -ServerGameThread::GetStateTimer() const -{ - return m_stateTimer; -} - -boost::timers::portable::microsec_timer & -ServerGameThread::GetStateTimer() -{ - return m_stateTimer; -} - -unsigned -ServerGameThread::GetStateTimerFlag() const -{ - return m_stateTimerFlag; -} -void -ServerGameThread::SetStateTimerFlag(unsigned flag) -{ - m_stateTimerFlag = flag; + m_curState = newState; } ReceiverHelper & diff --git a/src/net/common/serverlobbythread.cpp b/src/net/common/serverlobbythread.cpp index 6a512623..b220600e 100644 --- a/src/net/common/serverlobbythread.cpp +++ b/src/net/common/serverlobbythread.cpp @@ -444,6 +444,12 @@ ServerLobbyThread::RemoveGame(unsigned id) m_removeGameList.push_back(id); } +TimerManager & +ServerLobbyThread::GetTimerManager() +{ + return m_timerManager; +} + AvatarManager & ServerLobbyThread::GetAvatarManager() { @@ -564,12 +570,22 @@ ServerLobbyThread::RegisterTimers() void ServerLobbyThread::HandleRead(SessionId sessionId, const boost::system::error_code& error, size_t bytesRead) { + // Find the session. SessionWrapper session = m_sessionManager.GetSessionById(sessionId); if (!session.sessionData) session = m_gameSessionManager.GetSessionById(sessionId); if (session.sessionData) { + // Retrieve current game, if applicable. unsigned gameId = session.sessionData->GetGameId(); + boost::shared_ptr game; + if (gameId) + { + GameMap::iterator pos = m_gameMap.find(gameId); + + if (pos != m_gameMap.end()) + game = pos->second; + } if (!error) { @@ -581,10 +597,11 @@ ServerLobbyThread::HandleRead(SessionId sessionId, const boost::system::error_co { boost::shared_ptr packet = buf.receivedPackets.front(); buf.receivedPackets.pop_front(); -// if (game) -// game->HandlePacket(session, packet); -// else + if (game) + game->HandlePacket(session, packet); + else HandlePacket(session, packet); + // TODO state may have changed between. } session.sessionData->GetAsioSocket()->async_read_some( boost::asio::buffer(buf.recvBuf + buf.recvBufUsed, RECV_BUF_SIZE - buf.recvBufUsed), @@ -598,14 +615,6 @@ ServerLobbyThread::HandleRead(SessionId sessionId, const boost::system::error_co else { // On error: Close this session. - boost::shared_ptr game; - if (gameId) - { - GameMap::iterator pos = m_gameMap.find(gameId); - - if (pos != m_gameMap.end()) - game = pos->second; - } if (game) game->ErrorRemoveSession(session); else @@ -949,9 +958,6 @@ ServerLobbyThread::HandleNetPacketCreateGame(SessionWrapper session, const NetPa // Add game to list of games. InternalAddGame(game); - - // Start the game. - game->Run(); } void @@ -1055,15 +1061,16 @@ ServerLobbyThread::TimerRemoveGame() RemoveGameList::iterator end = m_removeGameList.end(); // Synchronously remove games which have been closed. + // TODO deprecated while (i != end) { GameMap::iterator pos = m_gameMap.find(*i); if (pos != m_gameMap.end()) { boost::shared_ptr tmpGame = pos->second; - tmpGame->SignalTermination(); - if (!tmpGame->Join(GAME_THREAD_TERMINATE_TIMEOUT)) - throw ServerException(__FILE__, __LINE__, ERR_NET_GAME_TERMINATION_FAILED, 0); +// tmpGame->SignalTermination(); +// if (!tmpGame->Join(GAME_THREAD_TERMINATE_TIMEOUT)) +// throw ServerException(__FILE__, __LINE__, ERR_NET_GAME_TERMINATION_FAILED, 0); InternalRemoveGame(tmpGame); } ++i; @@ -1241,13 +1248,14 @@ ServerLobbyThread::InternalResubscribeMsg(SessionWrapper session) void ServerLobbyThread::TerminateGames() { + // TODO deprecated GameMap::iterator i = m_gameMap.begin(); GameMap::iterator end = m_gameMap.end(); while (i != end) { - i->second->SignalTermination(); - i->second->Join(GAME_THREAD_TERMINATE_TIMEOUT); + //i->second->SignalTermination(); + //i->second->Join(GAME_THREAD_TERMINATE_TIMEOUT); ++i; } m_gameMap.clear(); diff --git a/src/net/servergamestate.h b/src/net/servergamestate.h index 84bff815..a1a0c7ce 100644 --- a/src/net/servergamestate.h +++ b/src/net/servergamestate.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2007 by Lothar May * + * Copyright (C) 2007-2009 by Lothar May * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -16,7 +16,7 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ -/* State of network server. */ +/* State of a network game. */ #ifndef _SERVERGAMESTATE_H_ #define _SERVERGAMESTATE_H_ @@ -40,353 +40,130 @@ class ServerCallback; class ServerGameState { public: + ServerGameState(ServerGameThread &serverGame) : m_serverGame(serverGame) {} virtual ~ServerGameState(); - // Initialize after switching to this state. - virtual void Init(ServerGameThread &server) = 0; - virtual void NotifyGameAdminChanged(ServerGameThread &server) = 0; + virtual void NotifyGameAdminChanged() = 0; // Handling of a new session. - virtual void HandleNewSession(ServerGameThread &server, SessionWrapper session) = 0; + virtual void HandleNewSession(SessionWrapper session) = 0; // Main processing function of the current state. - virtual int Process(ServerGameThread &server) = 0; + virtual int ProcessPacket(SessionWrapper session, boost::shared_ptr packet) = 0; + +protected: + ServerGameThread &GetServerGame() {return m_serverGame;} + +private: + ServerGameThread &m_serverGame; }; // Abstract State: Receiving. -class AbstractServerGameStateReceiving : virtual public ServerGameState +class AbstractServerGameStateReceiving : public ServerGameState { public: + AbstractServerGameStateReceiving(ServerGameThread &serverGame); virtual ~AbstractServerGameStateReceiving(); // Globally handle packets which are allowed in all running states. // Calls InternalProcess if packet has not been processed. - virtual int Process(ServerGameThread &server); + virtual int ProcessPacket(SessionWrapper session, boost::shared_ptr packet); protected: - AbstractServerGameStateReceiving(); - - virtual int InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet) = 0; -}; - -// Abstract State: Game is running. -class AbstractServerGameStateRunning : virtual public ServerGameState -{ -public: - virtual ~AbstractServerGameStateRunning(); - - virtual void NotifyGameAdminChanged(ServerGameThread &/*server*/) {} - - // Reject new connections. - virtual void HandleNewSession(ServerGameThread &server, SessionWrapper session); - -protected: - - AbstractServerGameStateRunning(); -}; - -// Abstract State: Timer. -class AbstractServerGameStateTimer : virtual public ServerGameState -{ -public: - virtual ~AbstractServerGameStateTimer(); - - virtual void Init(ServerGameThread &server); + virtual int InternalProcessPacket(SessionWrapper session, boost::shared_ptr packet) = 0; }; // State: Initialization. -class ServerGameStateInit : public AbstractServerGameStateReceiving, public AbstractServerGameStateTimer +class ServerGameStateInit : public AbstractServerGameStateReceiving { public: - // Access the state singleton. - static ServerGameStateInit &Instance(); - + ServerGameStateInit(ServerGameThread &serverGame); virtual ~ServerGameStateInit(); - virtual void NotifyGameAdminChanged(ServerGameThread &server); + virtual void NotifyGameAdminChanged(); - // - virtual int Process(ServerGameThread &server); - virtual void HandleNewSession(ServerGameThread &server, SessionWrapper session); + virtual void HandleNewSession(SessionWrapper session); protected: - // Protected constructor - this is a singleton. - ServerGameStateInit(); + void RegisterAdminTimers(); + void UnregisterAdminTimers(); + void TimerAdminWarning(); + void TimerAdminTimeout(); - virtual int InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet); + virtual int InternalProcessPacket(SessionWrapper session, boost::shared_ptr packet); static boost::shared_ptr CreateNetPacketPlayerJoined(const PlayerData &playerData); private: - static ServerGameStateInit m_state; + unsigned m_adminWarningTimerId; + unsigned m_adminTimeoutTimerId; + bool m_timerRegistered; }; -// Wait for Ack of start event. -class ServerGameStateWaitAck : public AbstractServerGameStateReceiving, public AbstractServerGameStateRunning, public AbstractServerGameStateTimer +// Wait for Ack of start event and start game. +class ServerGameStateStartGame : public AbstractServerGameStateReceiving { public: - // Access the state singleton. - static ServerGameStateWaitAck &Instance(); - - virtual ~ServerGameStateWaitAck(); - - // Timeout if waiting takes too long. - virtual int Process(ServerGameThread &server); - -protected: - - // Protected constructor - this is a singleton. - ServerGameStateWaitAck(); - - virtual int InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet); - -private: - - static ServerGameStateWaitAck m_state; -}; - -// State: Start server game. -class ServerGameStateStartGame : public AbstractServerGameStateRunning -{ -public: - // Access the state singleton. - static ServerGameStateStartGame &Instance(); + ServerGameStateStartGame(ServerGameThread &serverGame); virtual ~ServerGameStateStartGame(); - virtual void Init(ServerGameThread & /*server*/) {} - - // - virtual int Process(ServerGameThread &server); + virtual void NotifyGameAdminChanged() {} + virtual void HandleNewSession(SessionWrapper session); protected: - // Protected constructor - this is a singleton. - ServerGameStateStartGame(); + virtual int InternalProcessPacket(SessionWrapper session, boost::shared_ptr packet); + void TimerTimeout(); + void DoStart(); private: - - static ServerGameStateStartGame m_state; + unsigned m_timeoutTimerId; }; -// State: Start new hand. -class ServerGameStateStartHand : public AbstractServerGameStateRunning +// State: Within hand. +class ServerGameStateHand : public AbstractServerGameStateReceiving { public: - // Access the state singleton. - static ServerGameStateStartHand &Instance(); + ServerGameStateHand(ServerGameThread &serverGame); + virtual ~ServerGameStateHand(); - virtual ~ServerGameStateStartHand(); - - virtual void Init(ServerGameThread & /*server*/) {} - - // - virtual int Process(ServerGameThread &server); + virtual void NotifyGameAdminChanged() {} + virtual void HandleNewSession(SessionWrapper session); protected: - - // Protected constructor - this is a singleton. - ServerGameStateStartHand(); + virtual int InternalProcessPacket(SessionWrapper session, boost::shared_ptr packet); + void TimerLoop(); + void TimerShowCards(); + void TimerComputerAction(); + void TimerNextHand(); + void TimerNextGame(); + int GetDealCardsDelaySec(); private: - - static ServerGameStateStartHand m_state; -}; - -// State: Start new round. -class ServerGameStateStartRound : public AbstractServerGameStateRunning -{ -public: - // Access the state singleton. - static ServerGameStateStartRound &Instance(); - - virtual ~ServerGameStateStartRound(); - - virtual void Init(ServerGameThread & /*server*/) {} - - // - virtual int Process(ServerGameThread &server); - -protected: - - // Protected constructor - this is a singleton. - ServerGameStateStartRound(); - -private: - - static ServerGameStateStartRound m_state; + unsigned m_loopTimerId; }; // State: Wait for a player action. -class ServerGameStateWaitPlayerAction : public AbstractServerGameStateReceiving, public AbstractServerGameStateRunning, public AbstractServerGameStateTimer +class ServerGameStateWaitPlayerAction : public AbstractServerGameStateReceiving { public: - // Access the state singleton. - static ServerGameStateWaitPlayerAction &Instance(); - + ServerGameStateWaitPlayerAction(ServerGameThread &serverGame); virtual ~ServerGameStateWaitPlayerAction(); - // Handle leaving players. - virtual int Process(ServerGameThread &server); + virtual void NotifyGameAdminChanged() {} + virtual void HandleNewSession(SessionWrapper session); protected: - // Protected constructor - this is a singleton. - ServerGameStateWaitPlayerAction(); - - virtual int InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet); - - static void PerformPlayerAction(ServerGameThread &server, boost::shared_ptr player, PlayerAction action, int bet); + virtual int InternalProcessPacket(SessionWrapper session, boost::shared_ptr packet); + void TimerTimeout(); private: - - static ServerGameStateWaitPlayerAction m_state; -}; - -// State: Delay and computer action -class ServerGameStateComputerAction : public AbstractServerGameStateReceiving, public AbstractServerGameStateRunning, public AbstractServerGameStateTimer -{ -public: - // Access the state singleton. - static ServerGameStateComputerAction &Instance(); - - virtual ~ServerGameStateComputerAction(); - - // Overwrite default processing - virtual int Process(ServerGameThread &server); - -protected: - - // Protected constructor - this is a singleton. - ServerGameStateComputerAction(); - - virtual int InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet); - -private: - - static ServerGameStateComputerAction m_state; -}; - -// State: Delay after dealing cards -class ServerGameStateDealCardsDelay : public AbstractServerGameStateReceiving, public AbstractServerGameStateRunning, public AbstractServerGameStateTimer -{ -public: - // Access the state singleton. - static ServerGameStateDealCardsDelay &Instance(); - - virtual ~ServerGameStateDealCardsDelay(); - - // Overwrite default processing - virtual int Process(ServerGameThread &server); - -protected: - - // Protected constructor - this is a singleton. - ServerGameStateDealCardsDelay(); - - virtual int InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet); - -private: - - static ServerGameStateDealCardsDelay m_state; -}; - -// State: Delay after showing cards (all in) -class ServerGameStateShowCardsDelay : public AbstractServerGameStateReceiving, public AbstractServerGameStateRunning, public AbstractServerGameStateTimer -{ -public: - // Access the state singleton. - static ServerGameStateShowCardsDelay &Instance(); - - virtual ~ServerGameStateShowCardsDelay(); - - // Overwrite default processing - virtual int Process(ServerGameThread &server); - -protected: - - // Protected constructor - this is a singleton. - ServerGameStateShowCardsDelay(); - - virtual int InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet); - -private: - - static ServerGameStateShowCardsDelay m_state; -}; - -// State: Delay before next hand. -class ServerGameStateNextHandDelay : public AbstractServerGameStateReceiving, public AbstractServerGameStateRunning, public AbstractServerGameStateTimer -{ -public: - // Access the state singleton. - static ServerGameStateNextHandDelay &Instance(); - - virtual ~ServerGameStateNextHandDelay(); - - // Overwrite default processing - virtual int Process(ServerGameThread &server); - -protected: - - // Protected constructor - this is a singleton. - ServerGameStateNextHandDelay(); - - virtual int InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet); - -private: - - static ServerGameStateNextHandDelay m_state; -}; - -// State: Delay before next hand. -class ServerGameStateNextGameDelay : public AbstractServerGameStateReceiving, public AbstractServerGameStateRunning, public AbstractServerGameStateTimer -{ -public: - // Access the state singleton. - static ServerGameStateNextGameDelay &Instance(); - - virtual ~ServerGameStateNextGameDelay(); - - // Overwrite default processing - virtual int Process(ServerGameThread &server); - -protected: - - // Protected constructor - this is a singleton. - ServerGameStateNextGameDelay(); - - virtual int InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet); - -private: - - static ServerGameStateNextGameDelay m_state; -}; - -// State: Final. -class ServerGameStateFinal : public AbstractServerGameStateReceiving, public AbstractServerGameStateRunning -{ -public: - // Access the state singleton. - static ServerGameStateFinal &Instance(); - - virtual ~ServerGameStateFinal(); - - virtual void Init(ServerGameThread & /*server*/) {} - -protected: - - // Protected constructor - this is a singleton. - ServerGameStateFinal(); - - virtual int InternalProcess(ServerGameThread &server, SessionWrapper session, boost::shared_ptr packet); - -private: - - static ServerGameStateFinal m_state; + unsigned m_timeoutTimerId; }; #ifdef _MSC_VER diff --git a/src/net/servergamethread.h b/src/net/servergamethread.h index 78e33c25..efec8317 100644 --- a/src/net/servergamethread.h +++ b/src/net/servergamethread.h @@ -39,7 +39,7 @@ class ConfigFile; struct GameData; class Game; -class ServerGameThread : public Thread +class ServerGameThread { public: ServerGameThread( @@ -52,6 +52,8 @@ public: void AddSession(SessionWrapper session); void RemovePlayer(unsigned playerId, unsigned errorCode); + void HandlePacket(SessionWrapper session, boost::shared_ptr packet); + ServerCallback &GetCallback(); GameState GetCurRound() const; @@ -79,11 +81,8 @@ protected: typedef std::deque SessionQueue; - // Main function of the thread. - virtual void Main(); - void RemovePlayerLoop(); - void VoteKickLoop(); - void VoteKickAction(); + void TimerRemovePlayer(); + void TimerVoteKick(); void InternalStartGame(); void ResetGame(); @@ -115,12 +114,7 @@ protected: ServerLobbyThread &GetLobbyThread(); ServerGameState &GetState(); - void SetState(ServerGameState &newState); - - const boost::timers::portable::microsec_timer &GetStateTimer() const; - boost::timers::portable::microsec_timer &GetStateTimer(); - unsigned GetStateTimerFlag() const; - void SetStateTimerFlag(unsigned flag); + void SetState(boost::shared_ptr newState); ReceiverHelper &GetReceiver(); @@ -159,34 +153,26 @@ private: const GameData m_gameData; StartData m_startData; boost::shared_ptr m_game; + boost::shared_ptr m_curState; + const u_int32_t m_id; const std::string m_name; const std::string m_password; ConfigFile *m_playerConfig; - ServerGameState *m_curState; unsigned m_gameNum; unsigned m_curPetitionId; - - boost::timers::portable::microsec_timer m_stateTimer; - unsigned m_stateTimerFlag; + unsigned m_removePlayerTimerId; + unsigned m_voteKickTimerId; boost::timers::portable::microsec_timer m_voteKickActionTimer; friend class ServerLobbyThread; friend class AbstractServerGameStateReceiving; -friend class AbstractServerGameStateRunning; -friend class AbstractServerGameStateTimer; friend class ServerGameStateInit; friend class ServerGameStateWaitAck; friend class ServerGameStateStartGame; -friend class ServerGameStateStartHand; -friend class ServerGameStateStartRound; +friend class ServerGameStateHand; friend class ServerGameStateWaitPlayerAction; -friend class ServerGameStateComputerAction; -friend class ServerGameStateDealCardsDelay; -friend class ServerGameStateShowCardsDelay; -friend class ServerGameStateNextHandDelay; -friend class ServerGameStateNextGameDelay; }; #endif diff --git a/src/net/serverlobbythread.h b/src/net/serverlobbythread.h index 5a1cdad7..0bda4871 100644 --- a/src/net/serverlobbythread.h +++ b/src/net/serverlobbythread.h @@ -90,6 +90,7 @@ public: u_int32_t GetNextGameId(); ServerCallback &GetCallback(); + TimerManager &GetTimerManager(); AvatarManager &GetAvatarManager(); ServerStats GetStats() const;