From 89450a9a321474685d0ffb0b6068bfaf65644557 Mon Sep 17 00:00:00 2001 From: lotodore Date: Sun, 7 Dec 2008 11:04:42 +0000 Subject: [PATCH] More work on server side vote kick. Most error handling complete now. Client side still lacking. --- docs/net_protocol.txt | 2 +- src/gamedata.h | 2 +- src/net/common/servergamestate.cpp | 2 +- src/net/common/servergamethread.cpp | 71 +++++++++++++++++++---------- src/net/servergamethread.h | 3 +- 5 files changed, 51 insertions(+), 29 deletions(-) diff --git a/docs/net_protocol.txt b/docs/net_protocol.txt index 4d38d037..a016146b 100644 --- a/docs/net_protocol.txt +++ b/docs/net_protocol.txt @@ -829,7 +829,7 @@ Deny Reason: 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. + 0x0003: Kick request is already in progress. 0xFFFF: Other reason. diff --git a/src/gamedata.h b/src/gamedata.h index e5c407ad..7f28ba65 100644 --- a/src/gamedata.h +++ b/src/gamedata.h @@ -98,7 +98,7 @@ struct VoteKickData int numVotesToKick; int numVotesInFavourOfKicking; int numVotesAgainstKicking; - unsigned timeLimitSec; + int timeLimitSec; boost::timers::portable::microsec_timer voteTimer; PlayerIdList votedPlayerIds; }; diff --git a/src/net/common/servergamestate.cpp b/src/net/common/servergamestate.cpp index f9b80533..f722f765 100644 --- a/src/net/common/servergamestate.cpp +++ b/src/net/common/servergamestate.cpp @@ -213,7 +213,7 @@ AbstractServerGameStateReceiving::Process(ServerGameThread &server) NetPacketVoteKickPlayer::Data voteData; packet->ToNetPacketVoteKickPlayer()->GetData(voteData); - server.InternalVoteKick(voteData.petitionId, voteData.vote); + server.InternalVoteKick(session, voteData.petitionId, voteData.vote); } } // Chat text is always allowed. diff --git a/src/net/common/servergamethread.cpp b/src/net/common/servergamethread.cpp index 239ee3fd..d3ce01a4 100644 --- a/src/net/common/servergamethread.cpp +++ b/src/net/common/servergamethread.cpp @@ -176,7 +176,6 @@ ServerGameThread::VoteKickLoop() { // Do not call the vote kick action all the time. // Check the timer. - // The action will also be initiated after a player left. if (m_voteKickActionTimer.elapsed().total_milliseconds() >= SERVER_CHECK_VOTE_KICK_INTERVAL_MSEC) VoteKickAction(); } @@ -189,8 +188,8 @@ ServerGameThread::VoteKickAction() boost::mutex::scoped_lock lock(m_voteKickDataMutex); if (m_voteKickData) { + // Prepare some values. const PlayerIdList playerIds(GetPlayerIdList()); - // 1. Several players left the game, so a kick is no longer possible. 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. @@ -203,20 +202,29 @@ ServerGameThread::VoteKickAction() ++player_i; } bool abortPetition = false; - EndPetitionReason reason = PETITION_END_ENOUGH_VOTES; + bool doKick = false; + EndPetitionReason reason; - if (votesRequiredToKick > playersAllowedToVote) + // 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; } - // 2. The kick has become invalid because the player to be kicked left. + // 3. The kick has become invalid because the player to be kicked left. else if (find(playerIds.begin(), playerIds.end(), m_voteKickData->kickPlayerId) == playerIds.end()) { reason = PETITION_END_PLAYER_LEFT; abortPetition = true; } - // 3. A kick request timed out (because not everyone voted). + // 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; @@ -229,13 +237,16 @@ ServerGameThread::VoteKickAction() endPetitionData.petitionId = m_voteKickData->petitionId; endPetitionData.numVotesAgainstKicking = m_voteKickData->numVotesAgainstKicking; endPetitionData.numVotesInFavourOfKicking = m_voteKickData->numVotesInFavourOfKicking; - endPetitionData.playerKicked = false; + endPetitionData.playerKicked = doKick; endPetitionData.endReason = reason; m_voteKickData.reset(); - lock.unlock(); // Do not block the data longer than necessary. static_cast(endPetition.get())->SetData(endPetitionData); SendToAllPlayers(endPetition, SessionData::Game); + + // Perform kick. + if (doKick) + InternalKickPlayer(m_voteKickData->kickPlayerId); } } } @@ -329,16 +340,12 @@ ServerGameThread::InternalAskVoteKick(SessionWrapper byWhom, unsigned playerIdWh startPetitionData.kickPlayerId = m_voteKickData->kickPlayerId; startPetitionData.kickTimeoutSec = m_voteKickData->timeLimitSec; startPetitionData.numVotesNeededToKick = m_voteKickData->numVotesToKick; - lock.unlock(); // Do not block the data longer than necessary. static_cast(startPetition.get())->SetData(startPetitionData); SendToAllPlayers(startPetition, SessionData::Game); } else - { - lock.unlock(); // Do not block the data longer than necessary. InternalDenyAskVoteKick(byWhom, playerIdWho, KICK_DENIED_OTHER_IN_PROGRESS); - } } else InternalDenyAskVoteKick(byWhom, playerIdWho, KICK_DENIED_TOO_FEW_PLAYERS); @@ -359,29 +366,43 @@ ServerGameThread::InternalDenyAskVoteKick(SessionWrapper byWhom, unsigned player } void -ServerGameThread::InternalVoteKick(unsigned petitionId, KickVote vote) +ServerGameThread::InternalVoteKick(SessionWrapper byWhom, unsigned petitionId, KickVote vote) { - if (IsRunning()) + if (IsRunning() && byWhom.playerData) { boost::mutex::scoped_lock lock(m_voteKickDataMutex); + // Check whether this is the valid petition id. if (m_voteKickData->petitionId == petitionId) { - if (vote == KICK_VOTE_IN_FAVOUR) + // Check whether the player already voted. + unsigned playerId = byWhom.playerData->GetUniqueId(); + if (find(m_voteKickData->votedPlayerIds.begin(), m_voteKickData->votedPlayerIds.end(), playerId) == m_voteKickData->votedPlayerIds.end()) { - m_voteKickData->numVotesInFavourOfKicking++; - if (m_voteKickData->numVotesInFavourOfKicking >= m_voteKickData->numVotesToKick) - { - // Perform kick. - InternalKickPlayer(m_voteKickData->kickPlayerId); - } + m_voteKickData->votedPlayerIds.push_back(playerId); + if (vote == KICK_VOTE_IN_FAVOUR) + m_voteKickData->numVotesInFavourOfKicking++; + else + m_voteKickData->numVotesAgainstKicking++; } else - m_voteKickData->numVotesAgainstKicking++; - // TODO abort if no longer possible. - // TODO remove deprecated list entries. - // TODO error handling. + InternalDenyVoteKick(byWhom, petitionId, VOTE_DENIED_ALREADY_VOTED); } + else + InternalDenyVoteKick(byWhom, petitionId, VOTE_DENIED_INVALID_PETITION); } + else + InternalDenyVoteKick(byWhom, petitionId, VOTE_DENIED_IMPOSSIBLE); +} + +void +ServerGameThread::InternalDenyVoteKick(SessionWrapper byWhom, unsigned petitionId, DenyVoteReason reason) +{ + boost::shared_ptr denyVote(new NetPacketVoteKickPlayerDenied); + NetPacketVoteKickPlayerDenied::Data denyVoteData; + denyVoteData.petitionId = petitionId; + denyVoteData.denyReason = reason; + static_cast(denyVote.get())->SetData(denyVoteData); + GetSender().Send(byWhom.sessionData, denyVote); } PlayerDataList diff --git a/src/net/servergamethread.h b/src/net/servergamethread.h index e2f77469..2518a553 100644 --- a/src/net/servergamethread.h +++ b/src/net/servergamethread.h @@ -90,7 +90,8 @@ protected: void InternalKickPlayer(unsigned playerId); void InternalAskVoteKick(SessionWrapper byWhom, unsigned playerIdWho, unsigned timeoutSec); void InternalDenyAskVoteKick(SessionWrapper byWhom, unsigned playerIdWho, DenyKickPlayerReason reason); - void InternalVoteKick(unsigned petitionId, KickVote vote); + void InternalVoteKick(SessionWrapper byWhom, unsigned petitionId, KickVote vote); + void InternalDenyVoteKick(SessionWrapper byWhom, unsigned petitionId, DenyVoteReason reason); PlayerDataList GetFullPlayerDataList() const;