Use majority agreement for voting
This commit is contained in:
parent
0e17839136
commit
9029621b59
@ -229,6 +229,7 @@ void ClientLobby::addAllPlayers(Event* event)
|
||||
}
|
||||
|
||||
NetworkString& data = event->data();
|
||||
uint32_t winner_peer_id = data.getUInt32();
|
||||
PeerVote winner_vote(data);
|
||||
|
||||
m_game_setup->setRace(winner_vote);
|
||||
|
@ -161,10 +161,11 @@ void LobbyProtocol::startVotingPeriod(float max_time)
|
||||
/** Returns the remaining voting time in seconds. */
|
||||
float LobbyProtocol::getRemainingVotingTime()
|
||||
{
|
||||
if (m_end_voting_period.load() == 0)
|
||||
if (m_end_voting_period.load() == 0 ||
|
||||
StkTime::getRealTimeMs() >= m_end_voting_period.load())
|
||||
return 0.0f;
|
||||
uint64_t t = m_end_voting_period.load()- StkTime::getRealTimeMs();
|
||||
return t/1000.0f;
|
||||
uint64_t t = m_end_voting_period.load() - StkTime::getRealTimeMs();
|
||||
return t / 1000.0f;
|
||||
} // getRemainingVotingTime
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -541,8 +541,8 @@ void ServerLobby::asynchronousUpdate()
|
||||
{
|
||||
PeerVote winner_vote;
|
||||
uint32_t winner_peer_id = std::numeric_limits<uint32_t>::max();
|
||||
bool all_votes_in = handleAllVotes(&winner_vote, &winner_peer_id);
|
||||
if (isVotingOver() || all_votes_in)
|
||||
bool go_on_race = handleAllVotes(&winner_vote, &winner_peer_id);
|
||||
if (go_on_race)
|
||||
{
|
||||
m_game_setup->setRace(winner_vote);
|
||||
// Remove disconnected player (if any) one last time
|
||||
@ -560,6 +560,7 @@ void ServerLobby::asynchronousUpdate()
|
||||
NetworkString* load_world_message = getNetworkString();
|
||||
load_world_message->setSynchronous(true);
|
||||
load_world_message->addUInt8(LE_LOAD_WORLD);
|
||||
load_world_message->addUInt32(winner_peer_id);
|
||||
winner_vote.encode(load_world_message);
|
||||
load_world_message->addUInt8((uint8_t)players.size());
|
||||
for (unsigned i = 0; i < players.size(); i++)
|
||||
@ -2086,10 +2087,18 @@ void ServerLobby::handlePlayerVote(Event* event)
|
||||
/** Select the track to be used based on all votes being received.
|
||||
* \param winner_vote The PeerVote that was picked.
|
||||
* \param winner_peer_id The host id of winner (unchanged if no vote).
|
||||
* \return True if race can go on, otherwise wait.
|
||||
*/
|
||||
bool ServerLobby::handleAllVotes(PeerVote* winner_vote,
|
||||
uint32_t* winner_peer_id)
|
||||
{
|
||||
// Determine majority agreement when 35% of voting time remains,
|
||||
// reserve some time for kart selection so it's not 50%
|
||||
if (getRemainingVotingTime() / getMaxVotingTime() > 0.35f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// First remove all votes from disconnected hosts
|
||||
auto it = m_peers_votes.begin();
|
||||
while (it != m_peers_votes.end())
|
||||
@ -2103,36 +2112,151 @@ bool ServerLobby::handleAllVotes(PeerVote* winner_vote,
|
||||
it++;
|
||||
}
|
||||
|
||||
if (m_peers_votes.empty())
|
||||
{
|
||||
if (isVotingOver())
|
||||
{
|
||||
*winner_vote = *m_default_vote;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Count number of players
|
||||
unsigned int cur_players = 0;
|
||||
float cur_players = 0.0f;
|
||||
auto peers = STKHost::get()->getPeers();
|
||||
for (auto peer : peers)
|
||||
{
|
||||
if (peer->hasPlayerProfiles() && !peer->isWaitingForGame())
|
||||
cur_players++;
|
||||
cur_players += 1.0f;
|
||||
}
|
||||
if (cur_players == 0 || m_peers_votes.size() < cur_players)
|
||||
|
||||
std::string top_track = m_default_vote->m_track_name;
|
||||
int top_laps = m_default_vote->m_num_laps;
|
||||
|
||||
std::map<std::string, unsigned> tracks;
|
||||
std::map<unsigned, unsigned> laps;
|
||||
std::map<bool, unsigned> reverses;
|
||||
|
||||
// Ratio to determine majority agreement
|
||||
float tracks_rate = 0.0f;
|
||||
float laps_rate = 0.0f;
|
||||
float reverses_rate = 0.0f;
|
||||
RandomGenerator rg;
|
||||
|
||||
for (auto& p : m_peers_votes)
|
||||
{
|
||||
// Default settings if no votes at all
|
||||
*winner_vote = *m_default_vote;
|
||||
return false;
|
||||
auto track_vote = tracks.find(p.second.m_track_name);
|
||||
if (track_vote == tracks.end())
|
||||
tracks[p.second.m_track_name] = 1;
|
||||
else
|
||||
track_vote->second++;
|
||||
auto lap_vote = laps.find(p.second.m_num_laps);
|
||||
if (lap_vote == laps.end())
|
||||
laps[p.second.m_num_laps] = 1;
|
||||
else
|
||||
lap_vote->second++;
|
||||
auto reverse_vote = reverses.find(p.second.m_reverse);
|
||||
if (reverse_vote == reverses.end())
|
||||
reverses[p.second.m_reverse] = 1;
|
||||
else
|
||||
reverse_vote->second++;
|
||||
}
|
||||
|
||||
// Otherwise if we have all votes, randomly select the vote
|
||||
// to use in the next race.
|
||||
RandomGenerator r;
|
||||
auto vote = m_peers_votes.begin();
|
||||
std::advance(vote, r.get(m_peers_votes.size()) );
|
||||
unsigned vote = 0;
|
||||
auto track_vote = tracks.begin();
|
||||
// rg.get(2) == 0 will allow not always the "less" in map get picked
|
||||
for (auto c_vote = tracks.begin(); c_vote != tracks.end(); c_vote++)
|
||||
{
|
||||
if (c_vote->second > vote ||
|
||||
(c_vote->second >= vote && rg.get(2) == 0))
|
||||
{
|
||||
vote = c_vote->second;
|
||||
track_vote = c_vote;
|
||||
}
|
||||
}
|
||||
if (track_vote != tracks.end())
|
||||
{
|
||||
top_track = track_vote->first;
|
||||
tracks_rate = float(track_vote->second) / cur_players;
|
||||
}
|
||||
|
||||
*winner_peer_id = vote->first;
|
||||
*winner_vote = vote->second;
|
||||
vote = 0;
|
||||
auto lap_vote = laps.begin();
|
||||
for (auto c_vote = laps.begin(); c_vote != laps.end(); c_vote++)
|
||||
{
|
||||
if (c_vote->second > vote ||
|
||||
(c_vote->second >= vote && rg.get(2) == 0))
|
||||
{
|
||||
vote = c_vote->second;
|
||||
lap_vote = c_vote;
|
||||
}
|
||||
}
|
||||
if (lap_vote != laps.end())
|
||||
{
|
||||
top_laps = lap_vote->first;
|
||||
laps_rate = float(lap_vote->second) / cur_players;
|
||||
}
|
||||
|
||||
// For now: don't trigger start of race if all votes
|
||||
// have been received. Use:
|
||||
// return m_peers_votes.size() == cur_players;
|
||||
// to start the race when all votes have been received.
|
||||
vote = 0;
|
||||
auto reverse_vote = reverses.begin();
|
||||
for (auto c_vote = reverses.begin(); c_vote != reverses.end(); c_vote++)
|
||||
{
|
||||
if (c_vote->second > vote ||
|
||||
(c_vote->second >= vote && rg.get(2) == 0))
|
||||
{
|
||||
vote = c_vote->second;
|
||||
reverse_vote = c_vote;
|
||||
}
|
||||
}
|
||||
if (reverse_vote != reverses.end())
|
||||
{
|
||||
reverses_rate = float(reverse_vote->second) / cur_players;
|
||||
}
|
||||
|
||||
// End early if there is majority agreement which is all entries rate > 0.5
|
||||
it = m_peers_votes.begin();
|
||||
if (tracks_rate > 0.5f && laps_rate > 0.5f && reverses_rate > 0.5f)
|
||||
{
|
||||
while (it != m_peers_votes.end())
|
||||
{
|
||||
if (it->second.m_track_name == top_track)
|
||||
break;
|
||||
else
|
||||
it++;
|
||||
}
|
||||
if (it == m_peers_votes.end())
|
||||
{
|
||||
Log::warn("ServerLobby",
|
||||
"Missing track %s from majority.", top_track.c_str());
|
||||
it = m_peers_votes.begin();
|
||||
}
|
||||
*winner_peer_id = it->first;
|
||||
*winner_vote = it->second;
|
||||
return true;
|
||||
}
|
||||
else if (isVotingOver())
|
||||
{
|
||||
// Pick the best lap (or soccer goal / time) from only the top track
|
||||
// if no majority agreement from all
|
||||
int diff = std::numeric_limits<int>::max();
|
||||
auto closest_lap = m_peers_votes.begin();
|
||||
while (it != m_peers_votes.end())
|
||||
{
|
||||
if (it->second.m_track_name == top_track &&
|
||||
std::abs((int)it->second.m_num_laps - top_laps) < diff)
|
||||
{
|
||||
closest_lap = it;
|
||||
diff = std::abs((int)it->second.m_num_laps - top_laps);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
*winner_peer_id = closest_lap->first;
|
||||
*winner_vote = closest_lap->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
} // handleAllVotes
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -290,6 +290,15 @@ void TracksScreen::beforeAddingWidget()
|
||||
"restart.png")), 1);
|
||||
}
|
||||
calculateLayout();
|
||||
static bool shown_msg = false;
|
||||
if (!shown_msg)
|
||||
{
|
||||
shown_msg = true;
|
||||
//I18N: In track screen for networking, clarify voting phase
|
||||
core::stringw msg = _("If a majority of players all select the"
|
||||
" same track and race settings, voting will end early.");
|
||||
MessageQueue::add(MessageQueue::MT_GENERIC, msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -90,6 +90,7 @@ private:
|
||||
m_quit_server = false;
|
||||
m_bottom_box_height = -1;
|
||||
m_track_icons = NULL;
|
||||
m_timer = NULL;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void updateProgressBarText();
|
||||
|
Loading…
x
Reference in New Issue
Block a user