1) Added kart_packet files to Makefile.am

2) Improvements to network managers (finite state machine) and to the network GUI.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/trunk/supertuxkart@2235 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2008-09-07 14:05:48 +00:00
parent ed1d4b0192
commit 3ccd8500de
12 changed files with 632 additions and 227 deletions

View File

@ -30,9 +30,14 @@ supertuxkart_SOURCES = main.cpp \
coord.hpp \
actionmap.cpp actionmap.hpp \
material.cpp material.hpp \
network/network_manager.cpp network/network_manager.hpp \
network/network_kart.cpp network/network_kart.hpp \
network/remote_kart_info.hpp \
network/network_manager.cpp network/network_manager.hpp \
network/network_kart.cpp network/network_kart.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/connect_message.hpp \
network.num_players_message.hpp network/world_loaded_message.hpp \
network/connect_message.hpp network/character_info_message.hpp \
network/message.cpp network/message.hpp \
material_manager.cpp material_manager.hpp \
grand_prix_manager.cpp grand_prix_manager.hpp \
attachment.cpp attachment.hpp \
@ -112,20 +117,19 @@ supertuxkart_SOURCES = main.cpp \
gui/track_sel.cpp gui/track_sel.hpp \
gui/player_controls.cpp gui/player_controls.hpp \
gui/config_display.cpp gui/config_display.hpp \
gui/display_res_confirm.cpp gui/display_res_confirm.hpp \
gui/display_res_confirm.cpp gui/display_res_confirm.hpp \
gui/config_sound.cpp gui/config_sound.hpp \
gui/config_controls.cpp gui/config_controls.hpp \
gui/options.cpp gui/options.hpp \
gui/game_mode.cpp gui/game_mode.hpp \
gui/race_options.cpp gui/race_options.hpp \
gui/race_options.cpp gui/race_options.hpp \
gui/char_sel.cpp gui/char_sel.hpp \
gui/start_race_feedback.cpp gui/start_race_feedback.hpp \
gui/network_info.cpp gui/network_info.hpp \
gui/start_race_feedback.cpp gui/start_race_feedback.hpp \
gui/network_gui.cpp gui/network_gui.hpp \
gui/main_menu.cpp gui/main_menu.hpp \
gui/help_page_one.cpp gui/help_page_one.hpp \
gui/help_page_two.cpp gui/help_page_two.hpp \
gui/help_page_three.cpp gui/help_page_three.hpp \
gui/help_page_one.cpp gui/help_page_one.hpp \
gui/help_page_two.cpp gui/help_page_two.hpp \
gui/help_page_three.cpp gui/help_page_three.hpp \
gui/credits_menu.cpp gui/credits_menu.hpp \
gui/grand_prix_select.cpp gui/grand_prix_select.hpp \
gui/challenges_menu.cpp gui/challenges_menu.hpp \

View File

@ -162,6 +162,7 @@ void GameManager::run()
#if !defined(WIN32) && !defined(__CYGWIN__)
// usleep(2000);
#endif
//Draw the splash screen
glBindTexture(GL_TEXTURE_2D,TITLE_SCREEN_TEXTURE);

View File

