Synchronised race results menu, single races in LAN
mode can now be restarted (GP and FTL modes are still to be done). git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@2307 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
parent
44e9335baa
commit
1809dd1da1
@ -32,17 +32,19 @@ supertuxkart_SOURCES = main.cpp \
|
||||
material.cpp material.hpp \
|
||||
network/network_manager.cpp network/network_manager.hpp \
|
||||
network/network_kart.cpp network/network_kart.hpp \
|
||||
network/message.cpp network/message.hpp \
|
||||
network/message.cpp network/message.hpp \
|
||||
network/race_info_message.hpp network/race_info_message.cpp \
|
||||
network/remote_kart_info.hpp network/character_selected_message.hpp \
|
||||
network/race_start_message.hpp network/character_confirm_message.hpp \
|
||||
network/connect_message.hpp network/connect_message.cpp \
|
||||
network/connect_message.hpp network/connect_message.cpp \
|
||||
network/num_players_message.hpp network/world_loaded_message.hpp \
|
||||
network/connect_message.hpp network/character_info_message.hpp \
|
||||
network/kart_update_message.hpp network/kart_update_message.cpp \
|
||||
network/connect_message.hpp network/character_info_message.hpp \
|
||||
network/kart_update_message.hpp network/kart_update_message.cpp \
|
||||
network/kart_control_message.hpp network/kart_control_message.cpp \
|
||||
network/flyable_info.hpp network/herring_info.hpp \
|
||||
network/race_state.hpp network/race_state.cpp \
|
||||
network/flyable_info.hpp network/herring_info.hpp \
|
||||
network/race_state.hpp network/race_state.cpp \
|
||||
network/race_result_message.hpp network/race_result_message.cpp \
|
||||
network/race_result_ack_message \
|
||||
audio/music.hpp \
|
||||
audio/music_information.cpp audio/music_information.hpp \
|
||||
audio/music_ogg.cpp audio/music_ogg.hpp \
|
||||
|
@ -60,7 +60,8 @@ CharSel::CharSel(int whichPlayer)
|
||||
// in the server (even when NW_NONE). This if condition
|
||||
// needs to be checked (otherwise ghost karts and
|
||||
// a ghost camera will appear)
|
||||
if(network_manager->getState()==NetworkManager::NS_NONE ||
|
||||
m_first_frame = true;
|
||||
if(network_manager->getState()==NetworkManager::NS_MAIN_MENU ||
|
||||
network_manager->getMode()==NetworkManager::NW_NONE)
|
||||
network_manager->switchToCharacterSelection();
|
||||
|
||||
@ -336,8 +337,7 @@ void CharSel::update(float dt)
|
||||
return;
|
||||
}
|
||||
|
||||
static bool first=true;
|
||||
if(first)
|
||||
if(m_first_frame)
|
||||
{
|
||||
// Switch group will update the list of selected karts, i.e. use the
|
||||
// information about kart availability just received from the server
|
||||
@ -351,7 +351,7 @@ void CharSel::update(float dt)
|
||||
widget_manager->showWgt(WTOK_NAME0+i);
|
||||
widget_manager->showWgt(WTOK_RACER0+i);
|
||||
}
|
||||
first=false;
|
||||
m_first_frame = false;
|
||||
updateScrollPosition();
|
||||
return;
|
||||
}
|
||||
|
@ -36,6 +36,8 @@ private:
|
||||
int m_player_index;
|
||||
int m_offset; // index of first racer displayed
|
||||
unsigned int m_num_entries; // number of entries to display
|
||||
/** Helps to switch off the displayed text once only. */
|
||||
bool m_first_frame;
|
||||
std::vector<int> m_index_avail_karts;
|
||||
static const unsigned int m_max_entries=7;
|
||||
void updateScrollPosition();
|
||||
|
@ -140,7 +140,7 @@ void NetworkGUI::select()
|
||||
network_manager->setMode(NetworkManager::NW_NONE);
|
||||
// Disable accepting of clients
|
||||
if(network_manager->getMode()==NetworkManager::NW_SERVER)
|
||||
network_manager->setState(NetworkManager::NS_NONE);
|
||||
network_manager->setState(NetworkManager::NS_MAIN_MENU);
|
||||
// Leave menu.
|
||||
menu_manager->popMenu();
|
||||
|
||||
|
@ -331,7 +331,7 @@ void RaceGUI::drawPlayerIcons (const KartIconDisplayInfo* info)
|
||||
Material *last_players_gst = 0;
|
||||
|
||||
int bFirst = 1;
|
||||
const int kart_amount = race_manager->getNumKarts();
|
||||
const unsigned int kart_amount = race_manager->getNumKarts();
|
||||
for(unsigned int i = 0; i < kart_amount ; i++)
|
||||
{
|
||||
Kart* kart = RaceManager::getKart(i);
|
||||
|
@ -29,21 +29,15 @@
|
||||
#include "unlock_manager.hpp"
|
||||
#include "translation.hpp"
|
||||
#include "modes/world.hpp"
|
||||
|
||||
enum WidgetTokens
|
||||
{
|
||||
WTOK_CONTINUE,
|
||||
WTOK_RESTART_RACE,
|
||||
WTOK_SETUP_NEW_RACE,
|
||||
WTOK_RESULTS,
|
||||
WTOK_FIRST_RESULT,
|
||||
WTOK_FIRST_IMAGE=1000,
|
||||
WTOK_HIGHSCORES=2000,
|
||||
WTOK_FIRST_HIGHSCORE=2001,
|
||||
};
|
||||
#include "network/network_manager.hpp"
|
||||
|
||||
RaceResultsGUI::RaceResultsGUI()
|
||||
{
|
||||
m_first_time = true;
|
||||
m_selected_widget = WTOK_NONE;
|
||||
|
||||
// Switch to barrier mode: server waits for ack from each client
|
||||
network_manager->beginRaceResultBarrier();
|
||||
Widget *bottom_of_list;
|
||||
|
||||
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
|
||||
@ -51,36 +45,45 @@ RaceResultsGUI::RaceResultsGUI()
|
||||
else
|
||||
bottom_of_list = displayRaceResults();
|
||||
|
||||
// If a new feature was unlocked, only offer 'continue' otherwise add the
|
||||
// full menu choices. The new feature menu returns to this menu, and will
|
||||
// then display the whole menu.
|
||||
if(unlock_manager->getUnlockedFeatures().size()>0)
|
||||
// If it's a server, the user can only select 'ok', since the
|
||||
// server does all the game mode selection etc.
|
||||
if(network_manager->getMode()==NetworkManager::NW_CLIENT)
|
||||
{
|
||||
Widget *w=widget_manager->addTextButtonWgt( WTOK_CONTINUE, 60, 7, _("Continue") );
|
||||
Widget *w=widget_manager->addTextButtonWgt( WTOK_CONTINUE, 60, 7, _("OK") );
|
||||
w->setPosition(WGT_DIR_CENTER, 0, NULL, WGT_DIR_UNDER_WIDGET, 0.05f, bottom_of_list);
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
Widget *w;
|
||||
if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX)
|
||||
// If a new feature was unlocked, only offer 'continue' otherwise add the
|
||||
// full menu choices. The new feature menu returns to this menu, and will
|
||||
// then display the whole menu.
|
||||
if(unlock_manager->getUnlockedFeatures().size()>0)
|
||||
{
|
||||
w=widget_manager->addTextButtonWgt( WTOK_CONTINUE, 60, 7, _("Continue Grand Prix"));
|
||||
}
|
||||
else
|
||||
Widget *w=widget_manager->addTextButtonWgt( WTOK_CONTINUE, 60, 7, _("Continue") );
|
||||
w->setPosition(WGT_DIR_CENTER, 0, NULL, WGT_DIR_UNDER_WIDGET, 0.05f, bottom_of_list);
|
||||
} else
|
||||
{
|
||||
w=widget_manager->addTextButtonWgt( WTOK_CONTINUE, 60, 7, _("Back to the main menu"));
|
||||
}
|
||||
w->setPosition(WGT_DIR_CENTER, 0.0, NULL, WGT_DIR_UNDER_WIDGET, 0.1f, bottom_of_list);
|
||||
Widget *w_prev = w;
|
||||
w=widget_manager->addTextButtonWgt( WTOK_RESTART_RACE, 60, 7, _("Race in this track again"));
|
||||
w->setPosition(WGT_DIR_CENTER, 0.0, NULL, WGT_DIR_UNDER_WIDGET, 0, w_prev);
|
||||
w_prev = w;
|
||||
if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_SINGLE)
|
||||
{
|
||||
w=widget_manager->addTextButtonWgt( WTOK_SETUP_NEW_RACE, 60, 7, _("Setup New Race"));
|
||||
w->setPosition(WGT_DIR_CENTER, 0, NULL, WGT_DIR_UNDER_WIDGET, 0, w_prev);
|
||||
}
|
||||
} // if !unlock_manager has something unlocked*/
|
||||
|
||||
Widget *w;
|
||||
if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX)
|
||||
{
|
||||
w=widget_manager->addTextButtonWgt( WTOK_CONTINUE, 60, 7, _("Continue Grand Prix"));
|
||||
}
|
||||
else
|
||||
{
|
||||
w=widget_manager->addTextButtonWgt( WTOK_CONTINUE, 60, 7, _("Back to the main menu"));
|
||||
}
|
||||
w->setPosition(WGT_DIR_CENTER, 0.0, NULL, WGT_DIR_UNDER_WIDGET, 0.1f, bottom_of_list);
|
||||
Widget *w_prev = w;
|
||||
w=widget_manager->addTextButtonWgt( WTOK_RESTART_RACE, 60, 7, _("Race in this track again"));
|
||||
w->setPosition(WGT_DIR_CENTER, 0.0, NULL, WGT_DIR_UNDER_WIDGET, 0, w_prev);
|
||||
w_prev = w;
|
||||
if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_SINGLE)
|
||||
{
|
||||
w=widget_manager->addTextButtonWgt( WTOK_SETUP_NEW_RACE, 60, 7, _("Setup New Race"));
|
||||
w->setPosition(WGT_DIR_CENTER, 0, NULL, WGT_DIR_UNDER_WIDGET, 0, w_prev);
|
||||
}
|
||||
} // if !unlock_manager has something unlocked*/
|
||||
} // if not server
|
||||
widget_manager->layout(WGT_AREA_ALL);
|
||||
|
||||
} // RaceResultsGUI
|
||||
@ -252,6 +255,10 @@ RaceResultsGUI::~RaceResultsGUI()
|
||||
} // ~RaceResultsGUI
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** If an item is selected, store the selection to be handled in the next
|
||||
* update call. This is necessary for network support so that the right
|
||||
* action is executed once clients and server are all synchronised.
|
||||
*/
|
||||
void RaceResultsGUI::select()
|
||||
{
|
||||
// Push the unlocked-feature menu in for now
|
||||
@ -262,7 +269,71 @@ void RaceResultsGUI::select()
|
||||
menu_manager->pushMenu(MENUID_UNLOCKED_FEATURE);
|
||||
return;
|
||||
}
|
||||
switch( widget_manager->getSelectedWgt() )
|
||||
// The selected token is saved here, which triggers a change of the text
|
||||
// in update().
|
||||
m_selected_widget = (WidgetTokens)widget_manager->getSelectedWgt();
|
||||
|
||||
// Clients send the ack to the server
|
||||
if(network_manager->getMode()==NetworkManager::NW_CLIENT)
|
||||
network_manager->sendRaceResultAck();
|
||||
|
||||
} // select
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void RaceResultsGUI::handle(GameAction ga, int value)
|
||||
{
|
||||
// Attempts to close the menu are silently discarded
|
||||
// since they do not make sense at this point.
|
||||
if (ga == GA_LEAVE)
|
||||
return;
|
||||
else
|
||||
BaseGUI::handle(ga, value);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
/** Sets the selected token. This is used on the clients to allow the
|
||||
* NetworkManager to set the widget selected on the server. The clients will
|
||||
* then be able to select the correct next menu.
|
||||
* \param token Token to set as being selected.
|
||||
*/
|
||||
void RaceResultsGUI::setSelectedWidget(int token)
|
||||
{
|
||||
m_selected_widget = (WidgetTokens)token;
|
||||
} // setSelectedToken
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
/** This is used on the client and server to display a message while waiting
|
||||
* in a barrier for clients and server to ack the display.
|
||||
*/
|
||||
void RaceResultsGUI::update(float dt)
|
||||
{
|
||||
BaseGUI::update(dt);
|
||||
// If an item is selected (for the first time), change the text
|
||||
// so that the user has feedback about his selection.
|
||||
if(m_selected_widget!=WTOK_NONE && m_first_time)
|
||||
{
|
||||
m_first_time = false;
|
||||
// User feedback on first selection: display message, and on the
|
||||
// server remove unnecessary widgets.
|
||||
widget_manager->setWgtText(WTOK_CONTINUE, _("Synchronising."));
|
||||
if(network_manager->getMode()==NetworkManager::NW_SERVER)
|
||||
{
|
||||
widget_manager->hideWgt(WTOK_RESTART_RACE);
|
||||
widget_manager->hideWgt(WTOK_SETUP_NEW_RACE);
|
||||
}
|
||||
} // m_selected_token defined and not first time
|
||||
|
||||
// Wait till the barrier is finished. On the server this is the case when
|
||||
// the state ie MAIN_MENU, on the client when it is wait_for_available_characters.
|
||||
if(network_manager->getState()!=NetworkManager::NS_MAIN_MENU &&
|
||||
network_manager->getState()!=NetworkManager::NS_WAIT_FOR_AVAILABLE_CHARACTERS )
|
||||
return;
|
||||
|
||||
// Send selected menu to all clients
|
||||
if(m_selected_widget!=WTOK_NONE &&
|
||||
network_manager->getMode()==NetworkManager::NW_SERVER)
|
||||
network_manager->sendRaceResultAck(m_selected_widget);
|
||||
|
||||
switch(m_selected_widget)
|
||||
{
|
||||
case WTOK_CONTINUE:
|
||||
// Gets called when:
|
||||
@ -286,17 +357,6 @@ void RaceResultsGUI::select()
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} // select
|
||||
//-----------------------------------------------------------------------------
|
||||
void
|
||||
RaceResultsGUI::handle(GameAction ga, int value)
|
||||
{
|
||||
// Attempts to close the menu are silently discarded
|
||||
// since they do not make sense at this point.
|
||||
if (ga == GA_LEAVE)
|
||||
return;
|
||||
else
|
||||
BaseGUI::handle(ga, value);
|
||||
}
|
||||
|
||||
} // update
|
||||
/* EOF */
|
||||
|
@ -28,21 +28,39 @@
|
||||
/** GUI that shows the RaceResults, times and such */
|
||||
class RaceResultsGUI : public BaseGUI
|
||||
{
|
||||
private:
|
||||
/** Widgets. WTOK_NONE is used for detecting in update when
|
||||
* a selection was made (see m_selected_token). */
|
||||
enum WidgetTokens
|
||||
{
|
||||
WTOK_NONE,
|
||||
WTOK_CONTINUE,
|
||||
WTOK_RESTART_RACE,
|
||||
WTOK_SETUP_NEW_RACE,
|
||||
WTOK_RESULTS,
|
||||
WTOK_FIRST_RESULT,
|
||||
WTOK_FIRST_IMAGE = 1000,
|
||||
WTOK_HIGHSCORES = 2000,
|
||||
WTOK_FIRST_HIGHSCORE = 2001,
|
||||
};
|
||||
private:
|
||||
std::vector<int> m_order;
|
||||
|
||||
bool m_first_time;
|
||||
/** The widget selected by the user, so that the right action can be done
|
||||
* once clients and server are synchronised. */
|
||||
WidgetTokens m_selected_widget;
|
||||
Widget *displayLeaderResults();
|
||||
Widget *displayRaceResults();
|
||||
Widget *displayKartList(unsigned int from, unsigned int to,
|
||||
Widget *w_prev, int *order, bool display_time,
|
||||
float horizontal);
|
||||
public:
|
||||
RaceResultsGUI();
|
||||
~RaceResultsGUI();
|
||||
|
||||
void handle(GameAction, int);
|
||||
|
||||
void select();
|
||||
RaceResultsGUI();
|
||||
~RaceResultsGUI();
|
||||
void handle(GameAction, int);
|
||||
void select();
|
||||
virtual void update(float dt);
|
||||
void setSelectedWidget(int);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1090,6 +1090,10 @@
|
||||
RelativePath="..\..\network\race_info_message.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\network\race_result_message.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\network\race_state.cpp"
|
||||
>
|
||||
@ -1660,6 +1664,14 @@
|
||||
RelativePath="..\..\network\race_info_message.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\network\race_result_ack_message.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\network\race_result_message.hpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\network\race_start_message.hpp"
|
||||
>
|
||||
|
@ -385,6 +385,7 @@ void Kart::reset()
|
||||
m_rescue = false;
|
||||
TerrainInfo::update(getXYZ());
|
||||
} // reset
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void Kart::raceFinished(float time)
|
||||
{
|
||||
|
@ -115,14 +115,22 @@ void MainLoop::run()
|
||||
|
||||
// Server: Send the current position and previous controls to all clients
|
||||
// Client: send current controls to server
|
||||
network_manager->sendUpdates();
|
||||
// But don't do this if the race is in finish phase (otherwise
|
||||
// messages can be mixed up in the race manager)
|
||||
if(!race_manager->getWorld()->getClock().isFinishPhase())
|
||||
network_manager->sendUpdates();
|
||||
music_on = false;
|
||||
if(user_config->m_profile) dt=1.0f/60.0f;
|
||||
// In the first call dt might be large (includes loading time),
|
||||
// which can cause the camera to significantly tilt
|
||||
scene->draw(RaceManager::getWorld()->getPhase()==SETUP_PHASE ? 0.0f : dt);
|
||||
|
||||
network_manager->receiveUpdates();
|
||||
// Again, only receive updates if the race isn't over - once the
|
||||
// race results are displayed (i.e. game is in finish phase)
|
||||
// messages must be handled by the normal update of the network
|
||||
// manager
|
||||
if(!race_manager->getWorld()->getClock().isFinishPhase())
|
||||
network_manager->receiveUpdates();
|
||||
|
||||
if ( RaceManager::getWorld()->getPhase() != LIMBO_PHASE)
|
||||
{
|
||||
|
@ -72,7 +72,7 @@ void Clock::raceOver(const bool delay)
|
||||
m_phase = FINISH_PHASE;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void Clock::updateClock(const float dt)
|
||||
void Clock::update(const float dt)
|
||||
{
|
||||
switch(m_phase)
|
||||
{
|
||||
|
@ -112,9 +112,15 @@ public:
|
||||
void reset();
|
||||
|
||||
// Note: GO_PHASE is both: start phase and race phase
|
||||
bool isStartPhase() const { return m_phase<GO_PHASE; }
|
||||
bool isRacePhase() const { return m_phase>=GO_PHASE && m_phase<LIMBO_PHASE; }
|
||||
const Phase getPhase() const { return m_phase; }
|
||||
bool isStartPhase() const { return m_phase<GO_PHASE; }
|
||||
bool isRacePhase() const { return m_phase>=GO_PHASE &&
|
||||
m_phase<LIMBO_PHASE; }
|
||||
/** While the race menu is being displayed, m_phase is limbo, and
|
||||
* m_previous_phase is finish. So we have to test this case, too. */
|
||||
bool isFinishPhase() const { return m_phase==FINISH_PHASE ||
|
||||
(m_phase==LIMBO_PHASE &&
|
||||
m_previous_phase==FINISH_PHASE);}
|
||||
const Phase getPhase() const { return m_phase; }
|
||||
|
||||
/**
|
||||
* Call to specify what kind of clock you want. The second argument
|
||||
@ -126,7 +132,7 @@ public:
|
||||
/**
|
||||
* Call each frame, with the elapsed time as argument.
|
||||
*/
|
||||
void updateClock(const float dt);
|
||||
void update(const float dt);
|
||||
|
||||
float getTime() const { return m_time; }
|
||||
void setTime(const float time);
|
||||
|
@ -35,10 +35,10 @@ FollowTheLeaderRace::~FollowTheLeaderRace()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#ifdef __APPLE__
|
||||
#pragma mark -
|
||||
#pragma mark clock events
|
||||
|
||||
#endif
|
||||
//-----------------------------------------------------------------------------
|
||||
void FollowTheLeaderRace::countdownReachedZero()
|
||||
{
|
||||
@ -60,14 +60,14 @@ void FollowTheLeaderRace::onTerminate()
|
||||
World::terminateRace();
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
#pragma mark -
|
||||
#pragma mark overridden from World
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void FollowTheLeaderRace::update(float delta)
|
||||
{
|
||||
m_clock.updateClock(delta);
|
||||
|
||||
{
|
||||
LinearWorld::update(delta);
|
||||
if(!m_clock.isRacePhase()) return;
|
||||
|
||||
@ -111,7 +111,7 @@ void FollowTheLeaderRace::update(float delta)
|
||||
if(!m_kart[i]->isEliminated())
|
||||
race_manager->RaceFinished(m_kart[i], m_clock.getTime());
|
||||
|
||||
m_clock.raceOver();
|
||||
raceOver();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,9 @@
|
||||
|
||||
#include "modes/linear_world.hpp"
|
||||
|
||||
#if defined(WIN32) && !defined(__CYGWIN__)
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
#if defined(WIN32) && !defined(__CYGWIN__)
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#include "gui/race_gui.hpp"
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#include "gui/menu_manager.hpp"
|
||||
#include "translation.hpp"
|
||||
#include "audio/sound_manager.hpp"
|
||||
#include "network/network_manager.hpp"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
LinearWorld::LinearWorld() : World()
|
||||
@ -196,7 +197,11 @@ void LinearWorld::doLapCounting ( KartInfo& kart_info, Kart* kart )
|
||||
if(kart_info.m_race_lap >= race_manager->getNumLaps() &&
|
||||
race_manager->getMinorMode() != RaceManager::MINOR_MODE_FOLLOW_LEADER)
|
||||
{
|
||||
kart->raceFinished(RaceManager::getWorld()->getTime());
|
||||
// A client wait does not detect race finished by itself, it will
|
||||
// receive a message from the server. So a client does not do
|
||||
// anything here.
|
||||
if(network_manager->getMode()!=NetworkManager::NW_CLIENT)
|
||||
kart->raceFinished(RaceManager::getWorld()->getTime());
|
||||
}
|
||||
// Only do timings if original time was set properly. Driving backwards
|
||||
// over the start line will cause the lap start time to be set to -1.
|
||||
|
@ -32,8 +32,10 @@ StandardRace::~StandardRace()
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
#pragma mark -
|
||||
#pragma mark clock events
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void StandardRace::countdownReachedZero() { }
|
||||
@ -54,8 +56,10 @@ void StandardRace::onTerminate()
|
||||
World::terminateRace();
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
#pragma mark -
|
||||
#pragma mark overridden from World
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void StandardRace::restartRace()
|
||||
@ -64,16 +68,14 @@ void StandardRace::restartRace()
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void StandardRace::update(float delta)
|
||||
{
|
||||
m_clock.updateClock(delta);
|
||||
|
||||
{
|
||||
LinearWorld::update(delta);
|
||||
if(!m_clock.isRacePhase()) return;
|
||||
|
||||
// All karts are finished
|
||||
if(race_manager->getFinishedKarts() >= race_manager->getNumKarts() )
|
||||
{
|
||||
m_clock.raceOver();
|
||||
raceOver();
|
||||
if(user_config->m_profile<0) printProfileResultAndExit();
|
||||
unlock_manager->raceFinished();
|
||||
} // if all karts are finished
|
||||
@ -84,7 +86,7 @@ void StandardRace::update(float delta)
|
||||
{
|
||||
// Set delay mode to have time for camera animation, and
|
||||
// to give the AI some time to get non-estimated timings
|
||||
m_clock.raceOver(true /* delay */);
|
||||
raceOver(true /* delay */);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -257,6 +257,7 @@ void World::resetAllKarts()
|
||||
//-----------------------------------------------------------------------------
|
||||
void World::update(float dt)
|
||||
{
|
||||
m_clock.update(dt);
|
||||
// Clear race state so that new information can be stored
|
||||
race_state->clear();
|
||||
if(user_config->m_replay_history) dt=history->GetNextDelta();
|
||||
@ -277,6 +278,14 @@ void World::update(float dt)
|
||||
callback_manager->update(dt);
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
void World::raceOver(bool delay)
|
||||
{
|
||||
m_clock.raceOver(delay);
|
||||
if(network_manager->getMode()==NetworkManager::NW_SERVER)
|
||||
network_manager->sendRaceResults();
|
||||
} // raceOver
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
HighscoreEntry* World::getHighscores() const
|
||||
{
|
||||
if(!m_use_highscores) return NULL;
|
||||
@ -307,7 +316,7 @@ void World::updateHighscores()
|
||||
// again by a faster kart in the same race), which might be confusing
|
||||
// if we ever decide to display a message (e.g. during a race)
|
||||
unsigned int *index = new unsigned int[m_kart.size()];
|
||||
const int kart_amount = m_kart.size();
|
||||
const unsigned int kart_amount = m_kart.size();
|
||||
for (unsigned int i=0; i<kart_amount; i++ )
|
||||
{
|
||||
index[m_kart[i]->getPosition()-1] = i;
|
||||
|
@ -148,10 +148,10 @@ public:
|
||||
HighscoreEntry* getHighscores() const;
|
||||
float getTime() const { return m_clock.getTime(); }
|
||||
Phase getPhase() const { return m_clock.getPhase(); }
|
||||
const Clock& getClock() const { return m_clock; }
|
||||
|
||||
/** Called when race is over and should be terminated (mostly called by the clock).
|
||||
*/
|
||||
const Clock &getClock() { return m_clock; }
|
||||
/** Gets called when the race is about to finish (but with the option of adding
|
||||
* some delay to watch the end of the race. */
|
||||
void raceOver(bool delay=false);
|
||||
virtual void terminateRace();
|
||||
|
||||
/** Called to determine the default collectibles to give each player for this
|
||||
|
@ -63,6 +63,8 @@ void Message::receive(ENetPacket* pkt, MessageType m)
|
||||
m_data_size = pkt->dataLength;
|
||||
m_data = (char*)pkt->data;
|
||||
m_type = (MessageType)m_data[0];
|
||||
if(m_type!=m)
|
||||
printf("type %d %d\n",m_type,m);
|
||||
assert(m_type==m);
|
||||
m_pos = 1;
|
||||
m_needs_destroy = true;
|
||||
|
@ -43,10 +43,12 @@
|
||||
class Message
|
||||
{
|
||||
public:
|
||||
/** Contains all tags used in identifying a message. */
|
||||
enum MessageType {MT_CONNECT=1, MT_CHARACTER_INFO, MT_CHARACTER_CONFIRM,
|
||||
MT_RACE_INFO, MT_RACE_START, MT_WORLD_LOADED,
|
||||
MT_KART_INFO, MT_KART_CONTROL,
|
||||
MT_RACE_STATE};
|
||||
MT_KART_INFO, MT_KART_CONTROL, MT_RACE_STATE,
|
||||
MT_RACE_RESULT, MT_RACE_RESULT_ACK
|
||||
};
|
||||
private:
|
||||
ENetPacket *m_pkt;
|
||||
char *m_data;
|
||||
|
@ -18,6 +18,16 @@
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#include "network_manager.hpp"
|
||||
|
||||
#include "stk_config.hpp"
|
||||
#include "user_config.hpp"
|
||||
#include "race_manager.hpp"
|
||||
#include "kart_properties_manager.hpp"
|
||||
#include "translation.hpp"
|
||||
#include "gui/menu_manager.hpp"
|
||||
#include "gui/char_sel.hpp"
|
||||
#include "gui/race_results_gui.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "network/connect_message.hpp"
|
||||
#include "network/character_info_message.hpp"
|
||||
#include "network/character_selected_message.hpp"
|
||||
@ -27,13 +37,8 @@
|
||||
#include "network/race_state.hpp"
|
||||
#include "network/kart_control_message.hpp"
|
||||
#include "network/character_confirm_message.hpp"
|
||||
#include "stk_config.hpp"
|
||||
#include "user_config.hpp"
|
||||
#include "race_manager.hpp"
|
||||
#include "kart_properties_manager.hpp"
|
||||
#include "translation.hpp"
|
||||
#include "gui/menu_manager.hpp"
|
||||
#include "gui/char_sel.hpp"
|
||||
#include "network/race_result_message.hpp"
|
||||
#include "network/race_result_ack_message.hpp"
|
||||
|
||||
NetworkManager* network_manager = 0;
|
||||
|
||||
@ -272,6 +277,22 @@ void NetworkManager::handleMessageAtServer(ENetEvent *event)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NS_RACE_RESULT_BARRIER:
|
||||
{
|
||||
// Other message, esp. kart control, are silently ignored.
|
||||
// FIXME: we might want to make sure that no such message actually arrives
|
||||
if(Message::peekType(event->packet)!=Message::MT_RACE_RESULT_ACK)
|
||||
{
|
||||
enet_packet_destroy(event->packet);
|
||||
return;
|
||||
}
|
||||
m_barrier_count++;
|
||||
if(m_barrier_count==(int)m_num_clients)
|
||||
{
|
||||
m_state = NS_MAIN_MENU;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: assert(0); // should not happen
|
||||
} // switch m_state
|
||||
} // handleMessageAtServer
|
||||
@ -347,6 +368,16 @@ void NetworkManager::handleMessageAtClient(ENetEvent *event)
|
||||
assert(false); // should never be here while racing
|
||||
break;
|
||||
}
|
||||
case NS_RACE_RESULT_BARRIER:
|
||||
{
|
||||
RaceResultAckMessage message(event->packet);
|
||||
RaceResultsGUI *menu = dynamic_cast<RaceResultsGUI*>(menu_manager->getCurrentMenu());
|
||||
if(menu)
|
||||
menu->setSelectedWidget(message.getSelectedMenu());
|
||||
|
||||
m_state = NS_WAIT_FOR_AVAILABLE_CHARACTERS;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
printf("received unknown message: type %d\n",
|
||||
@ -592,6 +623,14 @@ void NetworkManager::receiveUpdates()
|
||||
}
|
||||
else
|
||||
{
|
||||
// Test if it is a game over message:
|
||||
if(Message::peekType(event.packet)==Message::MT_RACE_RESULT)
|
||||
{
|
||||
RaceResultMessage m(event.packet);
|
||||
m_state = NS_WAIT_FOR_RACE_RESULT;
|
||||
race_manager->getWorld()->raceOver();
|
||||
return;
|
||||
}
|
||||
race_state->receive(event.packet);
|
||||
}
|
||||
} // for i<num_messages
|
||||
@ -628,3 +667,50 @@ void NetworkManager::waitForClientData()
|
||||
|
||||
} // waitForClientData
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Sends the race result (kart positions and finishing times) from the server
|
||||
* to all clients. Clients keep on racing till they receive this message, and
|
||||
* will then copy the server's race results to the race manager.
|
||||
*/
|
||||
void NetworkManager::sendRaceResults()
|
||||
{
|
||||
RaceResultMessage m;
|
||||
broadcastToClients(m);
|
||||
} // sendRaceResults
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Changes the mode to wait in a barrier for all clients and the server to
|
||||
* acknowledge the result screen. The server waits for a message from all
|
||||
* clients, upon which it sends a message to all clients. The clients wait
|
||||
* for this message before continuing.
|
||||
*/
|
||||
void NetworkManager::beginRaceResultBarrier()
|
||||
{
|
||||
m_state = NS_RACE_RESULT_BARRIER;
|
||||
|
||||
if(m_mode==NW_SERVER)
|
||||
{
|
||||
m_barrier_count = 0;
|
||||
// In case of no networking set the next state
|
||||
if(m_num_clients == 0) m_state = NS_MAIN_MENU;
|
||||
}
|
||||
} // beginRaceResultBarrier
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Sends a race 'result acknowledge' message from the clients to the server,
|
||||
* or from the server to the clients (in this case it contains the selected
|
||||
* menu choice from the server).
|
||||
*/
|
||||
void NetworkManager::sendRaceResultAck(char menu_selection)
|
||||
{
|
||||
// Menu selection is actually not important for a client
|
||||
RaceResultAckMessage m(menu_selection);
|
||||
if (m_mode==NW_CLIENT)
|
||||
{
|
||||
sendToServer(m);
|
||||
}
|
||||
else
|
||||
{
|
||||
broadcastToClients(m);
|
||||
m_state = NS_MAIN_MENU;
|
||||
}
|
||||
} // sendRaceResultAck
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
enum NetworkMode {NW_SERVER, NW_CLIENT, NW_NONE};
|
||||
|
||||
// States for the finite state machine. First for server:
|
||||
enum NetworkState {NS_NONE,
|
||||
enum NetworkState {NS_MAIN_MENU, // before char sel gui
|
||||
NS_ACCEPT_CONNECTIONS, // server: accept connections
|
||||
NS_WAIT_FOR_AVAILABLE_CHARACTERS, // client: wait for list
|
||||
NS_ALL_REMOTE_CHARACTERS_DONE, // server: all client data received
|
||||
@ -48,7 +48,10 @@ public:
|
||||
NS_READY_SET_GO_BARRIER, // c&s: barrier before r.s.g.
|
||||
NS_CHARACTER_SELECT, // c&s: character select in progress
|
||||
NS_LOADING_WORLD, // client: loading world
|
||||
NS_RACING};
|
||||
NS_RACING,
|
||||
NS_WAIT_FOR_RACE_RESULT, // clients: waiting for race results
|
||||
NS_RACE_RESULT_BARRIER // Wait till all ack results
|
||||
};
|
||||
private:
|
||||
|
||||
NetworkMode m_mode;
|
||||
@ -108,6 +111,9 @@ public:
|
||||
void sendUpdates();
|
||||
void receiveUpdates();
|
||||
void waitForClientData();
|
||||
void sendRaceResults();
|
||||
void beginRaceResultBarrier();
|
||||
void sendRaceResultAck(char menu_selection=-1);
|
||||
};
|
||||
|
||||
extern NetworkManager *network_manager;
|
||||
|
59
src/network/race_result_ack_message.hpp
Executable file
59
src/network/race_result_ack_message.hpp
Executable file
@ -0,0 +1,59 @@
|
||||
// $Id$
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2008 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 HEADER_RACE_RESULT_ACK_MESSAGE_HPP
|
||||
#define HEADER_RACE_RESULT_ACK_MESSAGE_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "network/message.hpp"
|
||||
|
||||
|
||||
/** This message is sent from the clients to the server when the race result
|
||||
* screen was acknowledged, and then from the server to all clients to
|
||||
* finish synchronisation.
|
||||
*/
|
||||
class RaceResultAckMessage : public Message
|
||||
{
|
||||
private:
|
||||
char m_menu_selected;
|
||||
public:
|
||||
/** Constructor, creates an empty message
|
||||
*/
|
||||
RaceResultAckMessage(char menu_choice) : Message(Message::MT_RACE_RESULT_ACK)
|
||||
{
|
||||
allocate(getCharLength());
|
||||
addChar(menu_choice);
|
||||
} // RaceResultAckMessage
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
/** Receives the ack message.
|
||||
* \param pkt Received enet packet.
|
||||
*/
|
||||
RaceResultAckMessage(ENetPacket* pkt):Message(pkt, MT_RACE_RESULT_ACK)
|
||||
{
|
||||
m_menu_selected = getChar();
|
||||
} // RaceResultAckMessage(EnetPacket)
|
||||
// ------------------------------------------------------------------------
|
||||
/** Returns the menu selected on the server after this message is received
|
||||
* on a client. */
|
||||
char getSelectedMenu() const {return m_menu_selected; }
|
||||
|
||||
}; // RaceResultAckMessageMessage
|
||||
#endif
|
62
src/network/race_result_message.cpp
Executable file
62
src/network/race_result_message.cpp
Executable file
@ -0,0 +1,62 @@
|
||||
// $Id$
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2008 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.
|
||||
|
||||
#include "network/race_result_message.hpp"
|
||||
|
||||
#include "race_manager.hpp"
|
||||
#include "modes/world.hpp"
|
||||
#include "kart.hpp"
|
||||
|
||||
/** Creates a message containing the finishing time and rank of each kart.
|
||||
* This message is serialised so that it can be sent.
|
||||
*/
|
||||
RaceResultMessage::RaceResultMessage() : Message(MT_RACE_RESULT)
|
||||
{
|
||||
const unsigned int num_karts = race_manager->getNumKarts();
|
||||
allocate(num_karts * (getFloatLength()+getCharLength()));
|
||||
World *world = race_manager->getWorld();
|
||||
for(unsigned int i=0; i<num_karts; i++)
|
||||
{
|
||||
const Kart *kart = world->getKart(i);
|
||||
addFloat(kart->getFinishTime());
|
||||
addChar(kart->getPosition());
|
||||
} // for i in karts
|
||||
} // RaceResultMessage
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** De-serialises a race result message and sets the appropriate results in
|
||||
* the kart and the race manager.
|
||||
* \param pkt The enet message paket.
|
||||
*/
|
||||
RaceResultMessage::RaceResultMessage(ENetPacket* pkt)
|
||||
: Message(pkt, MT_RACE_RESULT)
|
||||
{
|
||||
const unsigned int num_karts = race_manager->getNumKarts();
|
||||
World *world = race_manager->getWorld();
|
||||
for(unsigned int i=0; i<num_karts; i++)
|
||||
{
|
||||
Kart *kart = world->getKart(i);
|
||||
float time = getFloat();
|
||||
char position = getChar();
|
||||
kart->setPosition(position);
|
||||
kart->raceFinished(time);
|
||||
race_manager->RaceFinished(kart, time);
|
||||
}
|
||||
} // RaceResultMessage
|
||||
|
46
src/network/race_result_message.hpp
Executable file
46
src/network/race_result_message.hpp
Executable file
@ -0,0 +1,46 @@
|
||||
// $Id$
|
||||
//
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2008 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 HEADER_RACE_RESULT_MESSAGE_HPP
|
||||
#define HEADER_RACE_RESULT_MESSAGE_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "network/message.hpp"
|
||||
|
||||
|
||||
/** This message is from the server to all clients to inform them about the
|
||||
* result of a race. The clients wait for this message before they finish
|
||||
* a race.
|
||||
*/
|
||||
class RaceResultMessage : public Message
|
||||
{
|
||||
struct RaceResult {
|
||||
float m_time;
|
||||
int m_score;
|
||||
}; // RaceResult
|
||||
private:
|
||||
std::vector<RaceResult> m_all_results;
|
||||
public:
|
||||
RaceResultMessage();
|
||||
RaceResultMessage(ENetPacket* pkt);
|
||||
void addRaceResult(int kart_id, float time, int points);
|
||||
void getRaceResult(int kart_id, float &time, int &points);
|
||||
}; // RaceResultMessage
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user