Store voting data in lobby protocol so it is available on client

and server. Voting behaviour is now to start the race as soon as
all votes are in.
This commit is contained in:
hiker
2018-11-28 08:13:59 +11:00
parent fa7d3eef73
commit bd0641f12a
8 changed files with 162 additions and 189 deletions

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<icon-button id="back" x="0" y="0" height="8%" icon="gui/icons/back.png"/>
<div id="all-track" x="1%" y="1%" width="98%" height="96%" layout="vertical-row" >
<header width="80%" I18N="In the track selection screen" text="Votes"
align="center" text_align="center" />
<div id="rect-box" width="100%" height="80%" padding="15" layout="vertical-row">
<div proportion="1" width="100%" layout="horizontal-row" padding="1">
<box id="rect-box0" width="24%" height="100%" padding="15" layout="vertical-row">
</box>
<box id="rect-box1" width="24%" height="100%" padding="15" layout="vertical-row">
</box>
<box id="rect-box2" width="24%" height="100%" padding="15" layout="vertical-row">
</box>
<box id="rect-box3" width="24%" height="100%" padding="15" layout="vertical-row">
</box>
</div>
<div proportion="1" width="100%" layout="horizontal-row" padding="1">
<box id="rect-box4" width="24%" height="100%" padding="15" layout="vertical-row">
</box>
<box id="rect-box5" width="24%" height="100%" padding="15" layout="vertical-row">
</box>
<box id="rect-box6" width="24%" height="100%" padding="15" layout="vertical-row">
</box>
<box id="rect-box7" width="24%" height="100%" padding="15" layout="vertical-row">
</box>
</div>
<progressbar id="timer" height="4%" width="100%"></progressbar>
</div>
</div>
</stkgui>

View File