@ -39,13 +39,13 @@
enum WidgetTokens
{
WTOK_TITLE,
WTOK_QUIT,
WTOK_EMPTY_UP,
WTOK_UP,
WTOK_EMPTY_DOWN,
WTOK_EMPTY_UP,
WTOK_TITLE,
WTOK_UP,
WTOK_DOWN,
WTOK_MESSAGE,
WTOK_QUIT,
WTOK_EMPTY0 = 10,
WTOK_NAME0 = 2000,
WTOK_RACER0 = 3000
@ -54,6 +54,11 @@ enum WidgetTokens
CharSel::CharSel(int whichPlayer)
: m_kart(0), m_player_index(whichPlayer)
{
// First time this is called --> switch client and server
// to character barrier mode
if(network_manager->getState()==NetworkManager::NS_NONE)
network_manager->switchToCharacterSelection();
// For some strange reasons plib calls makeCurrent() in ssgContext
// constructor, so we have to save the old one here and restore it
ssgContext* oldContext = ssgGetCurrentContext();
@ -106,6 +111,13 @@ CharSel::CharSel(int whichPlayer)
switchGroup(); // select all karts from the currently selected group
Widget *w=widget_manager->addTextWgt(WTOK_MESSAGE, 30, 7, "");
w->setPosition(WGT_DIR_CENTER, 0, WGT_DIR_CENTER, 0);
if(network_manager->getMode()==NetworkManager::NW_CLIENT)
widget_manager->setWgtText(WTOK_MESSAGE, _("Waiting for server"));
else
widget_manager->setWgtText(WTOK_MESSAGE, _("Waiting for clients"));
widget_manager->layout(WGT_AREA_RGT);
m_current_kart = -1;
@ -132,10 +144,20 @@ CharSel::CharSel(int whichPlayer)
m_offset = 0;
switchCharacter(0);
}
updateScrollPosition();
if(network_manager->getState()==NetworkManager::NS_WAIT_FOR_AVAILABLE_CHARACTERS)
{
widget_manager->hideWgt(WTOK_TITLE, WTOK_DOWN);
// Hide all widgets except the message widget
for (unsigned int i = 0; i < m_max_entries; i++)
{
widget_manager->hideWgt(WTOK_NAME0+i);
widget_manager->hideWgt(WTOK_RACER0+i);
}
}
else
updateScrollPosition();
m_clock = 0;
//test
} // CharSel
@ -164,17 +186,15 @@ void CharSel::updateScrollPosition()
{
if(i<start || i>end)
{
widget_manager->hideWgtRect (WTOK_NAME0 +i);
widget_manager->hideWgt (WTOK_NAME0 +i);
widget_manager->hideWgtRect (WTOK_RACER0+i);
widget_manager->hideWgtText (WTOK_NAME0 +i);
widget_manager->hideWgtTexture(WTOK_RACER0+i);
continue;
}
// Otherwise enable the widgets again (just in case that they
// had been disabled before)
widget_manager->showWgtRect (WTOK_NAME0 +i);
widget_manager->showWgtText (WTOK_NAME0 +i);
widget_manager->showWgt(WTOK_NAME0 +i);
int indx = (i+m_offset)%m_index_avail_karts.size();
indx = m_index_avail_karts[indx];
@ -272,6 +292,29 @@ void CharSel::switchCharacter(int n)
//-----------------------------------------------------------------------------
void CharSel::update(float dt)
{
// If we are still waiting in the barrier, don't do anything
if(network_manager->getState()==NetworkManager::NS_WAIT_FOR_AVAILABLE_CHARACTERS)
{
widget_manager->update(dt);
return;
}
static bool first=true;
if(first)
{
// Now hide the message window and display the widgets:
widget_manager->hideWgt(WTOK_MESSAGE);
widget_manager->showWgt(WTOK_TITLE, WTOK_EMPTY_DOWN);
// Hide all widgets except the message widget
for (unsigned int i = 0; i < m_max_entries; i++)
{
widget_manager->showWgt(WTOK_NAME0+i);
widget_manager->showWgt(WTOK_RACER0+i);
}
first=false;
updateScrollPosition();
return;
}
m_clock += dt * 40.0f;
if( widget_manager->selectionChanged() )
@ -362,6 +405,8 @@ void CharSel::select()
// Add selected kart (token) to selected karts vector so it cannot be
// selected again
kart_properties_manager->testAndSetKart(kart_id);
if(network_manager->getMode()==NetworkManager::NW_CLIENT)
network_manager->sendCharacterSelected(m_player_index);
}
if (race_manager->getNumLocalPlayers() > 1)
@ -391,13 +436,19 @@ void CharSel::select()
}
}
// Last character selected"
if(network_manager->getMode()==NetworkManager::NW_CLIENT)
{
network_manager->sendKartsInformationToServer();
// Switch state to wait for race information
network_manager->waitForRaceInformation();
menu_manager->pushMenu(MENUID_START_RACE_FEEDBACK);
}
else
{
// The state of the server does not change now (so that it can keep
// on handling client selections). Waiting for all client infos
// happens in the start_race_feedback menu (which then triggers
// sending the race info).
if (race_manager->getMajorMode() == RaceManager::RM_GRAND_PRIX)
menu_manager->pushMenu(MENUID_GRANDPRIXSELECT);
else

View File

@ -43,6 +43,10 @@ enum WidgetTokens
MainMenu::MainMenu()
{
// Reset the state of the network manager to none (which is correct
// independent if it is a client, server, or no networking
network_manager->setState(NetworkManager::NS_NONE);
widget_manager->switchOrder();
const int WIDTH=30;
@ -79,6 +83,7 @@ MainMenu::MainMenu()
widget_manager->hideWgtRect(WTOK_WARNING);
}
widget_manager->activateWgt(WTOK_SINGLE);
widget_manager->layout(WGT_AREA_ALL);
}

View File

@ -22,6 +22,9 @@
//This is needed in various platforms, but not all
# include <algorithm>
#define _WINSOCKAPI_
#include "network/network_manager.hpp"
#include "menu_manager.hpp"
#include "main_menu.hpp"
@ -54,7 +57,6 @@
#include "challenges_menu.hpp"
#include "feature_unlocked.hpp"
#include "start_race_feedback.hpp"
#include "network_info.hpp"
#include "network_gui.hpp"
using namespace std;
@ -149,6 +151,11 @@ void MenuManager::update()
{
case MENUID_MAINMENU:
m_current_menu= new MainMenu();
// in this case the network entry can be removed, resulting
// in warnings etc. if the widget manager then tries to select
// the widget again. To avoid this, set the saved widget to NONE.
if(network_manager->getMode()!=NetworkManager::NW_NONE)
saved_widget=WidgetManager::WGT_NONE;
break;
case MENUID_CHALLENGES:
m_current_menu= new ChallengesMenu();
@ -244,9 +251,6 @@ void MenuManager::update()
case MENUID_NETWORK_GUI:
m_current_menu = new NetworkGUI();
break;
case MENUID_NETWORK_INFO:
m_current_menu = new NetworkInfo();
break;
default:
break;
} // switch

View File

