diff --git a/sources.cmake b/sources.cmake index 13db008ff..3e53f13e9 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 819750963..95993f004 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -42,6 +42,7 @@ #include "race/history.hpp" #include "race/race_manager.hpp" #include "states_screens/state_manager.hpp" +#include "states_screens/online/vote_overview.hpp" #include "utils/profiler.hpp" #include "utils/time.hpp" @@ -542,6 +543,14 @@ void MainLoop::run() */ void MainLoop::renderGUI(int phase, int loop_index, int loop_size) { +#ifdef SERVER_ONLY + return; +#else + if (NetworkConfig::get()->isNetworking() && + NetworkConfig::get()->isServer() ) + { + return; + } // TODO: Rendering past 7000 causes the minimap to not work // on higher graphical settings //if(phase>7000) return; @@ -554,7 +563,10 @@ void MainLoop::renderGUI(int phase, int loop_index, int loop_size) m_curr_time = now; - + if (NetworkConfig::get()->isNetworking() && phase >= 5000) + { + VoteOverview::getInstance()->showVoteResult(); + } // TODO: remove debug output Log::verbose("mainloop", "Rendergui t %llu dt %f phase %d index %d / %d", now, dt, phase, loop_index, loop_size); @@ -564,5 +576,6 @@ void MainLoop::renderGUI(int phase, int loop_index, int loop_size) //TODO: remove debug output uint64_t now2 = StkTime::getRealTimeMs(); Log::verbose("mainloop", " duration t %llu dt %llu", now, now2-now); +#endif } // renderGUI /* EOF */ diff --git a/src/network/game_setup.hpp b/src/network/game_setup.hpp index 8c7eade06..b87beb5e1 100644 --- a/src/network/game_setup.hpp +++ b/src/network/game_setup.hpp @@ -24,6 +24,8 @@ #include "network/remote_kart_info.hpp" +#include "network/peer_vote.hpp" + #include #include #include @@ -106,12 +108,12 @@ public: /** Returns the number of connected players. */ unsigned getPlayerCount() { return m_connected_players_count.load(); } // ------------------------------------------------------------------------ - void setRace(const std::string& track, unsigned laps, bool reverse) + void setRace(const PeerVote &vote) { - m_tracks.push_back(track); - m_laps = laps; - m_reverse = reverse; - } + m_tracks.push_back(vote.m_track_name); + m_laps = vote.m_num_laps; + m_reverse = vote.m_reverse; + } // setRace // ------------------------------------------------------------------------ void reset() { diff --git a/src/network/peer_vote.hpp b/src/network/peer_vote.hpp new file mode 100644 index 000000000..87708e000 --- /dev/null +++ b/src/network/peer_vote.hpp @@ -0,0 +1,74 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2018 Joerg Henrichs +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 3 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef PEER_VOTE_HPP +#define PEER_VOTE_HPP + +#include "network/network_string.hpp" + +#include "irrString.h" +#include + +/** A simple structure to store a vote from a client: + * track name, number of laps and reverse or not. */ +class PeerVote +{ +public: + core::stringw m_player_name; + std::string m_track_name; + uint8_t m_num_laps; + bool m_reverse; + + // ------------------------------------------------------ + PeerVote() : m_player_name(""), m_track_name(""), + m_num_laps(1), m_reverse(false) + { + } // PeerVote() + // ------------------------------------------------------ + PeerVote(const core::stringw &name, + const std::string track, + int laps, bool reverse) : m_player_name(name), + m_track_name(track), + m_num_laps(laps), + m_reverse(reverse) + { + } // PeerVote(name, track, laps, reverse) + + // ------------------------------------------------------ + /** Initialised this object from a data in a network string. */ + PeerVote(NetworkString &ns) + { + ns.decodeStringW(&m_player_name); + ns.decodeString(&m_track_name); + m_num_laps = ns.getUInt8(); + m_reverse = ns.getUInt8(); + + } // PeerVote(NetworkString &) + + // ------------------------------------------------------ + /** Encodes this vote object into a network string. */ + void encode(NetworkString *ns) + { + ns->encodeString(m_player_name) + .encodeString(m_track_name) + .addUInt8(m_num_laps) + .addUInt8(m_reverse); + } // encode +}; // class PeerVote + +#endif // PEER_VOTE_HPP diff --git a/src/network/protocols/client_lobby.cpp b/src/network/protocols/client_lobby.cpp index 6748572f6..cc6001ee1 100644 --- a/src/network/protocols/client_lobby.cpp +++ b/src/network/protocols/client_lobby.cpp @@ -241,11 +241,10 @@ void ClientLobby::addAllPlayers(Event* event) } NetworkString& data = event->data(); - std::string track_name; - data.decodeString(&track_name); - uint8_t lap = data.getUInt8(); - uint8_t reverse = data.getUInt8(); - m_game_setup->setRace(track_name, lap, reverse == 1); + PeerVote winner_vote(data); + + m_game_setup->setRace(winner_vote); + VoteOverview::getInstance()->setResult(winner_vote); std::shared_ptr peer = event->getPeerSP(); peer->cleanPlayerProfiles(); diff --git a/src/network/protocols/lobby_protocol.hpp b/src/network/protocols/lobby_protocol.hpp index d743fd205..e14a93fe8 100644 --- a/src/network/protocols/lobby_protocol.hpp +++ b/src/network/protocols/lobby_protocol.hpp @@ -22,6 +22,7 @@ #include "network/protocol.hpp" #include "network/network_string.hpp" +#include "network/peer_vote.hpp" class GameSetup; class NetworkPlayerProfile; @@ -85,50 +86,6 @@ public: /** The maximum voting time. */ uint64_t m_max_voting_time; -public: - /** A simple structure to store a vote from a client: - * track name, number of laps and reverse or not. */ - class PeerVote - { - public: - core::stringw m_player_name; - std::string m_track_name; - uint8_t m_num_laps; - bool m_reverse; - - // ------------------------------------------------------ - PeerVote() : m_player_name(""), m_track_name(""), - m_num_laps(1), m_reverse(false) - {} - // ------------------------------------------------------ - PeerVote(const core::stringw &name, - const std::string track, - int laps, bool reverse) : m_player_name(name), - m_track_name(track), - m_num_laps(laps), - m_reverse(reverse) - {} - // ------------------------------------------------------ - /** Initialised this object from a data in a network string. */ - PeerVote(NetworkString &ns) - { - ns.decodeStringW(&m_player_name); - 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_player_name) - .encodeString(m_track_name) - .addUInt8(m_num_laps) - .addUInt8(m_reverse); - } // encode - }; // class PeerVote - protected: /** 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. */ diff --git a/src/network/protocols/server_lobby.cpp b/src/network/protocols/server_lobby.cpp index 747b59921..0381585c5 100644 --- a/src/network/protocols/server_lobby.cpp +++ b/src/network/protocols/server_lobby.cpp @@ -489,12 +489,11 @@ void ServerLobby::asynchronousUpdate() } case SELECTING: { - PeerVote winner; - bool all_votes_in = handleAllVotes(&winner); + PeerVote winner_vote; + bool all_votes_in = handleAllVotes(&winner_vote); if (isVotingOver() || all_votes_in) { - m_game_setup->setRace(winner.m_track_name, winner.m_num_laps, - winner.m_reverse ); + m_game_setup->setRace(winner_vote); // Remove disconnected player (if any) one last time m_game_setup->update(true); m_game_setup->sortPlayersForGrandPrix(); @@ -502,17 +501,17 @@ void ServerLobby::asynchronousUpdate() auto players = m_game_setup->getConnectedPlayers(); for (auto& player : players) player->getPeer()->clearAvailableKartIDs(); - NetworkString* load_world = getNetworkString(); - load_world->setSynchronous(true); - 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()); + + NetworkString* load_world_message = getNetworkString(); + load_world_message->setSynchronous(true); + load_world_message->addUInt8(LE_LOAD_WORLD); + winner_vote.encode(load_world_message); + load_world_message->addUInt8((uint8_t)players.size()); for (unsigned i = 0; i < players.size(); i++) { std::shared_ptr& player = players[i]; player->getPeer()->addAvailableKartID(i); - load_world->encodeString(player->getName()) + load_world_message->encodeString(player->getName()) .addUInt32(player->getHostId()) .addFloat(player->getDefaultKartColor()) .addUInt32(player->getOnlineId()) @@ -527,15 +526,15 @@ void ServerLobby::asynchronousUpdate() std::advance(it, rg.get((int)m_available_kts.first.size())); player->setKartName(*it); } - load_world->encodeString(player->getKartName()); + load_world_message->encodeString(player->getKartName()); } uint32_t random_seed = (uint32_t)StkTime::getTimeSinceEpoch(); ItemManager::updateRandomSeed(random_seed); - load_world->addUInt32(random_seed); + load_world_message->addUInt32(random_seed); if (race_manager->getMinorMode() == RaceManager::MINOR_MODE_BATTLE) { auto hcl = getHitCaptureLimit((float)players.size()); - load_world->addUInt32(hcl.first).addFloat(hcl.second); + load_world_message->addUInt32(hcl.first).addFloat(hcl.second); m_game_setup->setHitCaptureTime(hcl.first, hcl.second); } configRemoteKart(players); @@ -543,8 +542,8 @@ void ServerLobby::asynchronousUpdate() // Reset for next state usage resetPeersReady(); m_state = LOAD_WORLD; - sendMessageToPeers(load_world); - delete load_world; + sendMessageToPeers(load_world_message); + delete load_world_message; } break; } @@ -1898,9 +1897,9 @@ void ServerLobby::handlePlayerVote(Event* event) // ---------------------------------------------------------------------------- /** Select the track to be used based on all votes being received. - * \param winner The PeerVote that was picked. + * \param winner_vote The PeerVote that was picked. */ -bool ServerLobby::handleAllVotes(PeerVote *winner) +bool ServerLobby::handleAllVotes(PeerVote *winner_vote) { // First remove all votes from disconnected hosts auto it = m_peers_votes.begin(); @@ -1929,9 +1928,9 @@ bool ServerLobby::handleAllVotes(PeerVote *winner) 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; + winner_vote->m_track_name = *it; + winner_vote->m_num_laps = UserConfigParams::m_num_laps; + winner_vote->m_reverse = winner_vote->m_track_name.size() % 2 == 0; return false; } @@ -1941,7 +1940,7 @@ bool ServerLobby::handleAllVotes(PeerVote *winner) auto vote = m_peers_votes.begin(); std::advance(vote, r.get(m_peers_votes.size()) ); - *winner = vote->second; + *winner_vote = vote->second; return false; return m_peers_votes.size() == cur_players; } // handleAllVotes diff --git a/src/states_screens/online/tracks_screen.cpp b/src/states_screens/online/tracks_screen.cpp index 20efc0446..338ca6dd1 100644 --- a/src/states_screens/online/tracks_screen.cpp +++ b/src/states_screens/online/tracks_screen.cpp @@ -329,7 +329,8 @@ void TracksScreen::init() { getWidget("lap-text")->setVisible(true); //I18N: In track screen - getWidget("lap-text")->setText(_("Number of laps"), false); + getWidget("lap-text") + ->setText(_("Number of laps"), false); m_laps->setVisible(true); m_laps->setMin(1); m_laps->setMax(20); @@ -337,12 +338,12 @@ void TracksScreen::init() } getWidget("reverse-text")->setVisible(true); //I18N: In track screen - getWidget("reverse-text")->setText(_("Drive in reverse"), false); + getWidget("reverse-text") + ->setText(_("Drive in reverse"), false); m_reversed->setVisible(true); auto lp = LobbyProtocol::get(); - const LobbyProtocol::PeerVote *vote = - lp ->getVote(STKHost::get()->getMyHostId()); + const PeerVote *vote = lp ->getVote(STKHost::get()->getMyHostId()); DynamicRibbonWidget* w2 = getWidget("tracks"); if(vote) { @@ -486,10 +487,8 @@ void TracksScreen::voteForPlayer() } else { - LobbyProtocol::PeerVote pvote(player_name, - m_selected_track->getIdent(), - m_laps->getValue(), - m_reversed->getState() ); + PeerVote pvote(player_name, m_selected_track->getIdent(), + m_laps->getValue(), m_reversed->getState() ); pvote.encode(&vote); auto lp = LobbyProtocol::get(); diff --git a/src/states_screens/online/vote_overview.cpp b/src/states_screens/online/vote_overview.cpp index e01fc4638..90bd8ff99 100644 --- a/src/states_screens/online/vote_overview.cpp +++ b/src/states_screens/online/vote_overview.cpp @@ -41,7 +41,6 @@ using namespace irr::video; VoteOverview::VoteOverview() : Screen("online/vote_overview.stkgui") { m_quit_server = false; - m_bottom_box_height = -1; } // VoteOverview // ----------------------------------------------------------------------------- @@ -59,11 +58,8 @@ void VoteOverview::beforeAddingWidget() Widget* rect_box = getWidget("rect-box"); - if (m_bottom_box_height == -1) - m_bottom_box_height = rect_box->m_h; - rect_box->setVisible(true); - rect_box->m_properties[GUIEngine::PROP_HEIGHT] = StringUtils::toString(m_bottom_box_height); + rect_box->m_properties[GUIEngine::PROP_HEIGHT] = StringUtils::toString(rect_box->m_h); calculateLayout(); @@ -77,6 +73,8 @@ void VoteOverview::beforeAddingWidget() // ----------------------------------------------------------------------------- void VoteOverview::init() { + m_timer->setVisible(true); + m_random_anim_timer = 0.0f; // change the back button image (because it makes the game quit) if (m_quit_server) { @@ -147,7 +145,7 @@ void VoteOverview::showVote(int host_id) int index = it - m_index_to_hostid.begin(); auto lp = LobbyProtocol::get(); - const LobbyProtocol::PeerVote *vote = lp->getVote(host_id); + const PeerVote *vote = lp->getVote(host_id); assert(vote); std::string s = StringUtils::insertValues("name-%d", index); @@ -184,27 +182,83 @@ void VoteOverview::onUpdate(float dt) if(m_index_to_hostid.size()==0) return; - static float xx = 0.0f; - xx += 2*dt; - int index = int(xx) % m_index_to_hostid.size(); + // First make sure the old grid is set back to normal: + int old_index = int(m_random_anim_timer) % m_index_to_hostid.size(); + std::string box_name = StringUtils::insertValues("rect-box%d", old_index); + Widget *box = getWidget(box_name.c_str()); + box->setSelected(PLAYER_ID_GAME_MASTER, false); + + // Increase timer, and determine next index to show + m_random_anim_timer += 2*dt; + int new_index = int(m_random_anim_timer) % m_index_to_hostid.size(); + box_name = StringUtils::insertValues("rect-box%d", new_index); + box = getWidget(box_name.c_str()); + box->setSelected(PLAYER_ID_GAME_MASTER, true); +} // onUpdate + +// ----------------------------------------------------------------------------- +/** Received the winning vote. i.e. the data about the track to play (including + * #laps etc). + */ +void VoteOverview::setResult(const PeerVote &winner_vote) +{ + m_timer->setVisible(false); + // Note that the votes on the server might have a different order from + // the votes here on the client. Potentially there could also be a missing + // vote(??) + auto lp = LobbyProtocol::get(); + m_winning_index = -1; + for (unsigned int i=0; igetVote(m_index_to_hostid[i]); + if(!vote) continue; + if(vote->m_track_name == winner_vote.m_track_name && + vote->m_num_laps == winner_vote.m_num_laps && + vote->m_reverse == winner_vote.m_reverse ) + { + m_winning_index = i; + break; + } + // Try to prepare a fallback in case that the right vote is not here. + if (vote->m_track_name == winner_vote.m_track_name) + { + m_winning_index = i; + } + } // for i in m_index_to_hostid + + if(m_winning_index == -1) + { + // We don't have the right vote. Assume that a message got lost, + // In this case, change one non-local vote: + for(unsigned int i=0; igetMyHostId()) + { + lp->addVote(m_index_to_hostid[i], winner_vote); + m_winning_index = i; + break; + } + } + } // winnind==-1 +} // setResult + +// ----------------------------------------------------------------------------- +/** Called when the final 'random picking' animation is finished so that only + * the result is shown: all boxes except the winner is set to be invisible. + */ +void VoteOverview::showVoteResult() +{ for (unsigned int i = 0; i < 8; i++) { std::string box_name = StringUtils::insertValues("rect-box%d", i); Widget *box = getWidget(box_name.c_str()); - box->setSelected(PLAYER_ID_GAME_MASTER, i==index); + if(i!=m_winning_index) + box->setVisible(false); + else + box->setSelected(PLAYER_ID_GAME_MASTER, true); } - std::string s = StringUtils::insertValues("name-%d", index); - LabelWidget *name_widget = getWidget(s.c_str()); - - name_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); - name_widget->setErrorColor(); - - s = StringUtils::insertValues("name-%d", 1-index); - name_widget = getWidget(s.c_str()); - name_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER); - name_widget->setDefaultColor(); -} // onUpdate +} // showVoteResult // ----------------------------------------------------------------------------- /** Called on any event, e.g. user input. diff --git a/src/states_screens/online/vote_overview.hpp b/src/states_screens/online/vote_overview.hpp index f4cfca88e..6f44920e8 100644 --- a/src/states_screens/online/vote_overview.hpp +++ b/src/states_screens/online/vote_overview.hpp @@ -20,6 +20,8 @@ #include "guiengine/screen.hpp" +#include "network/peer_vote.hpp" + #include #include @@ -43,14 +45,18 @@ private: * (going backwards). */ GUIEngine::ProgressBarWidget *m_timer; - int m_bottom_box_height; - /** This stores which vote (hostid) is shown at which index in * the result gui. */ std::vector m_index_to_hostid; + /** Index of the winning vote. */ + int m_winning_index; + bool m_quit_server; + /* A timer used to randomly select tracks. */ + float m_random_anim_timer; + VoteOverview(); public: @@ -80,6 +86,8 @@ public: void addVote(int host_id); void showVote(int host_id); + void showVoteResult(); + void setResult(const PeerVote &winner_vote); // ------------------------------------------------------------------------ void setQuitServer() { m_quit_server = true; }