From fd93bfef1aa9bf04310dfb3e5c3c52aa5ffb0a64 Mon Sep 17 00:00:00 2001 From: hikerstk Date: Sun, 7 Sep 2008 13:58:37 +0000 Subject: [PATCH] Beginning of finite state machine for network manager. git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@2232 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- src/game_manager.cpp | 3 + src/gui/char_sel.cpp | 35 +++-------- src/gui/char_sel.hpp | 1 - src/gui/menu_manager.cpp | 5 ++ src/gui/menu_manager.hpp | 3 +- src/gui/network_info.cpp | 88 ++++++++++++++++++++++++++++ src/gui/network_info.hpp | 34 +++++++++++ src/gui/start_race_feedback.cpp | 13 ++++- src/ide/vc9/supertuxkart.vcproj | 8 +++ src/kart_properties_manager.cpp | 23 ++++++++ src/kart_properties_manager.hpp | 17 +++--- src/main.cpp | 6 +- src/network/network_manager.cpp | 100 ++++++++++++++++++++++++++++---- src/network/network_manager.hpp | 38 ++++++++---- src/world.cpp | 1 + 15 files changed, 315 insertions(+), 60 deletions(-) create mode 100644 src/gui/network_info.cpp create mode 100644 src/gui/network_info.hpp diff --git a/src/game_manager.cpp b/src/game_manager.cpp index 8188e1d5b..bdc0d1213 100644 --- a/src/game_manager.cpp +++ b/src/game_manager.cpp @@ -107,6 +107,9 @@ void GameManager::run() network_manager->update(dt); if (race_manager->raceIsActive()) { + // Busy wait if race_manager is active (i.e. creating of world is done) + // till all clients have reached this state. + if(network_manager->getState()==NetworkManager::NS_READY_SET_GO_BARRIER) continue; music_on = false; if(user_config->m_profile) dt=1.0f/60.0f; // In the first call dt might be large (includes loading time), diff --git a/src/gui/char_sel.cpp b/src/gui/char_sel.cpp index c59ce8e00..f14f71dc6 100644 --- a/src/gui/char_sel.cpp +++ b/src/gui/char_sel.cpp @@ -66,10 +66,10 @@ CharSel::CharSel(int whichPlayer) // the user is moving back through the menus and the last value in the vector // needs to be made available again. if (m_player_index == 0) - kart_properties_manager->m_selected_karts.clear(); + kart_properties_manager->clearAllSelectedKarts(); - if (m_player_index < (int)kart_properties_manager->m_selected_karts.size()) - kart_properties_manager->m_selected_karts.pop_back(); + if (m_player_index < (int)kart_properties_manager->getNumSelectedKarts()) + kart_properties_manager->removeLastSelectedKart(); char heading[MAX_MESSAGE_LENGTH]; snprintf(heading, sizeof(heading), _("Player %d, choose a driver"), @@ -111,7 +111,7 @@ CharSel::CharSel(int whichPlayer) m_current_kart = -1; const int LAST_KART = user_config->m_player[m_player_index].getLastKartId(); - if( LAST_KART != -1 && kartAvailable(LAST_KART))// is LAST_KART not in vector of selected karts + if( LAST_KART != -1 && kart_properties_manager->kartAvailable(LAST_KART))// is LAST_KART not in vector of selected karts { int local_index = 0; for(unsigned int i=0; igetKartsInGroup(user_config->m_kart_group); for(unsigned int i=0; ikartAvailable(karts[i])) { m_index_avail_karts.push_back(karts[i]); } @@ -361,7 +361,7 @@ void CharSel::select() user_config->m_player[m_player_index].setLastKartId(kart_id); // Add selected kart (token) to selected karts vector so it cannot be // selected again - kart_properties_manager->m_selected_karts.push_back(kart_id); + kart_properties_manager->testAndSetKart(kart_id); } if (race_manager->getNumLocalPlayers() > 1) @@ -393,6 +393,7 @@ void CharSel::select() if(network_manager->getMode()==NetworkManager::NW_CLIENT) { + network_manager->sendKartsInformationToServer(); menu_manager->pushMenu(MENUID_START_RACE_FEEDBACK); } else @@ -426,24 +427,4 @@ void CharSel::handle(GameAction action, int value) return; } // if cursor down BaseGUI::handle(action, value); -} // handle - -//---------------------------------------------------------------------------- -// Function checks the vector of previously selected karts and returns true if -// kart i is in the vector and false if it is not. - -bool CharSel::kartAvailable(int kartid) -{ - if (!kart_properties_manager->m_selected_karts.empty()) - { - std::vector::iterator it; - for (it = kart_properties_manager->m_selected_karts.begin(); - it < kart_properties_manager->m_selected_karts.end(); it++) - { - if ( kartid == *it) - return false; - } - } - const KartProperties *kartprop=kart_properties_manager->getKartById(kartid); - return !unlock_manager->isLocked(kartprop->getIdent()); -} // kartAvailable +} // handle \ No newline at end of file diff --git a/src/gui/char_sel.hpp b/src/gui/char_sel.hpp index cab354c5d..6ad05c400 100644 --- a/src/gui/char_sel.hpp +++ b/src/gui/char_sel.hpp @@ -38,7 +38,6 @@ private: unsigned int m_num_entries; // number of entries to display std::vector m_index_avail_karts; static const unsigned int m_max_entries=7; - bool kartAvailable(int kart); void updateScrollPosition(); int computeIndent(int n) {return 40+abs((int)(m_max_entries-1)/2 - n)*3;} void switchGroup(); diff --git a/src/gui/menu_manager.cpp b/src/gui/menu_manager.cpp index 676cc7c63..b7eee42e9 100644 --- a/src/gui/menu_manager.cpp +++ b/src/gui/menu_manager.cpp @@ -54,6 +54,7 @@ #include "challenges_menu.hpp" #include "feature_unlocked.hpp" #include "start_race_feedback.hpp" +#include "network_info.hpp" using namespace std; @@ -238,6 +239,10 @@ void MenuManager::update() break; case MENUID_START_RACE_FEEDBACK: m_current_menu = new StartRaceFeedback(); + break; + case MENUID_NETWORK_INFO: + m_current_menu = new NetworkInfo(); + break; default: break; } // switch diff --git a/src/gui/menu_manager.hpp b/src/gui/menu_manager.hpp index a98042e28..ce72f4f8d 100644 --- a/src/gui/menu_manager.hpp +++ b/src/gui/menu_manager.hpp @@ -44,7 +44,8 @@ enum MenuManagerIDs MENUID_EXITGAME, MENUID_GRANDPRIXSELECT, MENUID_UNLOCKED_FEATURE, - MENUID_START_RACE_FEEDBACK, + MENUID_START_RACE_FEEDBACK, // 'loading' + MENUID_NETWORK_INFO, // wait for clients to connect // menu configuration MENUID_CONFIG_DISPLAY, diff --git a/src/gui/network_info.cpp b/src/gui/network_info.cpp new file mode 100644 index 000000000..c34b893b9 --- /dev/null +++ b/src/gui/network_info.cpp @@ -0,0 +1,88 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2006 SuperTuxKart-Team +// +// 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 + +#include "network_info.hpp" +#include "widget_manager.hpp" +#include "translation.hpp" +#include "network/network_manager.hpp" +#include "stk_config.hpp" +#include "gui/menu_manager.hpp" + +enum WidgetTokens +{ + WTOK_MSG, + WTOK_CLIENT, + WTOK_OK +}; + +NetworkInfo::NetworkInfo() +{ + //Add some feedback so people know they are going to start the race + widget_manager->reset(); + Widget *w_prev=widget_manager->addTitleWgt( WTOK_MSG, 60, 7, _("Waiting for clients ...") ); + w_prev->setPosition(WGT_DIR_CENTER, 0.0, NULL, + WGT_DIR_FROM_TOP, 0.1f, NULL); + + // Display a single space to avoid warnings from the widget_manager + Widget *w=widget_manager->addTextWgt(WTOK_CLIENT, 60, 10, " "); + w->setPosition(WGT_DIR_CENTER, 0.0, NULL, + WGT_DIR_UNDER_WIDGET, 0.0, w_prev); + w_prev = w; + w=widget_manager->addTextButtonWgt(WTOK_OK, 60, 7, _("OK")); + w->setPosition(WGT_DIR_CENTER, 0.0, NULL, + WGT_DIR_UNDER_WIDGET, 0.1f, w_prev); + widget_manager->layout(WGT_AREA_TOP); + m_num_clients = 0; +} // NetworkInfo + +//----------------------------------------------------------------------------- +NetworkInfo::~NetworkInfo() +{ + widget_manager->reset(); +} + + +//----------------------------------------------------------------------------- +void NetworkInfo::update(float DELTA) +{ + widget_manager->update(0.0f); + if(m_num_clients==network_manager->getNumClients()) return; + + // At least one new client has connected: + std::string s=""; + m_num_clients = network_manager->getNumClients(); + for(unsigned int i=1; i<=m_num_clients; i++) + { + s+=network_manager->getClientName(i)+"\n"; + } + widget_manager->setWgtText(WTOK_CLIENT, s); + +} // update + +//----------------------------------------------------------------------------- +void NetworkInfo::select() +{ + switch( widget_manager->getSelectedWgt() ) + { + case WTOK_OK: + network_manager->switchToCharacterSelection(); + menu_manager->popMenu(); + break; + } +} // select diff --git a/src/gui/network_info.hpp b/src/gui/network_info.hpp new file mode 100644 index 000000000..024745c59 --- /dev/null +++ b/src/gui/network_info.hpp @@ -0,0 +1,34 @@ +// 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_NETWORK_INFO_H +#define HEADER_NETWORK_INFO_H + +#include "base_gui.hpp" + +class NetworkInfo : public BaseGUI +{ +private: + unsigned int m_num_clients; +public: + NetworkInfo(); + ~NetworkInfo(); + void update(float DELTA); + void select(); +}; + +#endif diff --git a/src/gui/start_race_feedback.cpp b/src/gui/start_race_feedback.cpp index ade964d08..cc45764a6 100644 --- a/src/gui/start_race_feedback.cpp +++ b/src/gui/start_race_feedback.cpp @@ -30,6 +30,8 @@ enum WidgetTokens StartRaceFeedback::StartRaceFeedback() { + network_manager->switchToRaceDataSynchronisation(); + m_state = network_manager->getMode()==NetworkManager::NW_NONE ? SRF_LOADING : SRF_NETWORK; //Add some feedback so people know they are going to start the race @@ -65,10 +67,17 @@ void StartRaceFeedback::update(float DELTA) m_state = SRF_NETWORK; break; case SRF_NETWORK: + // The server only waits for one frame: if(network_manager->getMode()==NetworkManager::NW_SERVER) + { network_manager->sendRaceInformationToClients(); - else - network_manager->waitForRaceInformation(); + m_state = SRF_LOADING_DISPLAY; + widget_manager->setWgtText(WTOK_MSG, _("Loading race...") ); + return; + } + // The client have to be busy waiting till the race data has arrived: + if(network_manager->getState()==NetworkManager::NS_WAITING_FOR_RACE_DATA) + return; m_state = SRF_LOADING_DISPLAY; widget_manager->setWgtText(WTOK_MSG, _("Loading race...") ); break; diff --git a/src/ide/vc9/supertuxkart.vcproj b/src/ide/vc9/supertuxkart.vcproj index 9999bc962..9125dd276 100644 --- a/src/ide/vc9/supertuxkart.vcproj +++ b/src/ide/vc9/supertuxkart.vcproj @@ -1006,6 +1006,10 @@ RelativePath="../../../src\gui\menu_manager.cpp" > + + @@ -1480,6 +1484,10 @@ RelativePath="../../../src\gui\menu_manager.hpp" > + + diff --git a/src/kart_properties_manager.cpp b/src/kart_properties_manager.cpp index a305c8656..f924179cd 100644 --- a/src/kart_properties_manager.cpp +++ b/src/kart_properties_manager.cpp @@ -26,6 +26,8 @@ #include "kart_properties.hpp" #include "translation.hpp" #include "user_config.hpp" +#include "unlock_manager.hpp" + #if defined(WIN32) && !defined(__CYGWIN__) # define snprintf _snprintf #endif @@ -159,6 +161,27 @@ int KartPropertiesManager::getKartByGroup(const std::string& group, int n) const return -1; } // getKartByGroup +//----------------------------------------------------------------------------- +bool KartPropertiesManager::testAndSetKart(int kartid) +{ + if(!kartAvailable(kartid)) return false; + m_selected_karts.push_back(kartid); + return true; +} // testAndSetKart + +//----------------------------------------------------------------------------- +bool KartPropertiesManager::kartAvailable(int kartid) +{ + std::vector::iterator it; + for (it = m_selected_karts.begin(); it < m_selected_karts.end(); it++) + { + if ( kartid == *it) return false; + } + const KartProperties *kartprop=getKartById(kartid); + if(unlock_manager->isLocked(kartprop->getIdent())) return false; + return true; +} // testAndSetKart + //----------------------------------------------------------------------------- void KartPropertiesManager::fillWithRandomKarts(std::vector& vec) { diff --git a/src/kart_properties_manager.hpp b/src/kart_properties_manager.hpp index cb498df28..c2c3f5a14 100644 --- a/src/kart_properties_manager.hpp +++ b/src/kart_properties_manager.hpp @@ -31,6 +31,10 @@ class KartPropertiesManager private: std::vector m_all_groups; std::map > m_groups; + // vector containing kart numbers that have been selected in multiplayer + // games. This it used to ensure the same kart can not be selected more + // than once. + std::vector m_selected_karts; protected: float m_max_steer_angle; @@ -41,12 +45,7 @@ protected: public: KartPropertiesManager(); ~KartPropertiesManager(); - - // vector containing kart numbers that have been selected in multiplayer - // games. This it used to ensure the same kart can not be selected more - // than once. - std::vector m_selected_karts; - + const KartProperties* getKartById (int i) const; const KartProperties* getKart (const std::string IDENT) const; const int getKartId (const std::string IDENT) const; @@ -58,7 +57,11 @@ public: getAllGroups () const {return m_all_groups; } const std::vector& getKartsInGroup (const std::string& g) {return m_groups[g]; } - + void clearAllSelectedKarts() {m_selected_karts.clear();} + void removeLastSelectedKart() {m_selected_karts.pop_back();} + int getNumSelectedKarts() const {return m_selected_karts.size();} + bool kartAvailable(int kartid); + bool testAndSetKart(int kartid); /** Fill the empty positions in the given vector with random karts */ void fillWithRandomKarts (std::vector& vec); void removeTextures (); diff --git a/src/main.cpp b/src/main.cpp index 680d5138c..f59a36c04 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -536,7 +536,11 @@ int main(int argc, char *argv[] ) fprintf(stderr, "Problems initialising network connections,\n" "Running in non-network mode.\n"); } - + // On the server start with the network information page + if(network_manager->getMode()==NetworkManager::NW_SERVER) + { + menu_manager->pushMenu(MENUID_NETWORK_INFO); + } // Not replaying // ============= if(!user_config->m_profile) diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index c940b0f98..7385045eb 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -21,13 +21,14 @@ #include "stk_config.hpp" #include "user_config.hpp" #include "race_manager.hpp" +#include "kart_properties_manager.hpp" NetworkManager* network_manager = 0; NetworkManager::NetworkManager() { m_mode = NW_NONE; - m_state = NS_SYNCHRONISING; + m_state = NS_ACCEPT_CONNECTIONS; m_port = 12345; m_server_address = "172.31.41.53"; m_num_clients = 0; @@ -86,6 +87,7 @@ bool NetworkManager::initServer() } fprintf(stderr, "Server initialised, waiting for connections ...\n"); + m_client_names.push_back("server"); return true; #endif } // initServer @@ -145,12 +147,12 @@ bool NetworkManager::initClient() return true; #endif -} // initServer +} // initClient // ---------------------------------------------------------------------------- void NetworkManager::handleNewConnection(ENetEvent *event) { - if(m_state!=NS_SYNCHRONISING) + if(m_state!=NS_ACCEPT_CONNECTIONS) { // We don't accept connections atm return; @@ -161,13 +163,14 @@ void NetworkManager::handleNewConnection(ENetEvent *event) event->peer -> address.port, m_num_clients); // FIXME: send m_num_clients as hostid back to new client. - + // FIXME: client should send an id as well to be displayed + m_client_names.push_back("client"); } // handleNewConnection // ---------------------------------------------------------------------------- void NetworkManager::handleDisconnection(ENetEvent *event) { - if(m_state!=NS_SYNCHRONISING) + if(m_state!=NS_ACCEPT_CONNECTIONS) { fprintf(stderr, "Disconnect while in race - close your eyes and hope for the best.\n"); return; @@ -178,14 +181,86 @@ void NetworkManager::handleDisconnection(ENetEvent *event) } // handleDisconnection // ---------------------------------------------------------------------------- -void NetworkManager::handleNewMessage(ENetEvent *event) +void NetworkManager::handleServerMessage(ENetEvent *event) { - if(m_state==NS_SYNCHRONISING) + switch(m_state) { + case NS_ACCEPT_CONNECTIONS: fprintf(stderr, "Received a receive event while waiting for client - ignored.\n"); return; - } -} // handleNewMessage + case NS_CHARACTER_SELECT: + { + // only accept testAndSet and 'character selected' messages here. + // Get character from message, check if it's still available + int kartid=0, playerid=0, hostid=0; + std::string name="tuxkart", user="guest"; + if(kart_properties_manager->testAndSetKart(kartid)) + { + // send 'ok' message to client and all other clients + m_kart_info.push_back(RemoteKartInfo(playerid, name, user, hostid)); + } + else + { + // send 'not avail' to sender + } + break; + } + case NS_READY_SET_GO_BARRIER: + m_barrier_count++; + if(m_barrier_count==m_num_clients) + { + // broadcast start message + m_state = NS_RACING; + } + break; + case NS_KART_INFO_BARRIER: + m_barrier_count++; + if(m_barrier_count==m_num_clients) + { + // broadcast start message + m_state = NS_RACING; + } + break; + + } // switch m_state +} // handleServerMessage + +// ---------------------------------------------------------------------------- +void NetworkManager::switchToReadySetGoBarrier() +{ + assert(m_state == NS_CHARACTER_SELECT); + m_state = NS_READY_SET_GO_BARRIER; + m_barrier_count = 0; +} // switchToReadySetGoBarrier + +// ---------------------------------------------------------------------------- +void NetworkManager::switchToCharacterSelection() +{ + // This must be called from the network info menu, + // so make sure the state is correct + assert(m_state == NS_ACCEPT_CONNECTIONS); + m_state = NS_CHARACTER_SELECT; +} // switchTocharacterSelection + +// ---------------------------------------------------------------------------- +void NetworkManager::switchToRaceDataSynchronisation() +{ + assert(m_state == NS_CHARACTER_SELECT); + m_state = NS_KART_INFO_BARRIER; + m_barrier_count = 0; +} // switchToRaceDataSynchronisation + +// ---------------------------------------------------------------------------- +void NetworkManager::handleClientMessage(ENetEvent *event) +{ + switch(m_state) + { + case NS_ACCEPT_CONNECTIONS: + fprintf(stderr, "Received a receive event while waiting for client - ignored.\n"); + return; + + } // switch m_state +} // handleClientMessage // ---------------------------------------------------------------------------- void NetworkManager::update(float dt) @@ -202,7 +277,12 @@ void NetworkManager::update(float dt) switch (event.type) { case ENET_EVENT_TYPE_CONNECT: handleNewConnection(&event); break; - case ENET_EVENT_TYPE_RECEIVE: handleNewMessage(&event); break; + case ENET_EVENT_TYPE_RECEIVE: + if(m_mode==NW_SERVER) + handleServerMessage(&event); + else + handleClientMessage(&event); + break; case ENET_EVENT_TYPE_DISCONNECT: handleDisconnection(&event); break; case ENET_EVENT_TYPE_NONE: break; } diff --git a/src/network/network_manager.hpp b/src/network/network_manager.hpp index 3ded85b49..edc31b067 100644 --- a/src/network/network_manager.hpp +++ b/src/network/network_manager.hpp @@ -35,8 +35,14 @@ public: // The mode the network manager is operating in enum NetworkMode {NW_SERVER, NW_CLIENT, NW_NONE}; - // The current state. - enum NetworkState {NS_SYNCHRONISING, NS_RACING}; + // States for the finite state machine. First for server: + enum NetworkState {NS_ACCEPT_CONNECTIONS, NS_KART_INFO_BARRIER, + // Then client only states: + NS_CHARACTER_CONFIRMED, + NS_CHARACTER_REJECTED, + NS_WAITING_FOR_RACE_DATA, + // Shared states + NS_CHARACTER_SELECT, NS_READY_SET_GO_BARRIER, NS_RACING}; private: NetworkMode m_mode; @@ -46,6 +52,8 @@ private: int m_num_clients; std::vector m_kart_info; int m_host_id; + std::vector m_client_names; + int m_barrier_count; #ifdef HAVE_ENET ENetHost *m_host; #endif @@ -53,20 +61,24 @@ private: bool initServer(); bool initClient(); void handleNewConnection(ENetEvent *event); - void handleNewMessage (ENetEvent *event); + void handleServerMessage(ENetEvent *event); + void handleClientMessage(ENetEvent *event); void handleDisconnection(ENetEvent *event); + void testSetCharacter (ENetEvent *event); public: NetworkManager(); ~NetworkManager(); - void setMode(NetworkMode m) {m_mode = m; } - NetworkMode getMode() const {return m_mode; } - void setState(NetworkState s) {m_state = s; } - NetworkState getState() const {return m_state; } - int getHostId() const {return m_host_id; } - int getNumClients() const {return m_num_clients; } - void setPort(int p) {m_port=p; } - void setServerIP(const std::string &s) {m_server_address=s; } + void setMode(NetworkMode m) {m_mode = m; } + NetworkMode getMode() const {return m_mode; } + void setState(NetworkState s) {m_state = s; } + NetworkState getState() const {return m_state; } + int getHostId() const {return m_host_id; } + unsigned int getNumClients() const {return m_num_clients; } + const std::string& + getClientName(int i) const {return m_client_names[i];} + void setPort(int p) {m_port=p; } + void setServerIP(const std::string &s) {m_server_address=s; } void setKartInfo(int player_id, const std::string& kart, const std::string& user="", int hostid=-1); bool initialiseConnections(); @@ -75,6 +87,10 @@ public: void waitForKartsInformation(); void sendRaceInformationToClients(); void waitForRaceInformation(); + void switchToReadySetGoBarrier(); + void switchToCharacterSelection(); + void switchToRaceDataSynchronisation(); + }; extern NetworkManager *network_manager; diff --git a/src/world.cpp b/src/world.cpp index 3ed85fe41..6c0293e97 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -189,6 +189,7 @@ World::World() } if( m_p_replay_player ) m_p_replay_player->showReplayAt( 0.0 ); #endif + network_manager->switchToReadySetGoBarrier(); } // World //-----------------------------------------------------------------------------