@ -37,6 +37,7 @@ enum WidgetTokens
WTOK_SERVER,
WTOK_SERVER_ADDRESS,
WTOK_CONNECTED,
WTOK_MESSAGE,
WTOK_QUIT
};
@ -46,14 +47,17 @@ const int NetworkGUI::SERVER_NAME_MAX = 50;
NetworkGUI::NetworkGUI()
{
m_num_clients = 0;
Widget *w=widget_manager->addTitleWgt( WTOK_TITLE, 60, 7, _("Select network mode"));
w->setPosition(WGT_DIR_CENTER, 0.0, WGT_DIR_FROM_TOP, 0.1f);
widget_manager->hideWgtRect(WTOK_TITLE);
w->setPosition(WGT_DIR_CENTER, 0.0, WGT_DIR_FROM_TOP, 0.15f);
Widget *w_prev=widget_manager->addTextButtonWgt( WTOK_CONNECT, 30, 7, _("Connect to server:") );
w_prev->setPosition(WGT_DIR_FROM_LEFT, 0.05f, WGT_DIR_FROM_TOP, 0.2f);
widget_manager->resizeWgtToText(WTOK_CONNECT);
w_prev->setPosition(WGT_DIR_FROM_LEFT, 0.05f, WGT_DIR_FROM_TOP, 0.25f);
w=widget_manager->addTextButtonWgt( WTOK_SERVER, 30, 7, _("Become server") );
widget_manager->resizeWgtToText(WTOK_SERVER);
w->setPosition(WGT_DIR_FROM_LEFT, 0.05f, NULL,
WGT_DIR_UNDER_WIDGET, 0, w_prev);
widget_manager->sameWidth(WTOK_CONNECT, WTOK_SERVER);
@ -63,22 +67,28 @@ NetworkGUI::NetworkGUI()
m_server_address=s.str();
w=widget_manager->addTextButtonWgt(WTOK_SERVER_ADDRESS, 50, 7, m_server_address);
w->setPosition(WGT_DIR_RIGHT_WIDGET, 0.05f, w_prev,
WGT_DIR_FROM_TOP, 0.2f, NULL);
WGT_DIR_FROM_TOP, 0.25f, NULL);
w=widget_manager->addTextButtonWgt(WTOK_CONNECTED, 50, 20, " ");
w->setPosition(WGT_DIR_RIGHT_WIDGET, 0.05f, w_prev,
WGT_DIR_FROM_TOP, 0.2f, NULL);
WGT_DIR_FROM_TOP, 0.25f, NULL);
widget_manager->hideWgt(WTOK_CONNECTED);
widget_manager->deactivateWgt(WTOK_CONNECTED);
w=widget_manager->addTextButtonWgt( WTOK_QUIT, 60, 7, _("Press <ESC> to go back") );
w->setPosition(WGT_DIR_CENTER, 0, WGT_DIR_FROM_BOTTOM, 0);
w=widget_manager->addTextWgt(WTOK_MESSAGE, 30, 7, "");
w->setPosition(WGT_DIR_CENTER, 0, WGT_DIR_CENTER, 0);
widget_manager->hideWgt(WTOK_MESSAGE);
// This can happen either when going back here, or when a command line
// option was specified causing the connection to already have happened
if(network_manager->getMode()==NetworkManager::NW_SERVER)
switchToWaitForConnectionMode();
widget_manager->layout(WGT_AREA_ALL);
m_state=NGS_NONE;
} // NetworkGUI
//-----------------------------------------------------------------------------
@ -96,20 +106,27 @@ void NetworkGUI::select()
case WTOK_SERVER_ADDRESS:
// Switch to typing in the address of the server
widget_manager->setWgtText(WTOK_SERVER_ADDRESS, (m_server_address + "<").c_str());
inputDriver->setMode(LOWLEVEL);
break;
case WTOK_CONNECT:
network_manager->setMode(NetworkManager::NW_CLIENT);
if(!network_manager->initialiseConnections())
{
fprintf(stderr, "Problems initialising network connections,\n"
"Running in non-network mode.\n");
}
// If we could connect here, no message could be displayed since
// glflush isn't called. So we only set a message for the network
// manager to display, and set the state so that the actual
// connection is done later when updating.
m_state=NGS_CONNECT_DISPLAY;
widget_manager->setWgtText(WTOK_MESSAGE, _("Waiting for server"));
widget_manager->resizeWgtToText(WTOK_MESSAGE);
widget_manager->showWgt(WTOK_MESSAGE);
widget_manager->hideWgt(WTOK_CONNECT, WTOK_SERVER_ADDRESS);
break;
case WTOK_SERVER:
network_manager->setMode(NetworkManager::NW_SERVER);
network_manager->setState(NetworkManager::NS_ACCEPT_CONNECTIONS);
widget_manager->hideWgt(WTOK_MESSAGE);
widget_manager->resizeWgtToText(WTOK_MESSAGE);
widget_manager->showWgt(WTOK_MESSAGE);
// Initialising the server does not block, so we don't have to
// do this in the update loop (to enable updates of the display).
if(!network_manager->initialiseConnections())
{
fprintf(stderr, "Problems initialising network connections,\n"
@ -118,8 +135,14 @@ void NetworkGUI::select()
switchToWaitForConnectionMode();
break;
case WTOK_QUIT:
// Don't do networking if no clients are connected
if(network_manager->getNumClients()==0)
network_manager->setMode(NetworkManager::NW_NONE);
// Disable accepting of clients
if(network_manager->getMode()==NetworkManager::NW_SERVER)
network_manager->setState(NetworkManager::NS_NONE);
// Leave menu.
menu_manager->popMenu();
menu_manager->popMenu();
break;
}
@ -129,18 +152,42 @@ void NetworkGUI::select()
void NetworkGUI::switchToWaitForConnectionMode()
{
widget_manager->setWgtText(WTOK_CONNECT, _("Connected:"));
widget_manager->hideWgtRect(WTOK_CONNECT);
widget_manager->deactivateWgt(WTOK_CONNECT); //make it non-selectable
widget_manager->setWgtText(WTOK_QUIT, _("OK"));
widget_manager->hideWgt(WTOK_SERVER_ADDRESS);
widget_manager->showWgt(WTOK_CONNECTED);
widget_manager->hideWgt(WTOK_SERVER);
widget_manager->setWgtText(WTOK_TITLE,_("Waiting for clients"));
widget_manager->hideWgt(WTOK_MESSAGE);
m_num_clients = 0;
} // switchToWaitForConnectionMode
//-----------------------------------------------------------------------------
void NetworkGUI::update(float dt)
{
// We need one 'in between' frame (finite state machine goes from
// NGS_CONNECT_DISPLAY to NGS_CONNECT_DOIT) since otherwise the text
// set for the message widget is not displayed (since glFlush isn't
// called before the blocking initialiseConnection call).
if(m_state==NGS_CONNECT_DOIT)
{
network_manager->setMode(NetworkManager::NW_CLIENT);
if(!network_manager->initialiseConnections())
{
widget_manager->setWgtText(WTOK_MESSAGE, _("Can't connect to server"));
widget_manager->resizeWgtToText(WTOK_MESSAGE);
network_manager->disableNetworking();
}
else
{
menu_manager->popMenu();
}
m_state=NGS_NONE;
}
else if(m_state==NGS_CONNECT_DISPLAY)
m_state=NGS_CONNECT_DOIT;
widget_manager->update(0.0f);
if(m_num_clients==network_manager->getNumClients()) return;

View File

@ -28,10 +28,12 @@
class NetworkGUI: public BaseGUI
{
private:
unsigned int m_num_clients;
std::string m_server_address;
unsigned int m_num_clients;
std::string m_server_address;
enum {NGS_NONE, NGS_CONNECT_DISPLAY, NGS_CONNECT_DOIT}
m_state;
static const int SERVER_NAME_MAX;
static const int SERVER_NAME_MAX;
void switchToWaitForConnectionMode();
public:

168
src/network/kart_packet.cpp Normal file
View File

@ -0,0 +1,168 @@
// $Id: network_manager.hpp 2128 2008-06-13 00:53:52Z cosmosninja $
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2008 Joerg Henrichs, Stephen Leak
//
// 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 "kart_packet.hpp"
#include <string>
#include <math.h>
#include <stdexcept>
// need a more elegant way of setting the data_size, esp when strings are being used
// also, looking at how the packets are actually used, we can probably serialise as
// part of the constructor, it seems packets to be sent are always created in a
// single line
KartPacket::KartPacket(int data_size)
{
m_data_size = data_size;
m_pkt = enet_packet_create (NULL, data_size*sizeof(int), ENET_PACKET_FLAG_RELIABLE);
m_pos = 0;
m_data = (int*) m_pkt->data;
}
KartPacket::KartPacket(ENetPacket* pkt)
{
m_pkt = pkt;
m_data = (int*) pkt->data;
m_data_size = pkt->dataLength/sizeof(int);
m_pos = 0;
}
KartPacket::~KartPacket()
{
enet_packet_destroy(m_pkt);
}
bool KartPacket::pushInt(int &data)
{
if (m_pos > m_data_size)
return false;
m_data[m_pos] = htonl(data);
++m_pos;
return true;
}
int KartPacket::pullInt()
{
m_pos++;
return ntohl(m_data[m_pos-1]);
}
bool KartPacket::pushFloat(float &data)
{
int *dcast = (int*) &data;
return pushInt(*dcast);
}
float KartPacket::pullFloat()
{ // ugly...
int i = pullInt();
float *f = (float*) &i;
return *f;
}
bool KartPacket::pushString(std::string &data)
{ // urk, this is a little ugly. want to put the string on a 4-byte boundary
int len = (int)ceil((float)data.length() / 4.0f) * 4; // round length up to next 4-char boundary
if (m_pos+len > m_data_size)
return false; // FIXME: resize the packet so it fits
memcpy (&(m_data[m_pos]), data.c_str(), data.length()+1);
m_pos += len;
return true;
}
std::string KartPacket::pullString()
{
char *str = (char*) &(m_data[m_pos]);
int len = strlen(str)+1;
int len4 = len/sizeof(int) + 1;
m_pos += len4; // I think this is correct ..
return std::string(str);
}
bool KartPacket::send(ENetPeer& peer)
{
this->serialise();
enet_peer_send(&peer, 0, m_pkt);
return true;
}
// ---------------------------------------------------------------------
ClientHostIdPacket::ClientHostIdPacket(int id)
: KartPacket(2)
{ // create a ClientHostIdPacket with id as data
m_type = CLIENT_HOST_ID_PACKET;
m_id = id;
}
ClientHostIdPacket::ClientHostIdPacket(ENetPacket* pkt)
: KartPacket(pkt)
{ // create a ClientHostIdPacket based on a received packet
m_type = static_cast<PacketType> (pullInt()); // (ntohl(m_data[0]));
if (m_type != CLIENT_HOST_ID_PACKET)
// FIXME: do something more elegant here
throw std::runtime_error("Received packet mismatch!");
m_id = pullInt(); // ntohl(m_data[1]);
}
void ClientHostIdPacket::serialise()
{
int type = m_type;
pushInt(type);
pushInt(m_id);
}
// --------------------------------------------------------------------
LocalKartInfoPacket::LocalKartInfoPacket(const std::string& player_name,
const std::string& kart_name)
: KartPacket(128) // a bit arbitrary, just to check it works
{
m_type = LOCAL_KART_INFO_PACKET;
m_player_name = std::string(player_name);
m_kart_name = std::string(kart_name);
}
LocalKartInfoPacket::LocalKartInfoPacket(ENetPacket* pkt)
: KartPacket(pkt)
{ // create a LocalKartInfoPacket based on a received packet
m_type = static_cast<PacketType> (pullInt()); // (ntohl(m_data[0]));
if (m_type != LOCAL_KART_INFO_PACKET)
// FIXME: do something more elegant here
throw std::runtime_error("Received packet mismatch!");
m_player_name = pullString(); // new string((char*) &(m_data[1]));
m_kart_name = pullString(); // new string(&(m_data[1]));
}
//LocalKartInfoPacket::~LocalKartInfoPacket()
//{
// delete m_player_name;
// delete m_kart_name;
// // FIXME: poss mem leak here, need to call ~KartPacket
//}
void LocalKartInfoPacket::serialise()
{
int type = m_type;
pushInt(type);
pushString(m_player_name);
pushString(m_kart_name);
}

102
src/network/kart_packet.hpp Normal file
View File

@ -0,0 +1,102 @@
// $Id: network_manager.hpp 2128 2008-06-13 00:53:52Z cosmosninja $
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2008 Joerg Henrichs, Stephen Leak
//
// 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_KART_PACKET_H
#define HEADER_KART_PACKET_H
#include <string>
#ifdef HAVE_ENET
# include "enet/enet.h"
#else
# error "need enet for this implementation!"
#endif
// sjl: when a message is received, need to work out what kind of message it
// is and therefore what to do with it
class KartPacket
{ // collects and serialises/deserialises kart info to send
public:
KartPacket(int data_size); // create from scratch (to send)
KartPacket(ENetPacket* pkt); // create from (received) ENetacket
~KartPacket();
enum PacketType { LOCAL_KART_INFO_PACKET, CLIENT_HOST_ID_PACKET };
PacketType getType();
bool send(ENetPeer& peer);
protected:
bool pushInt(int &data);
bool pushFloat(float &data);
bool pushString(std::string &data);
int pullInt();
float pullFloat();
std::string pullString();
PacketType m_type;
int *m_data;
//virtual ENetPacket* serialise(); // convert KartPacket into ENetPacket
virtual void serialise() =0; // convert KartPacket into ENetPacket
private:
ENetPacket *m_pkt;
int m_data_size;
int m_pos; // simple stack counter for constructing packet data
};
class ClientHostIdPacket : public KartPacket
{ // during init phase, server sends this to client
public:
ClientHostIdPacket(int id); // create one to send
ClientHostIdPacket(ENetPacket* pkt); // wait for one from server
int getId() { return m_id; };
protected:
void serialise(); // convert KartPacket into ENetPacket
private:
int m_id;
};
class LocalKartInfoPacket : public KartPacket
{ // before a race, each client sends server one of these for each player on that client
public:
LocalKartInfoPacket(const std::string& player_name,
const std::string& kart_name);
LocalKartInfoPacket(ENetPacket* pkt);
// ~LocalKartInfoPacket();
std::string getPlayerName();
std::string getKartName();
protected:
void serialise(); // convert KartPacket into ENetPacket
private:
std::string m_player_name;
std::string m_kart_name;
};
#endif

View File

@ -18,10 +18,18 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network_manager.hpp"
#include "connect_message.hpp"
#include "character_info_message.hpp"
#include "character_selected_message.hpp"
#include "race_info_message.hpp"
#include "race_start_message.hpp"
#include "world_loaded_message.hpp"
#include "stk_config.hpp"
#include "user_config.hpp"
#include "race_manager.hpp"
#include "kart_properties_manager.hpp"
#include "translation.hpp"
#include "gui/font.hpp"
NetworkManager* network_manager = 0;
@ -32,6 +40,7 @@ NetworkManager::NetworkManager()
m_num_clients = 0;
m_host_id = 0;
#ifdef HAVE_ENET
if (enet_initialize () != 0)
{
@ -66,8 +75,6 @@ NetworkManager::~NetworkManager()
bool NetworkManager::initServer()
{
#ifdef HAVE_ENET
fprintf(stderr, "Initialising server, listening on %d\n", user_config->m_server_port);
ENetAddress address;
address.host = ENET_HOST_ANY;
address.port = user_config->m_server_port;
@ -88,7 +95,6 @@ bool NetworkManager::initServer()
m_server = NULL;
m_clients.push_back(NULL); // server has host_id=0, so put a dummy entry at 0 in client array
fprintf(stderr, "Server initialised, waiting for connections ...\n");
m_client_names.push_back("server");
return true;
#endif
@ -97,7 +103,6 @@ bool NetworkManager::initServer()
// -----------------------------------------------------------------------------
bool NetworkManager::initClient()
{
fprintf(stderr, "Initialising client\n");
#ifdef HAVE_ENET
m_host = enet_host_create (NULL /* create a client host */,
1 /* only allow 1 outgoing connection */,
@ -123,9 +128,9 @@ bool NetworkManager::initClient()
if (peer == NULL)
{
fprintf (stderr,
"No available peers for initiating an ENet connection.\n");
return false;
fprintf(stderr,
"No available peers for initiating an ENet connection.\n");
return false;
}
/* Wait up to 5 seconds for the connection attempt to succeed. */
@ -141,50 +146,34 @@ bool NetworkManager::initClient()
user_config->m_server_address.c_str(), user_config->m_server_port);
return false;
}
fprintf(stderr, "Connection to %s:%d succeeded.\n",
user_config->m_server_address.c_str(), user_config->m_server_port);
m_server = peer;
if (enet_host_service(m_host, &event, 5000) > 0)
if (event.type == ENET_EVENT_TYPE_RECEIVE)
{
m_host_id = ClientHostIdPacket(event.packet).getId();
fprintf(stderr, "got host id %d from server\n", m_host_id);
return true;
}
fprintf(stderr, "didn't receive a host_id!\n");
return false;
return true;
#endif
} // initClient
// ----------------------------------------------------------------------------
/** Called in case of an error, to switch back to non-networking mode.
*/
void NetworkManager::disableNetworking()
{
m_mode=NW_NONE;
// FIXME: what enet data structures do we have to free/reset???
} // disableNetworking
// ----------------------------------------------------------------------------
void NetworkManager::handleNewConnection(ENetEvent *event)
{
ClientHostIdPacket *msg;
int new_host_id;
// Only accept while waiting for connections
if(m_state!=NS_ACCEPT_CONNECTIONS) return;
if(m_state!=NS_ACCEPT_CONNECTIONS)
{
// We don't accept connections atm
return;
}
m_num_clients++;
fprintf (stderr, "A new client connected from %x:%u. Connected: %d.\n",
event->peer -> address.host,
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");
// send a hostid back
new_host_id = m_clients.size();
// fprintf (stderr, "allocated client host_id as %d\n", new_host_id);
// The logical connection (from STK point of view) happens when
// the connection message is received. But for now reserve the
// space in the data structures (e.g. in case that two connects
// happen before a connect message is received
m_client_names.push_back("NOT SET YET");
m_clients.push_back(event->peer);
msg = new ClientHostIdPacket(new_host_id);
msg->send(*(m_clients[new_host_id]));
enet_host_flush(m_host);
delete msg;
event->peer->data = (void*)int(m_clients.size()-1); // save hostid in peer data
} // handleNewConnection
@ -196,113 +185,98 @@ void NetworkManager::handleDisconnection(ENetEvent *event)
fprintf(stderr, "Disconnect while in race - close your eyes and hope for the best.\n");
return;
}
fprintf(stderr, "%x:%d disconected.\n", event->peer->address.host,
event->peer->address.port );
fprintf(stderr, "%x:%d disconnected (host id %d).\n", event->peer->address.host,
event->peer->address.port, (int)event->peer->data );
m_num_clients--;
} // handleDisconnection
// ----------------------------------------------------------------------------
void NetworkManager::handleServerMessage(ENetEvent *event)
void NetworkManager::handleMessageAtServer(ENetEvent *event)
{ // handle message at server (from client)
switch(m_state)
{
case NS_ACCEPT_CONNECTIONS:
fprintf(stderr, "Received a receive event while waiting for client - ignored.\n");
return;
{
ConnectMessage m(event->packet);
m_client_names[(int)event->peer->data] = m.getId();
m_num_clients++;
return;
}
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))
{
CharacterSelectedMessage m(event->packet);
int hostid=(int)event->peer->data;
assert(hostid>=1 && hostid<=m_num_clients);
if(m_num_local_players[hostid]==-1) // first package from that host
{
// send 'ok' message to client and all other clients
m_kart_info.push_back(RemoteKartInfo(playerid, name, user, hostid));
m_num_local_players[hostid] = m.getNumPlayers();
m_num_all_players += m.getNumPlayers();
// count how many hosts have sent (at least) one message
m_barrier_count ++;
}
else
RemoteKartInfo ki=m.getKartInfo();
ki.setHostId(hostid);
m_kart_info.push_back(ki);
// See if this was the last message, i.e. we have received at least
// one message from each client, and the size of the kart_info
// array is the same as the number of all players (which does not
// yet include the number of players on the host).
if(m_barrier_count = m_num_clients &&
m_num_all_players==m_kart_info.size())
{
// send 'not avail' to sender
// we can't send the race info yet, since the server might
// not yet have selected all characters!
m_state = NS_ALL_REMOTE_CHARACTERS_DONE;
}
break;
}
case NS_READY_SET_GO_BARRIER:
m_barrier_count++;
if(m_barrier_count==m_num_clients)
{
// broadcast start message
m_state = NS_RACING;
m_barrier_count ++;
if(m_barrier_count==m_num_clients)
{
m_state = NS_RACING;
RaceStartMessage m;
broadcastToClients(m);
}
}
break;
case NS_WAIT_FOR_KART_INFO:
m_barrier_count++;
// handle message, i.e. append to m_kart_info
if(m_barrier_count==m_num_clients)
{
// broadcast start message
m_state = NS_READY_SET_GO_BARRIER;
}
break;
} // switch m_state
} // handleServerMessage
} // handleMessageAtServer
// ----------------------------------------------------------------------------
void NetworkManager::switchToReadySetGoBarrier()
{
if(m_num_clients==0)
{
m_state = NS_RACING;
}
else
{
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::switchToReceiveKartInfo()
{
assert(m_state == NS_CHARACTER_SELECT);
m_state = NS_WAIT_FOR_KART_INFO;
} // switchToReceiveKartInfo
// ----------------------------------------------------------------------------
void NetworkManager::switchToRaceDataSynchronisation()
{
m_state = NS_WAIT_FOR_RACE_DATA;
m_barrier_count = 0;
} // switchToRaceDataSynchronisation
// ----------------------------------------------------------------------------
void NetworkManager::handleClientMessage(ENetEvent *event)
void NetworkManager::handleMessageAtClient(ENetEvent *event)
{ // handle message at client (from server)
switch(m_state)
{
case NS_WAIT_FOR_AVAILABLE_CHARACTERS:
{
CharacterInfoMessage m(event->packet);
// FIXME: handle list of available characters
m_state = NS_CHARACTER_SELECT;
break;
}
case NS_WAIT_FOR_RACE_DATA:
// check if message is really race data
fprintf(stderr, "Client received race data\n");
break;
{
RaceInfoMessage m(event->packet);
// The constructor actually sets the information in the race manager
m_state = NS_LOADING_WORLD;
break;
}
case NS_READY_SET_GO_BARRIER:
{
m_state = NS_RACING;
break;
}
} // switch m_state
} // handleClientMessage
} // handleMessageAtClient
// ----------------------------------------------------------------------------
void NetworkManager::update(float dt)
{
if(m_mode==NW_NONE) return;
ENetEvent event;
int result = enet_host_service (m_host, &event, 1);
int result = enet_host_service (m_host, &event, 0);
if(result==0) return;
if(result<0)
{
@ -314,60 +288,97 @@ void NetworkManager::update(float dt)
case ENET_EVENT_TYPE_CONNECT: handleNewConnection(&event); break;
case ENET_EVENT_TYPE_RECEIVE:
if(m_mode==NW_SERVER)
handleServerMessage(&event);
handleMessageAtServer(&event);
else
handleClientMessage(&event);
handleMessageAtClient(&event);
break;
case ENET_EVENT_TYPE_DISCONNECT: handleDisconnection(&event); break;
case ENET_EVENT_TYPE_NONE: break;
}
} // update
// ----------------------------------------------------------------------------
void NetworkManager::broadcastToClients(Message &m)
{
enet_host_broadcast(m_host, 0, m.getPacket());
enet_host_flush(m_host);
} // broadcastToClients
// ----------------------------------------------------------------------------
/** Send race_manager->getNumPlayers(), the kart and the name of each
player to the server.
*/
void NetworkManager::sendKartsInformationToServer()
void NetworkManager::sendToServer(Message* m)
{
LocalKartInfoPacket *msg;
for(int i=0; i<(int)race_manager->getNumLocalPlayers(); i++)
enet_peer_send(m_server, 0, m->getPacket());
enet_host_flush(m_host);
} // sendToServer
// ----------------------------------------------------------------------------
void NetworkManager::switchToCharacterSelection()
{
// This is called the first time the character selection menu is displayed
assert(m_state == NS_NONE);
if(m_mode==NW_CLIENT)
{
fprintf(stderr, "Sending name '%s', ",user_config->m_player[i].getName().c_str());
fprintf(stderr, "kart name '%s'\n", race_manager->getLocalKartInfo(i).getKartName().c_str());
msg = new LocalKartInfoPacket(user_config->m_player[i].getName(), /* player name */
race_manager->getLocalKartInfo(i).getKartName()); /* kart name */
msg->send(*m_server);
enet_host_flush(m_host);
delete msg;
// Change state to wait for list of characters from server
m_state = NS_WAIT_FOR_AVAILABLE_CHARACTERS;
}
else
{ // server: create message with all valid characters
// ================================================
CharacterInfoMessage m;
broadcastToClients(m);
} // for i<getNumLocalPlayers
fprintf(stderr, "Client sending kart information to server\n");
// Prepare the data structures to receive and
// store information from all clients.
m_num_local_players.clear();
// Server (hostid 0) is not included in the num_clients count. So to
// be able to use the hostid as index, we have to allocate one
// additional element.
m_num_local_players.resize(m_num_clients+1, -1);
m_kart_info.clear();
m_num_all_players = 0;
// use barrier count to see if we had at least one message from each host
m_barrier_count = 0;
m_state = NS_CHARACTER_SELECT;
}
} // switchTocharacterSelection
// ----------------------------------------------------------------------------
void NetworkManager::sendCharacterSelected(int player_id)
{
CharacterSelectedMessage m(player_id);
sendToServer(&m);
} // sendCharacterSelected
// ----------------------------------------------------------------------------
void NetworkManager::waitForRaceInformation()
{
m_state = NS_WAIT_FOR_RACE_DATA;
} // sendKartsInformationToServer
} // waitForRaceInformation
// ----------------------------------------------------------------------------
void NetworkManager::worldLoaded()
{
if(m_mode==NW_CLIENT)
{
WorldLoadedMessage m;
sendToServer(&m);
}
else if(m_mode==NW_SERVER)
{
assert(m_state=NS_READY_SET_GO_BARRIER);
RaceStartMessage m;
broadcastToClients(m);
}
} // worldLoaded
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
/** Receive and store the information from sendKartsInformation()
*/
void NetworkManager::setupPlayerKartInfo()
{
LocalKartInfoPacket *msg;
m_kart_info.clear();
// FIXME: debugging
//m_kart_info.push_back(RemoteKartInfo(0, "tuxkart","xx", 1));
//m_kart_info.push_back(RemoteKartInfo(1, "yetikart", "yy", 1));
// FIXME: put this into state machine
//if (enet_host_service(m_host, &event, 15000) > 0)
// if (event.type == ENET_EVENT_TYPE_RECEIVE)
// {
// msg = new LocalKartDetails(event.packet)
// m_kart_info.push_back(RemoteKartInfo(0, "tuxkart","xx", 1));
// m_kart_info.push_back(RemoteKartInfo(1, "yetikart", "yy", 1));
// }
//fprintf(stderr, "didn't receive kart info!\n");
// Get the local kart info
for(unsigned int i=0; i<race_manager->getNumLocalPlayers(); i++)
m_kart_info.push_back(race_manager->getLocalKartInfo(i));
@ -379,8 +390,6 @@ void NetworkManager::setupPlayerKartInfo()
for(unsigned int i=0; i<m_kart_info.size(); i++)
m_kart_info[i].setGlobalPlayerId(i);
// FIXME: distribute m_kart_info to all clients
// Set the player kart information
race_manager->setNumPlayers(m_kart_info.size());
for(unsigned int i=0; i<m_kart_info.size(); i++)
@ -394,23 +403,18 @@ void NetworkManager::setupPlayerKartInfo()
*/
void NetworkManager::sendRaceInformationToClients()
{
fprintf(stderr, "server sending race_manager information to all clients\n");
for(unsigned i=0; i<race_manager->getNumLocalPlayers(); i++)
{
const RemoteKartInfo& ki=race_manager->getLocalKartInfo(i);
fprintf(stderr, "Sending kart '%s' playerid %d host %d\n",
ki.getKartName().c_str(), ki.getLocalPlayerId(), ki.getHostId());
} // for i
// Go
m_state=NS_READY_SET_GO_BARRIER;
setupPlayerKartInfo();
RaceInfoMessage m(m_kart_info);
broadcastToClients(m);
m_state = NS_READY_SET_GO_BARRIER;
m_barrier_count = 0;
if(m_num_clients==0) m_state = NS_RACING;
} // sendRaceInformationToClients
// ----------------------------------------------------------------------------
/** Receives and sets the race_manager information.
*/
void NetworkManager::waitForRaceInformation()
void NetworkManager::sendConnectMessage()
{
fprintf(stderr, "Client waiting for race information\n");
} // waitForRaceInformation
ConnectMessage msg;
sendToServer(&msg);
} // sendConnectMessage
// ----------------------------------------------------------------------------

