Added initial voting animation while loading the track.

This commit is contained in:
hiker 2018-12-10 00:29:14 +11:00
parent c0816995ac
commit ed29c280b3
10 changed files with 214 additions and 109 deletions

View File

@ -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/*")

View File

@ -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 */

View File

@ -24,6 +24,8 @@
#include "network/remote_kart_info.hpp"
#include "network/peer_vote.hpp"
#include <atomic>
#include <cassert>
#include <memory>
@ -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()
{

74
src/network/peer_vote.hpp Normal file
View File

@ -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 <string>
/** 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

View File

@ -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<STKPeer> peer = event->getPeerSP();
peer->cleanPlayerProfiles();

View File

@ -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. */

View File

@ -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<NetworkPlayerProfile>& 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<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;
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

View File

@ -329,7 +329,8 @@ void TracksScreen::init()
{
getWidget("lap-text")->setVisible(true);
//I18N: In track screen
getWidget<LabelWidget>("lap-text")->setText(_("Number of laps"), false);
getWidget<LabelWidget>("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<LabelWidget>("reverse-text")->setText(_("Drive in reverse"), false);
getWidget<LabelWidget>("reverse-text")
->setText(_("Drive in reverse"), false);
m_reversed->setVisible(true);
auto lp = LobbyProtocol::get<LobbyProtocol>();
const LobbyProtocol::PeerVote *vote =
lp ->getVote(STKHost::get()->getMyHostId());
const PeerVote *vote = lp ->getVote(STKHost::get()->getMyHostId());
DynamicRibbonWidget* w2 = getWidget<DynamicRibbonWidget>("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<LobbyProtocol>();

View File

@ -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<LobbyProtocol>();
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<LobbyProtocol>();
m_winning_index = -1;
for (unsigned int i=0; i<m_index_to_hostid.size(); i++)
{
const PeerVote *vote = lp->getVote(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; i<m_index_to_hostid.size(); i++)
{
if(m_index_to_hostid[i] != STKHost::get()->getMyHostId())
{
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<LabelWidget>(s.c_str());
name_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
name_widget->setErrorColor();
s = StringUtils::insertValues("name-%d", 1-index);
name_widget = getWidget<LabelWidget>(s.c_str());
name_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
name_widget->setDefaultColor();
} // onUpdate
} // showVoteResult
// -----------------------------------------------------------------------------
/** Called on any event, e.g. user input.

View File

@ -20,6 +20,8 @@
#include "guiengine/screen.hpp"
#include "network/peer_vote.hpp"
#include <string>
#include <vector>
@ -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<int> 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; }