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

@ -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 \

View File

@ -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;
}

View File

@ -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();

View File

@ -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();

View File

@ -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);

View File

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

View File

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

View File

@ -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"
>

View File

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

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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);

View File

@ -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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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