diff --git a/docs/net_protocol.txt b/docs/net_protocol.txt index 80cbec81..f7846c41 100644 --- a/docs/net_protocol.txt +++ b/docs/net_protocol.txt @@ -821,13 +821,15 @@ Server Reply: Ask Kick Player Denied +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Message Type = 114 | Message Length = 12 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | Player ID | + | Player ID (who cannot be kicked) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Deny Reason | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Deny Reason: - 0x0000: Another kick cannot be requested at this time. - 0x0001: Kick request is already in progress for this player. + 0x0000: Invalid state. Vote kick is only possible when the game is running. + 0x0001: Kick not possible due to number of players. + 0x0002: Another kick cannot be requested at this time. + 0x0003: Kick request is already in progress for this player. 0xFFFF: Other reason. diff --git a/src/game_defs.h b/src/game_defs.h index 0a8d756f..bd4b366b 100644 --- a/src/game_defs.h +++ b/src/game_defs.h @@ -68,7 +68,9 @@ enum PlayerActionCode enum DenyKickPlayerReason { - KICK_DENIED_TEMPORARY = 0, + KICK_DENIED_INVALID_STATE = 0, + KICK_DENIED_TOO_FEW_PLAYERS, + KICK_DENIED_TEMPORARY, KICK_DENIED_OTHER_IN_PROGRESS, KICK_DENIED_OTHER_REASON }; @@ -83,6 +85,7 @@ enum DenyVoteReason { VOTE_DENIED_INVALID_PETITION = 0, VOTE_DENIED_ALREADY_VOTED, + VOTE_DENIED_IMPOSSIBLE, VOTE_DENIED_OTHER_REASON }; diff --git a/src/net/common/netpacket.cpp b/src/net/common/netpacket.cpp index 5e763afc..df07cc36 100644 --- a/src/net/common/netpacket.cpp +++ b/src/net/common/netpacket.cpp @@ -120,8 +120,10 @@ using namespace std; #define NET_LEFT_OTHER_REASON 0xFFFF // Reasons why ask kick was denied. -#define NET_ASK_KICK_DENIED_TEMPORARY 0x0000 -#define NET_ASK_KICK_DENIED_OTHER_IN_PROGRESS 0x0001 +#define NET_ASK_KICK_DENIED_INVALID_STATE 0x0000 +#define NET_ASK_KICK_DENIED_TOO_FEW_PLAYERS 0x0001 +#define NET_ASK_KICK_DENIED_TEMPORARY 0x0002 +#define NET_ASK_KICK_DENIED_OTHER_IN_PROGRESS 0x0003 #define NET_ASK_KICK_DENIED_OTHER_REASON 0xFFFF // Vote types @@ -131,6 +133,7 @@ using namespace std; // Reasons why vote to kick was denied. #define NET_VOTE_KICK_DENIED_INVALID_PETITION 0x0000 #define NET_VOTE_KICK_DENIED_ALREADY_VOTED 0x0001 +#define NET_VOTE_KICK_DENIED_IMPOSSIBLE 0x0002 #define NET_VOTE_KICK_DENIED_OTHER_REASON 0xFFFF // Reasons for timeout warning @@ -4485,6 +4488,12 @@ NetPacketAskKickPlayerDenied::SetData(const NetPacketAskKickPlayerDenied::Data & tmpData->playerId = htonl(inData.playerId); switch (inData.denyReason) { + case KICK_DENIED_INVALID_STATE: + tmpData->denyReason = htons(NET_ASK_KICK_DENIED_INVALID_STATE); + break; + case KICK_DENIED_TOO_FEW_PLAYERS: + tmpData->denyReason = htons(NET_ASK_KICK_DENIED_TOO_FEW_PLAYERS); + break; case KICK_DENIED_TEMPORARY: tmpData->denyReason = htons(NET_ASK_KICK_DENIED_TEMPORARY); break; @@ -4508,6 +4517,12 @@ NetPacketAskKickPlayerDenied::GetData(NetPacketAskKickPlayerDenied::Data &outDat outData.playerId = ntohl(tmpData->playerId); switch (ntohs(tmpData->denyReason)) { + case NET_ASK_KICK_DENIED_INVALID_STATE: + outData.denyReason = KICK_DENIED_INVALID_STATE; + break; + case NET_ASK_KICK_DENIED_TOO_FEW_PLAYERS: + outData.denyReason = KICK_DENIED_TOO_FEW_PLAYERS; + break; case NET_ASK_KICK_DENIED_TEMPORARY: outData.denyReason = KICK_DENIED_TEMPORARY; break; @@ -4758,6 +4773,9 @@ NetPacketVoteKickPlayerDenied::SetData(const NetPacketVoteKickPlayerDenied::Data case VOTE_DENIED_ALREADY_VOTED: tmpData->denyReason = htons(NET_VOTE_KICK_DENIED_ALREADY_VOTED); break; + case VOTE_DENIED_IMPOSSIBLE: + tmpData->denyReason = htons(NET_VOTE_KICK_DENIED_IMPOSSIBLE); + break; default: tmpData->denyReason = htons(NET_VOTE_KICK_DENIED_OTHER_REASON); break; @@ -4781,6 +4799,9 @@ NetPacketVoteKickPlayerDenied::GetData(NetPacketVoteKickPlayerDenied::Data &outD case NET_VOTE_KICK_DENIED_ALREADY_VOTED: outData.denyReason = VOTE_DENIED_ALREADY_VOTED; break; + case NET_VOTE_KICK_DENIED_IMPOSSIBLE: + outData.denyReason = VOTE_DENIED_IMPOSSIBLE; + break; default: outData.denyReason = VOTE_DENIED_OTHER_REASON; break; diff --git a/src/net/common/servergamestate.cpp b/src/net/common/servergamestate.cpp index 33228c5a..f9b80533 100644 --- a/src/net/common/servergamestate.cpp +++ b/src/net/common/servergamestate.cpp @@ -38,8 +38,6 @@ using namespace std; //#define SERVER_TEST -#define SERVER_START_GAME_TIMEOUT_SEC 10 - #ifdef SERVER_TEST #define SERVER_DELAY_NEXT_HAND_SEC 0 #define SERVER_DELAY_NEXT_GAME_SEC 0 @@ -62,6 +60,7 @@ using namespace std; #define SERVER_COMPUTER_ACTION_DELAY_SEC 2 #endif +#define SERVER_START_GAME_TIMEOUT_SEC 10 #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 @@ -204,20 +203,7 @@ AbstractServerGameStateReceiving::Process(ServerGameThread &server) NetPacketAskKickPlayer::Data askKickData; packet->ToNetPacketAskKickPlayer()->GetData(askKickData); - boost::shared_ptr voteData(server.InternalAskVoteKick(session.playerData->GetUniqueId(), askKickData.playerId)); - if (voteData) - { - boost::shared_ptr startPetition(new NetPacketStartKickPlayerPetition); - NetPacketStartKickPlayerPetition::Data startPetitionData; - startPetitionData.petitionId = voteData->petitionId; - startPetitionData.proposingPlayerId = session.playerData->GetUniqueId(); - startPetitionData.kickPlayerId = voteData->kickPlayerId; - startPetitionData.kickTimeoutSec = SERVER_VOTE_KICK_TIMEOUT_SEC; - startPetitionData.numVotesNeededToKick = voteData->initialNumVotesToKick; - static_cast(startPetition.get())->SetData(startPetitionData); - server.SendToAllPlayers(startPetition, SessionData::Game); - // TODO notify first vote. - } + server.InternalAskVoteKick(session, askKickData.playerId, SERVER_VOTE_KICK_TIMEOUT_SEC); } } else if (packet->ToNetPacketVoteKickPlayer()) diff --git a/src/net/common/servergamethread.cpp b/src/net/common/servergamethread.cpp index 7570afd0..2d86cb38 100644 --- a/src/net/common/servergamethread.cpp +++ b/src/net/common/servergamethread.cpp @@ -135,6 +135,7 @@ ServerGameThread::Main() // Process current state. GetState().Process(*this); RemovePlayerLoop(); + VoteKickLoop(); } while (!ShouldTerminate() && GetSessionManager().HasSessions()); } catch (const PokerTHException &e) { @@ -167,6 +168,19 @@ ServerGameThread::RemovePlayerLoop() m_removePlayerList.clear(); } +void +ServerGameThread::VoteKickLoop() +{ +/* boost::mutex::scoped_lock lock(m_voteKickMapMutex); + // Check the list whether the player is already being kicked. + VoteKickMap::iterator i = m_voteKickMap.begin(); + VoteKickMap::iterator end = m_voteKickMap.end(); + while (i != end) + { + ++i; + }*/ +} + void ServerGameThread::InternalStartGame() { @@ -226,25 +240,71 @@ ServerGameThread::InternalKickPlayer(unsigned playerId) MoveSessionToLobby(tmpSession, NTF_NET_REMOVED_KICKED); } -boost::shared_ptr -ServerGameThread::InternalAskVoteKick(unsigned playerIdByWhom, unsigned playerIdWho) +void +ServerGameThread::InternalAskVoteKick(SessionWrapper byWhom, unsigned playerIdWho, unsigned timeoutSec) { - // TODO: Check whether player is allowed to initiate vote. - // TODO: Check whether there are more than two players. - boost::shared_ptr voteData; - if (IsRunning()) + if (IsRunning() && byWhom.playerData) { - voteData.reset(new VoteKickData); - voteData->petitionId = m_curPetitionId++; - voteData->kickPlayerId = playerIdWho; - voteData->initialNumVotesToKick = static_cast(ceil(GetCurNumberOfPlayers() / 3. * 2.)); - // Consider first vote. - voteData->numVotesInFavourOfKicking = 1; - voteData->votedPlayerIds.push_back(playerIdByWhom); - boost::mutex::scoped_lock lock(m_voteKickMapMutex); - m_voteKickMap.insert(VoteKickMap::value_type(voteData->petitionId, voteData)); + size_t numPlayers = GetCurNumberOfPlayers(); + if (numPlayers > 2) + { + // Lock the vote kick list. + boost::mutex::scoped_lock lock(m_voteKickMapMutex); + // Check the list whether the player is already being kicked. + VoteKickMap::const_iterator i = m_voteKickMap.begin(); + VoteKickMap::const_iterator end = m_voteKickMap.end(); + while (i != end) + { + if (i->second->kickPlayerId == playerIdWho) + break; + ++i; + } + if (i == end) + { + // Initiate a vote kick. + unsigned playerIdByWhom = byWhom.playerData->GetUniqueId(); + boost::shared_ptr voteData(new VoteKickData); + voteData->petitionId = m_curPetitionId++; + voteData->kickPlayerId = playerIdWho; + voteData->initialNumVotesToKick = static_cast(ceil(numPlayers / 3. * 2.)); + // Consider first vote. + voteData->numVotesInFavourOfKicking = 1; + voteData->votedPlayerIds.push_back(playerIdByWhom); + m_voteKickMap.insert(VoteKickMap::value_type(voteData->petitionId, voteData)); + lock.unlock(); // Do not block the list longer than necessary. + + boost::shared_ptr startPetition(new NetPacketStartKickPlayerPetition); + NetPacketStartKickPlayerPetition::Data startPetitionData; + startPetitionData.petitionId = voteData->petitionId; + startPetitionData.proposingPlayerId = playerIdByWhom; + startPetitionData.kickPlayerId = voteData->kickPlayerId; + startPetitionData.kickTimeoutSec = timeoutSec; + startPetitionData.numVotesNeededToKick = voteData->initialNumVotesToKick; + static_cast(startPetition.get())->SetData(startPetitionData); + SendToAllPlayers(startPetition, SessionData::Game); + } + else + { + lock.unlock(); // Do not block the list longer than necessary. + InternalDenyAskVoteKick(byWhom, playerIdWho, KICK_DENIED_OTHER_IN_PROGRESS); + } + } + else + InternalDenyAskVoteKick(byWhom, playerIdWho, KICK_DENIED_TOO_FEW_PLAYERS); } - return voteData; + else + InternalDenyAskVoteKick(byWhom, playerIdWho, KICK_DENIED_INVALID_STATE); +} + +void +ServerGameThread::InternalDenyAskVoteKick(SessionWrapper byWhom, unsigned playerIdWho, DenyKickPlayerReason reason) +{ + boost::shared_ptr denyPetition(new NetPacketAskKickPlayerDenied); + NetPacketAskKickPlayerDenied::Data denyPetitionData; + denyPetitionData.playerId = playerIdWho; + denyPetitionData.denyReason = reason; + static_cast(denyPetition.get())->SetData(denyPetitionData); + GetSender().Send(byWhom.sessionData, denyPetition); } void diff --git a/src/net/servergamethread.h b/src/net/servergamethread.h index 4cde759b..a19d1fab 100644 --- a/src/net/servergamethread.h +++ b/src/net/servergamethread.h @@ -82,12 +82,14 @@ protected: // Main function of the thread. virtual void Main(); void RemovePlayerLoop(); + void VoteKickLoop(); void InternalStartGame(); void ResetGame(); void InternalKickPlayer(unsigned playerId); - boost::shared_ptr InternalAskVoteKick(unsigned playerIdByWhom, unsigned playerIdWho); + void InternalAskVoteKick(SessionWrapper byWhom, unsigned playerIdWho, unsigned timeoutSec); + void InternalDenyAskVoteKick(SessionWrapper byWhom, unsigned playerIdWho, DenyKickPlayerReason reason); void InternalVoteKick(unsigned petitionId, KickVote vote); PlayerDataList GetFullPlayerDataList() const;