@@ -44,6 +44,7 @@
#include "network/stk_peer.hpp"
#include "states_screens/online/networking_lobby.hpp"
#include "states_screens/online/network_kart_selection.hpp"
#include "states_screens/online/vote_overview.hpp"
#include "states_screens/race_result_gui.hpp"
#include "states_screens/state_manager.hpp"
#include "states_screens/online/tracks_screen.hpp"
@@ -121,7 +122,7 @@ void ClientLobby::setup()
{
clearPlayers();
m_received_server_result = false;
TracksScreen::getInstance()->resetVote();
VoteOverview::getInstance()->resetVote();
LobbyProtocol::setup();
m_state.store(NONE);
} // setup
@@ -162,7 +163,7 @@ bool ClientLobby::notifyEvent(Event* event)
case LE_SERVER_INFO: handleServerInfo(event); break;
case LE_PLAYER_DISCONNECTED : disconnectedPlayer(event); break;
case LE_CONNECTION_REFUSED: connectionRefused(event); break;
case LE_VOTE: displayPlayerVote(event); break;
case LE_VOTE: receivePlayerVote(event); break;
case LE_SERVER_OWNERSHIP: becomingServerOwner(); break;
case LE_BAD_TEAM: handleBadTeam(); break;
case LE_BAD_CONNECTION: handleBadConnection(); break;
@@ -435,68 +436,32 @@ void ClientLobby::finalizeConnectionRequest(NetworkString* header,
} // finalizeConnectionRequest
//-----------------------------------------------------------------------------
void ClientLobby::displayPlayerVote(Event* event)
void ClientLobby::receivePlayerVote(Event* event)
{
if (!checkDataSize(event, 4)) return;
// Get the player name who voted
NetworkString& data = event->data();
uint32_t host_id2 = data.getUInt32();
std::shared_ptr<STKPeer> peer = STKHost::get()->findPeerByHostId(host_id2);
std::string player_name;
data.decodeString(&player_name);
// if (host_id2 != STKHost::get()->getMyHostId())
{
// std::string local_name = StringUtils::wideToUtf8(peer->getPlayerProfiles()[0]->getName());
}
uint32_t host_id = data.getUInt32();
player_name += ": ";
std::string track_name;
data.decodeString(&track_name);
Track* track = track_manager->getTrack(track_name);
PeerVote vote(data);
m_peers_votes[event->getPeer()->getHostId()] = vote;
Track* track = track_manager->getTrack(vote.m_track_name);
if (!track)
Log::fatal("ClientLobby", "Missing track %s", track_name.c_str());
core::stringw track_readable = track->getName();
int lap = data.getUInt8();
int rev = data.getUInt8();
core::stringw yes = _("Yes");
core::stringw no = _("No");
core::stringw vote_msg;
if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE &&
race_manager->getMajorMode() == RaceManager::MAJOR_MODE_FREE_FOR_ALL)
{
//I18N: Vote message in network game from a player
vote_msg = _("Track: %s,\nrandom item location: %s",
track_readable, rev == 1 ? yes : no);
}
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE &&
race_manager->getMajorMode() ==
RaceManager::MAJOR_MODE_CAPTURE_THE_FLAG)
{
//I18N: Vote message in network game from a player
vote_msg = _("Track: %s", track_readable);
}
else if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_SOCCER)
{
if (m_game_setup->isSoccerGoalTarget())
{
//I18N: Vote message in network game from a player
vote_msg = _("Track: %s,\n"
"number of goals to win: %d,\nrandom item location: %s",
track_readable, lap, rev == 1 ? yes : no);
}
else
{
//I18N: Vote message in network game from a player
vote_msg = _("Track: %s,\n"
"maximum time: %d,\nrandom item location: %s",
track_readable, lap, rev == 1 ? yes : no);
}
}
else
{
//I18N: Vote message in network game from a player
vote_msg = _("Track: %s,\nlaps: %d, reversed: %s",
track_readable, lap, rev == 1 ? yes : no);
}
vote_msg = StringUtils::utf8ToWide(player_name) + vote_msg;
TracksScreen::getInstance()->addVoteMessage(player_name +
StringUtils::toString(host_id), vote_msg);
} // displayPlayerVote
Log::fatal("ClientLobby", "Missing track %s", vote.m_track_name.c_str());
} // receivePlayerVote
//-----------------------------------------------------------------------------
/*! \brief Called when a new player is disconnected

View File

@@ -44,7 +44,7 @@ private:
void raceFinished(Event* event);
void exitResultScreen(Event *event);
// race votes
void displayPlayerVote(Event* event);
void receivePlayerVote(Event* event);
void updatePlayerList(Event* event);
void handleChat(Event* event);
void handleServerInfo(Event* event);

View File

@@ -21,11 +21,14 @@
#include "network/protocol.hpp"
#include "network/network_string.hpp"
class GameSetup;
class NetworkPlayerProfile;
#include <atomic>
#include <cassert>
#include <map>
#include <memory>
#include <thread>
#include <vector>
@@ -83,6 +86,43 @@ public:
uint64_t m_max_voting_time;
protected:
/** A simple structure to store a vote from a client:
* track name, number of laps and reverse or not. */
class PeerVote
{
public:
std::string m_track_name;
uint8_t m_num_laps;
bool m_reverse;
// ------------------------------------------------------
PeerVote() : m_track_name(""), m_num_laps(1),
m_reverse(false)
{}
// ------------------------------------------------------
/** Initialised this object from a data in a network string. */
PeerVote(NetworkString &ns)
{
ns.decodeString(&m_track_name);
m_num_laps = ns.getUInt8();
m_reverse = ns.getUInt8();
} // PeerVote
// ------------------------------------------------------
/** Encodes this vote object into a network string. */
void encode(NetworkString *ns)
{
ns->encodeString(m_track_name)
.addUInt8(m_num_laps)
.addUInt8(m_reverse);
} // encode
}; // class PeerVote
/** Vote from each peer. The host id is used as a key. Note that
* host ids can be non-consecutive, so we cannot use std::vector. */
std::map<int, PeerVote> m_peers_votes;
std::thread m_start_game_thread;
static std::weak_ptr<LobbyProtocol> m_lobby;

View File

