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();
|
NetworkString& data = event->data();
|
||||||
|
uint32_t winner_peer_id = data.getUInt32();
|
||||||
PeerVote winner_vote(data);
|
PeerVote winner_vote(data);
|
||||||
|
|
||||||
m_game_setup->setRace(winner_vote);
|
m_game_setup->setRace(winner_vote);
|
||||||
|
@ -161,10 +161,11 @@ void LobbyProtocol::startVotingPeriod(float max_time)
|
|||||||
/** Returns the remaining voting time in seconds. */
|
/** Returns the remaining voting time in seconds. */
|
||||||
float LobbyProtocol::getRemainingVotingTime()
|
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;
|
return 0.0f;
|
||||||
uint64_t t = m_end_voting_period.load()- StkTime::getRealTimeMs();
|
uint64_t t = m_end_voting_period.load() - StkTime::getRealTimeMs();
|
||||||
return t/1000.0f;
|
return t / 1000.0f;
|
||||||
} // getRemainingVotingTime
|
} // getRemainingVotingTime
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -541,8 +541,8 @@ void ServerLobby::asynchronousUpdate()
|
|||||||
{
|
{
|
||||||
PeerVote winner_vote;
|
PeerVote winner_vote;
|
||||||
uint32_t winner_peer_id = std::numeric_limits<uint32_t>::max();
|
uint32_t winner_peer_id = std::numeric_limits<uint32_t>::max();
|
||||||
bool all_votes_in = handleAllVotes(&winner_vote, &winner_peer_id);
|
bool go_on_race = handleAllVotes(&winner_vote, &winner_peer_id);
|
||||||
if (isVotingOver() || all_votes_in)
|
if (go_on_race)
|
||||||
{
|
{
|
||||||
m_game_setup->setRace(winner_vote);
|
m_game_setup->setRace(winner_vote);
|
||||||
// Remove disconnected player (if any) one last time
|
// Remove disconnected player (if any) one last time
|
||||||
@ -560,6 +560,7 @@ void ServerLobby::asynchronousUpdate()
|
|||||||
NetworkString* load_world_message = getNetworkString();
|
NetworkString* load_world_message = getNetworkString();
|
||||||
load_world_message->setSynchronous(true);
|
load_world_message->setSynchronous(true);
|
||||||
load_world_message->addUInt8(LE_LOAD_WORLD);
|
load_world_message->addUInt8(LE_LOAD_WORLD);
|
||||||
|
load_world_message->addUInt32(winner_peer_id);
|
||||||
winner_vote.encode(load_world_message);
|
winner_vote.encode(load_world_message);
|
||||||
load_world_message->addUInt8((uint8_t)players.size());
|
load_world_message->addUInt8((uint8_t)players.size());
|
||||||
for (unsigned i = 0; i < players.size(); i++)
|
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.
|
/** Select the track to be used based on all votes being received.
|
||||||
* \param winner_vote The PeerVote that was picked.
|
* \param winner_vote The PeerVote that was picked.
|
||||||
* \param winner_peer_id The host id of winner (unchanged if no vote).
|
* \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,
|
bool ServerLobby::handleAllVotes(PeerVote* winner_vote,
|
||||||
uint32_t* winner_peer_id)
|
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
|
// First remove all votes from disconnected hosts
|
||||||
auto it = m_peers_votes.begin();
|
auto it = m_peers_votes.begin();
|
||||||
while (it != m_peers_votes.end())
|
while (it != m_peers_votes.end())
|
||||||
@ -2103,36 +2112,151 @@ bool ServerLobby::handleAllVotes(PeerVote* winner_vote,
|
|||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_peers_votes.empty())
|
||||||
|
{
|
||||||
|
if (isVotingOver())
|
||||||
|
{
|
||||||
|
*winner_vote = *m_default_vote;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Count number of players
|
// Count number of players
|
||||||
unsigned int cur_players = 0;
|
float cur_players = 0.0f;
|
||||||
auto peers = STKHost::get()->getPeers();
|
auto peers = STKHost::get()->getPeers();
|
||||||
for (auto peer : peers)
|
for (auto peer : peers)
|
||||||
{
|
{
|
||||||
if (peer->hasPlayerProfiles() && !peer->isWaitingForGame())
|
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
|
auto track_vote = tracks.find(p.second.m_track_name);
|
||||||
*winner_vote = *m_default_vote;
|
if (track_vote == tracks.end())
|
||||||
return false;
|
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
|
unsigned vote = 0;
|
||||||
// to use in the next race.
|
auto track_vote = tracks.begin();
|
||||||
RandomGenerator r;
|
// rg.get(2) == 0 will allow not always the "less" in map get picked
|
||||||
auto vote = m_peers_votes.begin();
|
for (auto c_vote = tracks.begin(); c_vote != tracks.end(); c_vote++)
|
||||||
std::advance(vote, r.get(m_peers_votes.size()) );
|
{
|
||||||
|
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;
|
vote = 0;
|
||||||
*winner_vote = vote->second;
|
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
|
vote = 0;
|
||||||
// have been received. Use:
|
auto reverse_vote = reverses.begin();
|
||||||
// return m_peers_votes.size() == cur_players;
|
for (auto c_vote = reverses.begin(); c_vote != reverses.end(); c_vote++)
|
||||||
// to start the race when all votes have been received.
|
{
|
||||||
|
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;
|
return false;
|
||||||
|
|
||||||
} // handleAllVotes
|
} // handleAllVotes
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -290,6 +290,15 @@ void TracksScreen::beforeAddingWidget()
|
|||||||
"restart.png")), 1);
|
"restart.png")), 1);
|
||||||
}
|
}
|
||||||
calculateLayout();
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -90,6 +90,7 @@ private:
|
|||||||
m_quit_server = false;
|
m_quit_server = false;
|
||||||
m_bottom_box_height = -1;
|
m_bottom_box_height = -1;
|
||||||
m_track_icons = NULL;
|
m_track_icons = NULL;
|
||||||
|
m_timer = NULL;
|
||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void updateProgressBarText();
|
void updateProgressBarText();
|
||||||
|
Loading…
Reference in New Issue
Block a user