diff --git a/src/Makefile.am b/src/Makefile.am index b6e3a8b42..9d3fbf61a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/gui/char_sel.cpp b/src/gui/char_sel.cpp index db38348d8..104c63a0d 100644 --- a/src/gui/char_sel.cpp +++ b/src/gui/char_sel.cpp @@ -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; } diff --git a/src/gui/char_sel.hpp b/src/gui/char_sel.hpp index 25a1b7b2d..abc590833 100644 --- a/src/gui/char_sel.hpp +++ b/src/gui/char_sel.hpp @@ -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 m_index_avail_karts; static const unsigned int m_max_entries=7; void updateScrollPosition(); diff --git a/src/gui/network_gui.cpp b/src/gui/network_gui.cpp index a4d7a3f4e..7cfb252dc 100644 --- a/src/gui/network_gui.cpp +++ b/src/gui/network_gui.cpp @@ -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(); diff --git a/src/gui/race_gui.cpp b/src/gui/race_gui.cpp index 08dcc3cc4..671f0abec 100644 --- a/src/gui/race_gui.cpp +++ b/src/gui/race_gui.cpp @@ -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); diff --git a/src/gui/race_results_gui.cpp b/src/gui/race_results_gui.cpp index 7c53ab0d9..590f1106d 100644 --- a/src/gui/race_results_gui.cpp +++ b/src/gui/race_results_gui.cpp @@ -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 */ diff --git a/src/gui/race_results_gui.hpp b/src/gui/race_results_gui.hpp index 41d6e6cba..ff2402843 100644 --- a/src/gui/race_results_gui.hpp +++ b/src/gui/race_results_gui.hpp @@ -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 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 diff --git a/src/ide/vc9/supertuxkart.vcproj b/src/ide/vc9/supertuxkart.vcproj index c729f0f79..8a7875905 100644 --- a/src/ide/vc9/supertuxkart.vcproj +++ b/src/ide/vc9/supertuxkart.vcproj @@ -1090,6 +1090,10 @@ RelativePath="..\..\network\race_info_message.cpp" > + + @@ -1660,6 +1664,14 @@ RelativePath="..\..\network\race_info_message.hpp" > + + + + diff --git a/src/kart.cpp b/src/kart.cpp index fa941ab18..1c7502c44 100644 --- a/src/kart.cpp +++ b/src/kart.cpp @@ -385,6 +385,7 @@ void Kart::reset() m_rescue = false; TerrainInfo::update(getXYZ()); } // reset + //----------------------------------------------------------------------------- void Kart::raceFinished(float time) { diff --git a/src/main_loop.cpp b/src/main_loop.cpp index a9375068d..996acdc48 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -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) { diff --git a/src/modes/clock.cpp b/src/modes/clock.cpp index f29f10343..8dd5fa0bf 100644 --- a/src/modes/clock.cpp +++ b/src/modes/clock.cpp @@ -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) { diff --git a/src/modes/clock.hpp b/src/modes/clock.hpp index 32938b127..6b4dd494f 100644 --- a/src/modes/clock.hpp +++ b/src/modes/clock.hpp @@ -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 && m_phase=GO_PHASE && + m_phaseisEliminated()) race_manager->RaceFinished(m_kart[i], m_clock.getTime()); - m_clock.raceOver(); + raceOver(); return; } } diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index 744fd4b3d..9fe3e5794 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -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. diff --git a/src/modes/standard_race.cpp b/src/modes/standard_race.cpp index 1ae8ae3c4..d5f4a05dd 100644 --- a/src/modes/standard_race.cpp +++ b/src/modes/standard_race.cpp @@ -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 */); } } diff --git a/src/modes/world.cpp b/src/modes/world.cpp index cae703e16..f99e39150 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -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; igetPosition()-1] = i; diff --git a/src/modes/world.hpp b/src/modes/world.hpp index 5590676ac..5c3bf34c7 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -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 diff --git a/src/network/message.cpp b/src/network/message.cpp index e6b60128e..41896ec2a 100644 --- a/src/network/message.cpp +++ b/src/network/message.cpp @@ -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; diff --git a/src/network/message.hpp b/src/network/message.hpp index 8ace585ea..d4140ee2e 100644 --- a/src/network/message.hpp +++ b/src/network/message.hpp @@ -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; diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index 328464fec..2bf2cbfb6 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -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(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 + +#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 diff --git a/src/network/race_result_message.cpp b/src/network/race_result_message.cpp new file mode 100755 index 000000000..28c436055 --- /dev/null +++ b/src/network/race_result_message.cpp @@ -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; igetKart(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; igetKart(i); + float time = getFloat(); + char position = getChar(); + kart->setPosition(position); + kart->raceFinished(time); + race_manager->RaceFinished(kart, time); + } +} // RaceResultMessage + diff --git a/src/network/race_result_message.hpp b/src/network/race_result_message.hpp new file mode 100755 index 000000000..0415b5dfa --- /dev/null +++ b/src/network/race_result_message.hpp @@ -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 + +#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 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