diff --git a/data/gui/screens/online/vote_overview.stkgui b/data/gui/screens/online/vote_overview.stkgui new file mode 100644 index 000000000..888aa0f9c --- /dev/null +++ b/data/gui/screens/online/vote_overview.stkgui @@ -0,0 +1,34 @@ + + + + +
+
+ +
+
+ + + + + + + + +
+ +
+ + + + + + + + +
+ +
+
+
diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 204cd9fe1..a37f7cefc 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -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 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 diff --git a/src/network/protocols/client_lobby.hpp b/src/network/protocols/client_lobby.hpp index 3d5a034a6..15a0526b9 100644 --- a/src/network/protocols/client_lobby.hpp +++ b/src/network/protocols/client_lobby.hpp @@ -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); diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index 4f31e9d80..2ebbc9a9c 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -21,11 +21,14 @@ #include "network/protocol.hpp" +#include "network/network_string.hpp" + class GameSetup; class NetworkPlayerProfile; #include #include +#include #include #include #include @@ -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 m_peers_votes; + + std::thread m_start_game_thread; static std::weak_ptr m_lobby; diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 017175e55..1c37a79ca 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -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& 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::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 tracks; - std::map laps; - std::map 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::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 diff --git a/src/network/protocols/server_lobby.hpp b/src/network/protocols/server_lobby.hpp index 009da99fc..51e32af3c 100644 --- a/src/network/protocols/server_lobby.hpp +++ b/src/network/protocols/server_lobby.hpp @@ -86,10 +86,6 @@ private: std::map, bool, std::owner_less > > m_peers_ready; - /** Vote from each peer. */ - std::map, std::tuple, - std::owner_less > > 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 p); void submitRankingsToAddons(); void computeNewRankings(); diff --git a/src/states_screens/online/network_kart_selection.cpp b/src/states_screens/online/network_kart_selection.cpp old mode 100755 new mode 100644 diff --git a/src/states_screens/online/tracks_screen.hpp b/src/states_screens/online/tracks_screen.hpp index 474d4d9f4..8e75eeb9d 100644 --- a/src/states_screens/online/tracks_screen.hpp +++ b/src/states_screens/online/tracks_screen.hpp @@ -57,8 +57,6 @@ private: int m_bottom_box_height; - std::map m_vote_messages; - std::deque 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; - } };