View File

@ -30,6 +30,8 @@
# include "enet/enet.h"
#endif
class Message;
class NetworkManager
{
public:
@ -37,13 +39,15 @@ public:
enum NetworkMode {NW_SERVER, NW_CLIENT, NW_NONE};
// States for the finite state machine. First for server:
enum NetworkState {NS_ACCEPT_CONNECTIONS, NS_WAIT_FOR_KART_INFO,
// Then client only states:
NS_CHARACTER_CONFIRMED,
NS_CHARACTER_REJECTED,
NS_WAIT_FOR_RACE_DATA,
// Shared states
NS_CHARACTER_SELECT, NS_READY_SET_GO_BARRIER, NS_RACING};
enum NetworkState {NS_NONE,
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
NS_WAIT_FOR_RACE_DATA, // client: wait for race info
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};
private:
NetworkMode m_mode;
@ -52,7 +56,10 @@ private:
std::vector<RemoteKartInfo> m_kart_info;
int m_host_id;
std::vector<std::string> m_client_names;
std::vector<int> m_num_local_players;
int m_num_all_players;
int m_barrier_count;
#ifdef HAVE_ENET
ENetHost *m_host; // me
ENetPeer *m_server; // (clients only)
@ -62,11 +69,14 @@ private:
bool initServer();
bool initClient();
void handleNewConnection(ENetEvent *event);
void handleServerMessage(ENetEvent *event);
void handleClientMessage(ENetEvent *event);
void handleMessageAtServer(ENetEvent *event);
void handleMessageAtClient(ENetEvent *event);
void handleDisconnection(ENetEvent *event);
void testSetCharacter (ENetEvent *event);
unsigned int getHostId(ENetPeer *p) const {return (int)p->data; }
void sendToServer(Message *m);
void broadcastToClients(Message &m);
void sendToClient(int id, const Message *m);
public:
NetworkManager();
~NetworkManager();
@ -82,16 +92,20 @@ public:
const std::string& user="", int hostid=-1);
bool initialiseConnections();
void update(float dt);
void sendKartsInformationToServer();
void disableNetworking();
void sendConnectMessage(); // client send initial info to server
void switchToCharacterSelection();
void sendCharacterSelected(int player_id);
void waitForRaceInformation();
void worldLoaded();
// which one is actually necessary:
void setupPlayerKartInfo();
void sendRaceInformationToClients();
void waitForRaceInformation();
void switchToCharacterSelection();
void switchToReceiveKartInfo();
void switchToRaceDataSynchronisation();
void switchToReadySetGoBarrier();
};
extern NetworkManager *network_manager;

View File

@ -128,8 +128,9 @@ class WidgetManager
int m_default_show_track;
int m_default_track_num;
public:
int findId(const int TOKEN) const;
private:
int calcLineWidth( const int POS );
int calcLineHeight( const int POS );
@ -300,7 +301,9 @@ public:
void showWgtRect(const int TOKEN);
// Completely hide and show a widget
void hideWgt(const int t) {hideWgtRect(t); hideWgtText(t);}
void hideWgt(int t0, int t2) {for(int i=t0; i<=t2; hideWgt(i++));}
void showWgt(const int t) {showWgtRect(t); showWgtText(t);}
void showWgt(int t0, int t2) {for(int i=t0; i<=t2; showWgt(i++));}
void hideWgtRect(const int TOKEN);
void setWgtBorderColor(const int TOKEN, const GLfloat* const COLOR);