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:
hikerstk 2008-09-29 02:29:33 +00:00
parent 44e9335baa
commit 1809dd1da1
24 changed files with 495 additions and 107 deletions

View File

@ -43,6 +43,8 @@ supertuxkart_SOURCES = main.cpp \
network/kart_control_message.hpp network/kart_control_message.cpp \ network/kart_control_message.hpp network/kart_control_message.cpp \
network/flyable_info.hpp network/herring_info.hpp \ network/flyable_info.hpp network/herring_info.hpp \
network/race_state.hpp network/race_state.cpp \ 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.hpp \
audio/music_information.cpp audio/music_information.hpp \ audio/music_information.cpp audio/music_information.hpp \
audio/music_ogg.cpp audio/music_ogg.hpp \ audio/music_ogg.cpp audio/music_ogg.hpp \

View File

@ -60,7 +60,8 @@ CharSel::CharSel(int whichPlayer)
// in the server (even when NW_NONE). This if condition // in the server (even when NW_NONE). This if condition
// needs to be checked (otherwise ghost karts and // needs to be checked (otherwise ghost karts and
// a ghost camera will appear) // 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->getMode()==NetworkManager::NW_NONE)
network_manager->switchToCharacterSelection(); network_manager->switchToCharacterSelection();
@ -336,8 +337,7 @@ void CharSel::update(float dt)
return; return;
} }
static bool first=true; if(m_first_frame)
if(first)
{ {
// Switch group will update the list of selected karts, i.e. use the // Switch group will update the list of selected karts, i.e. use the
// information about kart availability just received from the server // 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_NAME0+i);
widget_manager->showWgt(WTOK_RACER0+i); widget_manager->showWgt(WTOK_RACER0+i);
} }
first=false; m_first_frame = false;
updateScrollPosition(); updateScrollPosition();
return; return;
} }

View File

@ -36,6 +36,8 @@ private:
int m_player_index; int m_player_index;
int m_offset; // index of first racer displayed int m_offset; // index of first racer displayed
unsigned int m_num_entries; // number of entries to display 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; std::vector<int> m_index_avail_karts;
static const unsigned int m_max_entries=7; static const unsigned int m_max_entries=7;
void updateScrollPosition(); void updateScrollPosition();

View File

@ -140,7 +140,7 @@ void NetworkGUI::select()
network_manager->setMode(NetworkManager::NW_NONE); network_manager->setMode(NetworkManager::NW_NONE);
// Disable accepting of clients // Disable accepting of clients
if(network_manager->getMode()==NetworkManager::NW_SERVER) if(network_manager->getMode()==NetworkManager::NW_SERVER)
network_manager->setState(NetworkManager::NS_NONE); network_manager->setState(NetworkManager::NS_MAIN_MENU);
// Leave menu. // Leave menu.
menu_manager->popMenu(); menu_manager->popMenu();

View File