@@ -334,7 +334,7 @@ bool ServerLobby::notifyEventAsynchronous(Event* event)
case LE_CONNECTION_REQUESTED: connectionRequested(event); break;
case LE_KART_SELECTION: kartSelectionRequested(event); break;
case LE_CLIENT_LOADED_WORLD: finishedLoadingWorldClient(event); break;
case LE_VOTE: playerVote(event); break;
case LE_VOTE: handlePlayerVote(event); break;
case LE_KICK_HOST: kickHost(event); break;
case LE_CHANGE_TEAM: changeTeam(event); break;
case LE_REQUEST_BEGIN: startSelection(event); break;
@@ -489,13 +489,12 @@ void ServerLobby::asynchronousUpdate()
}
case SELECTING:
{
std::string track_name;
int num_laps;
bool reverse;
bool all_votes_in = handleAllVotes(&track_name, &num_laps, &reverse);
PeerVote winner;
bool all_votes_in = handleAllVotes(&winner);
if (isVotingOver() || all_votes_in)
{
m_game_setup->setRace(track_name, num_laps, reverse);
m_game_setup->setRace(winner.m_track_name, winner.m_num_laps,
winner.m_reverse );
// Remove disconnected player (if any) one last time
m_game_setup->update(true);
m_game_setup->sortPlayersForGrandPrix();
@@ -505,9 +504,10 @@ void ServerLobby::asynchronousUpdate()
player->getPeer()->clearAvailableKartIDs();
NetworkString* load_world = getNetworkString();
load_world->setSynchronous(true);
load_world->addUInt8(LE_LOAD_WORLD).encodeString(track_name)
.addUInt8(num_laps).addUInt8(reverse)
.addUInt8((uint8_t)players.size());
load_world->addUInt8(LE_LOAD_WORLD)
.encodeString(winner.m_track_name)
.addUInt8(winner.m_num_laps).addUInt8(winner.m_reverse)
.addUInt8((uint8_t)players.size());
for (unsigned i = 0; i < players.size(); i++)
{
std::shared_ptr<NetworkPlayerProfile>& player = players[i];
@@ -1832,7 +1832,7 @@ void ServerLobby::kartSelectionRequested(Event* event)
/*! \brief Called when a player votes for track(s).
* \param event : Event providing the information.
*/
void ServerLobby::playerVote(Event* event)
void ServerLobby::handlePlayerVote(Event* event)
{
if (m_state != SELECTING)
{
@@ -1849,145 +1849,97 @@ void ServerLobby::playerVote(Event* event)
if (isVotingOver()) return;
NetworkString& data = event->data();
std::string track_name;
data.decodeString(&track_name);
uint8_t lap = data.getUInt8();
uint8_t reverse = data.getUInt8();
PeerVote vote(data);
if (race_manager->modeHasLaps())
{
if (ServerConfig::m_auto_lap_ratio > 0.0f)
{
Track* t = track_manager->getTrack(track_name);
Track* t = track_manager->getTrack(vote.m_track_name);
if (t)
{
lap = (uint8_t)(fmaxf(1.0f,
(float)t->getDefaultNumberOfLaps() *
ServerConfig::m_auto_lap_ratio));
vote.m_num_laps =
(uint8_t)(fmaxf(1.0f,
(float)t->getDefaultNumberOfLaps()
*ServerConfig::m_auto_lap_ratio ) );
}
else
{
// Prevent someone send invalid vote
track_name = *m_available_kts.second.begin();
lap = (uint8_t)3;
vote.m_track_name = *m_available_kts.second.begin();
vote.m_num_laps = (uint8_t)3;
}
}
else if (lap == 0)
lap = (uint8_t)3;
else if (vote.m_num_laps == 0)
vote.m_num_laps = (uint8_t)3;
}
NetworkString other = NetworkString(PROTOCOL_LOBBY_ROOM);
std::string name = StringUtils::wideToUtf8(event->getPeer()
->getPlayerProfiles()[0]->getName());
other.setSynchronous(true);
other.addUInt8(LE_VOTE)
.encodeString(name).addUInt32(event->getPeer()->getHostId())
.encodeString(track_name).addUInt8(lap).addUInt8(reverse);
// Store vote:
m_peers_votes[event->getPeer()->getHostId()] = vote;
// Now inform all clients about the vote
NetworkString other = NetworkString(PROTOCOL_LOBBY_ROOM);
std::string name =
StringUtils::wideToUtf8(event->getPeer()
->getPlayerProfiles()[0]->getName());
other.setSynchronous(true);
other.addUInt8(LE_VOTE);
other.addUInt32(event->getPeer()->getHostId()) .encodeString(name)
.addUInt32(event->getPeer()->getHostId());
vote.encode(&other);
m_peers_votes[event->getPeerSP()] =
std::make_tuple(track_name, lap, reverse == 1);
sendMessageToPeers(&other);
} // playerVote
} // handlePlayerVote
// ----------------------------------------------------------------------------
/** Select the track to be used based on all votes being received.
* \param track_name Name of the track voted for.
* \param num_laps Number of laps.
* \param reverse If reverse track is to be used.
* \result True if a vote from each player has been received, false otherwise.
* \param winner The PeerVote that was picked.
*/
bool ServerLobby::handleAllVotes(std::string *track_name, int *num_laps,
bool *reverse)
bool ServerLobby::handleAllVotes(PeerVote *winner)
{
// Default settings if no votes at all
RandomGenerator rg;
std::set<std::string>::iterator it = m_available_kts.second.begin();
std::advance(it, rg.get((int)m_available_kts.second.size()));
*track_name = *it;
*num_laps = UserConfigParams::m_num_laps;
*reverse = track_name->size() % 2 == 0;
// First remove all votes from disconnected hosts
auto it = m_peers_votes.begin();
while (it != m_peers_votes.end())
{
auto peer = STKHost::get()->findPeerByHostId(it->first);
if (peer == nullptr)
{
it = m_peers_votes.erase(it);
}
else
it++;
}
int cur_players = 0;
// Count number of players
unsigned int cur_players = 0;
auto peers = STKHost::get()->getPeers();
for (auto peer : peers)
{
if (peer->hasPlayerProfiles() && !peer->isWaitingForGame())
cur_players ++;
}
if (cur_players == 0) return false;
std::map<std::string, unsigned> tracks;
std::map<unsigned, unsigned> laps;
std::map<bool, unsigned> reverses;
for (auto p : m_peers_votes)
if (cur_players == 0 || m_peers_votes.size() < cur_players)
{
if (p.first.expired())
continue;
auto track_vote = tracks.find(std::get<0>(p.second));
if (track_vote == tracks.end())
tracks[std::get<0>(p.second)] = 1;
else
track_vote->second++;
auto lap_vote = laps.find(std::get<1>(p.second));
if (lap_vote == laps.end())
laps[std::get<1>(p.second)] = 1;
else
lap_vote->second++;
auto reverse_vote = reverses.find(std::get<2>(p.second));
if (reverse_vote == reverses.end())
reverses[std::get<2>(p.second)] = 1;
else
reverse_vote->second++;
} // for p in m_peers_votes
unsigned vote = 0;
auto track_vote = tracks.begin();
for (auto c_vote = tracks.begin(); c_vote != tracks.end(); c_vote++)
{
if (c_vote->second > vote)
{
vote = c_vote->second;
track_vote = c_vote;
}
}
if (track_vote != tracks.end())
{
*track_name = track_vote->first;
// Default settings if no votes at all
RandomGenerator rg;
std::set<std::string>::iterator it = m_available_kts.second.begin();
std::advance(it, rg.get((int)m_available_kts.second.size()));
winner->m_track_name = *it;
winner->m_num_laps = UserConfigParams::m_num_laps;
winner->m_reverse = winner->m_track_name.size() % 2 == 0;
return false;
}
vote = 0;
auto lap_vote = laps.begin();
for (auto c_vote = laps.begin(); c_vote != laps.end(); c_vote++)
{
if (c_vote->second > vote)
{
vote = c_vote->second;
lap_vote = c_vote;
}
}
if (lap_vote != laps.end())
{
*num_laps = lap_vote->first;
}
RandomGenerator r;
auto vote = m_peers_votes.begin();
std::advance(vote, r.get(m_peers_votes.size()) );
vote = 0;
auto reverse_vote = reverses.begin();
for (auto c_vote = reverses.begin(); c_vote != reverses.end(); c_vote++)
{
if (c_vote->second > vote)
{
vote = c_vote->second;
reverse_vote = c_vote;
}
}
if (reverse_vote != reverses.end())
{
*reverse = reverse_vote->first;
}
*winner = vote->second;
return false;
return m_peers_votes.size() == cur_players;
} // handleAllVotes

View File

@@ -86,10 +86,6 @@ private:
std::map<std::weak_ptr<STKPeer>, bool,
std::owner_less<std::weak_ptr<STKPeer> > > m_peers_ready;
/** Vote from each peer. */
std::map<std::weak_ptr<STKPeer>, std::tuple<std::string, uint8_t, bool>,
std::owner_less<std::weak_ptr<STKPeer> > > m_peers_votes;
bool m_has_created_server_id_file;
/** It indicates if this server is unregistered with the stk server. */
@@ -166,7 +162,7 @@ private:
// kart selection
void kartSelectionRequested(Event* event);
// Track(s) votes
void playerVote(Event *event);
void handlePlayerVote(Event *event);
void playerFinishedResult(Event *event);
bool registerServer(bool now);
void finishedLoadingWorldClient(Event *event);
@@ -238,8 +234,7 @@ private:
const std::string& iv,
uint32_t online_id,
const irr::core::stringw& online_name);
bool handleAllVotes(std::string *track_name, int *num_laps,
bool *reverse);
bool handleAllVotes(PeerVote *winner);
void getRankingForPlayer(std::shared_ptr<NetworkPlayerProfile> p);
void submitRankingsToAddons();
void computeNewRankings();

0
src/states_screens/online/network_kart_selection.cpp Executable file → Normal file
View File

View File

@@ -57,8 +57,6 @@ private:
int m_bottom_box_height;
std::map<std::string, core::stringw> m_vote_messages;
std::deque<std::string> m_random_track_list;
/** adds the tracks from the current track group into the tracks ribbon */
@@ -104,17 +102,6 @@ public:
void setNetworkTracks() { m_network_tracks = true; }
// ------------------------------------------------------------------------
void setQuitServer() { m_quit_server = true; }
// ------------------------------------------------------------------------
void resetVote()
{
m_vote_messages.clear();
}
// ------------------------------------------------------------------------
void addVoteMessage(const std::string& user,
const irr::core::stringw& message)
{
m_vote_messages[user] = message;
}
};