From 3b25e18fde69a35a007f89c251a995ddacb7e1e1 Mon Sep 17 00:00:00 2001 From: lotodore Date: Fri, 12 Jun 2009 15:48:34 +0000 Subject: [PATCH] Rewriting boost asio timer stuff. (Did I mention I dislike the asio "cancel" semantics?!). TimerManager is no longer used. Should be stable now, but might leak some memory (needs further testing). --- src/net/common/servergame.cpp | 173 +++--- src/net/common/servergamestate.cpp | 754 ++++++++++++++------------- src/net/common/serverlobbythread.cpp | 226 ++++---- src/net/servergame.h | 16 +- src/net/servergamestate.h | 80 +-- src/net/serverlobbythread.h | 27 +- 6 files changed, 681 insertions(+), 595 deletions(-) diff --git a/src/net/common/servergame.cpp b/src/net/common/servergame.cpp index 0ce56764..fc46ec9c 100644 --- a/src/net/common/servergame.cpp +++ b/src/net/common/servergame.cpp @@ -38,10 +38,10 @@ using namespace std; -ServerGame::ServerGame(ServerLobbyThread &lobbyThread, u_int32_t id, const string &name, const string &pwd, const GameData &gameData, unsigned adminPlayerId, GuiInterface &gui, ConfigFile *playerConfig) +ServerGame::ServerGame(boost::shared_ptr 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_curState(NULL), m_id(id), m_name(name), m_password(pwd), m_playerConfig(playerConfig), - m_gameNum(1), m_curPetitionId(1), m_stateTimerId(0) + m_gameNum(1), m_curPetitionId(1), m_voteKickTimer(lobbyThread->GetIOService()), m_stateTimer(lobbyThread->GetIOService()) { LOG_VERBOSE("Game object " << GetId() << " created."); @@ -56,10 +56,11 @@ ServerGame::~ServerGame() void ServerGame::Init() { - m_voteKickTimerId = GetLobbyThread().GetTimerManager().RegisterTimer( - SERVER_CHECK_VOTE_KICK_INTERVAL_MSEC, - boost::bind(&ServerGame::TimerVoteKick, this), - true); + m_voteKickTimer.expires_from_now( + boost::posix_time::milliseconds(SERVER_CHECK_VOTE_KICK_INTERVAL_MSEC)); + m_voteKickTimer.async_wait( + boost::bind( + &ServerGame::TimerVoteKick, shared_from_this(), boost::asio::placeholders::error)); SetState(SERVER_INITIAL_STATE::Instance()); } @@ -67,8 +68,9 @@ ServerGame::Init() void ServerGame::Exit() { - GetLobbyThread().GetTimerManager().UnregisterTimer(m_voteKickTimerId); - GetLobbyThread().GetTimerManager().UnregisterTimer(m_stateTimerId); + m_voteKickTimer.cancel(); + if (m_curState) + m_curState->Exit(shared_from_this()); } u_int32_t @@ -87,7 +89,7 @@ void ServerGame::AddSession(SessionWrapper session) { if (session.sessionData) - GetState().HandleNewSession(*this, session); + GetState().HandleNewSession(shared_from_this(), session); } void @@ -103,7 +105,7 @@ void ServerGame::HandlePacket(SessionWrapper session, boost::shared_ptr packet) { if (session.sessionData && packet) - GetState().ProcessPacket(*this, session, packet); + GetState().ProcessPacket(shared_from_this(), session, packet); } GameState @@ -123,77 +125,85 @@ ServerGame::RemoveAllSessions() { // Called from lobby thread. // Clean up ALL sessions which are left. - GetSessionManager().ForEach(boost::bind(&ServerLobbyThread::RemoveSessionFromGame, boost::ref(m_lobbyThread), _1)); + GetSessionManager().ForEach(boost::bind(&ServerLobbyThread::RemoveSessionFromGame, boost::ref(*m_lobbyThread), _1)); } void -ServerGame::TimerVoteKick() +ServerGame::TimerVoteKick(const boost::system::error_code &ec) { - // Check whether someone should be kicked, or whether a vote kick should be aborted. - // Only one vote kick can be active at a time. - if (m_voteKickData) + if (!ec) { - // Prepare some values. - const PlayerIdList playerIds(GetPlayerIdList()); - int votesRequiredToKick = m_voteKickData->numVotesToKick - m_voteKickData->numVotesInFavourOfKicking; - int playersAllowedToVote = 0; - // We need to count the number of players which are still allowed to vote. - PlayerIdList::const_iterator player_i = playerIds.begin(); - PlayerIdList::const_iterator player_end = playerIds.end(); - while (player_i != player_end) + // Check whether someone should be kicked, or whether a vote kick should be aborted. + // Only one vote kick can be active at a time. + if (m_voteKickData) { - if (find(m_voteKickData->votedPlayerIds.begin(), m_voteKickData->votedPlayerIds.end(), *player_i) == m_voteKickData->votedPlayerIds.end()) - playersAllowedToVote++; - ++player_i; - } - bool abortPetition = false; - bool doKick = false; - EndPetitionReason reason; + // Prepare some values. + const PlayerIdList playerIds(GetPlayerIdList()); + int votesRequiredToKick = m_voteKickData->numVotesToKick - m_voteKickData->numVotesInFavourOfKicking; + int playersAllowedToVote = 0; + // We need to count the number of players which are still allowed to vote. + PlayerIdList::const_iterator player_i = playerIds.begin(); + PlayerIdList::const_iterator player_end = playerIds.end(); + while (player_i != player_end) + { + if (find(m_voteKickData->votedPlayerIds.begin(), m_voteKickData->votedPlayerIds.end(), *player_i) == m_voteKickData->votedPlayerIds.end()) + playersAllowedToVote++; + ++player_i; + } + bool abortPetition = false; + bool doKick = false; + EndPetitionReason reason; - // 1. Enough votes to kick the player. - if (m_voteKickData->numVotesInFavourOfKicking >= m_voteKickData->numVotesToKick) - { - reason = PETITION_END_ENOUGH_VOTES; - abortPetition = true; - doKick = true; - } - // 2. Several players left the game, so a kick is no longer possible. - else if (votesRequiredToKick > playersAllowedToVote) - { - reason = PETITION_END_NOT_ENOUGH_PLAYERS; - abortPetition = true; - } - // 3. The kick has become invalid because the player to be kicked left. - else if (!IsValidPlayer(m_voteKickData->kickPlayerId)) - { - reason = PETITION_END_PLAYER_LEFT; - abortPetition = true; - } - // 4. A kick request timed out (because not everyone voted). - else if (m_voteKickData->voteTimer.elapsed().total_seconds() >= m_voteKickData->timeLimitSec) - { - reason = PETITION_END_TIMEOUT; - abortPetition = true; - } - if (abortPetition) - { - boost::shared_ptr endPetition(new NetPacketEndKickPlayerPetition); - NetPacketEndKickPlayerPetition::Data endPetitionData; - endPetitionData.petitionId = m_voteKickData->petitionId; - endPetitionData.numVotesAgainstKicking = m_voteKickData->numVotesAgainstKicking; - endPetitionData.numVotesInFavourOfKicking = m_voteKickData->numVotesInFavourOfKicking; - endPetitionData.playerKicked = doKick; - endPetitionData.endReason = reason; + // 1. Enough votes to kick the player. + if (m_voteKickData->numVotesInFavourOfKicking >= m_voteKickData->numVotesToKick) + { + reason = PETITION_END_ENOUGH_VOTES; + abortPetition = true; + doKick = true; + } + // 2. Several players left the game, so a kick is no longer possible. + else if (votesRequiredToKick > playersAllowedToVote) + { + reason = PETITION_END_NOT_ENOUGH_PLAYERS; + abortPetition = true; + } + // 3. The kick has become invalid because the player to be kicked left. + else if (!IsValidPlayer(m_voteKickData->kickPlayerId)) + { + reason = PETITION_END_PLAYER_LEFT; + abortPetition = true; + } + // 4. A kick request timed out (because not everyone voted). + else if (m_voteKickData->voteTimer.elapsed().total_seconds() >= m_voteKickData->timeLimitSec) + { + reason = PETITION_END_TIMEOUT; + abortPetition = true; + } + if (abortPetition) + { + boost::shared_ptr endPetition(new NetPacketEndKickPlayerPetition); + NetPacketEndKickPlayerPetition::Data endPetitionData; + endPetitionData.petitionId = m_voteKickData->petitionId; + endPetitionData.numVotesAgainstKicking = m_voteKickData->numVotesAgainstKicking; + endPetitionData.numVotesInFavourOfKicking = m_voteKickData->numVotesInFavourOfKicking; + endPetitionData.playerKicked = doKick; + endPetitionData.endReason = reason; - static_cast(endPetition.get())->SetData(endPetitionData); - SendToAllPlayers(endPetition, SessionData::Game); + static_cast(endPetition.get())->SetData(endPetitionData); + SendToAllPlayers(endPetition, SessionData::Game); - // Perform kick. - if (doKick) - InternalKickPlayer(m_voteKickData->kickPlayerId); - // This petition has ended. - m_voteKickData.reset(); + // Perform kick. + if (doKick) + InternalKickPlayer(m_voteKickData->kickPlayerId); + // This petition has ended. + m_voteKickData.reset(); + } } + m_voteKickTimer.expires_from_now( + boost::posix_time::milliseconds(SERVER_CHECK_VOTE_KICK_INTERVAL_MSEC)); + m_voteKickTimer.async_wait( + boost::bind( + &ServerGame::TimerVoteKick, shared_from_this(), boost::asio::placeholders::error)); } } @@ -545,7 +555,7 @@ ServerGame::RemovePlayerData(boost::shared_ptr player, int reason) SetAdminPlayerId(newAdmin->GetUniqueId()); newAdmin->SetRights(PLAYER_RIGHTS_ADMIN); // Notify game state on admin change - GetState().NotifyGameAdminChanged(*this); + GetState().NotifyGameAdminChanged(shared_from_this()); // Send "Game Admin Changed" to clients. boost::shared_ptr adminChanged(new NetPacketGameAdminChanged); NetPacketGameAdminChanged::Data adminChangedData; @@ -666,7 +676,8 @@ ServerGame::GetSessionManager() const ServerLobbyThread & ServerGame::GetLobbyThread() { - return m_lobbyThread; + assert(m_lobbyThread); + return *m_lobbyThread; } ServerCallback & @@ -686,21 +697,15 @@ void ServerGame::SetState(ServerGameState &newState) { if (m_curState) - m_curState->Exit(*this); + m_curState->Exit(shared_from_this()); m_curState = &newState; - m_curState->Enter(*this); + m_curState->Enter(shared_from_this()); } -unsigned -ServerGame::GetStateTimerId() const +boost::asio::deadline_timer & +ServerGame::GetStateTimer() { - return m_stateTimerId; -} - -void -ServerGame::SetStateTimerId(unsigned newTimerId) -{ - m_stateTimerId = newTimerId; + return m_stateTimer; } ReceiverHelper & diff --git a/src/net/common/servergamestate.cpp b/src/net/common/servergamestate.cpp index 2d3eb415..9cabdc53 100644 --- a/src/net/common/servergamestate.cpp +++ b/src/net/common/servergamestate.cpp @@ -183,7 +183,7 @@ AbstractServerGameStateReceiving::~AbstractServerGameStateReceiving() } int -AbstractServerGameStateReceiving::ProcessPacket(ServerGame &server, SessionWrapper session, boost::shared_ptr packet) +AbstractServerGameStateReceiving::ProcessPacket(boost::shared_ptr server, SessionWrapper session, boost::shared_ptr packet) { // This is the receive loop for the server. int retVal = MSG_SOCK_INTERNAL_PENDING; @@ -193,27 +193,27 @@ AbstractServerGameStateReceiving::ProcessPacket(ServerGame &server, SessionWrapp if (packet->ToNetPacketRetrievePlayerInfo()) { // Delegate to Lobby. - server.GetLobbyThread().HandleGameRetrievePlayerInfo(session, *packet->ToNetPacketRetrievePlayerInfo()); + server->GetLobbyThread().HandleGameRetrievePlayerInfo(session, *packet->ToNetPacketRetrievePlayerInfo()); } else if (packet->ToNetPacketRetrieveAvatar()) { // Delegate to Lobby. - server.GetLobbyThread().HandleGameRetrieveAvatar(session, *packet->ToNetPacketRetrieveAvatar()); + server->GetLobbyThread().HandleGameRetrieveAvatar(session, *packet->ToNetPacketRetrieveAvatar()); } else if (packet->ToNetPacketLeaveCurrentGame()) { - server.MoveSessionToLobby(session, NTF_NET_REMOVED_ON_REQUEST); + 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()) + if (session.playerData->GetRights() == PLAYER_RIGHTS_ADMIN && !server->IsRunning()) { NetPacketKickPlayer::Data kickPlayerData; packet->ToNetPacketKickPlayer()->GetData(kickPlayerData); - server.InternalKickPlayer(kickPlayerData.playerId); + server->InternalKickPlayer(kickPlayerData.playerId); } } else if (packet->ToNetPacketAskKickPlayer()) @@ -223,7 +223,7 @@ AbstractServerGameStateReceiving::ProcessPacket(ServerGame &server, SessionWrapp NetPacketAskKickPlayer::Data askKickData; packet->ToNetPacketAskKickPlayer()->GetData(askKickData); - server.InternalAskVoteKick(session, askKickData.playerId, SERVER_VOTE_KICK_TIMEOUT_SEC); + server->InternalAskVoteKick(session, askKickData.playerId, SERVER_VOTE_KICK_TIMEOUT_SEC); } } else if (packet->ToNetPacketVoteKickPlayer()) @@ -233,7 +233,7 @@ AbstractServerGameStateReceiving::ProcessPacket(ServerGame &server, SessionWrapp NetPacketVoteKickPlayer::Data voteData; packet->ToNetPacketVoteKickPlayer()->GetData(voteData); - server.InternalVoteKick(session, voteData.petitionId, voteData.vote); + server->InternalVoteKick(session, voteData.petitionId, voteData.vote); } } // Chat text is always allowed. @@ -251,7 +251,7 @@ AbstractServerGameStateReceiving::ProcessPacket(ServerGame &server, SessionWrapp outChatData.playerId = session.playerData->GetUniqueId(); outChatData.text = inChatData.text; static_cast(outChat.get())->SetData(outChatData); - server.SendToAllPlayers(outChat, SessionData::Game); + server->SendToAllPlayers(outChat, SessionData::Game); } } else if (packet->ToNetPacketUnsubscribeGameList()) @@ -264,7 +264,7 @@ AbstractServerGameStateReceiving::ProcessPacket(ServerGame &server, SessionWrapp // 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); + server->GetLobbyThread().ResubscribeLobbyMsg(session); } else { @@ -294,44 +294,44 @@ ServerGameStateInit::~ServerGameStateInit() } void -ServerGameStateInit::Enter(ServerGame &server) +ServerGameStateInit::Enter(boost::shared_ptr server) { RegisterAdminTimer(server); } void -ServerGameStateInit::Exit(ServerGame &server) +ServerGameStateInit::Exit(boost::shared_ptr server) { UnregisterAdminTimer(server); } void -ServerGameStateInit::NotifyGameAdminChanged(ServerGame &server) +ServerGameStateInit::NotifyGameAdminChanged(boost::shared_ptr server) { UnregisterAdminTimer(server); RegisterAdminTimer(server); } void -ServerGameStateInit::HandleNewSession(ServerGame &server, SessionWrapper session) +ServerGameStateInit::HandleNewSession(boost::shared_ptr server, SessionWrapper session) { if (session.sessionData.get() && session.playerData.get()) { - size_t curNumPlayers = server.GetCurNumberOfPlayers(); + size_t curNumPlayers = server->GetCurNumberOfPlayers(); // Check the number of players. - if (curNumPlayers >= (size_t)server.GetGameData().maxNumberOfPlayers) + if (curNumPlayers >= (size_t)server->GetGameData().maxNumberOfPlayers) { - server.MoveSessionToLobby(session, NTF_NET_REMOVED_GAME_FULL); + 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()) + else if ((size_t)server->GetGameData().maxNumberOfPlayers > session.sessionData->GetMaxNumPlayers()) { - server.MoveSessionToLobby(session, NTF_NET_REMOVED_GAME_FULL); + server->MoveSessionToLobby(session, NTF_NET_REMOVED_GAME_FULL); } else { - if (session.playerData->GetUniqueId() == server.GetAdminPlayerId()) + if (session.playerData->GetUniqueId() == server->GetAdminPlayerId()) { // This is the admin player. session.playerData->SetRights(PLAYER_RIGHTS_ADMIN); @@ -340,86 +340,93 @@ ServerGameStateInit::HandleNewSession(ServerGame &server, SessionWrapper session // Send ack to client. boost::shared_ptr joinGameAck(new NetPacketJoinGameAck); NetPacketJoinGameAck::Data joinGameAckData; - joinGameAckData.gameId = server.GetId(); + joinGameAckData.gameId = server->GetId(); joinGameAckData.prights = session.playerData->GetRights(); - joinGameAckData.gameData = server.GetGameData(); + joinGameAckData.gameData = server->GetGameData(); static_cast(joinGameAck.get())->SetData(joinGameAckData); - server.GetLobbyThread().GetSender().Send(session.sessionData, joinGameAck); + server->GetLobbyThread().GetSender().Send(session.sessionData, joinGameAck); // Send notifications for connected players to client. - PlayerDataList tmpPlayerList = server.GetFullPlayerDataList(); + PlayerDataList tmpPlayerList = server->GetFullPlayerDataList(); PlayerDataList::iterator player_i = tmpPlayerList.begin(); PlayerDataList::iterator player_end = tmpPlayerList.end(); while (player_i != player_end) { - server.GetLobbyThread().GetSender().Send(session.sessionData, CreateNetPacketPlayerJoined(*(*player_i))); + server->GetLobbyThread().GetSender().Send(session.sessionData, CreateNetPacketPlayerJoined(*(*player_i))); ++player_i; } // Send "Player Joined" to other fully connected clients. - server.SendToAllPlayers(CreateNetPacketPlayerJoined(*session.playerData), SessionData::Game); + server->SendToAllPlayers(CreateNetPacketPlayerJoined(*session.playerData), SessionData::Game); // Accept session. - server.GetSessionManager().AddSession(session); + server->GetSessionManager().AddSession(session); // Notify lobby. - server.GetLobbyThread().NotifyPlayerJoinedGame(server.GetId(), session.playerData->GetUniqueId()); + server->GetLobbyThread().NotifyPlayerJoinedGame(server->GetId(), session.playerData->GetUniqueId()); } } } void -ServerGameStateInit::RegisterAdminTimer(ServerGame &server) +ServerGameStateInit::RegisterAdminTimer(boost::shared_ptr server) { - server.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - (SERVER_GAME_ADMIN_TIMEOUT_SEC - SERVER_GAME_ADMIN_WARNING_REMAINING_SEC) * 1000, - boost::bind(&ServerGameStateInit::TimerAdminWarning, this, boost::ref(server)))); + server->GetStateTimer().expires_from_now( + boost::posix_time::seconds(SERVER_GAME_ADMIN_TIMEOUT_SEC - SERVER_GAME_ADMIN_WARNING_REMAINING_SEC)); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateInit::TimerAdminWarning, this, boost::asio::placeholders::error, server)); } void -ServerGameStateInit::UnregisterAdminTimer(ServerGame &server) +ServerGameStateInit::UnregisterAdminTimer(boost::shared_ptr server) { - server.GetLobbyThread().GetTimerManager().UnregisterTimer(server.GetStateTimerId()); - server.SetStateTimerId(0); + server->GetStateTimer().cancel(); } void -ServerGameStateInit::TimerAdminWarning(ServerGame &server) +ServerGameStateInit::TimerAdminWarning(const boost::system::error_code &ec, boost::shared_ptr server) { - // Find game admin. - SessionWrapper session = server.GetSessionManager().GetSessionByUniquePlayerId(server.GetAdminPlayerId()); - if (session.sessionData.get()) + if (!ec && &server->GetState() == this) { - // 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); - server.GetLobbyThread().GetSender().Send(session.sessionData, warning); + // Find game admin. + SessionWrapper session = server->GetSessionManager().GetSessionByUniquePlayerId(server->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); + server->GetLobbyThread().GetSender().Send(session.sessionData, warning); + } + // Start timeout timer. + server->GetStateTimer().expires_from_now( + boost::posix_time::seconds(SERVER_GAME_ADMIN_WARNING_REMAINING_SEC)); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateInit::TimerAdminTimeout, this, boost::asio::placeholders::error, server)); } - // Start timeout timer. - server.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - SERVER_GAME_ADMIN_WARNING_REMAINING_SEC * 1000, - boost::bind(&ServerGameStateInit::TimerAdminTimeout, this, boost::ref(server)))); } void -ServerGameStateInit::TimerAdminTimeout(ServerGame &server) +ServerGameStateInit::TimerAdminTimeout(const boost::system::error_code &ec, boost::shared_ptr server) { - // Find game admin. - SessionWrapper session = server.GetSessionManager().GetSessionByUniquePlayerId(server.GetAdminPlayerId()); - if (session.sessionData.get()) + if (!ec && &server->GetState() == this) { - // Remove him from the game. - server.MoveSessionToLobby(session, NTF_NET_REMOVED_TIMEOUT); + // Find game admin. + SessionWrapper session = server->GetSessionManager().GetSessionByUniquePlayerId(server->GetAdminPlayerId()); + if (session.sessionData.get()) + { + // Remove him from the game. + server->MoveSessionToLobby(session, NTF_NET_REMOVED_TIMEOUT); + } } } int -ServerGameStateInit::InternalProcessPacket(ServerGame &server, SessionWrapper session, boost::shared_ptr packet) +ServerGameStateInit::InternalProcessPacket(boost::shared_ptr server, SessionWrapper session, boost::shared_ptr packet) { int retVal = MSG_SOCK_INTERNAL_PENDING; @@ -432,45 +439,44 @@ ServerGameStateInit::InternalProcessPacket(ServerGame &server, SessionWrapper se packet->ToNetPacketStartEvent()->GetData(startData); // Fill up with computer players. - server.ResetComputerPlayerList(); + server->ResetComputerPlayerList(); if (startData.fillUpWithCpuPlayers) { - int remainingSlots = server.GetGameData().maxNumberOfPlayers - server.GetCurNumberOfPlayers(); + 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)); + 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); + server->AddComputerPlayer(tmpPlayerData); // Send "Player Joined" to other fully connected clients. - server.SendToAllPlayers(CreateNetPacketPlayerJoined(*tmpPlayerData), SessionData::Game); + server->SendToAllPlayers(CreateNetPacketPlayerJoined(*tmpPlayerData), SessionData::Game); // Notify lobby. - server.GetLobbyThread().NotifyPlayerJoinedGame(server.GetId(), tmpPlayerData->GetUniqueId()); + 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->SendToAllPlayers(boost::shared_ptr(packet->Clone()), SessionData::Game); - server.SetState(ServerGameStateStartGame::Instance()); + server->SetState(ServerGameStateStartGame::Instance()); } } else if (packet->ToNetPacketResetTimeout()) { if (session.playerData->GetRights() == PLAYER_RIGHTS_ADMIN) { - UnregisterAdminTimer(server); RegisterAdminTimer(server); } } else { - server.SessionError(session, ERR_SOCK_INVALID_PACKET); + server->SessionError(session, ERR_SOCK_INVALID_PACKET); } return retVal; @@ -506,40 +512,40 @@ ServerGameStateStartGame::~ServerGameStateStartGame() } void -ServerGameStateStartGame::Enter(ServerGame &server) +ServerGameStateStartGame::Enter(boost::shared_ptr server) { - server.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - SERVER_START_GAME_TIMEOUT_SEC * 1000, - boost::bind(&ServerGameStateStartGame::TimerTimeout, this, boost::ref(server)))); + server->GetStateTimer().expires_from_now( + boost::posix_time::seconds(SERVER_START_GAME_TIMEOUT_SEC)); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateStartGame::TimerTimeout, this, boost::asio::placeholders::error, server)); } void -ServerGameStateStartGame::Exit(ServerGame &server) +ServerGameStateStartGame::Exit(boost::shared_ptr server) { - server.GetLobbyThread().GetTimerManager().UnregisterTimer(server.GetStateTimerId()); - server.SetStateTimerId(0); + server->GetStateTimer().cancel(); } void -ServerGameStateStartGame::HandleNewSession(ServerGame &server, SessionWrapper session) +ServerGameStateStartGame::HandleNewSession(boost::shared_ptr server, SessionWrapper session) { // Do not accept new sessions in this state. - server.MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING); + server->MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING); } int -ServerGameStateStartGame::InternalProcessPacket(ServerGame &server, SessionWrapper session, boost::shared_ptr packet) +ServerGameStateStartGame::InternalProcessPacket(boost::shared_ptr 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()) + if (server->GetSessionManager().CountReadySessions() == server->GetSessionManager().GetRawSessionCount()) { // Everyone is ready. - server.GetSessionManager().ResetAllReadyFlags(); + server->GetSessionManager().ResetAllReadyFlags(); DoStart(server); retVal = MSG_SOCK_INIT_DONE; } @@ -549,37 +555,40 @@ ServerGameStateStartGame::InternalProcessPacket(ServerGame &server, SessionWrapp } void -ServerGameStateStartGame::TimerTimeout(ServerGame &server) +ServerGameStateStartGame::TimerTimeout(const boost::system::error_code &ec, boost::shared_ptr server) { - // On timeout: start anyway. - server.GetSessionManager().ResetAllReadyFlags(); - // TODO report successful start! -> new callback?! - //retVal = MSG_SOCK_INIT_DONE; - DoStart(server); + if (!ec && &server->GetState() == this) + { + // On timeout: start anyway. + server->GetSessionManager().ResetAllReadyFlags(); + // TODO report successful start! -> new callback?! + //retVal = MSG_SOCK_INIT_DONE; + DoStart(server); + } } void -ServerGameStateStartGame::DoStart(ServerGame &server) +ServerGameStateStartGame::DoStart(boost::shared_ptr server) { - PlayerDataList tmpPlayerList = server.GetFullPlayerDataList(); + PlayerDataList tmpPlayerList = server->GetFullPlayerDataList(); if (tmpPlayerList.size() <= 1) { if (!tmpPlayerList.empty()) { boost::shared_ptr tmpPlayer(tmpPlayerList.front()); - SessionWrapper tmpSession = server.GetSessionManager().GetSessionByUniquePlayerId(tmpPlayer->GetUniqueId()); + SessionWrapper tmpSession = server->GetSessionManager().GetSessionByUniquePlayerId(tmpPlayer->GetUniqueId()); if (tmpSession.sessionData.get()) - server.MoveSessionToLobby(tmpSession, NTF_NET_REMOVED_START_FAILED); + server->MoveSessionToLobby(tmpSession, NTF_NET_REMOVED_START_FAILED); } } else { - server.InternalStartGame(); + server->InternalStartGame(); boost::shared_ptr answer(new NetPacketGameStart); NetPacketGameStart::Data gameStartData; - gameStartData.startData = server.GetStartData(); + gameStartData.startData = server->GetStartData(); // Send player order to clients. // Assume player list is sorted by number. @@ -595,11 +604,11 @@ ServerGameStateStartGame::DoStart(ServerGame &server) } static_cast(answer.get())->SetData(gameStartData); - server.SendToAllPlayers(answer, SessionData::Game); + server->SendToAllPlayers(answer, SessionData::Game); // Start the first hand. ServerGameStateHand::StartNewHand(server); - server.SetState(ServerGameStateHand::Instance()); + server->SetState(ServerGameStateHand::Instance()); } } @@ -622,290 +631,313 @@ ServerGameStateHand::~ServerGameStateHand() } void -ServerGameStateHand::Enter(ServerGame &server) +ServerGameStateHand::Enter(boost::shared_ptr server) { - server.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - SERVER_LOOP_DELAY_MSEC, - boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)))); + server->GetStateTimer().expires_from_now( + boost::posix_time::milliseconds(SERVER_LOOP_DELAY_MSEC)); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateHand::TimerLoop, this, boost::asio::placeholders::error, server)); } void -ServerGameStateHand::Exit(ServerGame &server) +ServerGameStateHand::Exit(boost::shared_ptr server) { - server.GetLobbyThread().GetTimerManager().UnregisterTimer(server.GetStateTimerId()); - server.SetStateTimerId(0); + server->GetStateTimer().cancel(); } void -ServerGameStateHand::HandleNewSession(ServerGame &server, SessionWrapper session) +ServerGameStateHand::HandleNewSession(boost::shared_ptr server, SessionWrapper session) { // Do not accept new sessions in this state. - server.MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING); + server->MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING); } int -ServerGameStateHand::InternalProcessPacket(ServerGame &/*server*/, SessionWrapper /*session*/, boost::shared_ptr /*packet*/) +ServerGameStateHand::InternalProcessPacket(boost::shared_ptr /*server*/, SessionWrapper /*session*/, boost::shared_ptr /*packet*/) { // TODO: maybe reject packet. return MSG_SOCK_INTERNAL_PENDING; } void -ServerGameStateHand::TimerLoop(ServerGame &server) +ServerGameStateHand::TimerLoop(const boost::system::error_code &ec, boost::shared_ptr server) { - Game &curGame = server.GetGame(); - - // Main game loop. - int curRound = curGame.getCurrentHand()->getCurrentRound(); - curGame.getCurrentHand()->switchRounds(); - if (!curGame.getCurrentHand()->getAllInCondition()) - curGame.getCurrentHand()->getCurrentBeRo()->run(); - int newRound = curGame.getCurrentHand()->getCurrentRound(); - - // If round changes, deal cards if needed. - if (newRound != curRound && newRound != GAME_STATE_POST_RIVER) + if (!ec && &server->GetState() == this) { - if (newRound <= curRound) - throw ServerException(__FILE__, __LINE__, ERR_NET_INVALID_GAME_ROUND, 0); + Game &curGame = server->GetGame(); - // Retrieve non-fold players. If only one player is left, no cards are shown. - list > nonFoldPlayers = *curGame.getActivePlayerList(); - nonFoldPlayers.remove_if(boost::bind(&PlayerInterface::getMyAction, _1) == PLAYER_ACTION_FOLD); + // Main game loop. + int curRound = curGame.getCurrentHand()->getCurrentRound(); + curGame.getCurrentHand()->switchRounds(); + if (!curGame.getCurrentHand()->getAllInCondition()) + curGame.getCurrentHand()->getCurrentBeRo()->run(); + int newRound = curGame.getCurrentHand()->getCurrentRound(); - if (curGame.getCurrentHand()->getAllInCondition() - && !curGame.getCurrentHand()->getCardsShown() - && nonFoldPlayers.size() > 1) + // If round changes, deal cards if needed. + if (newRound != curRound && newRound != GAME_STATE_POST_RIVER) { - // Send cards of all active players to all players (all in). - boost::shared_ptr allIn(new NetPacketAllInShowCards); - NetPacketAllInShowCards::Data allInData; - - PlayerListConstIterator i = nonFoldPlayers.begin(); - PlayerListConstIterator end = nonFoldPlayers.end(); - - while (i != end) - { - NetPacketAllInShowCards::PlayerCards tmpPlayerCards; - tmpPlayerCards.playerId = (*i)->getMyUniqueID(); - - int tmpCards[2]; - (*i)->getMyCards(tmpCards); - tmpPlayerCards.cards[0] = static_cast(tmpCards[0]); - tmpPlayerCards.cards[1] = static_cast(tmpCards[1]); - - allInData.playerCards.push_back(tmpPlayerCards); - ++i; - } - static_cast(allIn.get())->SetData(allInData); - server.SendToAllPlayers(allIn, SessionData::Game); - curGame.getCurrentHand()->setCardsShown(true); - - server.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - SERVER_SHOW_CARDS_DELAY_SEC * 1000, - boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)))); - } - else - { - SendNewRoundCards(server, curGame, newRound); - - server.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - GetDealCardsDelaySec(server) * 1000, - boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)))); - } - } - else - { - if (newRound != GAME_STATE_POST_RIVER) // continue hand - { - if (curGame.getCurrentHand()->getAllInCondition()) - throw ServerException(__FILE__, __LINE__, ERR_NET_INTERNAL_GAME_ERROR, 0); - - // Retrieve current player. - boost::shared_ptr curPlayer = curGame.getCurrentPlayer(); - if (!curPlayer.get()) - throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0); - if (!curPlayer->getMyActiveStatus()) - throw ServerException(__FILE__, __LINE__, ERR_NET_PLAYER_NOT_ACTIVE, 0); - - boost::shared_ptr notification(new NetPacketPlayersTurn); - NetPacketPlayersTurn::Data playersTurnData; - playersTurnData.gameState = (GameState)curGame.getCurrentHand()->getCurrentRound(); - playersTurnData.playerId = curPlayer->getMyUniqueID(); - static_cast(notification.get())->SetData(playersTurnData); - - server.SendToAllPlayers(notification, SessionData::Game); - - // If the player is computer controlled, let the engine act. - if (curPlayer->getMyType() == PLAYER_TYPE_COMPUTER) - { - server.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - SERVER_COMPUTER_ACTION_DELAY_SEC * 1000, - boost::bind(&ServerGameStateHand::TimerComputerAction, this, boost::ref(server)))); - } - // 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.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - SERVER_LOOP_DELAY_MSEC, - boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)))); - } - else - { - server.SetState(ServerGameStateWaitPlayerAction::Instance()); - } - } - else // hand is over - { - // Engine will find out who won. - curGame.getCurrentHand()->getCurrentBeRo()->postRiverRun(); + if (newRound <= curRound) + throw ServerException(__FILE__, __LINE__, ERR_NET_INVALID_GAME_ROUND, 0); // Retrieve non-fold players. If only one player is left, no cards are shown. list > nonFoldPlayers = *curGame.getActivePlayerList(); nonFoldPlayers.remove_if(boost::bind(&PlayerInterface::getMyAction, _1) == PLAYER_ACTION_FOLD); - if (nonFoldPlayers.size() == 1) + if (curGame.getCurrentHand()->getAllInCondition() + && !curGame.getCurrentHand()->getCardsShown() + && nonFoldPlayers.size() > 1) { - // End of Hand, but keep cards hidden. - boost::shared_ptr player = nonFoldPlayers.front(); - boost::shared_ptr endHand(new NetPacketEndOfHandHideCards); - NetPacketEndOfHandHideCards::Data endHandData; - endHandData.playerId = player->getMyUniqueID(); - endHandData.moneyWon = player->getLastMoneyWon(); - endHandData.playerMoney = player->getMyCash(); - static_cast(endHand.get())->SetData(endHandData); - - server.SendToAllPlayers(endHand, SessionData::Game); - } - else - { - // End of Hand - show cards of active players. - boost::shared_ptr endHand(new NetPacketEndOfHandShowCards); - NetPacketEndOfHandShowCards::Data endHandData; + // Send cards of all active players to all players (all in). + boost::shared_ptr allIn(new NetPacketAllInShowCards); + NetPacketAllInShowCards::Data allInData; PlayerListConstIterator i = nonFoldPlayers.begin(); PlayerListConstIterator end = nonFoldPlayers.end(); while (i != end) { - NetPacketEndOfHandShowCards::PlayerResult tmpPlayerResult; - tmpPlayerResult.playerId = (*i)->getMyUniqueID(); + NetPacketAllInShowCards::PlayerCards tmpPlayerCards; + tmpPlayerCards.playerId = (*i)->getMyUniqueID(); int tmpCards[2]; - int bestHandPos[5]; (*i)->getMyCards(tmpCards); - tmpPlayerResult.cards[0] = static_cast(tmpCards[0]); - tmpPlayerResult.cards[1] = static_cast(tmpCards[1]); + tmpPlayerCards.cards[0] = static_cast(tmpCards[0]); + tmpPlayerCards.cards[1] = static_cast(tmpCards[1]); - (*i)->getMyBestHandPosition(bestHandPos); - for (int num = 0; num < 5; num++) - tmpPlayerResult.bestHandPos[num] = bestHandPos[num]; - - tmpPlayerResult.valueOfCards = (*i)->getMyCardsValueInt(); - tmpPlayerResult.moneyWon = (*i)->getLastMoneyWon(); - tmpPlayerResult.playerMoney = (*i)->getMyCash(); - - endHandData.playerResults.push_back(tmpPlayerResult); + allInData.playerCards.push_back(tmpPlayerCards); ++i; } - static_cast(endHand.get())->SetData(endHandData); + static_cast(allIn.get())->SetData(allInData); + server->SendToAllPlayers(allIn, SessionData::Game); + curGame.getCurrentHand()->setCardsShown(true); - server.SendToAllPlayers(endHand, SessionData::Game); - } - - // Remove disconnected players. This is the one and only place to do this. - server.RemoveDisconnectedPlayers(); - - // Start next hand - if enough players are left. - list > playersWithCash = *curGame.getActivePlayerList(); - playersWithCash.remove_if(boost::bind(&PlayerInterface::getMyCash, _1) < 1); - - if (playersWithCash.empty()) - { - // No more players left - restart. - server.SetState(SERVER_INITIAL_STATE::Instance()); - } - else if (playersWithCash.size() == 1) - { - // View a dialog for a new game - delayed. - server.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - SERVER_DELAY_NEXT_GAME_SEC * 1000, - boost::bind(&ServerGameStateHand::TimerNextGame, this, boost::ref(server)))); + server->GetStateTimer().expires_from_now( + boost::posix_time::seconds(SERVER_SHOW_CARDS_DELAY_SEC)); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateHand::TimerLoop, this, boost::asio::placeholders::error, server)); } else { - server.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - SERVER_DELAY_NEXT_HAND_SEC * 1000, - boost::bind(&ServerGameStateHand::TimerNextHand, this, boost::ref(server)))); + SendNewRoundCards(*server, curGame, newRound); + + server->GetStateTimer().expires_from_now( + boost::posix_time::seconds(GetDealCardsDelaySec(*server))); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateHand::TimerLoop, this, boost::asio::placeholders::error, server)); + } + } + else + { + if (newRound != GAME_STATE_POST_RIVER) // continue hand + { + if (curGame.getCurrentHand()->getAllInCondition()) + throw ServerException(__FILE__, __LINE__, ERR_NET_INTERNAL_GAME_ERROR, 0); + + // Retrieve current player. + boost::shared_ptr curPlayer = curGame.getCurrentPlayer(); + if (!curPlayer.get()) + throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0); + if (!curPlayer->getMyActiveStatus()) + throw ServerException(__FILE__, __LINE__, ERR_NET_PLAYER_NOT_ACTIVE, 0); + + boost::shared_ptr notification(new NetPacketPlayersTurn); + NetPacketPlayersTurn::Data playersTurnData; + playersTurnData.gameState = (GameState)curGame.getCurrentHand()->getCurrentRound(); + playersTurnData.playerId = curPlayer->getMyUniqueID(); + static_cast(notification.get())->SetData(playersTurnData); + + server->SendToAllPlayers(notification, SessionData::Game); + + // If the player is computer controlled, let the engine act. + if (curPlayer->getMyType() == PLAYER_TYPE_COMPUTER) + { + server->GetStateTimer().expires_from_now( + boost::posix_time::seconds(SERVER_COMPUTER_ACTION_DELAY_SEC)); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateHand::TimerComputerAction, this, boost::asio::placeholders::error, server)); + } + // 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->GetStateTimer().expires_from_now( + boost::posix_time::milliseconds(SERVER_LOOP_DELAY_MSEC)); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateHand::TimerLoop, this, boost::asio::placeholders::error, server)); + } + else + { + server->SetState(ServerGameStateWaitPlayerAction::Instance()); + } + } + else // hand is over + { + // Engine will find out who won. + curGame.getCurrentHand()->getCurrentBeRo()->postRiverRun(); + + // Retrieve non-fold players. If only one player is left, no cards are shown. + list > nonFoldPlayers = *curGame.getActivePlayerList(); + nonFoldPlayers.remove_if(boost::bind(&PlayerInterface::getMyAction, _1) == PLAYER_ACTION_FOLD); + + if (nonFoldPlayers.size() == 1) + { + // End of Hand, but keep cards hidden. + boost::shared_ptr player = nonFoldPlayers.front(); + boost::shared_ptr endHand(new NetPacketEndOfHandHideCards); + NetPacketEndOfHandHideCards::Data endHandData; + endHandData.playerId = player->getMyUniqueID(); + endHandData.moneyWon = player->getLastMoneyWon(); + endHandData.playerMoney = player->getMyCash(); + static_cast(endHand.get())->SetData(endHandData); + + server->SendToAllPlayers(endHand, SessionData::Game); + } + else + { + // End of Hand - show cards of active players. + boost::shared_ptr endHand(new NetPacketEndOfHandShowCards); + NetPacketEndOfHandShowCards::Data endHandData; + + PlayerListConstIterator i = nonFoldPlayers.begin(); + PlayerListConstIterator end = nonFoldPlayers.end(); + + while (i != end) + { + NetPacketEndOfHandShowCards::PlayerResult tmpPlayerResult; + tmpPlayerResult.playerId = (*i)->getMyUniqueID(); + + int tmpCards[2]; + int bestHandPos[5]; + (*i)->getMyCards(tmpCards); + tmpPlayerResult.cards[0] = static_cast(tmpCards[0]); + tmpPlayerResult.cards[1] = static_cast(tmpCards[1]); + + (*i)->getMyBestHandPosition(bestHandPos); + for (int num = 0; num < 5; num++) + tmpPlayerResult.bestHandPos[num] = bestHandPos[num]; + + tmpPlayerResult.valueOfCards = (*i)->getMyCardsValueInt(); + tmpPlayerResult.moneyWon = (*i)->getLastMoneyWon(); + tmpPlayerResult.playerMoney = (*i)->getMyCash(); + + endHandData.playerResults.push_back(tmpPlayerResult); + ++i; + } + static_cast(endHand.get())->SetData(endHandData); + + server->SendToAllPlayers(endHand, SessionData::Game); + } + + // Remove disconnected players. This is the one and only place to do this. + server->RemoveDisconnectedPlayers(); + + // Start next hand - if enough players are left. + list > playersWithCash = *curGame.getActivePlayerList(); + playersWithCash.remove_if(boost::bind(&PlayerInterface::getMyCash, _1) < 1); + + if (playersWithCash.empty()) + { + // No more players left - restart. + server->SetState(SERVER_INITIAL_STATE::Instance()); + } + else if (playersWithCash.size() == 1) + { + // View a dialog for a new game - delayed. + server->GetStateTimer().expires_from_now( + boost::posix_time::seconds(SERVER_DELAY_NEXT_GAME_SEC)); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateHand::TimerNextGame, this, boost::asio::placeholders::error, server)); + } + else + { + server->GetStateTimer().expires_from_now( + boost::posix_time::seconds(SERVER_DELAY_NEXT_HAND_SEC)); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateHand::TimerNextHand, this, boost::asio::placeholders::error, server)); + } } } } } void -ServerGameStateHand::TimerShowCards(ServerGame &server) +ServerGameStateHand::TimerShowCards(const boost::system::error_code &ec, boost::shared_ptr server) { - Game &curGame = server.GetGame(); - SendNewRoundCards(server, curGame, curGame.getCurrentHand()->getCurrentRound()); - - server.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - GetDealCardsDelaySec(server) * 1000, - boost::bind(&ServerGameStateHand::TimerLoop, this, boost::ref(server)))); -} - -void -ServerGameStateHand::TimerComputerAction(ServerGame &server) -{ - boost::shared_ptr tmpPlayer = server.GetGame().getCurrentPlayer(); - - tmpPlayer->action(); - SendPlayerAction(server, tmpPlayer); - TimerLoop(server); -} - -void -ServerGameStateHand::TimerNextHand(ServerGame &server) -{ - StartNewHand(server); - TimerLoop(server); -} - -void -ServerGameStateHand::TimerNextGame(ServerGame &server) -{ - 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) + if (!ec && &server->GetState() == this) { - winnerPlayer = *i; - if (winnerPlayer->getMyCash() > 0) - break; - ++i; + Game &curGame = server->GetGame(); + SendNewRoundCards(*server, curGame, curGame.getCurrentHand()->getCurrentRound()); + + server->GetStateTimer().expires_from_now( + boost::posix_time::seconds(GetDealCardsDelaySec(*server))); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateHand::TimerLoop, this, boost::asio::placeholders::error, server)); } +} - boost::shared_ptr endGame(new NetPacketEndOfGame); - NetPacketEndOfGame::Data endGameData; - endGameData.winnerPlayerId = winnerPlayer->getMyUniqueID(); - static_cast(endGame.get())->SetData(endGameData); +void +ServerGameStateHand::TimerComputerAction(const boost::system::error_code &ec, boost::shared_ptr server) +{ + if (!ec && &server->GetState() == this) + { + boost::shared_ptr tmpPlayer = server->GetGame().getCurrentPlayer(); - server.SendToAllPlayers(endGame, SessionData::Game); + tmpPlayer->action(); + SendPlayerAction(*server, tmpPlayer); + TimerLoop(ec, server); + } +} - // Wait for the start of a new game. - server.ResetComputerPlayerList(); - server.ResetGame(); - server.GetLobbyThread().NotifyReopeningGame(server.GetId()); - server.SetState(ServerGameStateInit::Instance()); +void +ServerGameStateHand::TimerNextHand(const boost::system::error_code &ec, boost::shared_ptr server) +{ + if (!ec && &server->GetState() == this) + { + StartNewHand(server); + TimerLoop(ec, server); + } +} + +void +ServerGameStateHand::TimerNextGame(const boost::system::error_code &ec, boost::shared_ptr server) +{ + if (!ec && &server->GetState() == this) + { + 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->GetLobbyThread().NotifyReopeningGame(server->GetId()); + server->SetState(ServerGameStateInit::Instance()); + } } int @@ -931,10 +963,10 @@ ServerGameStateHand::GetDealCardsDelaySec(ServerGame &server) } void -ServerGameStateHand::StartNewHand(ServerGame &server) +ServerGameStateHand::StartNewHand(boost::shared_ptr server) { // Initialize hand. - Game &curGame = server.GetGame(); + Game &curGame = server->GetGame(); curGame.initHand(); // HACK: Skip GUI notification run @@ -962,7 +994,7 @@ ServerGameStateHand::StartNewHand(ServerGame &server) handStartData.smallBlind = curGame.getCurrentHand()->getSmallBlind(); static_cast(notifyCards.get())->SetData(handStartData); - server.GetLobbyThread().GetSender().Send(tmpPlayer->getNetSessionData(), notifyCards); + server->GetLobbyThread().GetSender().Send(tmpPlayer->getNetSessionData(), notifyCards); } ++i; } @@ -986,10 +1018,10 @@ ServerGameStateHand::StartNewHand(ServerGame &server) actionDoneData.playerAction = (PlayerAction)tmpPlayer->getMyAction(); actionDoneData.totalPlayerBet = tmpPlayer->getMySet(); actionDoneData.playerMoney = tmpPlayer->getMyCash(); - actionDoneData.highestSet = server.GetGame().getCurrentHand()->getCurrentBeRo()->getHighestSet(); - actionDoneData.minimumRaise = server.GetGame().getCurrentHand()->getCurrentBeRo()->getMinimumRaise(); + actionDoneData.highestSet = server->GetGame().getCurrentHand()->getCurrentBeRo()->getHighestSet(); + actionDoneData.minimumRaise = server->GetGame().getCurrentHand()->getCurrentBeRo()->getMinimumRaise(); static_cast(notifySmallBlind.get())->SetData(actionDoneData); - server.SendToAllPlayers(notifySmallBlind, SessionData::Game); + server->SendToAllPlayers(notifySmallBlind, SessionData::Game); break; } ++i; @@ -1009,10 +1041,10 @@ ServerGameStateHand::StartNewHand(ServerGame &server) actionDoneData.playerAction = (PlayerAction)tmpPlayer->getMyAction(); actionDoneData.totalPlayerBet = tmpPlayer->getMySet(); actionDoneData.playerMoney = tmpPlayer->getMyCash(); - actionDoneData.highestSet = server.GetGame().getCurrentHand()->getCurrentBeRo()->getHighestSet(); - actionDoneData.minimumRaise = server.GetGame().getCurrentHand()->getCurrentBeRo()->getMinimumRaise(); + actionDoneData.highestSet = server->GetGame().getCurrentHand()->getCurrentBeRo()->getHighestSet(); + actionDoneData.minimumRaise = server->GetGame().getCurrentHand()->getCurrentBeRo()->getMinimumRaise(); static_cast(notifyBigBlind.get())->SetData(actionDoneData); - server.SendToAllPlayers(notifyBigBlind, SessionData::Game); + server->SendToAllPlayers(notifyBigBlind, SessionData::Game); break; } ++i; @@ -1039,36 +1071,35 @@ ServerGameStateWaitPlayerAction::~ServerGameStateWaitPlayerAction() } void -ServerGameStateWaitPlayerAction::Enter(ServerGame &server) +ServerGameStateWaitPlayerAction::Enter(boost::shared_ptr server) { #ifdef SERVER_TEST int timeoutSec = 0; #else - int timeoutSec = server.GetGameData().playerActionTimeoutSec + SERVER_PLAYER_TIMEOUT_ADD_DELAY_SEC; + int timeoutSec = server->GetGameData().playerActionTimeoutSec + SERVER_PLAYER_TIMEOUT_ADD_DELAY_SEC; #endif - server.SetStateTimerId( - server.GetLobbyThread().GetTimerManager().RegisterTimer( - timeoutSec * 1000, - boost::bind(&ServerGameStateWaitPlayerAction::TimerTimeout, this, boost::ref(server)))); + server->GetStateTimer().expires_from_now(boost::posix_time::seconds(timeoutSec)); + server->GetStateTimer().async_wait( + boost::bind( + &ServerGameStateWaitPlayerAction::TimerTimeout, this, boost::asio::placeholders::error, server)); } void -ServerGameStateWaitPlayerAction::Exit(ServerGame &server) +ServerGameStateWaitPlayerAction::Exit(boost::shared_ptr server) { - server.GetLobbyThread().GetTimerManager().UnregisterTimer(server.GetStateTimerId()); - server.SetStateTimerId(0); + server->GetStateTimer().cancel(); } void -ServerGameStateWaitPlayerAction::HandleNewSession(ServerGame &server, SessionWrapper session) +ServerGameStateWaitPlayerAction::HandleNewSession(boost::shared_ptr server, SessionWrapper session) { // Do not accept new sessions in this state. - server.MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING); + server->MoveSessionToLobby(session, NTF_NET_REMOVED_ALREADY_RUNNING); } int -ServerGameStateWaitPlayerAction::InternalProcessPacket(ServerGame &server, SessionWrapper session, boost::shared_ptr packet) +ServerGameStateWaitPlayerAction::InternalProcessPacket(boost::shared_ptr server, SessionWrapper session, boost::shared_ptr packet) { int retVal = MSG_SOCK_INTERNAL_PENDING; @@ -1077,7 +1108,7 @@ ServerGameStateWaitPlayerAction::InternalProcessPacket(ServerGame &server, Sessi NetPacketPlayersAction::Data actionData; packet->ToNetPacketPlayersAction()->GetData(actionData); - Game &curGame = server.GetGame(); + Game &curGame = server->GetGame(); boost::shared_ptr tmpPlayer = curGame.getPlayerByUniqueId(session.playerData->GetUniqueId()); if (!tmpPlayer.get()) throw ServerException(__FILE__, __LINE__, ERR_NET_UNKNOWN_PLAYER_ID, 0); @@ -1088,7 +1119,7 @@ ServerGameStateWaitPlayerAction::InternalProcessPacket(ServerGame &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 = server->GetGame().getCurrentPlayer(); if (code == ACTION_CODE_VALID && (curPlayer->getMyUniqueID() != tmpPlayer->getMyUniqueID())) { @@ -1120,8 +1151,8 @@ ServerGameStateWaitPlayerAction::InternalProcessPacket(ServerGame &server, Sessi if (code == ACTION_CODE_VALID) { - PerformPlayerAction(server, tmpPlayer, actionData.playerAction, actionData.playerBet); - server.SetState(ServerGameStateHand::Instance()); + PerformPlayerAction(*server, tmpPlayer, actionData.playerAction, actionData.playerBet); + server->SetState(ServerGameStateHand::Instance()); retVal = MSG_NET_GAME_SERVER_ACTION; } else @@ -1134,7 +1165,7 @@ ServerGameStateWaitPlayerAction::InternalProcessPacket(ServerGame &server, Sessi rejectData.playerBet = actionData.playerBet; rejectData.rejectionReason = code; static_cast(reject.get())->SetData(rejectData); - server.GetLobbyThread().GetSender().Send(session.sessionData, reject); + server->GetLobbyThread().GetSender().Send(session.sessionData, reject); } } @@ -1142,21 +1173,24 @@ ServerGameStateWaitPlayerAction::InternalProcessPacket(ServerGame &server, Sessi } void -ServerGameStateWaitPlayerAction::TimerTimeout(ServerGame &server) +ServerGameStateWaitPlayerAction::TimerTimeout(const boost::system::error_code &ec, boost::shared_ptr server) { - Game &curGame = server.GetGame(); - // Retrieve current player. - boost::shared_ptr curPlayer = curGame.getCurrentPlayer(); - if (!curPlayer.get()) - throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0); + if (!ec && &server->GetState() == this) + { + Game &curGame = server->GetGame(); + // Retrieve current player. + boost::shared_ptr curPlayer = curGame.getCurrentPlayer(); + if (!curPlayer.get()) + throw ServerException(__FILE__, __LINE__, ERR_NET_NO_CURRENT_PLAYER, 0); - // Player did not act fast enough. Act for him. - if (curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet() == curPlayer->getMySet()) - PerformPlayerAction(server, curPlayer, PLAYER_ACTION_CHECK, 0); - else - PerformPlayerAction(server, curPlayer, PLAYER_ACTION_FOLD, 0); + // Player did not act fast enough. Act for him. + if (curGame.getCurrentHand()->getCurrentBeRo()->getHighestSet() == curPlayer->getMySet()) + PerformPlayerAction(*server, curPlayer, PLAYER_ACTION_CHECK, 0); + else + PerformPlayerAction(*server, curPlayer, PLAYER_ACTION_FOLD, 0); - server.SetState(ServerGameStateHand::Instance()); + server->SetState(ServerGameStateHand::Instance()); + } } //----------------------------------------------------------------------------- diff --git a/src/net/common/serverlobbythread.cpp b/src/net/common/serverlobbythread.cpp index d5ea02d0..43d88b5e 100644 --- a/src/net/common/serverlobbythread.cpp +++ b/src/net/common/serverlobbythread.cpp @@ -89,9 +89,12 @@ private: ServerLobbyThread::ServerLobbyThread(GuiInterface &gui, ConfigFile *playerConfig, AvatarManager &avatarManager, boost::shared_ptr ioService) -: m_ioService(ioService), m_timerManager(ioService), m_curBanId(0), m_gui(gui), m_avatarManager(avatarManager), +: m_ioService(ioService), m_curBanId(0), m_gui(gui), m_avatarManager(avatarManager), m_playerConfig(playerConfig), m_curGameId(0), m_curUniquePlayerId(0), m_curSessionId(INVALID_SESSION + 1), - m_statDataChanged(false), m_startTime(boost::posix_time::second_clock::local_time()) + m_statDataChanged(false), m_removeGameTimer(*ioService), m_removePlayerTimer(*ioService), + m_sessionTimeoutTimer(*ioService), m_avatarCleanupTimer(*ioService), + m_saveStatisticsTimer(*ioService), m_avatarLockTimer(*ioService), + m_startTime(boost::posix_time::second_clock::local_time()) { m_senderCallback.reset(new ServerSenderCallback(*this)); m_sender.reset(new SenderHelper(*m_senderCallback, m_ioService)); @@ -449,12 +452,6 @@ ServerLobbyThread::RemoveComputerPlayer(boost::shared_ptr player) m_computerPlayers.erase(player->GetUniqueId()); } -TimerManager & -ServerLobbyThread::GetTimerManager() -{ - return m_timerManager; -} - AvatarManager & ServerLobbyThread::GetAvatarManager() { @@ -481,6 +478,13 @@ ServerLobbyThread::GetSender() return *m_sender; } +boost::asio::io_service & +ServerLobbyThread::GetIOService() +{ + assert(m_ioService); + return *m_ioService; +} + u_int32_t ServerLobbyThread::GetNextUniquePlayerId() { @@ -505,11 +509,10 @@ ServerLobbyThread::GetNextGameId() void ServerLobbyThread::Main() { + // Register all timers. + RegisterTimers(); try { - // Register all timers. - RegisterTimers(); - m_work.reset(new boost::asio::io_service::work(*m_ioService)); m_ioService->run(); // Will only be aborted asynchronously. m_work.reset(); @@ -525,41 +528,60 @@ ServerLobbyThread::Main() GetCallback().SignalNetServerError(e.GetErrorId(), e.GetOsErrorCode()); LOG_ERROR(e.what()); } + // Cancel pending timer callbacks. + CancelTimers(); } void ServerLobbyThread::RegisterTimers() { // Remove closed games. - m_timerManager.RegisterTimer( - SERVER_REMOVE_GAME_INTERVAL_MSEC, - boost::bind(&ServerLobbyThread::TimerRemoveGame, this), - true); + m_removeGameTimer.expires_from_now( + boost::posix_time::milliseconds(SERVER_REMOVE_GAME_INTERVAL_MSEC)); + m_removeGameTimer.async_wait( + boost::bind( + &ServerLobbyThread::TimerRemoveGame, shared_from_this(), boost::asio::placeholders::error)); // Remove inactive/kicked players. - m_timerManager.RegisterTimer( - SERVER_REMOVE_PLAYER_INTERVAL_MSEC, - boost::bind(&ServerLobbyThread::TimerRemovePlayer, this), - true); + m_removePlayerTimer.expires_from_now( + boost::posix_time::milliseconds(SERVER_REMOVE_PLAYER_INTERVAL_MSEC)); + m_removePlayerTimer.async_wait( + boost::bind( + &ServerLobbyThread::TimerRemovePlayer, shared_from_this(), boost::asio::placeholders::error)); // Check the timeout of sessions which have not been initialised. - m_timerManager.RegisterTimer( - SERVER_CHECK_SESSION_TIMEOUTS_INTERVAL_MSEC, - boost::bind(&ServerLobbyThread::TimerCheckSessionTimeouts, this), - true); + m_sessionTimeoutTimer.expires_from_now( + boost::posix_time::milliseconds(SERVER_CHECK_SESSION_TIMEOUTS_INTERVAL_MSEC)); + m_sessionTimeoutTimer.async_wait( + boost::bind( + &ServerLobbyThread::TimerCheckSessionTimeouts, shared_from_this(), boost::asio::placeholders::error)); // Cleanup the avatar cache. Note: Only works if there are no users on the server. - m_timerManager.RegisterTimer( - SERVER_CACHE_CLEANUP_INTERVAL_SEC * 1000, - boost::bind(&ServerLobbyThread::TimerCleanupAvatarCache, this), - true); + m_avatarCleanupTimer.expires_from_now( + boost::posix_time::seconds(SERVER_CACHE_CLEANUP_INTERVAL_SEC)); + m_avatarCleanupTimer.async_wait( + boost::bind( + &ServerLobbyThread::TimerCleanupAvatarCache, shared_from_this(), boost::asio::placeholders::error)); // Update the statistics file. - m_timerManager.RegisterTimer( - SERVER_SAVE_STATISTICS_INTERVAL_SEC * 1000, - boost::bind(&ServerLobbyThread::TimerSaveStatisticsFile, this), - true); + m_saveStatisticsTimer.expires_from_now( + boost::posix_time::seconds(SERVER_SAVE_STATISTICS_INTERVAL_SEC)); + m_saveStatisticsTimer.async_wait( + boost::bind( + &ServerLobbyThread::TimerSaveStatisticsFile, shared_from_this(), boost::asio::placeholders::error)); // Update the avatar upload locks. - m_timerManager.RegisterTimer( - SERVER_UPDATE_AVATAR_LOCK_INTERVAL_MSEC, - boost::bind(&ServerLobbyThread::TimerUpdateClientAvatarLock, this), - true); + m_avatarLockTimer.expires_from_now( + boost::posix_time::milliseconds(SERVER_UPDATE_AVATAR_LOCK_INTERVAL_MSEC)); + m_avatarLockTimer.async_wait( + boost::bind( + &ServerLobbyThread::TimerUpdateClientAvatarLock, shared_from_this(), boost::asio::placeholders::error)); +} + +void +ServerLobbyThread::CancelTimers() +{ + m_removeGameTimer.cancel(); + m_removePlayerTimer.cancel(); + m_sessionTimeoutTimer.cancel(); + m_avatarCleanupTimer.cancel(); + m_saveStatisticsTimer.cancel(); + m_avatarLockTimer.cancel(); } void @@ -941,7 +963,7 @@ ServerLobbyThread::HandleNetPacketCreateGame(SessionWrapper session, const NetPa boost::shared_ptr game( new ServerGame( - *this, + shared_from_this(), GetNextGameId(), createGameData.gameName, createGameData.password, @@ -1032,75 +1054,90 @@ ServerLobbyThread::RequestPlayerAvatar(SessionWrapper session) } void -ServerLobbyThread::TimerRemoveGame() +ServerLobbyThread::TimerRemoveGame(const boost::system::error_code &ec) { - // Synchronously remove games which have been closed. - GameMap::iterator i = m_gameMap.begin(); - GameMap::iterator end = m_gameMap.end(); - while (i != end) + if (!ec) { - GameMap::iterator next = i; - ++next; - boost::shared_ptr tmpGame = i->second; - if (!tmpGame->GetSessionManager().HasSessions()) - InternalRemoveGame(tmpGame); // This will delete the game. - i = next; + // Synchronously remove games which have been closed. + GameMap::iterator i = m_gameMap.begin(); + GameMap::iterator end = m_gameMap.end(); + while (i != end) + { + GameMap::iterator next = i; + ++next; + boost::shared_ptr tmpGame = i->second; + if (!tmpGame->GetSessionManager().HasSessions()) + InternalRemoveGame(tmpGame); // This will delete the game. + i = next; + } } } void -ServerLobbyThread::TimerRemovePlayer() +ServerLobbyThread::TimerRemovePlayer(const boost::system::error_code &ec) { - boost::mutex::scoped_lock lock(m_removePlayerListMutex); - - if (!m_removePlayerList.empty()) + if (!ec) { - RemovePlayerList::iterator i = m_removePlayerList.begin(); - RemovePlayerList::iterator end = m_removePlayerList.end(); + boost::mutex::scoped_lock lock(m_removePlayerListMutex); + + if (!m_removePlayerList.empty()) + { + RemovePlayerList::iterator i = m_removePlayerList.begin(); + RemovePlayerList::iterator end = m_removePlayerList.end(); + + while (i != end) + { + InternalRemovePlayer(i->first, i->second); + ++i; + } + m_removePlayerList.clear(); + } + } +} + +void +ServerLobbyThread::TimerUpdateClientAvatarLock(const boost::system::error_code &ec) +{ + if (!ec) + { + boost::mutex::scoped_lock lock(m_timerAvatarClientAddressMapMutex); + + TimerClientAddressMap::iterator i = m_timerAvatarClientAddressMap.begin(); + TimerClientAddressMap::iterator end = m_timerAvatarClientAddressMap.end(); while (i != end) { - InternalRemovePlayer(i->first, i->second); - ++i; + TimerClientAddressMap::iterator next = i; + ++next; + if (i->second.elapsed().total_seconds() > SERVER_INIT_AVATAR_CLIENT_LOCK_SEC) + m_timerAvatarClientAddressMap.erase(i); + i = next; } - m_removePlayerList.clear(); } } void -ServerLobbyThread::TimerUpdateClientAvatarLock() +ServerLobbyThread::TimerCheckSessionTimeouts(const boost::system::error_code &ec) { - boost::mutex::scoped_lock lock(m_timerAvatarClientAddressMapMutex); - - TimerClientAddressMap::iterator i = m_timerAvatarClientAddressMap.begin(); - TimerClientAddressMap::iterator end = m_timerAvatarClientAddressMap.end(); - - while (i != end) + if (!ec) { - TimerClientAddressMap::iterator next = i; - ++next; - if (i->second.elapsed().total_seconds() > SERVER_INIT_AVATAR_CLIENT_LOCK_SEC) - m_timerAvatarClientAddressMap.erase(i); - i = next; + m_sessionManager.ForEach(boost::bind(&ServerLobbyThread::InternalCheckSessionTimeouts, boost::ref(*this), _1)); + m_gameSessionManager.ForEach(boost::bind(&ServerLobbyThread::InternalCheckSessionTimeouts, boost::ref(*this), _1)); } } void -ServerLobbyThread::TimerCheckSessionTimeouts() +ServerLobbyThread::TimerCleanupAvatarCache(const boost::system::error_code &ec) { - m_sessionManager.ForEach(boost::bind(&ServerLobbyThread::InternalCheckSessionTimeouts, boost::ref(*this), _1)); - m_gameSessionManager.ForEach(boost::bind(&ServerLobbyThread::InternalCheckSessionTimeouts, boost::ref(*this), _1)); -} - -void -ServerLobbyThread::TimerCleanupAvatarCache() -{ - // Only act if there are no sessions. - if (!m_sessionManager.HasSessions() && !m_gameSessionManager.HasSessions()) + if (!ec) { - LOG_VERBOSE("Cleaning up avatar cache."); + // Only act if there are no sessions. + if (!m_sessionManager.HasSessions() && !m_gameSessionManager.HasSessions()) + { + LOG_VERBOSE("Cleaning up avatar cache."); - m_avatarManager.RemoveOldAvatarCacheEntries(); + m_avatarManager.RemoveOldAvatarCacheEntries(); + } } } @@ -1380,22 +1417,25 @@ ServerLobbyThread::ReadStatisticsFile() } void -ServerLobbyThread::TimerSaveStatisticsFile() +ServerLobbyThread::TimerSaveStatisticsFile(const boost::system::error_code &ec) { - LOG_VERBOSE("Saving statistics."); - boost::mutex::scoped_lock lock(m_statMutex); - if (m_statDataChanged) + if (!ec) { - ofstream o(m_statisticsFileName.c_str(), ios_base::out | ios_base::trunc); - if (!o.fail()) + LOG_VERBOSE("Saving statistics."); + boost::mutex::scoped_lock lock(m_statMutex); + if (m_statDataChanged) { - o << SERVER_STATISTICS_STR_TOTAL_PLAYERS " " << m_statData.totalPlayersEverLoggedIn << endl; - o << SERVER_STATISTICS_STR_TOTAL_GAMES " " << m_statData.totalGamesEverCreated << endl; - o << SERVER_STATISTICS_STR_MAX_PLAYERS " " << m_statData.maxPlayersLoggedIn << endl; - o << SERVER_STATISTICS_STR_MAX_GAMES " " << m_statData.maxGamesOpen << endl; - o << SERVER_STATISTICS_STR_CUR_PLAYERS " " << m_statData.numberOfPlayersOnServer << endl; - o << SERVER_STATISTICS_STR_CUR_GAMES " " << m_statData.numberOfGamesOpen << endl; - m_statDataChanged = false; + ofstream o(m_statisticsFileName.c_str(), ios_base::out | ios_base::trunc); + if (!o.fail()) + { + o << SERVER_STATISTICS_STR_TOTAL_PLAYERS " " << m_statData.totalPlayersEverLoggedIn << endl; + o << SERVER_STATISTICS_STR_TOTAL_GAMES " " << m_statData.totalGamesEverCreated << endl; + o << SERVER_STATISTICS_STR_MAX_PLAYERS " " << m_statData.maxPlayersLoggedIn << endl; + o << SERVER_STATISTICS_STR_MAX_GAMES " " << m_statData.maxGamesOpen << endl; + o << SERVER_STATISTICS_STR_CUR_PLAYERS " " << m_statData.numberOfPlayersOnServer << endl; + o << SERVER_STATISTICS_STR_CUR_GAMES " " << m_statData.numberOfGamesOpen << endl; + m_statDataChanged = false; + } } } } diff --git a/src/net/servergame.h b/src/net/servergame.h index 6d0662f2..81edce81 100644 --- a/src/net/servergame.h +++ b/src/net/servergame.h @@ -21,6 +21,7 @@ #ifndef _SERVERGAME_H_ #define _SERVERGAME_H_ +#include #include #include @@ -36,11 +37,11 @@ class ConfigFile; struct GameData; class Game; -class ServerGame +class ServerGame : public boost::enable_shared_from_this { public: ServerGame( - ServerLobbyThread &lobbyThread, u_int32_t id, const std::string &name, const std::string &pwd, const GameData &gameData, unsigned adminPlayerId, GuiInterface &gui, ConfigFile *playerConfig); + boost::shared_ptr lobbyThread, u_int32_t id, const std::string &name, const std::string &pwd, const GameData &gameData, unsigned adminPlayerId, GuiInterface &gui, ConfigFile *playerConfig); virtual ~ServerGame(); void Init(); @@ -81,7 +82,7 @@ protected: typedef std::deque SessionQueue; - void TimerVoteKick(); + void TimerVoteKick(const boost::system::error_code &ec); void InternalStartGame(); void ResetGame(); @@ -115,8 +116,7 @@ protected: ServerGameState &GetState(); void SetState(ServerGameState &newState); - unsigned GetStateTimerId() const; - void SetStateTimerId(unsigned newTimerId); + boost::asio::deadline_timer &GetStateTimer(); ReceiverHelper &GetReceiver(); @@ -141,7 +141,7 @@ private: boost::shared_ptr m_voteKickData; - ServerLobbyThread &m_lobbyThread; + boost::shared_ptr m_lobbyThread; boost::shared_ptr m_receiver; GuiInterface &m_gui; @@ -156,8 +156,8 @@ private: ConfigFile *m_playerConfig; unsigned m_gameNum; unsigned m_curPetitionId; - unsigned m_voteKickTimerId; - unsigned m_stateTimerId; + boost::asio::deadline_timer m_voteKickTimer; + boost::asio::deadline_timer m_stateTimer; friend class ServerLobbyThread; friend class AbstractServerGameStateReceiving; diff --git a/src/net/servergamestate.h b/src/net/servergamestate.h index d699bf3e..bb42f498 100644 --- a/src/net/servergamestate.h +++ b/src/net/servergamestate.h @@ -41,16 +41,16 @@ class ServerGameState { public: virtual ~ServerGameState(); - virtual void Enter(ServerGame &server) = 0; - virtual void Exit(ServerGame &server) = 0; + virtual void Enter(boost::shared_ptr server) = 0; + virtual void Exit(boost::shared_ptr server) = 0; - virtual void NotifyGameAdminChanged(ServerGame &server) = 0; + virtual void NotifyGameAdminChanged(boost::shared_ptr server) = 0; // Handling of a new session. - virtual void HandleNewSession(ServerGame &server, SessionWrapper session) = 0; + virtual void HandleNewSession(boost::shared_ptr server, SessionWrapper session) = 0; // Main processing function of the current state. - virtual int ProcessPacket(ServerGame &server, SessionWrapper session, boost::shared_ptr packet) = 0; + virtual int ProcessPacket(boost::shared_ptr server, SessionWrapper session, boost::shared_ptr packet) = 0; }; // Abstract State: Receiving. @@ -61,11 +61,11 @@ public: // Globally handle packets which are allowed in all running states. // Calls InternalProcess if packet has not been processed. - virtual int ProcessPacket(ServerGame &server, SessionWrapper session, boost::shared_ptr packet); + virtual int ProcessPacket(boost::shared_ptr server, SessionWrapper session, boost::shared_ptr packet); protected: - virtual int InternalProcessPacket(ServerGame &server, SessionWrapper session, boost::shared_ptr packet) = 0; + virtual int InternalProcessPacket(boost::shared_ptr server, SessionWrapper session, boost::shared_ptr packet) = 0; }; // State: Initialization. @@ -73,24 +73,24 @@ class ServerGameStateInit : public AbstractServerGameStateReceiving { public: static ServerGameStateInit &Instance(); - virtual void Enter(ServerGame &server); - virtual void Exit(ServerGame &server); + virtual void Enter(boost::shared_ptr server); + virtual void Exit(boost::shared_ptr server); virtual ~ServerGameStateInit(); - virtual void NotifyGameAdminChanged(ServerGame &server); + virtual void NotifyGameAdminChanged(boost::shared_ptr server); - virtual void HandleNewSession(ServerGame &server, SessionWrapper session); + virtual void HandleNewSession(boost::shared_ptr server, SessionWrapper session); protected: ServerGameStateInit(); - void RegisterAdminTimer(ServerGame &server); - void UnregisterAdminTimer(ServerGame &server); - void TimerAdminWarning(ServerGame &server); - void TimerAdminTimeout(ServerGame &server); + void RegisterAdminTimer(boost::shared_ptr server); + void UnregisterAdminTimer(boost::shared_ptr server); + void TimerAdminWarning(const boost::system::error_code &ec, boost::shared_ptr server); + void TimerAdminTimeout(const boost::system::error_code &ec, boost::shared_ptr server); - virtual int InternalProcessPacket(ServerGame &server, SessionWrapper session, boost::shared_ptr packet); + virtual int InternalProcessPacket(boost::shared_ptr server, SessionWrapper session, boost::shared_ptr packet); static boost::shared_ptr CreateNetPacketPlayerJoined(const PlayerData &playerData); @@ -103,20 +103,20 @@ class ServerGameStateStartGame : public AbstractServerGameStateReceiving { public: static ServerGameStateStartGame &Instance(); - virtual void Enter(ServerGame &server); - virtual void Exit(ServerGame &server); + virtual void Enter(boost::shared_ptr server); + virtual void Exit(boost::shared_ptr server); virtual ~ServerGameStateStartGame(); - virtual void NotifyGameAdminChanged(ServerGame &/*server*/) {} - virtual void HandleNewSession(ServerGame &server, SessionWrapper session); + virtual void NotifyGameAdminChanged(boost::shared_ptr /*server*/) {} + virtual void HandleNewSession(boost::shared_ptr server, SessionWrapper session); protected: ServerGameStateStartGame(); - virtual int InternalProcessPacket(ServerGame &server, SessionWrapper session, boost::shared_ptr packet); - void TimerTimeout(ServerGame &server); - void DoStart(ServerGame &server); + virtual int InternalProcessPacket(boost::shared_ptr server, SessionWrapper session, boost::shared_ptr packet); + void TimerTimeout(const boost::system::error_code &ec, boost::shared_ptr server); + void DoStart(boost::shared_ptr server); private: static ServerGameStateStartGame s_state; @@ -127,25 +127,25 @@ class ServerGameStateHand : public AbstractServerGameStateReceiving { public: static ServerGameStateHand &Instance(); - virtual void Enter(ServerGame &server); - virtual void Exit(ServerGame &server); + virtual void Enter(boost::shared_ptr server); + virtual void Exit(boost::shared_ptr server); virtual ~ServerGameStateHand(); - virtual void NotifyGameAdminChanged(ServerGame &/*server*/) {} - virtual void HandleNewSession(ServerGame &server, SessionWrapper session); + virtual void NotifyGameAdminChanged(boost::shared_ptr /*server*/) {} + virtual void HandleNewSession(boost::shared_ptr server, SessionWrapper session); protected: ServerGameStateHand(); - virtual int InternalProcessPacket(ServerGame &server, SessionWrapper session, boost::shared_ptr packet); - void TimerLoop(ServerGame &server); - void TimerShowCards(ServerGame &server); - void TimerComputerAction(ServerGame &server); - void TimerNextHand(ServerGame &server); - void TimerNextGame(ServerGame &server); + virtual int InternalProcessPacket(boost::shared_ptr server, SessionWrapper session, boost::shared_ptr packet); + void TimerLoop(const boost::system::error_code &ec, boost::shared_ptr server); + void TimerShowCards(const boost::system::error_code &ec, boost::shared_ptr server); + void TimerComputerAction(const boost::system::error_code &ec, boost::shared_ptr server); + void TimerNextHand(const boost::system::error_code &ec, boost::shared_ptr server); + void TimerNextGame(const boost::system::error_code &ec, boost::shared_ptr server); int GetDealCardsDelaySec(ServerGame &server); - static void StartNewHand(ServerGame &server); + static void StartNewHand(boost::shared_ptr server); private: static ServerGameStateHand s_state; @@ -158,19 +158,19 @@ class ServerGameStateWaitPlayerAction : public AbstractServerGameStateReceiving { public: static ServerGameStateWaitPlayerAction &Instance(); - virtual void Enter(ServerGame &server); - virtual void Exit(ServerGame &server); + virtual void Enter(boost::shared_ptr server); + virtual void Exit(boost::shared_ptr server); virtual ~ServerGameStateWaitPlayerAction(); - virtual void NotifyGameAdminChanged(ServerGame &/*server*/) {} - virtual void HandleNewSession(ServerGame &server, SessionWrapper session); + virtual void NotifyGameAdminChanged(boost::shared_ptr /*server*/) {} + virtual void HandleNewSession(boost::shared_ptr server, SessionWrapper session); protected: ServerGameStateWaitPlayerAction(); - virtual int InternalProcessPacket(ServerGame &server, SessionWrapper session, boost::shared_ptr packet); - void TimerTimeout(ServerGame &server); + virtual int InternalProcessPacket(boost::shared_ptr server, SessionWrapper session, boost::shared_ptr packet); + void TimerTimeout(const boost::system::error_code &ec, boost::shared_ptr server); private: static ServerGameStateWaitPlayerAction s_state; diff --git a/src/net/serverlobbythread.h b/src/net/serverlobbythread.h index 04ddac40..8943617e 100644 --- a/src/net/serverlobbythread.h +++ b/src/net/serverlobbythread.h @@ -24,8 +24,8 @@ #include #include #include +#include -#include #include #include #include @@ -45,7 +45,7 @@ class AvatarManager; struct GameData; class Game; -class ServerLobbyThread : public Thread +class ServerLobbyThread : public Thread, public boost::enable_shared_from_this { public: ServerLobbyThread(GuiInterface &gui, ConfigFile *playerConfig, AvatarManager &avatarManager, @@ -89,13 +89,13 @@ public: u_int32_t GetNextGameId(); ServerCallback &GetCallback(); - TimerManager &GetTimerManager(); AvatarManager &GetAvatarManager(); ServerStats GetStats() const; boost::posix_time::ptime GetStartTime() const; SenderHelper &GetSender(); + boost::asio::io_service &GetIOService(); protected: @@ -113,6 +113,7 @@ protected: // Main function of the thread. virtual void Main(); void RegisterTimers(); + void CancelTimers(); void HandleRead(SessionId sessionId, const boost::system::error_code &error, size_t bytesRead); void HandlePacket(SessionWrapper session, boost::shared_ptr packet); @@ -127,11 +128,11 @@ protected: void HandleNetPacketJoinGame(SessionWrapper session, const NetPacketJoinGame &tmpPacket); void EstablishSession(SessionWrapper session); void RequestPlayerAvatar(SessionWrapper session); - void TimerRemoveGame(); - void TimerRemovePlayer(); - void TimerUpdateClientAvatarLock(); - void TimerCheckSessionTimeouts(); - void TimerCleanupAvatarCache(); + void TimerRemoveGame(const boost::system::error_code &ec); + void TimerRemovePlayer(const boost::system::error_code &ec); + void TimerUpdateClientAvatarLock(const boost::system::error_code &ec); + void TimerCheckSessionTimeouts(const boost::system::error_code &ec); + void TimerCleanupAvatarCache(const boost::system::error_code &ec); boost::shared_ptr InternalGetGameFromId(unsigned gameId); void InternalAddGame(boost::shared_ptr game); @@ -153,7 +154,7 @@ protected: void BroadcastStatisticsUpdate(const ServerStats &stats); void ReadStatisticsFile(); - void TimerSaveStatisticsFile(); + void TimerSaveStatisticsFile(const boost::system::error_code &ec); ReceiverHelper &GetReceiver(); @@ -180,7 +181,6 @@ private: SessionManager m_sessionManager; SessionManager m_gameSessionManager; - TimerManager m_timerManager; TimerClientAddressMap m_timerAvatarClientAddressMap; mutable boost::mutex m_timerAvatarClientAddressMapMutex; @@ -217,6 +217,13 @@ private: bool m_statDataChanged; mutable boost::mutex m_statMutex; + boost::asio::deadline_timer m_removeGameTimer; + boost::asio::deadline_timer m_removePlayerTimer; + boost::asio::deadline_timer m_sessionTimeoutTimer; + boost::asio::deadline_timer m_avatarCleanupTimer; + boost::asio::deadline_timer m_saveStatisticsTimer; + boost::asio::deadline_timer m_avatarLockTimer; + const boost::posix_time::ptime m_startTime; };