@ -331,7 +331,7 @@ void RaceGUI::drawPlayerIcons (const KartIconDisplayInfo* info)
Material *last_players_gst = 0; Material *last_players_gst = 0;
int bFirst = 1; 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++) for(unsigned int i = 0; i < kart_amount ; i++)
{ {
Kart* kart = RaceManager::getKart(i); Kart* kart = RaceManager::getKart(i);

View File

@ -29,21 +29,15 @@
#include "unlock_manager.hpp" #include "unlock_manager.hpp"
#include "translation.hpp" #include "translation.hpp"
#include "modes/world.hpp" #include "modes/world.hpp"
#include "network/network_manager.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,
};
RaceResultsGUI::RaceResultsGUI() 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; Widget *bottom_of_list;
if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER) if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
@ -51,6 +45,15 @@ RaceResultsGUI::RaceResultsGUI()
else else
bottom_of_list = displayRaceResults(); bottom_of_list = displayRaceResults();
// 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, _("OK") );
w->setPosition(WGT_DIR_CENTER, 0, NULL, WGT_DIR_UNDER_WIDGET, 0.05f, bottom_of_list);
}
else
{
// If a new feature was unlocked, only offer 'continue' otherwise add the // 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 // full menu choices. The new feature menu returns to this menu, and will
// then display the whole menu. // then display the whole menu.
@ -80,7 +83,7 @@ RaceResultsGUI::RaceResultsGUI()
w->setPosition(WGT_DIR_CENTER, 0, NULL, WGT_DIR_UNDER_WIDGET, 0, w_prev); w->setPosition(WGT_DIR_CENTER, 0, NULL, WGT_DIR_UNDER_WIDGET, 0, w_prev);
} }
} // if !unlock_manager has something unlocked*/ } // if !unlock_manager has something unlocked*/
} // if not server
widget_manager->layout(WGT_AREA_ALL); widget_manager->layout(WGT_AREA_ALL);
} // RaceResultsGUI } // RaceResultsGUI
@ -252,6 +255,10 @@ RaceResultsGUI::~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() void RaceResultsGUI::select()
{ {
// Push the unlocked-feature menu in for now // Push the unlocked-feature menu in for now
@ -262,7 +269,71 @@ void RaceResultsGUI::select()
menu_manager->pushMenu(MENUID_UNLOCKED_FEATURE); menu_manager->pushMenu(MENUID_UNLOCKED_FEATURE);
return; 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: case WTOK_CONTINUE:
// Gets called when: // Gets called when:
@ -286,17 +357,6 @@ void RaceResultsGUI::select()
default: default:
break; 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 */ /* EOF */

View File

@ -28,9 +28,27 @@
/** GUI that shows the RaceResults, times and such */ /** GUI that shows the RaceResults, times and such */
class RaceResultsGUI : public BaseGUI 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: private:
std::vector<int> m_order; 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 *displayLeaderResults();
Widget *displayRaceResults(); Widget *displayRaceResults();
Widget *displayKartList(unsigned int from, unsigned int to, Widget *displayKartList(unsigned int from, unsigned int to,
@ -39,10 +57,10 @@ private:
public: public:
RaceResultsGUI(); RaceResultsGUI();
~RaceResultsGUI(); ~RaceResultsGUI();
void handle(GameAction, int); void handle(GameAction, int);
void select(); void select();
virtual void update(float dt);
void setSelectedWidget(int);
}; };
#endif #endif

View File

@ -1090,6 +1090,10 @@
RelativePath="..\..\network\race_info_message.cpp" RelativePath="..\..\network\race_info_message.cpp"
> >
</File> </File>
<File
RelativePath="..\..\network\race_result_message.cpp"
>
</File>
<File <File
RelativePath="..\..\network\race_state.cpp" RelativePath="..\..\network\race_state.cpp"
> >
@ -1660,6 +1664,14 @@
RelativePath="..\..\network\race_info_message.hpp" RelativePath="..\..\network\race_info_message.hpp"
> >
</File> </File>
<File
RelativePath="..\..\network\race_result_ack_message.hpp"
>
</File>
<File
RelativePath="..\..\network\race_result_message.hpp"
>
</File>
<File <File
RelativePath="..\..\network\race_start_message.hpp" RelativePath="..\..\network\race_start_message.hpp"
> >

View File

@ -385,6 +385,7 @@ void Kart::reset()
m_rescue = false; m_rescue = false;
TerrainInfo::update(getXYZ()); TerrainInfo::update(getXYZ());
} // reset } // reset
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void Kart::raceFinished(float time) void Kart::raceFinished(float time)
{ {

View File

@ -115,6 +115,9 @@ void MainLoop::run()
// Server: Send the current position and previous controls to all clients // Server: Send the current position and previous controls to all clients
// Client: send current controls to server // Client: send current controls to server
// 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(); network_manager->sendUpdates();
music_on = false; music_on = false;
if(user_config->m_profile) dt=1.0f/60.0f; if(user_config->m_profile) dt=1.0f/60.0f;
@ -122,6 +125,11 @@ void MainLoop::run()
// which can cause the camera to significantly tilt // which can cause the camera to significantly tilt
scene->draw(RaceManager::getWorld()->getPhase()==SETUP_PHASE ? 0.0f : dt); scene->draw(RaceManager::getWorld()->getPhase()==SETUP_PHASE ? 0.0f : dt);
// 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(); network_manager->receiveUpdates();
if ( RaceManager::getWorld()->getPhase() != LIMBO_PHASE) if ( RaceManager::getWorld()->getPhase() != LIMBO_PHASE)

View File

@ -72,7 +72,7 @@ void Clock::raceOver(const bool delay)
m_phase = FINISH_PHASE; m_phase = FINISH_PHASE;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void Clock::updateClock(const float dt) void Clock::update(const float dt)
{ {
switch(m_phase) switch(m_phase)
{ {

View File

@ -113,7 +113,13 @@ public:
// Note: GO_PHASE is both: start phase and race phase // Note: GO_PHASE is both: start phase and race phase
bool isStartPhase() const { return m_phase<GO_PHASE; } bool isStartPhase() const { return m_phase<GO_PHASE; }
bool isRacePhase() const { return m_phase>=GO_PHASE && m_phase<LIMBO_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; } const Phase getPhase() const { return m_phase; }
/** /**
@ -126,7 +132,7 @@ public:
/** /**
* Call each frame, with the elapsed time as argument. * 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; } float getTime() const { return m_time; }
void setTime(const float time); void setTime(const float time);

View File

@ -35,10 +35,10 @@ FollowTheLeaderRace::~FollowTheLeaderRace()
{ {
} }
#ifdef __APPLE__
#pragma mark - #pragma mark -
#pragma mark clock events #pragma mark clock events
#endif
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void FollowTheLeaderRace::countdownReachedZero() void FollowTheLeaderRace::countdownReachedZero()
{ {
@ -60,14 +60,14 @@ void FollowTheLeaderRace::onTerminate()
World::terminateRace(); World::terminateRace();
} }
#ifdef __APPLE__
#pragma mark - #pragma mark -
#pragma mark overridden from World #pragma mark overridden from World
#endif
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void FollowTheLeaderRace::update(float delta) void FollowTheLeaderRace::update(float delta)
{ {
m_clock.updateClock(delta);
LinearWorld::update(delta); LinearWorld::update(delta);
if(!m_clock.isRacePhase()) return; if(!m_clock.isRacePhase()) return;
@ -111,7 +111,7 @@ void FollowTheLeaderRace::update(float delta)
if(!m_kart[i]->isEliminated()) if(!m_kart[i]->isEliminated())
race_manager->RaceFinished(m_kart[i], m_clock.getTime()); race_manager->RaceFinished(m_kart[i], m_clock.getTime());
m_clock.raceOver(); raceOver();
return; return;
} }
} }

View File

@ -27,6 +27,7 @@
#include "gui/menu_manager.hpp" #include "gui/menu_manager.hpp"
#include "translation.hpp" #include "translation.hpp"
#include "audio/sound_manager.hpp" #include "audio/sound_manager.hpp"
#include "network/network_manager.hpp"
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
LinearWorld::LinearWorld() : World() LinearWorld::LinearWorld() : World()
@ -196,6 +197,10 @@ void LinearWorld::doLapCounting ( KartInfo& kart_info, Kart* kart )
if(kart_info.m_race_lap >= race_manager->getNumLaps() && if(kart_info.m_race_lap >= race_manager->getNumLaps() &&
race_manager->getMinorMode() != RaceManager::MINOR_MODE_FOLLOW_LEADER) race_manager->getMinorMode() != RaceManager::MINOR_MODE_FOLLOW_LEADER)
{ {
// 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()); kart->raceFinished(RaceManager::getWorld()->getTime());
} }
// Only do timings if original time was set properly. Driving backwards // Only do timings if original time was set properly. Driving backwards

View File

@ -32,8 +32,10 @@ StandardRace::~StandardRace()
{ {
} }
#ifdef __APPLE__
#pragma mark - #pragma mark -
#pragma mark clock events #pragma mark clock events
#endif
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void StandardRace::countdownReachedZero() { } void StandardRace::countdownReachedZero() { }
@ -54,8 +56,10 @@ void StandardRace::onTerminate()
World::terminateRace(); World::terminateRace();
} }
#ifdef __APPLE__
#pragma mark - #pragma mark -
#pragma mark overridden from World #pragma mark overridden from World
#endif
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void StandardRace::restartRace() void StandardRace::restartRace()
@ -65,15 +69,13 @@ void StandardRace::restartRace()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void StandardRace::update(float delta) void StandardRace::update(float delta)
{ {
m_clock.updateClock(delta);
LinearWorld::update(delta); LinearWorld::update(delta);
if(!m_clock.isRacePhase()) return; if(!m_clock.isRacePhase()) return;
// All karts are finished // All karts are finished
if(race_manager->getFinishedKarts() >= race_manager->getNumKarts() ) if(race_manager->getFinishedKarts() >= race_manager->getNumKarts() )
{ {
m_clock.raceOver(); raceOver();
if(user_config->m_profile<0) printProfileResultAndExit(); if(user_config->m_profile<0) printProfileResultAndExit();
unlock_manager->raceFinished(); unlock_manager->raceFinished();
} // if all karts are finished } // if all karts are finished
@ -84,7 +86,7 @@ void StandardRace::update(float delta)
{ {
// Set delay mode to have time for camera animation, and // Set delay mode to have time for camera animation, and
// to give the AI some time to get non-estimated timings // to give the AI some time to get non-estimated timings
m_clock.raceOver(true /* delay */); raceOver(true /* delay */);
} }
} }

View File

@ -257,6 +257,7 @@ void World::resetAllKarts()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void World::update(float dt) void World::update(float dt)
{ {
m_clock.update(dt);
// Clear race state so that new information can be stored // Clear race state so that new information can be stored
race_state->clear(); race_state->clear();
if(user_config->m_replay_history) dt=history->GetNextDelta(); if(user_config->m_replay_history) dt=history->GetNextDelta();
@ -277,6 +278,14 @@ void World::update(float dt)
callback_manager->update(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 HighscoreEntry* World::getHighscores() const
{ {
if(!m_use_highscores) return NULL; 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 // 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) // if we ever decide to display a message (e.g. during a race)
unsigned int *index = new unsigned int[m_kart.size()]; 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++ ) for (unsigned int i=0; i<kart_amount; i++ )
{ {
index[m_kart[i]->getPosition()-1] = i; index[m_kart[i]->getPosition()-1] = i;

View File

@ -148,10 +148,10 @@ public:
HighscoreEntry* getHighscores() const; HighscoreEntry* getHighscores() const;
float getTime() const { return m_clock.getTime(); } float getTime() const { return m_clock.getTime(); }
Phase getPhase() const { return m_clock.getPhase(); } Phase getPhase() const { return m_clock.getPhase(); }
const Clock& getClock() const { return m_clock; } const Clock &getClock() { return m_clock; }
/** Gets called when the race is about to finish (but with the option of adding
/** Called when race is over and should be terminated (mostly called by the clock). * some delay to watch the end of the race. */
*/ void raceOver(bool delay=false);
virtual void terminateRace(); virtual void terminateRace();
/** Called to determine the default collectibles to give each player for this /** Called to determine the default collectibles to give each player for this

View File

@ -63,6 +63,8 @@ void Message::receive(ENetPacket* pkt, MessageType m)
m_data_size = pkt->dataLength; m_data_size = pkt->dataLength;
m_data = (char*)pkt->data; m_data = (char*)pkt->data;
m_type = (MessageType)m_data[0]; m_type = (MessageType)m_data[0];
if(m_type!=m)
printf("type %d %d\n",m_type,m);
assert(m_type==m); assert(m_type==m);
m_pos = 1; m_pos = 1;
m_needs_destroy = true; m_needs_destroy = true;

View File

@ -43,10 +43,12 @@
class Message class Message
{ {
public: public:
/** Contains all tags used in identifying a message. */
enum MessageType {MT_CONNECT=1, MT_CHARACTER_INFO, MT_CHARACTER_CONFIRM, enum MessageType {MT_CONNECT=1, MT_CHARACTER_INFO, MT_CHARACTER_CONFIRM,
MT_RACE_INFO, MT_RACE_START, MT_WORLD_LOADED, MT_RACE_INFO, MT_RACE_START, MT_WORLD_LOADED,
MT_KART_INFO, MT_KART_CONTROL, MT_KART_INFO, MT_KART_CONTROL, MT_RACE_STATE,
MT_RACE_STATE}; MT_RACE_RESULT, MT_RACE_RESULT_ACK
};
private: private:
ENetPacket *m_pkt; ENetPacket *m_pkt;
char *m_data; char *m_data;

View File

@ -18,6 +18,16 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network_manager.hpp" #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/connect_message.hpp"
#include "network/character_info_message.hpp" #include "network/character_info_message.hpp"
#include "network/character_selected_message.hpp" #include "network/character_selected_message.hpp"
@ -27,13 +37,8 @@
#include "network/race_state.hpp" #include "network/race_state.hpp"
#include "network/kart_control_message.hpp" #include "network/kart_control_message.hpp"
#include "network/character_confirm_message.hpp" #include "network/character_confirm_message.hpp"
#include "stk_config.hpp" #include "network/race_result_message.hpp"
#include "user_config.hpp" #include "network/race_result_ack_message.hpp"
#include "race_manager.hpp"
#include "kart_properties_manager.hpp"
#include "translation.hpp"
#include "gui/menu_manager.hpp"
#include "gui/char_sel.hpp"
NetworkManager* network_manager = 0; NetworkManager* network_manager = 0;
@ -272,6 +277,22 @@ void NetworkManager::handleMessageAtServer(ENetEvent *event)
} }
break; 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 default: assert(0); // should not happen
} // switch m_state } // switch m_state
} // handleMessageAtServer } // handleMessageAtServer
@ -347,6 +368,16 @@ void NetworkManager::handleMessageAtClient(ENetEvent *event)
assert(false); // should never be here while racing assert(false); // should never be here while racing
break; 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: default:
{ {
printf("received unknown message: type %d\n", printf("received unknown message: type %d\n",
@ -592,6 +623,14 @@ void NetworkManager::receiveUpdates()
} }
else 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); race_state->receive(event.packet);
} }
} // for i<num_messages } // for i<num_messages
@ -628,3 +667,50 @@ void NetworkManager::waitForClientData()
} // 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

View File

@ -37,7 +37,7 @@ public:
enum NetworkMode {NW_SERVER, NW_CLIENT, NW_NONE}; enum NetworkMode {NW_SERVER, NW_CLIENT, NW_NONE};
// States for the finite state machine. First for server: // 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_ACCEPT_CONNECTIONS, // server: accept connections
NS_WAIT_FOR_AVAILABLE_CHARACTERS, // client: wait for list NS_WAIT_FOR_AVAILABLE_CHARACTERS, // client: wait for list
NS_ALL_REMOTE_CHARACTERS_DONE, // server: all client data received 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_READY_SET_GO_BARRIER, // c&s: barrier before r.s.g.
NS_CHARACTER_SELECT, // c&s: character select in progress NS_CHARACTER_SELECT, // c&s: character select in progress
NS_LOADING_WORLD, // client: loading world 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: private:
NetworkMode m_mode; NetworkMode m_mode;
@ -108,6 +111,9 @@ public:
void sendUpdates(); void sendUpdates();
void receiveUpdates(); void receiveUpdates();
void waitForClientData(); void waitForClientData();
void sendRaceResults();
void beginRaceResultBarrier();
void sendRaceResultAck(char menu_selection=-1);
}; };
extern NetworkManager *network_manager; extern NetworkManager *network_manager;

View 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

View 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

View 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