Merge remote-tracking branch 'origin/kobato' into game_protocol

This commit is contained in:
hiker 2018-03-31 01:12:22 +11:00
commit 1b72d88593
133 changed files with 3685 additions and 3740 deletions

View File

@ -32,6 +32,8 @@ mass.png by Auria, released under CC-0
power.png by Auria, based on https://openclipart.org/detail/193925/check-engine and https://openclipart.org/detail/144799/power-icon, released under CC-0
crown.png by glitch, from https://openclipart.org/detail/210257/misc-game-crown, released under public domain
====
Glass Skin by Auria, under CC-BY-SA 3+

BIN
data/gui/crown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<div x="1%" y="1%" width="98%" height="99%" layout="vertical-row" >
<header width="80%"
I18N="In the kart selection (player setup) screen"
text="Choose a Kart"
align="center" text_align="center" />
<placeholder id="playerskarts" width="100%" align="center" proportion="4">
<!-- Contents is added programatically -->
</placeholder>
<spacer height="15" width="25"/>
<box proportion="2" width="100%" layout="vertical-row" padding="2">
<ribbon_grid id="karts" proportion="1" square_items="true" width="100%" align="center"
child_width="90" child_height="90" max_rows="3"/>
</box>
<!-- Groups will be added dynamically at runtime -->
<tabs width="98%" x="1%" height="5%" id="kartgroups">
</tabs>
<spacer width="100%" height="2%"/>
</div>
<icon-button id="back" x="0" y="0" height="8%" icon="gui/back.png"/>
</stkgui>

View File

@ -2,69 +2,70 @@
<stkgui>
<div x="0" y="0" width="100%" height="100%" layout="vertical-row" >
<header id="title" text_align="center" width="80%" align="center" I18N="In the server creation screen" text="Server Creation"/>
<spacer height="15" width="10"/>
<box proportion="4" width="90%" layout="vertical-row" align="center">
<div width="90%" align="center" layout="vertical-row" y="2%" height="96%">
<div width="100%" align="center" layout="vertical-row" height="fit" >
<div width="100%" height="fit" layout="horizontal-row" >
<label proportion="1" text_align="left" I18N="In the server creation screen" text="Name of the server"/>
<textbox proportion="1" id="name" I18N="In the server creation screen"/>
</div>
<box proportion="1" width="90%" layout="vertical-row" align="center">
<spacer height="20" width="20"/>
<div width="100%" height="fit" layout="horizontal-row" >
<label proportion="1" text_align="left" I18N="In the server creation screen" text="Max. number of players"/>
<gauge id="max_players" proportion="1" min_value="2"/>
</div>
<spacer height="20" width="20"/>
<div width="100%" height="fit" layout="horizontal-row" >
<label proportion="1" text_align="left" I18N="In the server creation screen" text="Password (optional)"/>
<textbox proportion="1" id="password" I18N="In the server creation screen"/>
</div>
<spacer height="20" width="20"/>
<label width="100%" height="fit" text_align="left" I18N="In the server creation screen" text="Difficulty"/>
<!--<gauge id="difficulty" proportion="1" min_value="1" max_value="4"/>-->
<ribbon id="difficulty" height="135" width="90%" align="center">
<icon-button id="novice" width="128" height="128" icon="gui/difficulty_easy.png"
I18N="Difficulty" text="Novice"/>
<icon-button id="intermediate" width="128" height="128" icon="gui/difficulty_medium.png"
I18N="Difficulty" text="Intermediate"/>
<icon-button id="expert" width="128" height="128" icon="gui/difficulty_hard.png"
I18N="Difficulty" text="Expert"/>
<icon-button id="best" width="128" height="128" icon="gui/difficulty_best.png"
I18N="Difficulty" text="SuperTux"/>
</ribbon>
<spacer height="20" width="20"/>
<label width="100%" height="fit" text_align="left" I18N="In the server creation screen" text="Game mode"/>
<ribbon id="gamemode" height="135" width="50%" align="center">
<icon-button id="normal" width="128" height="128" icon="gui/mode_normal.png"
I18N="Multiplayer game mode" text="Normal Race"/>
<icon-button id="timetrial" width="128" height="128" icon="gui/mode_tt.png"
I18N="Multiplayer game mode" text="Time Trial"/>
</ribbon>
<!--
<scrollable_toolbar id="gamemode" height="135" width="90%" label_location="bottom" align="center"
child_width="135" child_height="135" />
-->
</div>
<label id="info" proportion="1" width="100%" align="center" text_align="center" word_wrap="true" text=""/>
<buttonbar id="options" x="0" y="0" width="25%" height="12%" align="center">
<icon-button id="create" width="64" height="64" icon="gui/green_check.png"
I18N="In the server creation screen" text="Create" label_location="bottom"/>
<icon-button id="cancel" width="64" height="64" icon="gui/main_quit.png"
I18N="In the server creation screen" text="Cancel" label_location="bottom"/>
</buttonbar>
<div width="100%" height="fit" layout="horizontal-row" >
<label proportion="1" text_align="left" I18N="In the server creation screen" text="Name of the server"/>
<textbox proportion="1" id="name" I18N="In the server creation screen"/>
</div>
<spacer height="10" width="20"/>
<div width="100%" height="fit" layout="horizontal-row" >
<label proportion="1" text_align="left" I18N="In the server creation screen" text="Max. number of players"/>
<gauge id="max_players" proportion="1" min_value="2"/>
</div>
<spacer height="10" width="20"/>
<div width="100%" height="fit" layout="horizontal-row" >
<label proportion="1" text_align="left" I18N="In the server creation screen" text="Password (optional)"/>
<textbox proportion="1" id="password" I18N="In the server creation screen"/>
</div>
<spacer height="5" width="20"/>
<label width="100%" height="fit" text_align="left" I18N="In the server creation screen" text="Difficulty"/>
<ribbon id="difficulty" height="15%" width="90%" align="center">
<icon-button id="novice" width="128" height="128" icon="gui/difficulty_easy.png"
I18N="Difficulty" text="Novice"/>
<icon-button id="intermediate" width="128" height="128" icon="gui/difficulty_medium.png"
I18N="Difficulty" text="Intermediate"/>
<icon-button id="expert" width="128" height="128" icon="gui/difficulty_hard.png"
I18N="Difficulty" text="Expert"/>
<icon-button id="best" width="128" height="128" icon="gui/difficulty_best.png"
I18N="Difficulty" text="SuperTux"/>
</ribbon>
<label width="100%" height="fit" text_align="left" I18N="In the server creation screen" text="Game mode"/>
<ribbon id="gamemode" height="15%" width="90%" align="center">
<icon-button id="normal" width="128" height="128" icon="gui/mode_normal.png"
I18N="Multiplayer game mode" text="Normal Race"/>
<icon-button id="timetrial" width="128" height="128" icon="gui/mode_tt.png"
I18N="Multiplayer game mode" text="Time Trial"/>
<icon-button id="3strikes" width="128" height="128" icon="gui/mode_3strikes.png"
I18N="Multiplayer game mode" text="3 Strikes Battle"/>
<icon-button id="soccer" width="128" height="128" icon="gui/mode_soccer.png"
I18N="Multiplayer game mode" text="Soccer"/>
</ribbon>
<spacer height="10" width="20"/>
<div width="100%" height="fit" layout="horizontal-row" >
<label id="more-options" proportion="1" text_align="left"/>
<spinner id="more-options-spinner" proportion="1" wrap_around="true"/>
</div>
<label id="info" proportion="1" width="100%" align="center" text_align="center" word_wrap="true" text=""/>
<buttonbar id="options" x="0" y="0" width="25%" height="12%" align="center">
<icon-button id="create" width="64" height="64" icon="gui/green_check.png"
I18N="In the server creation screen" text="Create" label_location="bottom"/>
<icon-button id="cancel" width="64" height="64" icon="gui/main_quit.png"
I18N="In the server creation screen" text="Cancel" label_location="bottom"/>
</buttonbar>
<spacer width="10" height="20"/>
</box>
<spacer height="15" width="10"/>
<spacer width="10" height="10"/>
</div>
</stkgui>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<div x="0" y="0" width="100%" height="100%" layout="vertical-row" >
<header text_align="center" width="80%" align="center" I18N="In networking lobby" text="Lobby"/>
<header id="lobby-text" text_align="center" width="80%" align="center" I18N="In networking lobby" text="Lobby"/>
<spacer height="15" width="10"/>
<div proportion="4" x="2%" width="96%" layout="vertical-row">
<div width="100%" proportion="2" layout="horizontal-row">
@ -20,7 +20,7 @@
<box proportion="2" height="100%" layout="vertical-row">
<textbox id="chat" width="100%" height="30%"/>
<spacer height="20"/>
<button id="send_text" height="30%" width="fit" I18N="In the network lobby" text="Send text" />
<button id="send" height="30%" width="fit" I18N="In the network lobby" text="Send" />
</box>
<spacer width="40"/>
<buttonbar id="actions" proportion="1" width="75%" height="75%">

View File

@ -4,9 +4,20 @@
<header text_align="center" width="80%" align="center" text="Online"/>
<spacer height="15" width="10"/>
<button id="user-id" width="20%" height="fit" align="center"/>
<spacer height="20"/>
<box width="50%" height="10%" layout="horizontal-row" align="center" valign="center">
<spacer proportion="1"/>
<label I18N="In the networking menu" align="center"
text="Enable splitscreen or player handicaps" text_align="right"/>
<spacer width="25"/>
<checkbox id="enable-splitscreen" align="center" />
<spacer proportion="1"/>
</box>
<spacer height="15" width="10"/>
<icon id="logo" align="center" proportion="4" width="100%" icon="gui/logo.png"/>
<spacer height="15" width="10"/>
<buttonbar id="menu_toprow" proportion="3" width="90%" align="center">

View File

@ -26,6 +26,10 @@
<label proportion="1" text_align="left" I18N="In the networking lobby" text="Game mode:"/>
<label proportion="2" text_align="left" id="server_game_mode" text=""/>
</div>
<div width="100%" height="fit" layout="horizontal-row" >
<label id="label_password" text_align="left" proportion="1" text="Password"/>
<textbox id="password" proportion="2" height="fit"/>
</div>
</div>
<spacer height="20" width="50"/>

View File

@ -11,5 +11,13 @@
<box proportion="1" width="98%" align="center" layout="vertical-row" padding="6">
<list id="server_list" x="0" y="0" width="100%" height="100%"/>
</box>
<div width="99%" align="center" layout="vertical-row" height="fit">
<div width="100%" height="fit" layout="horizontal-row" >
<checkbox width="fit" id="private_server" text_align="left"/>
<spacer width="10"/>
<label proportion="1" height="100%" text_align="left"
I18N="In the server selection screen" text="Show only private server(s)"/>
</div>
</div>
</div>
</stkgui>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<div x="2%" y="5%" width="96%" height="85%" layout="vertical-row" >
<header id="title" width="96%" height="fit" text_align="center" word_wrap="true"
I18N="Splitscreen player in network" text="Add player"/>
<spacer height="20" width="50"/>
<div width="80%" align="center" layout="vertical-row" height="fit" >
<div width="100%" height="fit" layout="horizontal-row" >
<label id="name-text" proportion="1" text_align="left" I18N="Splitscreen player in network" text="Name"/>
<spinner id="name-spinner" width="50%" align="center" wrap_around="true" />
</div>
</div>
<spacer height="20" width="50"/>
<div id="handicap-row" width="80%" align="center" layout="vertical-row" height="fit" >
<div width="100%" height="fit" layout="horizontal-row" >
<label id="handicap-text" proportion="1" text_align="left" I18N="Splitscreen player in network" text="Handicap"/>
<checkbox id="handicap" align="center" />
</div>
</div>
<spacer height="20" width="50"/>
<div width="80%" align="center" layout="vertical-row" height="fit" >
<div width="100%" height="fit" layout="horizontal-row" >
<label id="message-label" proportion="1" text_align="left" I18N="Splitscreen player in network"
text="Press the 'All players ready' button after the player list is ready."/>
</div>
</div>
<spacer height="20" width="50"/>
<buttonbar id="options" width="90%" height="20%" align="center">
<icon-button id="add" width="64" height="64" icon="gui/blue_plus.png"
I18N="Splitscreen player in network" text="Add player" label_location="bottom"/>
<icon-button id="connect" width="64" height="64" icon="gui/green_check.png"
I18N="Splitscreen player in network" text="All players ready" label_location="bottom"/>
<icon-button id="cancel" width="64" height="64" icon="gui/main_quit.png"
I18N="Splitscreen player in network" text="Cancel" label_location="bottom"/>
<icon-button id="reset" width="64" height="64" icon="gui/remove.png"
I18N="Splitscreen player in network" text="Clear added player" label_location="bottom"/>
</buttonbar>
</div>
</stkgui>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<stkgui>
<div x="2%" y="5%" width="96%" height="90%" layout="vertical-row" >
<header id="title" width="96%" height="fit" text_align="center" I18N="Networking screen" text="Waiting for the others..."/>
<spacer height="40" width="50"/>
<label proportion="1" width="100%" text_align="left" id="lblDetails"/>
</div>
</stkgui>

View File

@ -80,6 +80,16 @@
</div>
</div>
<spacer height="4" width="10" />
<div layout="horizontal-row" width="100%" height="fit">
<div proportion="1" height="fit" layout="horizontal-row" >
<checkbox id="enable-lobby-chat"/>
<spacer width="20" height="100%" />
<label height="100%" id="label-lobby-chat" I18N="In the ui settings" text="Enable chatting in networking lobby"/>
</div>
</div>
<spacer height="18" width="4"/>
<!-- ************ LANGUAGE CHOICE ************ -->

View File

@ -2,7 +2,7 @@
<stkgui>
<icon-button id="back" x="0" y="0" height="8%" icon="gui/back.png"/>
<div x="1%" y="1%" width="98%" height="96%" layout="vertical-row" >
<div id="all-track" x="1%" y="1%" width="60%" height="96%" layout="vertical-row" >
<header width="80%" I18N="In the track selection screen" text="All Tracks"
align="center" text_align="center" />
@ -17,5 +17,32 @@
<tabs width="100%" height="5%" id="trackgroups"> </tabs>
<spacer width="100%" height="2%" />
<box id="rect-box" width="100%" height="20%" padding="15" layout="vertical-row">
<div width="100%" height="fit" layout="horizontal-row" >
<label id="lap-text" proportion="1" I18N="In the track screen" text_align="right"/>
<spacer width="40"/>
<div proportion="1" height="fit" layout="horizontal-row">
<spinner id="lap-spinner" width="50%" min_value="1" max_value="20" align="center"
wrap_around="true" />
</div>
</div>
<spacer height="10"/>
<div width="100%" height="fit" layout="horizontal-row" >
<label id="reverse-text" proportion="1" I18N="In the track screen" text_align="right"/>
<spacer width="40"/>
<div proportion="1" height="fit" layout="horizontal-row">
<div width="50%" height="fit" text-align="center" layout="vertical-row" >
<checkbox id="reverse" align="center"/>
</div>
</div>
</div>
</box>
</div>
<div id="vote" x="63%" y="1%" width="37%" height="96%" layout="vertical-row">
<div width="95%" proportion="2" layout="horizontal-row">
<box proportion="2" height="100%" layout="vertical-row">
<label id="vote-text" word_wrap="true" id="text" proportion="3" width="100%" height="100%" text_valign="top"/>
</box>
</div>
</div>
</stkgui>

View File

@ -1,5 +1,5 @@
# Modify this file to change the last-modified date when you add/remove a file.
# This will then trigger a new cmake run automatically.
# This will then trigger a new cmake run automatically.
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")

View File

@ -181,146 +181,6 @@ void GroupUserConfigParam::addChild(UserConfigParam* child)
m_attributes.push_back(child);
} // addChild
// ============================================================================
template<typename T, typename U>
ListUserConfigParam<T, U>::ListUserConfigParam(const char* param_name,
const char* comment)
{
m_param_name = param_name;
all_params.push_back(this);
if(comment != NULL) m_comment = comment;
} // ListUserConfigParam
// ============================================================================
template<typename T, typename U>
ListUserConfigParam<T,U>::ListUserConfigParam(const char* param_name,
const char* comment,
int nb_elements,
...)
{
m_param_name = param_name;
all_params.push_back(this);
if(comment != NULL) m_comment = comment;
// add the default list
va_list arguments;
va_start ( arguments, nb_elements );
for ( int i = 0; i < nb_elements; i++ )
m_elements.push_back(T(va_arg ( arguments, U )));
va_end ( arguments ); // Cleans up the list
} // ListUserConfigParam
// ============================================================================
template<typename T, typename U>
ListUserConfigParam<T, U>::ListUserConfigParam(const char* param_name,
GroupUserConfigParam* group,
const char* comment)
{
m_param_name = param_name;
group->addChild(this);
if(comment != NULL) m_comment = comment;
} // ListUserConfigParam
// ============================================================================
template<typename T, typename U>
ListUserConfigParam<T, U>::ListUserConfigParam(const char* param_name,
GroupUserConfigParam* group,
const char* comment,
int nb_elements,
...)
{
m_param_name = param_name;
group->addChild(this);
if(comment != NULL) m_comment = comment;
// add the default list
va_list arguments;
va_start ( arguments, nb_elements );
for ( int i = 0; i < nb_elements; i++ )
m_elements.push_back(va_arg ( arguments, T ));
va_end ( arguments ); // Cleans up the list
} // ListUserConfigParam
// ----------------------------------------------------------------------------
template<typename T, typename U>
void ListUserConfigParam<T, U>::write(std::ofstream& stream) const
{
const int elts_amount = (int)m_elements.size();
// comment
if(m_comment.size() > 0) stream << " <!-- " << m_comment.c_str();
stream << " -->\n <" << m_param_name.c_str() << "\n";
stream << " Size=\"" << elts_amount << "\"\n";
// actual elements
for (int n=0; n<elts_amount; n++)
{
stream << " " << n << "=\"" << m_elements[n].c_str() << "\"\n";
}
stream << " >\n";
stream << " </" << m_param_name.c_str() << ">\n\n";
} // write
// ----------------------------------------------------------------------------
template<typename T, typename U>
void ListUserConfigParam<T, U>::findYourDataInAChildOf(const XMLNode* node)
{
const XMLNode* child = node->getNode( m_param_name );
if (child == NULL)
{
//Log::error("User Config", "Couldn't find parameter group %s", m_param_name.c_str());
return;
}
int attr_count = 0;
child->get( "Size", &attr_count);
for (int n=0; n<attr_count; n++)
{
T elt;
std::string str;
child->get( StringUtils::toString(n), &str);
StringUtils::fromString<T>(str, elt);
// check if the element is already there :
bool there = false;
for (unsigned int i = 0; i < m_elements.size(); i++)
{
if (elt == m_elements[i])
{
there = true;
break;
}
}
if (!there)
{
m_elements.push_back(elt);
}
}
} // findYourDataInAChildOf
// ----------------------------------------------------------------------------
template<typename T, typename U>
void ListUserConfigParam<T, U>::findYourDataInAnAttributeOf(const XMLNode* node)
{
} // findYourDataInAnAttributeOf
// ----------------------------------------------------------------------------
template<typename T, typename U>
void ListUserConfigParam<T,U>::addElement(T element)
{
m_elements.push_back(element);
} // findYourDataInAnAttributeOf
// ----------------------------------------------------------------------------
template<typename T, typename U>
core::stringc ListUserConfigParam<T, U>::toString() const
{
return "";
} // toString
// ----------------------------------------------------------------------------
template<typename T, typename U>
MapUserConfigParam<T, U>::MapUserConfigParam(const char* param_name,
@ -334,26 +194,12 @@ MapUserConfigParam<T, U>::MapUserConfigParam(const char* param_name,
// ----------------------------------------------------------------------------
template<typename T, typename U>
MapUserConfigParam<T, U>::MapUserConfigParam(const char* param_name,
const char* comment,
int nb_elements,
...)
const char* comment, std::map<T, U> default_value)
{
m_param_name = param_name;
all_params.push_back(this);
if (comment != NULL) m_comment = comment;
// add the default list
va_list arguments;
va_start(arguments, nb_elements);
struct pair_type { T key; U value; };
for (int i = 0; i < nb_elements; i++)
{
pair_type key_value_pair = va_arg(arguments, pair_type);
m_elements.insert(std::pair<T, U>(key_value_pair.key, key_value_pair.value));
}
va_end(arguments); // Cleans up the list
m_elements = default_value;
} // MapUserConfigParam
// ----------------------------------------------------------------------------
@ -370,27 +216,14 @@ MapUserConfigParam<T, U>::MapUserConfigParam(const char* param_name,
// ----------------------------------------------------------------------------
template<typename T, typename U>
MapUserConfigParam<T, U>::MapUserConfigParam(const char* param_name,
GroupUserConfigParam* group,
const char* comment,
int nb_elements,
...)
GroupUserConfigParam* group, const char* comment,
std::map<T, U> default_value)
{
m_param_name = param_name;
group->addChild(this);
if (comment != NULL) m_comment = comment;
// add the default list
va_list arguments;
va_start(arguments, nb_elements);
struct pair_type { T key; U value; };
for (int i = 0; i < nb_elements; i++)
{
pair_type key_value_pair = va_arg(arguments, pair_type);
m_elements.insert(std::pair<T, U>(key_value_pair.key, key_value_pair.value));
}
va_end(arguments); // Cleans up the list
m_elements = default_value;
} // MapUserConfigParam
// ----------------------------------------------------------------------------
@ -401,7 +234,7 @@ void MapUserConfigParam<T, U>::write(std::ofstream& stream) const
if (m_comment.size() > 0) stream << " <!-- " << m_comment.c_str();
stream << " -->\n <" << m_param_name.c_str() << "\n";
for (const auto& kv : m_elements)
for (const auto& kv : m_elements)
{
stream << " " << kv.first << "=\"" << kv.second << "\"\n";
}
@ -410,7 +243,6 @@ void MapUserConfigParam<T, U>::write(std::ofstream& stream) const
} // write
// ----------------------------------------------------------------------------
template<typename T, typename U>
void MapUserConfigParam<T, U>::findYourDataInAChildOf(const XMLNode* node)
{

View File

@ -37,6 +37,7 @@
cause an undefined game action now
6: Added stick configurations.
*/
#include <iterator>
#include <string>
#include <map>
#include <vector>
@ -98,44 +99,6 @@ public:
}; // GroupUserConfigParam
// ============================================================================
template<typename T, typename U>
class ListUserConfigParam : public UserConfigParam
{
std::vector<T> m_elements;
public:
ListUserConfigParam(const char* param_name,
const char* comment = NULL);
ListUserConfigParam(const char* param_name,
const char* comment,
int nb_elts,
...);
ListUserConfigParam(const char* param_name,
GroupUserConfigParam* group,
const char* comment = NULL);
ListUserConfigParam(const char* param_name,
GroupUserConfigParam* group,
const char* comment,
int nb_elts,
...);
void write(std::ofstream& stream) const;
void findYourDataInAChildOf(const XMLNode* node);
void findYourDataInAnAttributeOf(const XMLNode* node);
void addElement(T element);
irr::core::stringc toString() const;
operator std::vector<T>() const
{ return m_elements; }
float& operator=(const std::vector<T>& v)
{ m_elements = std::vector<T>(v); return m_elements; }
float& operator=(const ListUserConfigParam& v)
{ m_elements = std::vector<T>(v); return m_elements; }
}; // ListUserConfigParam
typedef ListUserConfigParam<std::string, const char*> StringListUserConfigParam;
template<typename T, typename U>
class MapUserConfigParam : public UserConfigParam
{
@ -146,16 +109,14 @@ public:
const char* comment = NULL);
MapUserConfigParam(const char* param_name,
const char* comment,
int nb_elts,
...);
std::map<T, U> default_value);
MapUserConfigParam(const char* param_name,
GroupUserConfigParam* group,
const char* comment = NULL);
MapUserConfigParam(const char* param_name,
GroupUserConfigParam* group,
const char* comment,
int nb_elts,
...);
std::map<T, U> default_value);
void write(std::ofstream& stream) const;
void findYourDataInAChildOf(const XMLNode* node);
@ -165,10 +126,18 @@ public:
irr::core::stringc toString() const;
operator std::map<T,U>() const
operator std::map<T, U>() const
{
return m_elements;
}
typename std::map<T, U>::iterator begin()
{
return m_elements.begin();
}
typename std::map<T, U>::iterator end()
{
return m_elements.end();
}
std::map<T, U>& operator=(const std::map<T,U>& v)
{
m_elements = std::map<T, U>(v);
@ -183,8 +152,9 @@ public:
{
return m_elements[key];
}
}; // ListUserConfigParam
typedef MapUserConfigParam<int, int> IntToIntUserConfigParam;
}; // MapUserConfigParam
typedef MapUserConfigParam<uint32_t, uint32_t> UIntToUIntUserConfigParam;
typedef MapUserConfigParam<std::string, uint32_t> StringToUIntUserConfigParam;
// ============================================================================
class IntUserConfigParam : public UserConfigParam
{
@ -708,38 +678,25 @@ namespace UserConfigParams
* can store. */
PARAM_PREFIX float m_profiler_buffer_duration PARAM_DEFAULT(20.0f);
// not saved to file
// ---- Networking
PARAM_PREFIX IntUserConfigParam m_server_max_players
PARAM_DEFAULT( IntUserConfigParam(12, "server_max_players",
"Maximum number of players on the server.") );
PARAM_PREFIX StringListUserConfigParam m_stun_servers
PARAM_DEFAULT( StringListUserConfigParam("Stun_servers", "The stun servers"
" that will be used to know the public address.",
10,
"stun.cope.es",
"stun.12connect.com",
"stun.callwithus.com",
"stun.counterpath.net",
"stun.ekiga.net",
"stun.schlund.de",
"stun.stunprotocol.org",
"stun.voip.aebc.com",
"numb.viagenie.ca",
"stun.ivao.aero") );
// ---- Gamemode setup
PARAM_PREFIX IntToIntUserConfigParam m_num_karts_per_gamemode
PARAM_DEFAULT(IntToIntUserConfigParam("num_karts_per_gamemode",
"The Number of karts per gamemode.",
1,
std::make_pair(1100, 4)
PARAM_PREFIX StringToUIntUserConfigParam m_stun_list
PARAM_DEFAULT(StringToUIntUserConfigParam("stun_list",
"The stun servers that will be used to know the public address,"
" LHS: server address, RHS: ping time.",
{
{ "numb.viagenie.ca", 0u },
{ "stun.12connect.com", 0u },
{ "stun.callwithus.com", 0u },
{ "stun.cope.es", 0u },
{ "stun.counterpath.net", 0u },
{ "stun.ekiga.net", 0u },
{ "stun.ivao.aero", 0u },
{ "stun.schlund.de", 0u },
{ "stun.stunprotocol.org", 0u },
{ "stun.voip.aebc.com", 0u }
}
));
// ---- Network
PARAM_PREFIX GroupUserConfigParam m_network_group
PARAM_DEFAULT(GroupUserConfigParam("Network", "Network Settings"));
PARAM_PREFIX BoolUserConfigParam m_log_packets
@ -748,24 +705,43 @@ namespace UserConfigParams
PARAM_PREFIX BoolUserConfigParam m_random_ports
PARAM_DEFAULT(BoolUserConfigParam(true, "randrom-ports",
&m_network_group, "Use random ports for client and server connection"));
PARAM_PREFIX BoolUserConfigParam m_lobby_chat
PARAM_DEFAULT(BoolUserConfigParam(false, "lobby-chat",
&m_network_group, "Enable chatting in networking lobby, if off than "
"no chat message will be displayed from any players."));
PARAM_PREFIX FloatUserConfigParam m_voting_timeout
PARAM_DEFAULT(FloatUserConfigParam(10.0f, "voting-timeout",
&m_network_group, "Timeout in seconds for voting tracks in server."));
PARAM_PREFIX IntUserConfigParam m_server_max_players
PARAM_DEFAULT(IntUserConfigParam(12, "server_max_players",
&m_network_group, "Maximum number of players on the server."));
PARAM_PREFIX StringToUIntUserConfigParam m_server_ban_list
PARAM_DEFAULT(StringToUIntUserConfigParam("server_ban_list",
"LHS: IP in x.x.x.x format, RHS: online id, if 0 than all players "
"from this IP will be banned.",
{ { "0.0.0.0", 0u } }
));
// ---- Gamemode setup
PARAM_PREFIX UIntToUIntUserConfigParam m_num_karts_per_gamemode
PARAM_DEFAULT(UIntToUIntUserConfigParam("num_karts_per_gamemode",
"The Number of karts per gamemode.",
{
{ 0u, 4u },
{ 1002u, 5u },
{ 1100u, 4u },
{ 1101u, 4u },
{ 2000u, 4u },
{ 2001u, 4u }
}
));
// ---- Graphic Quality
PARAM_PREFIX GroupUserConfigParam m_graphics_quality
PARAM_DEFAULT( GroupUserConfigParam("GFX",
"Graphics Quality Settings") );
// On OSX 10.4 and before there may be driver issues with FBOs, so to be
// safe disable them by default
#ifdef __APPLE__
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
#define FBO_DEFAULT false
#else
#define FBO_DEFAULT true
#endif
#else
#define FBO_DEFAULT true
#endif
PARAM_PREFIX IntUserConfigParam m_particles_effects
PARAM_DEFAULT( IntUserConfigParam(2, "particles-effecs",
&m_graphics_quality, "Particles effects: 0 disabled, 1 only important, 2 enabled") );

View File

@ -375,7 +375,10 @@ void EventHandler::processGUIAction(const PlayerAction action,
case PA_RESCUE:
case PA_MENU_CANCEL:
if (pressedDown) GUIEngine::getStateManager()->escapePressed();
if (pressedDown&& !isWithinATextBox())
{
GUIEngine::getStateManager()->escapePressed();
}
break;
case PA_FIRE:
@ -813,7 +816,7 @@ EventPropagation EventHandler::onGUIEvent(const SEvent& event)
const int playerID = input_manager->getPlayerKeyboardID();
if (input_manager->masterPlayerOnly() && playerID != PLAYER_ID_GAME_MASTER) break;
if (!w->isFocusedForPlayer(playerID)) w->setFocusForPlayer(playerID);
if (playerID != -1 && !w->isFocusedForPlayer(playerID)) w->setFocusForPlayer(playerID);
break;
}

View File

@ -48,14 +48,9 @@ using namespace irr;
*/
namespace GUIEngine
{
#define DEFINE_SCREEN_SINGLETON( ClassName ) \
template<> ClassName* GUIEngine::ScreenSingleton< ClassName >::singleton = NULL
/**
* \brief Declares a class to be a singleton.
* Normally, all screens will be singletons.
* Note that you need to use the 'DEFINE_SCREEN_SINGLETON' macro in a .
* cpp file to actually define the instance (as this can't be done in a .h)
* \ingroup guiengine
*/
template<typename SCREEN>
@ -83,6 +78,8 @@ namespace GUIEngine
}
};
template <typename SCREEN> SCREEN*
ScreenSingleton<SCREEN>::singleton = nullptr;
/**
* \brief Represents a single GUI screen.

View File

@ -1675,7 +1675,7 @@ void Skin::renderSections(PtrVector<Widget>* within_vector)
if (widget.m_type == WTYPE_DIV)
{
if (widget.m_show_bounding_box)
if (widget.m_show_bounding_box && widget.isVisible())
{
if (widget.m_is_bounding_box_round)
{

View File

@ -309,7 +309,9 @@ bool Widget::isVisible() const
{
if (m_element != NULL)
{
assert(m_element->isVisible() == m_is_visible);
// repair mismatch
if (m_element->isVisible() != m_is_visible)
m_element->setVisible(m_is_visible);
}
return m_is_visible;
}

View File

@ -659,6 +659,7 @@ namespace GUIEngine
/** Gets called when the widget is active and got clicked. (Only works for button widgets for now.) */
virtual void onClick() { }
virtual irr::core::dimension2di getDimension() const { return irr::core::dimension2di(m_w, m_h); }
};

View File

@ -823,6 +823,9 @@ bool CGUIEditBox::processKey(const SEvent& event)
calculateScrollPos();
if (CursorPos > (s32)Text.size())
CursorPos = Text.size();
#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_)
switch(event.KeyInput.Key)
{

View File

@ -44,6 +44,9 @@ void CheckBoxWidget::add()
m_id = m_element->getID();
m_element->setTabOrder(m_id);
m_element->setTabGroup(false);
if (!m_is_visible)
m_element->setVisible(false);
}
// -----------------------------------------------------------------------------
EventPropagation CheckBoxWidget::transmitEvent(Widget* w,

View File

@ -110,6 +110,9 @@ void LabelWidget::add()
if (m_scroll_speed <= 0)
m_element->setNotClipped(true);
if (!m_is_visible)
m_element->setVisible(false);
} // add
// ----------------------------------------------------------------------------

View File

@ -108,7 +108,9 @@ namespace GUIEngine
// ------------------------------------------------------------------------
/** Called when players are renumbered (changes the player ID) */
void setPlayerID(const int newPlayerID);
// ------------------------------------------------------------------------
PlayerNameSpinner* getPlayerNameSpinner() const
{ return m_player_ident_spinner; }
// ------------------------------------------------------------------------
/** Returns the ID of this player */
int getPlayerID() const;

View File

@ -63,7 +63,17 @@ public:
m_listeners[n].onTextUpdated();
}
}
if (event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.Key == IRR_KEY_RETURN)
{
for (unsigned int n=0; n<m_listeners.size(); n++)
{
if (m_listeners[n].onEnterPressed(Text))
{
Text = L"";
CursorPos = 0;
}
}
}
return out;
}

View File

@ -33,6 +33,7 @@ namespace GUIEngine
public:
virtual ~ITextBoxWidgetListener() {}
virtual void onTextUpdated() = 0;
virtual bool onEnterPressed(const irr::core::stringw& text) { return false; }
};
/** \brief A text field widget.

View File

@ -37,10 +37,12 @@
#include "modes/demo_world.hpp"
#include "modes/profile_world.hpp"
#include "modes/world.hpp"
#include "network/network_config.hpp"
#include "network/rewind_manager.hpp"
#include "physics/physics.hpp"
#include "race/history.hpp"
#include "replay/replay_recorder.hpp"
#include "states_screens/dialogs/splitscreen_player_dialog.hpp"
#include "states_screens/kart_selection.hpp"
#include "states_screens/main_menu_screen.hpp"
#include "states_screens/options_screen_device.hpp"
@ -710,6 +712,31 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID,
// when a device presses fire or rescue
if (m_device_manager->getAssignMode() == DETECT_NEW)
{
if (NetworkConfig::get()->isNetworking() &&
NetworkConfig::get()->isAddingNetworkPlayers())
{
// Ignore release event
if (value == 0)
return;
InputDevice *device = NULL;
if (type == Input::IT_KEYBOARD)
{
//Log::info("InputManager", "New Player Joining with Key %d", button);
device = m_device_manager->getKeyboardFromBtnID(button);
}
else if (type == Input::IT_STICKBUTTON ||
type == Input::IT_STICKMOTION )
{
device = m_device_manager->getGamePadFromIrrID(deviceID);
}
if (device && (action == PA_FIRE || action == PA_MENU_SELECT))
{
if (!GUIEngine::ModalDialog::isADialogActive())
new SplitscreenPlayerDialog(device);
return;
}
}
// Player is unjoining
if ((player != NULL) && (action == PA_RESCUE ||
action == PA_MENU_CANCEL ) )
@ -749,7 +776,7 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID,
if (device != NULL)
{
KartSelectionScreen::getRunningInstance()->joinPlayer(device);
KartSelectionScreen::getRunningInstance()->joinPlayer(device, NULL/*player profile*/);
}
}
return; // we're done here, ignore devices that aren't

View File

@ -1043,10 +1043,11 @@ int handleCmdLine()
if(CommandLine::has("--network-console"))
STKHost::m_enable_console = true;
std::string server_password;
if (CommandLine::has("--server-password", &s))
{
core::stringw pw = StringUtils::xmlDecode(s);
NetworkConfig::get()->setPassword(StringUtils::wideToUtf8(pw));
server_password = s;
NetworkConfig::get()->setPassword(server_password);
}
if (CommandLine::has("--server-id-file", &s))
@ -1082,7 +1083,7 @@ int handleCmdLine()
NetworkConfig::get()->getMaxPlayers(), 0,
race_manager->getDifficulty(),
NetworkConfig::get()->getServerGameMode(race_manager->getMinorMode(),
race_manager->getMajorMode()), ip);
race_manager->getMajorMode()), ip, !server_password.empty());
NetworkingLobby::getInstance()->setJoinedServer(server);
STKHost::create(server);
}

View File

@ -41,10 +41,13 @@
#include "states_screens/state_manager.hpp"
#include "utils/profiler.hpp"
#include <assert.h>
#ifndef WIN32
#include <unistd.h>
#endif
MainLoop* main_loop = 0;
// ----------------------------------------------------------------------------
MainLoop::MainLoop(unsigned parent_pid)
: m_abort(false), m_parent_pid(parent_pid)
{
@ -314,6 +317,10 @@ void MainLoop::run()
if (WaitForSingleObject(parent, 0) != WAIT_TIMEOUT)
m_abort = true;
}
#else
// POSIX equivalent
if (m_parent_pid != 0 && getppid() != (int)m_parent_pid)
m_abort = true;
#endif
m_is_last_substep = false;
PROFILER_PUSH_CPU_MARKER("Main loop", 0xFF, 0x00, 0xF7);
@ -327,7 +334,7 @@ void MainLoop::run()
STKHost::get()->requestedShutdown())
{
SFXManager::get()->quickSound("anvil");
core::stringw msg = _("Connection to server is lost.");
core::stringw msg = _("Server connection timed out.");
if (!STKHost::get()->getErrorMessage().empty())
{
msg = STKHost::get()->getErrorMessage();

View File

@ -341,6 +341,13 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index,
gk = ReplayPlay::get()->getNumGhostKart();
std::shared_ptr<RenderInfo> ri = std::make_shared<RenderInfo>();
if (global_player_id > -1 && race_manager->getKartInfo(global_player_id)
.getDefaultKartColor() > 0.0f)
{
ri->setHue(race_manager->getKartInfo(global_player_id)
.getDefaultKartColor());
}
int position = index+1;
btTransform init_pos = getStartTransform(index - gk);
AbstractKart *new_kart;
@ -357,15 +364,12 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index,
{
case RaceManager::KT_PLAYER:
{
controller = new LocalPlayerController(new_kart,
local_player_id);
const float hue = StateManager::get()->getActivePlayer(local_player_id)
->getConstProfile()->getDefaultKartColor();
if (hue > 0.0f)
controller = new LocalPlayerController(new_kart, local_player_id);
const PlayerProfile* p = StateManager::get()
->getActivePlayer(local_player_id)->getConstProfile();
if (p && p->getDefaultKartColor() > 0.0f)
{
ri->setHue(hue);
ri->setHue(p->getDefaultKartColor());
}
m_num_players ++;

View File

@ -48,7 +48,7 @@ WorldStatus::WorldStatus()
m_play_track_intro_sound = UserConfigParams::m_music;
m_play_ready_set_go_sounds = true;
m_play_racestart_sounds = true;
m_server_is_ready = false;
m_server_is_ready.store(false);
IrrlichtDevice *device = irr_driver->getDevice();
@ -99,7 +99,7 @@ void WorldStatus::reset()
// In case of a networked race the race can only start once
// all protocols are up. This flag is used to wait for
// a confirmation before starting the actual race.
m_server_is_ready = false;
m_server_is_ready.store(false);
} // reset
//-----------------------------------------------------------------------------
@ -262,7 +262,7 @@ void WorldStatus::updateTime(int ticks)
// loaded the world). The server waits for a confirmation from
// each client that they have started (to guarantee that the
// server is running with a local time behind all clients).
if (!m_server_is_ready) return;
if (m_server_is_ready.load() == false) return;
m_phase = READY_PHASE;
auto cl = LobbyProtocol::get<ClientLobby>();
@ -484,7 +484,7 @@ float WorldStatus::adjustDT(float dt)
*/
void WorldStatus::startReadySetGo()
{
m_server_is_ready = true;
m_server_is_ready.store(true);
} // startReadySetGo
//-----------------------------------------------------------------------------

View File

@ -19,6 +19,7 @@
#define HEADER_WORLD_STATUS_HPP
#include "utils/cpp2011.hpp"
#include <atomic>
class SFXBase;
@ -143,7 +144,7 @@ private:
* set go' to make sure all client are actually ready to start the game.
* A server on the other hand will run behind all clients, so it will
* wait for all clients to indicate that they have started the race. */
bool m_server_is_ready;
std::atomic_bool m_server_is_ready;
void startEngines();
@ -216,7 +217,7 @@ public:
/** Get the ticks since start regardless of which way the clock counts */
int getTicksSinceStart() const { return m_count_up_ticks; }
// ------------------------------------------------------------------------
void setReadyToRace() { m_server_is_ready = true; }
void setReadyToRace() { m_server_is_ready.store(true); }
// ------------------------------------------------------------------------
/** Sets a time by which the clock should be adjusted. Used by networking
* if too many rewinds are detected. */

View File

@ -18,7 +18,6 @@
#include "network/event.hpp"
#include "network/stk_host.hpp"
#include "network/stk_peer.hpp"
#include "utils/log.hpp"
#include "utils/time.hpp"
@ -28,9 +27,10 @@
/** \brief Constructor
* \param event : The event that needs to be translated.
*/
Event::Event(ENetEvent* event)
Event::Event(ENetEvent* event, std::shared_ptr<STKPeer> peer)
{
m_arrival_time = (double)StkTime::getTimeSinceEpoch();
m_pdi = PDI_TIMEOUT;
switch (event->type)
{
@ -39,6 +39,7 @@ Event::Event(ENetEvent* event)
break;
case ENET_EVENT_TYPE_DISCONNECT:
m_type = EVENT_TYPE_DISCONNECTED;
m_pdi = (PeerDisconnectInfo)event->data;
break;
case ENET_EVENT_TYPE_RECEIVE:
m_type = EVENT_TYPE_MESSAGE;
@ -61,16 +62,7 @@ Event::Event(ENetEvent* event)
enet_packet_destroy(event->packet);
}
m_peer = STKHost::get()->getPeer(event->peer);
if(m_type == EVENT_TYPE_MESSAGE && m_peer->isClientServerTokenSet() &&
m_data->getToken()!=m_peer->getClientServerToken() )
{
Log::error("Event", "Received event with invalid token!");
Log::error("Event", "HostID %d Token %d message token %d",
m_peer->getHostId(), m_peer->getClientServerToken(),
m_data->getToken());
Log::error("Event", m_data->getLogMessage().c_str());
}
m_peer = peer;
} // Event(ENetEvent)
// ----------------------------------------------------------------------------

View File

@ -30,6 +30,8 @@
#include "enet/enet.h"
#include <memory>
class STKPeer;
/*!
@ -42,6 +44,7 @@ enum EVENT_TYPE
EVENT_TYPE_DISCONNECTED,//!< A peer is disconnected
EVENT_TYPE_MESSAGE //!< A message between server and client protocols
};
enum PeerDisconnectInfo : unsigned int;
/*!
* \class Event
@ -65,22 +68,27 @@ private:
EVENT_TYPE m_type;
/** Pointer to the peer that triggered that event. */
STKPeer* m_peer;
std::shared_ptr<STKPeer> m_peer;
/** Arrivial time of the event, for timeouts. */
double m_arrival_time;
/** For disconnection event, a bit more info is provided. */
PeerDisconnectInfo m_pdi;
public:
Event(ENetEvent* event);
Event(ENetEvent* event, std::shared_ptr<STKPeer> peer);
~Event();
// ------------------------------------------------------------------------
/** Returns the type of this event. */
EVENT_TYPE getType() const { return m_type; }
// ------------------------------------------------------------------------
/** Returns the peer of this event (shared pointer). */
std::shared_ptr<STKPeer> getPeerSP() const { return m_peer; }
// ------------------------------------------------------------------------
/** Returns the peer of this event. */
STKPeer* getPeer() const { return m_peer; }
STKPeer* getPeer() const { return m_peer.get(); }
// ------------------------------------------------------------------------
/** \brief Get a const reference to the received data.
* This is empty for events like connection or disconnections.
@ -99,7 +107,8 @@ public:
// ------------------------------------------------------------------------
/** Returns the arrival time of this event. */
double getArrivalTime() const { return m_arrival_time; }
// ------------------------------------------------------------------------
PeerDisconnectInfo getPeerDisconnectInfo() const { return m_pdi; }
// ------------------------------------------------------------------------
}; // class Event

View File

@ -18,6 +18,7 @@
#include "network/game_setup.hpp"
#include "config/player_manager.hpp"
#include "karts/abstract_kart.hpp"
#include "modes/world.hpp"
#include "network/network_player_profile.hpp"
@ -26,202 +27,43 @@
#include "utils/log.hpp"
//-----------------------------------------------------------------------------
GameSetup::GameSetup()
{
m_race_config = new RaceConfig();
m_num_local_players = 0;
m_local_master = 0;
m_laps = 0;
m_reverse = false;
} // GameSetup
//-----------------------------------------------------------------------------
GameSetup::~GameSetup()
{
// remove all players
for (unsigned int i = 0; i < m_players.size(); i++)
{
delete m_players[i];
};
m_players.clear();
delete m_race_config;
} // ~GameSetup
//-----------------------------------------------------------------------------
void GameSetup::addPlayer(NetworkPlayerProfile* profile)
{
m_players.push_back(profile);
Log::info("GameSetup", "New player in the game setup. Player id : %d.",
profile->getGlobalPlayerId());
} // addPlayer
//-----------------------------------------------------------------------------
/** Removed a player give his NetworkPlayerProfile.
* \param profile The NetworkPlayerProfile to remove.
* \return True if the player was found and removed, false otherwise.
/** Update and see if any player disconnects.
* \param remove_disconnected_players remove the disconnected players,
* otherwise replace with AI (when racing), so this function must be called
* in main thread.
*/
bool GameSetup::removePlayer(const NetworkPlayerProfile *profile)
void GameSetup::update(bool remove_disconnected_players)
{
for (unsigned int i = 0; i < m_players.size(); i++)
std::unique_lock<std::mutex> lock(m_players_mutex);
if (remove_disconnected_players)
{
if (m_players[i] == profile)
{
delete m_players[i];
m_players.erase(m_players.begin()+i, m_players.begin()+i+1);
Log::verbose("GameSetup",
"Removed a player from the game setup. Remains %u.",
m_players.size());
return true;
}
m_players.erase(std::remove_if(m_players.begin(), m_players.end(), []
(const std::weak_ptr<NetworkPlayerProfile> npp)->bool
{
return npp.expired();
}), m_players.end());
return;
}
return false;
lock.unlock();
if (!World::getWorld())
return;
} // removePlayer
//-----------------------------------------------------------------------------
/** Sets the player id of the local master.
* \param player_id The id of the player who is the local master.
*/
void GameSetup::setLocalMaster(uint8_t player_id)
void GameSetup::loadWorld()
{
m_local_master = player_id;
} // setLocalMaster
//-----------------------------------------------------------------------------
/** Returns true if the player id is the local game master (used in the
* network game selection.
* \param Local player id to test.
*/
bool GameSetup::isLocalMaster(uint8_t player_id)
{
return m_local_master == player_id;
} // isLocalMaster
//-----------------------------------------------------------------------------
/** Sets the kart the specified player uses.
* \param player_id ID of this player (in this race).
* \param kart_name Name of the kart the player picked.
*/
void GameSetup::setPlayerKart(uint8_t player_id, const std::string &kart_name)
{
bool found = false;
for (unsigned int i = 0; i < m_players.size(); i++)
{
if (m_players[i]->getGlobalPlayerId() == player_id)
{
m_players[i]->setKartName(kart_name);
Log::info("GameSetup::setPlayerKart", "Player %d took kart %s",
player_id, kart_name.c_str());
found = true;
}
}
if (!found)
{
Log::info("GameSetup::setPlayerKart", "The player %d was unknown.",
player_id);
}
} // setPlayerKart
//-----------------------------------------------------------------------------
void GameSetup::bindKartsToProfiles()
{
World::KartList karts = World::getWorld()->getKarts();
for (unsigned int i = 0; i < m_players.size(); i++)
{
Log::info("GameSetup", "Player %d has id %d and kart %s", i,
m_players[i]->getGlobalPlayerId(),
m_players[i]->getKartName().c_str());
}
for (unsigned int i = 0; i < karts.size(); i++)
{
Log::info("GameSetup", "Kart %d has id %d and kart %s", i,
karts[i]->getWorldKartId(), karts[i]->getIdent().c_str());
}
for (unsigned int j = 0; j < m_players.size(); j++)
{
bool found = false;
for (unsigned int i = 0 ; i < karts.size(); i++)
{
if (karts[i]->getIdent() == m_players[j]->getKartName())
{
m_players[j]->setWorldKartID(karts[i]->getWorldKartId());
found = true;
break;
}
}
if (!found)
{
Log::error("GameSetup", "Error while binding world kart ids to players profiles.");
}
}
} // bindKartsToProfiles
//-----------------------------------------------------------------------------
/** \brief Get a network player profile with the specified player id.
* \param player_id : Player id in this race.
* \return The profile of the player having the specified player id, or
* NULL if no such player exists.
*/
const NetworkPlayerProfile* GameSetup::getProfile(uint8_t player_id)
{
for (unsigned int i = 0; i < m_players.size(); i++)
{
if (m_players[i]->getGlobalPlayerId()== player_id)
{
return m_players[i];
}
}
return NULL;
} // getProfile
//-----------------------------------------------------------------------------
/** \brief Get a network player profile matching a kart name.
* \param kart_name : Name of the kart used by the player.
* \return The profile of the player having the kart kart_name, or NULL
* if no such network profile exists.
*/
const NetworkPlayerProfile* GameSetup::getProfile(const std::string &kart_name)
{
for (unsigned int i = 0; i < m_players.size(); i++)
{
if (m_players[i]->getKartName() == kart_name)
{
return m_players[i];
}
}
return NULL;
} // getProfile(kart_name)
//-----------------------------------------------------------------------------
/** Returns the list of all player profiles from a specified host. Note that
* this function is somewhat expensive (it loops over all network profiles
* to find the ones with the specified host id).
* \param host_id The host id which players should be collected.
* \return List of NetworkPlayerProfile pointers/
*/
std::vector<NetworkPlayerProfile*> GameSetup::getAllPlayersOnHost(uint8_t host_id)
{
std::vector<NetworkPlayerProfile*> result;
for (unsigned int i = 0; i < m_players.size(); i++)
{
if (m_players[i]->getHostId() == host_id)
result.push_back(m_players[i]);
}
return result;
} // getAllPlayersOnHost
//-----------------------------------------------------------------------------
bool GameSetup::isKartAvailable(std::string kart_name)
{
for (unsigned int i = 0; i < m_players.size(); i++)
{
if (m_players[i]->getKartName() == kart_name)
return false;
}
return true;
}
assert(!m_track.empty());
// Disable accidentally unlocking of a challenge
PlayerManager::getCurrentPlayer()->setCurrentChallenge("");
race_manager->setReverseTrack(m_reverse);
race_manager->startSingleRace(m_track, m_laps, false/*from_overworld*/);
} // loadWorld

View File

@ -22,16 +22,15 @@
#ifndef GAME_SETUP_HPP
#define GAME_SETUP_HPP
#include "network/race_config.hpp"
#include "network/remote_kart_info.hpp"
#include <vector>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
namespace Online { class OnlineProfile; }
class NetworkPlayerProfile;
// ============================================================================
/*! \class GameSetup
* \brief Used to store the needed data about the players that join a game.
@ -40,11 +39,10 @@ class NetworkPlayerProfile;
class GameSetup
{
private:
/** Information about all connected players. */
std::vector<NetworkPlayerProfile*> m_players;
mutable std::mutex m_players_mutex;
/** The race configuration. */
RaceConfig* m_race_config;
/** Information about all connected players. */
std::vector<std::weak_ptr<NetworkPlayerProfile> > m_players;
/** Stores the number of local players. */
int m_num_local_players;
@ -52,26 +50,23 @@ private:
/** The player id of the local game master, used in
* kart selection screen. */
uint8_t m_local_master;
std::string m_track;
unsigned m_laps;
bool m_reverse;
public:
GameSetup();
virtual ~GameSetup();
void addPlayer(NetworkPlayerProfile* profile); //!< Add a player.
bool removePlayer(const NetworkPlayerProfile *profile);
void setPlayerKart(uint8_t player_id, const std::string &kart_name);
void bindKartsToProfiles(); //!< Sets the right world_kart_id in profiles
void setLocalMaster(uint8_t player_id);
bool isLocalMaster(uint8_t player_id);
const NetworkPlayerProfile* getProfile(uint8_t id);
const NetworkPlayerProfile* getProfile(const std::string &kart_name);
std::vector<NetworkPlayerProfile*> getAllPlayersOnHost(uint8_t host_id);
/*! \brief Used to know if a kart is available.
* \param kart_name : Name of the kart to check.
* \return True if the kart hasn't been selected yet, false elseway.
*/
bool isKartAvailable(std::string kart_name);
// ------------------------------------------------------------------------
GameSetup();
// ------------------------------------------------------------------------
~GameSetup() {}
// ------------------------------------------------------------------------
void addPlayer(std::shared_ptr<NetworkPlayerProfile> profile)
{ m_players.push_back(profile); }
// ------------------------------------------------------------------------
void update(bool remove_disconnected_players);
// ------------------------------------------------------------------------
/** Sets the number of local players. */
void setNumLocalPlayers(int n) { m_num_local_players = n; }
@ -79,29 +74,47 @@ public:
/** Returns the nunber of local players. */
int getNumLocalPlayers() const { return m_num_local_players; }
// ------------------------------------------------------------------------
/*! \brief Used to know if a kart is playable.
* \param kart_name : Name of the kart to check.
* \return True if the kart is playable (standard kart).
* Currently this is always true as the kart selection screen shows
* only the standard karts.
*/
bool isKartAllowed(std::string kart_name) { return true; }
// ------------------------------------------------------------------------
/** Returns the configuration for this race. */
RaceConfig* getRaceConfig() { return m_race_config; }
// ------------------------------------------------------------------------
/** \brief Get the players that are in the game
/** \brief Get the players that are / were in the game
* \return A vector containing pointers on the players profiles. */
const std::vector<NetworkPlayerProfile*>& getPlayers() const
const std::vector<std::weak_ptr<NetworkPlayerProfile> >& getPlayers() const
{
std::lock_guard<std::mutex> lock(m_players_mutex);
return m_players;
} // getPlayers
// ------------------------------------------------------------------------
/** \brief Get the players that are in the game
* \return A vector containing pointers on the players profiles. */
std::vector<std::shared_ptr<NetworkPlayerProfile> >
getConnectedPlayers() const
{
std::lock_guard<std::mutex> lock(m_players_mutex);
std::vector<std::shared_ptr<NetworkPlayerProfile> > players;
for (auto player_weak : m_players)
{
if (auto player_connected = player_weak.lock())
players.push_back(player_connected);
}
return players;
} // getConnectedPlayers
// ------------------------------------------------------------------------
/** Returns the number of connected players. */
int getPlayerCount() { return (int)m_players.size(); }
unsigned getPlayerCount()
{
std::lock_guard<std::mutex> lock(m_players_mutex);
return (unsigned)m_players.size();
}
// ------------------------------------------------------------------------
/** Returns the id of the local master. */
int getLocalMasterID() const { return m_local_master; }
// ------------------------------------------------------------------------
void setRace(const std::string& track, unsigned laps, bool reverse)
{
m_track = track;
m_laps = laps;
m_reverse = reverse;
}
// ------------------------------------------------------------------------
void loadWorld();
};
#endif // GAME_SETUP_HPP

View File

@ -23,6 +23,7 @@
NetworkConfig *NetworkConfig::m_network_config = NULL;
bool NetworkConfig::m_disable_lan = false;
const uint8_t NetworkConfig::m_server_version = 1;
/** \class NetworkConfig
* This class is the interface between STK and the online code, particularly
@ -42,6 +43,7 @@ NetworkConfig::NetworkConfig()
m_auto_connect = false;
m_is_server = false;
m_is_public_server = false;
m_done_adding_network_players = false;
m_max_players = 4;
m_cur_user_id = 0;
m_cur_user_token = "";

View File

@ -26,12 +26,17 @@
#include "race/race_manager.hpp"
#include "irrString.h"
#include <tuple>
#include <vector>
namespace Online
{
class XMLRequest;
}
class InputDevice;
class PlayerProfile;
class NetworkConfig
{
private:
@ -70,12 +75,14 @@ private:
uint16_t m_client_port;
/** Maximum number of players on the server. */
int m_max_players;
unsigned m_max_players;
/** True if a client should connect to the first server it finds and
* immediately start a race. */
bool m_auto_connect;
bool m_done_adding_network_players;
/** If this is a server, the server name. */
irr::core::stringw m_server_name;
@ -86,6 +93,9 @@ private:
/** Used by client server to determine if the child server is created. */
std::string m_server_id_file;
std::vector<std::tuple<InputDevice*, PlayerProfile*,
/*is_handicap*/bool> > m_network_players;
NetworkConfig();
public:
@ -93,6 +103,9 @@ public:
* WAN code to be used when connection client and server). */
static bool m_disable_lan;
/** Server version, will be advanced if there are protocol changes. */
static const uint8_t m_server_version;
/** Singleton get, which creates this object if necessary. */
static NetworkConfig *get()
{
@ -161,13 +174,54 @@ public:
void setIsWAN() { m_network_type = NETWORK_WAN; }
// ------------------------------------------------------------------------
/** Set that this is not a networked game. */
void unsetNetworking() { m_network_type = NETWORK_NONE; }
void unsetNetworking()
{
m_network_type = NETWORK_NONE;
m_password = "";
}
// ------------------------------------------------------------------------
const std::vector<std::tuple<InputDevice*, PlayerProfile*, bool> >&
getNetworkPlayers() const { return m_network_players; }
// ------------------------------------------------------------------------
bool isAddingNetworkPlayers() const
{ return !m_done_adding_network_players; }
// ------------------------------------------------------------------------
void doneAddingNetworkPlayers() { m_done_adding_network_players = true; }
// ------------------------------------------------------------------------
bool addNetworkPlayer(InputDevice* device, PlayerProfile* profile, bool h)
{
for (auto& p : m_network_players)
{
if (std::get<0>(p) == device)
return false;
if (std::get<1>(p) == profile)
return false;
}
m_network_players.emplace_back(device, profile, h);
return true;
}
// ------------------------------------------------------------------------
bool playerExists(PlayerProfile* profile) const
{
for (auto& p : m_network_players)
{
if (std::get<1>(p) == profile)
return true;
}
return false;
}
// ------------------------------------------------------------------------
void cleanNetworkPlayers()
{
m_network_players.clear();
m_done_adding_network_players = false;
}
// ------------------------------------------------------------------------
/** Sets the maximum number of players for this server. */
void setMaxPlayers(int n) { m_max_players = n; }
void setMaxPlayers(unsigned n) { m_max_players = n; }
// ------------------------------------------------------------------------
/** Returns the maximum number of players for this server. */
int getMaxPlayers() const { return m_max_players; }
unsigned getMaxPlayers() const { return m_max_players; }
// ------------------------------------------------------------------------
/** Returns if this instance is a server. */
bool isServer() const { return m_is_server; }

View File

@ -16,13 +16,12 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "config/user_config.hpp"
#include "network/network_config.hpp"
#include "network/network_player_profile.hpp"
#include "network/stk_host.hpp"
#include "network/protocols/client_lobby.hpp"
#include "network/protocols/server_lobby.hpp"
#include "network/stk_peer.hpp"
#include "utils/log.hpp"
#include "network/protocols/server_lobby.hpp"
#include "utils/time.hpp"
#include "utils/vs.hpp"
#include "main_loop.hpp"
@ -32,110 +31,93 @@
namespace NetworkConsole
{
// ----------------------------------------------------------------------------
void kickAllPlayers(STKHost* host)
void showHelp()
{
const std::vector<STKPeer*> &peers = host->getPeers();
for (unsigned int i = 0; i < peers.size(); i++)
{
peers[i]->disconnect();
}
} // kickAllPlayers
std::cout << "Available command:" << std::endl;
std::cout << "help, Print this." << std::endl;
std::cout << "quit, Shut down the server." << std::endl;
std::cout << "kickall, Kick all players out of STKHost." << std::endl;
std::cout << "kick #, kick # peer of STKHost." << std::endl;
std::cout << "kickban #, kick and ban # peer of STKHost." << std::endl;
std::cout << "listpeers, List all peers with host ID and IP." << std::endl;
std::cout << "listban, List IP ban list of server." << std::endl;
} // showHelp
// ----------------------------------------------------------------------------
void mainLoop(STKHost* host)
{
VS::setThreadName("NetworkConsole");
showHelp();
std::string str = "";
while (!host->requestedShutdown())
{
getline(std::cin, str);
if (str == "quit")
std::stringstream ss(str);
int number = -1;
ss >> str >> number;
if (str == "help")
{
showHelp();
}
else if (str == "quit")
{
host->requestShutdown();
}
else if (str == "kickall" && NetworkConfig::get()->isServer())
else if (str == "kickall")
{
kickAllPlayers(host);
auto peers = host->getPeers();
for (unsigned int i = 0; i < peers.size(); i++)
{
peers[i]->kick();
}
}
else if (str == "start" && NetworkConfig::get()->isServer())
else if (str == "kick" && number != -1 &&
NetworkConfig::get()->isServer())
{
auto sl = LobbyProtocol::get<ServerLobby>();
sl->signalRaceStartToClients();
std::shared_ptr<STKPeer> peer = host->findPeerByHostId(number);
if (peer)
peer->kick();
else
std::cout << "Unknown host id: " << number << std::endl;
}
else if (str == "selection" && NetworkConfig::get()->isServer())
else if (str == "kickban" && number != -1 &&
NetworkConfig::get()->isServer())
{
auto sl = LobbyProtocol::get<ServerLobby>();
sl->startSelection();
std::shared_ptr<STKPeer> peer = host->findPeerByHostId(number);
if (peer)
{
peer->kick();
UserConfigParams::m_server_ban_list
[peer->getAddress().toString(false/*show_port*/)] = 0;
LobbyProtocol::get<ServerLobby>()->updateBanList();
}
else
std::cout << "Unknown host id: " << number << std::endl;
}
else if (str == "select" && NetworkConfig::get()->isClient())
else if (str == "listpeers")
{
std::string str2;
getline(std::cin, str2);
auto clrp = LobbyProtocol::get<ClientLobby>();
std::vector<NetworkPlayerProfile*> players =
host->getMyPlayerProfiles();
// For now send a vote for each local player
for(unsigned int i=0; i<players.size(); i++)
auto peers = host->getPeers();
if (peers.empty())
std::cout << "No peers exist" << std::endl;
for (unsigned int i = 0; i < peers.size(); i++)
{
clrp->requestKartSelection(players[i]->getGlobalPlayerId(),
str2);
} // for i in players
std::cout << peers[i]->getHostId() << ": " <<
peers[i]->getAddress().toString() << std::endl;
}
}
else if (str == "vote" && NetworkConfig::get()->isClient())
else if (str == "listban")
{
std::cout << "Vote for ? (track/laps/reversed/major/minor/race#) :";
std::string str2;
getline(std::cin, str2);
auto clrp = LobbyProtocol::get<ClientLobby>();
std::vector<NetworkPlayerProfile*> players =
host->getMyPlayerProfiles();
if (str2 == "track")
for (auto& ban : UserConfigParams::m_server_ban_list)
{
std::cin >> str2;
// For now send a vote for each local player
for(unsigned int i=0; i<players.size(); i++)
clrp->voteTrack(i, str2);
if (ban.first == "0.0.0.0")
continue;
std::cout << "IP: " << ban.first << " online id: " <<
ban.second << std::endl;
}
else if (str2 == "laps")
{
int cnt;
std::cin >> cnt;
for(unsigned int i=0; i<players.size(); i++)
clrp->voteLaps(i, cnt);
}
else if (str2 == "reversed")
{
bool cnt;
std::cin >> cnt;
for(unsigned int i=0; i<players.size(); i++)
clrp->voteReversed(i, cnt);
}
else if (str2 == "major")
{
int cnt;
std::cin >> cnt;
for(unsigned int i=0; i<players.size(); i++)
clrp->voteMajor(i, cnt);
}
else if (str2 == "minor")
{
int cnt;
std::cin >> cnt;
for(unsigned int i=0; i<players.size(); i++)
clrp->voteMinor(i, cnt);
}
else if (str2 == "race#")
{
int cnt;
std::cin >> cnt;
for(unsigned int i=0; i<players.size(); i++)
clrp->voteRaceCount(i, cnt);
}
std::cout << "\n";
}
else
{
Log::info("Console", "Unknown command '%s'.", str.c_str());
std::cout << "Unknown command: " << str << std::endl;
}
} // while !stop
main_loop->abort();

View File

@ -17,33 +17,9 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network/network_player_profile.hpp"
#include "network/network_config.hpp"
#include "network/stk_host.hpp"
#include "online/online_player_profile.hpp"
/** Constructor.
* \param global_player_id A unique number assigned from the server to this
* player (though it might not be the index in the peer list).
* \param name Name of this player.
* \param global_player_id Global id of this player.
* \param host_id The id of the host the player is connected from.
*/
NetworkPlayerProfile::NetworkPlayerProfile(const irr::core::stringw &name,
int global_player_id,
int host_id )
{
m_global_player_id = global_player_id;
m_host_id = host_id;
m_kart_name = "";
m_world_kart_id = 0;
m_per_player_difficulty = PLAYER_DIFFICULTY_NORMAL;
m_player_name = name;
} // BetworkPlayerProfile
// ----------------------------------------------------------------------------
NetworkPlayerProfile::~NetworkPlayerProfile()
{
} // ~NetworkPlayerProfile
// ----------------------------------------------------------------------------
/** Returns true if this player is local, i.e. running on this computer. This
* is done by comparing the host id of this player with the host id of this
@ -51,5 +27,7 @@ NetworkPlayerProfile::~NetworkPlayerProfile()
*/
bool NetworkPlayerProfile::isLocalPlayer() const
{
return m_host_id == STKHost::get()->getMyHostId();
} // isLocalPlayer
// Server never has local player atm
return NetworkConfig::get()->isClient() &&
m_host_id == STKHost::get()->getMyHostId();
} // isLocalPlayer

View File

@ -26,10 +26,11 @@
#include "utils/types.hpp"
#include "irrString.h"
#include <memory>
#include <string>
namespace Online { class OnlineProfile; }
#include <tuple>
class STKPeer;
/*! \class NetworkPlayerProfile
* \brief Contains the profile of a player.
@ -37,31 +38,51 @@ namespace Online { class OnlineProfile; }
class NetworkPlayerProfile
{
private:
std::weak_ptr<STKPeer> m_peer;
/** The name of the player. */
irr::core::stringw m_player_name;
/** Host id of this player. */
uint32_t m_host_id;
float m_default_kart_color;
uint32_t m_online_id;
/** Per player difficulty. */
PerPlayerDifficulty m_per_player_difficulty;
/** The selected kart id. */
std::string m_kart_name;
/** The unique id of the player for this race. The number is assigned
* by the server (and it might not be the index of this player in the
* peer list. */
uint8_t m_global_player_id;
/** Host id of this player. */
uint8_t m_host_id;
/** The selected kart id. */
std::string m_kart_name;
/** The name of the player. */
irr::core::stringw m_player_name;
/** The kart id in the World class (pointer to AbstractKart). */
uint8_t m_world_kart_id;
/** Per player difficulty. */
PerPlayerDifficulty m_per_player_difficulty;
public:
NetworkPlayerProfile(const irr::core::stringw &name,
int global_player_id, int host_id);
~NetworkPlayerProfile();
NetworkPlayerProfile(std::shared_ptr<STKPeer> peer,
const irr::core::stringw &name, uint32_t host_id,
float default_kart_color, uint32_t online_id,
PerPlayerDifficulty per_player_difficulty)
{
m_peer = peer;
m_player_name = name;
m_host_id = host_id;
m_default_kart_color = default_kart_color;
m_online_id = online_id;
m_per_player_difficulty = per_player_difficulty;
m_global_player_id = 0;
m_world_kart_id = 0;
}
// ------------------------------------------------------------------------
~NetworkPlayerProfile() {}
// ------------------------------------------------------------------------
bool isLocalPlayer() const;
// ------------------------------------------------------------------------
/** Sets the global player id of this player. */
void setGlobalPlayerId(int player_id) { m_global_player_id = player_id; }
@ -70,7 +91,7 @@ public:
int getGlobalPlayerId() const { return m_global_player_id; }
// ------------------------------------------------------------------------
/** Returns the host id of this player. */
uint8_t getHostId() const { return m_host_id; }
uint32_t getHostId() const { return m_host_id; }
// ------------------------------------------------------------------------
/** Sets the kart name for this player. */
void setKartName(const std::string &kart_name) { m_kart_name = kart_name; }
@ -93,6 +114,13 @@ public:
/** Returns the name of this player. */
const irr::core::stringw& getName() const { return m_player_name; }
// ------------------------------------------------------------------------
float getDefaultKartColor() const { return m_default_kart_color; }
// ------------------------------------------------------------------------
uint32_t getOnlineId() const { return m_online_id; }
// ------------------------------------------------------------------------
bool isOfflineAccount() const { return m_online_id == 0; }
// ------------------------------------------------------------------------
std::shared_ptr<STKPeer> getPeer() const { return m_peer.lock(); }
}; // class NetworkPlayerProfile

View File

@ -119,11 +119,7 @@ void Protocol::requestTerminate()
void Protocol::sendMessageToPeersChangingToken(NetworkString *message,
bool reliable)
{
const std::vector<STKPeer*> &peers = STKHost::get()->getPeers();
for (unsigned int i = 0; i < peers.size(); i++)
{
peers[i]->sendPacket(message, reliable);
}
STKHost::get()->sendPacketToAllPeers(message, reliable);
} // sendMessageToPeersChangingToken
// ----------------------------------------------------------------------------

View File

@ -20,13 +20,12 @@
#include "network/event.hpp"
#include "network/protocol.hpp"
#include "network/stk_host.hpp"
#include "network/stk_peer.hpp"
#include "utils/log.hpp"
#include "utils/profiler.hpp"
#include "utils/time.hpp"
#include "utils/vs.hpp"
#include <algorithm>
#include <assert.h>
#include <cstdlib>
#include <errno.h>
@ -229,8 +228,9 @@ void ProtocolManager::startProtocol(std::shared_ptr<Protocol> protocol)
protocol->setup();
protocol->setState(PROTOCOL_STATE_RUNNING);
opt.unlock();
Protocol* protocol_ptr = protocol.get();
Log::info("ProtocolManager",
"A %s protocol has been started.", typeid(*protocol).name());
"A %s protocol has been started.", typeid(*protocol_ptr).name());
// setup the protocol and notify it that it's started
} // startProtocol
@ -278,9 +278,10 @@ void ProtocolManager::OneProtocolType::removeProtocol(std::shared_ptr<Protocol>
m_protocols.getData().end(), p);
if (i == m_protocols.getData().end())
{
Protocol* protocol_ptr = p.get();
Log::error("ProtocolManager",
"Trying to delete protocol '%s', which was not found",
typeid(*p).name());
typeid(*protocol_ptr).name());
}
else
{
@ -305,8 +306,9 @@ void ProtocolManager::terminateProtocol(std::shared_ptr<Protocol> protocol)
opt.unlock();
protocol->setState(PROTOCOL_STATE_TERMINATED);
protocol->terminated();
Protocol* protocol_ptr = protocol.get();
Log::info("ProtocolManager",
"A %s protocol has been terminated.", typeid(*protocol).name());
"A %s protocol has been terminated.", typeid(*protocol_ptr).name());
} // terminateProtocol
// ----------------------------------------------------------------------------
@ -502,15 +504,13 @@ void ProtocolManager::asynchronousUpdate()
for (unsigned int i = 0; i < m_all_protocols.size(); i++)
{
OneProtocolType &opt = m_all_protocols[i];
// The lock is likely not necessary, since this function is only
// called from the ProtocolManager thread, and this thread is also
// We don't need lock here because it can hang the GUI when connecting
// to or creating server, since this function is only called from
// the ProtocolManager thread, and this thread is also
// the only one who changes the number of protocols.
// Edit: remove this lock can avoid hanging the GUI when connecting
// to or creating server, but you need to make sure async and non-async
// update in each protocol will have atomic or mutex write
//opt.lock();
// But you need to make sure async and non-async
// update in each protocol will have atomic or mutex-protected write
opt.update(0, /*async*/true); // dt does not matter, so set it to 0
//opt.unlock();
}
PROFILER_POP_CPU_MARKER();

View File

@ -18,22 +18,29 @@
#include "network/protocols/client_lobby.hpp"
#include "config/user_config.hpp"
#include "config/player_manager.hpp"
#include "guiengine/modaldialog.hpp"
#include "guiengine/message_queue.hpp"
#include "input/device_manager.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/world_with_rank.hpp"
#include "network/event.hpp"
#include "network/game_setup.hpp"
#include "network/network_config.hpp"
#include "network/network_player_profile.hpp"
#include "network/protocol_manager.hpp"
#include "network/protocols/latency_protocol.hpp"
#include "network/race_event_manager.hpp"
#include "network/stk_host.hpp"
#include "network/stk_peer.hpp"
#include "online/online_player_profile.hpp"
#include "online/online_profile.hpp"
#include "states_screens/networking_lobby.hpp"
#include "states_screens/network_kart_selection.hpp"
#include "states_screens/race_result_gui.hpp"
#include "states_screens/state_manager.hpp"
#include "states_screens/tracks_screen.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/log.hpp"
@ -61,16 +68,25 @@ ClientLobby::ClientLobby() : LobbyProtocol(NULL)
{
m_server_address.clear();
m_server = NULL;
setHandleDisconnections(true);
} // ClientLobby
//-----------------------------------------------------------------------------
ClientLobby::~ClientLobby()
{
clearPlayers();
} // ClientLobby
//-----------------------------------------------------------------------------
void ClientLobby::clearPlayers()
{
StateManager::get()->resetActivePlayers();
input_manager->getDeviceManager()->setAssignMode(NO_ASSIGN);
input_manager->getDeviceManager()->setSinglePlayer(NULL);
input_manager->setMasterPlayerOnly(false);
input_manager->getDeviceManager()->clearLatestUsedDevice();
} // clearPlayers
//-----------------------------------------------------------------------------
/** Sets the address of the server.
*/
@ -78,140 +94,16 @@ void ClientLobby::setAddress(const TransportAddress &address)
{
m_server_address.copy(address);
} // setAddress
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void ClientLobby::setup()
{
m_game_setup = STKHost::get()->setupNewGame(); // create a new game setup
clearPlayers();
TracksScreen::getInstance()->resetVote();
LobbyProtocol::setup();
m_state = NONE;
} // setup
//-----------------------------------------------------------------------------
/** Sends the selection of a kart from this client to the server.
* \param player_id The global player id of the voting player.
* \param kart_name Name of the selected kart.
*/
void ClientLobby::requestKartSelection(uint8_t player_id,
const std::string &kart_name)
{
NetworkString *request = getNetworkString(3+kart_name.size());
request->addUInt8(LE_KART_SELECTION).addUInt8(player_id)
.encodeString(kart_name);
sendToServer(request, /*reliable*/ true);
delete request;
} // requestKartSelection
//-----------------------------------------------------------------------------
/** Sends a vote for a major vote from a client to the server. Note that even
* this client will only store the vote when it is received back from the
* server.
* \param player_id The global player id of the voting player.
* \param major Major mode voted for.
*/
void ClientLobby::voteMajor(uint8_t player_id, uint32_t major)
{
NetworkString *request = getNetworkString(6);
request->addUInt8(LE_VOTE_MAJOR).addUInt8(player_id)
.addUInt32(major);
sendToServer(request, true);
delete request;
} // voteMajor
//-----------------------------------------------------------------------------
/** Sends a vote for the number of tracks from a client to the server. Note
* that even this client will only store the vote when it is received back
* from the server.
* \param player_id The global player id of the voting player.
* \param count NUmber of tracks to play.
*/
void ClientLobby::voteRaceCount(uint8_t player_id, uint8_t count)
{
NetworkString *request = getNetworkString(3);
request->addUInt8(LE_VOTE_RACE_COUNT).addUInt8(player_id).addUInt8(count);
sendToServer(request, true);
delete request;
} // voteRaceCount
//-----------------------------------------------------------------------------
/** Sends a vote for the minor game mode from a client to the server. Note that
* even this client will only store the vote when it is received back from the
* server.
* \param player_id The global player id of the voting player.
* \param minor Voted minor mode.
*/
void ClientLobby::voteMinor(uint8_t player_id, uint32_t minor)
{
NetworkString *request = getNetworkString(6);
request->addUInt8(LE_VOTE_MINOR).addUInt8(player_id).addUInt32(minor);
sendToServer(request, true);
delete request;
} // voteMinor
//-----------------------------------------------------------------------------
/** Sends the vote about which track to play at which place in the list of
* tracks (like a custom GP definition). Note that even this client will only
* store the vote when it is received back from the server.
* \param player_id The global player id of the voting player.
* \param track Name of the track.
* \param At which place in the list of tracks this track should be played.
*/
void ClientLobby::voteTrack(uint8_t player_id,
const std::string &track,
uint8_t track_nb)
{
NetworkString *request = getNetworkString(2+1+track.size());
request->addUInt8(LE_VOTE_TRACK).addUInt8(player_id).addUInt8(track_nb)
.encodeString(track);
sendToServer(request, true);
delete request;
} // voteTrack
//-----------------------------------------------------------------------------
/** Sends a vote if a track at a specified place in the list of all tracks
* should be played in reverse or not. Note that even this client will only
* store the vote when it is received back from the server.
* \param player_id Global player id of the voting player.
* \param reversed True if the track should be played in reverse.
* \param track_nb Index for the track to be voted on in the list of all
* tracks.
*/
void ClientLobby::voteReversed(uint8_t player_id, bool reversed,
uint8_t track_nb)
{
NetworkString *request = getNetworkString(9);
request->addUInt8(LE_VOTE_REVERSE).addUInt8(player_id).addUInt8(reversed)
.addUInt8(track_nb);
sendToServer(request, true);
delete request;
} // voteReversed
//-----------------------------------------------------------------------------
/** Vote for the number of laps of the specified track. Note that even this
* client will only store the vote when it is received back from the server.
* \param player_id Global player id of the voting player.
* \param laps Number of laps for the specified track.
* \param track_nb Index of the track in the list of all tracks.
*/
void ClientLobby::voteLaps(uint8_t player_id, uint8_t laps,
uint8_t track_nb)
{
NetworkString *request = getNetworkString(10);
request->addUInt8(LE_VOTE_LAPS).addUInt8(player_id).addUInt8(laps)
.addUInt8(track_nb);
sendToServer(request, true);
delete request;
} // voteLaps
//-----------------------------------------------------------------------------
/** Called when a client selects to exit a server.
*/
void ClientLobby::leave()
{
m_server->disconnect();
STKHost::get()->removePeer(m_server);
m_server_address.clear();
} // leave
//-----------------------------------------------------------------------------
/** Called from the gui when a client clicked on 'continue' on the race result
* screen. It notifies the server that this client has exited the screen and
@ -226,7 +118,6 @@ void ClientLobby::doneWithResults()
} // doneWithResults
//-----------------------------------------------------------------------------
bool ClientLobby::notifyEvent(Event* event)
{
assert(m_game_setup); // assert that the setup exists
@ -239,10 +130,11 @@ bool ClientLobby::notifyEvent(Event* event)
switch(message_type)
{
case LE_START_SELECTION: startSelection(event); break;
case LE_KART_SELECTION_UPDATE: kartSelectionUpdate(event); break;
case LE_LOAD_WORLD: loadWorld(); break;
case LE_LOAD_WORLD: addAllPlayers(event); break;
case LE_RACE_FINISHED: raceFinished(event); break;
case LE_EXIT_RESULT: exitResultScreen(event); break;
case LE_UPDATE_PLAYER_LIST: updatePlayerList(event); break;
case LE_CHAT: handleChat(event); break;
default:
return false;
break;
@ -251,7 +143,6 @@ bool ClientLobby::notifyEvent(Event* event)
} // notifyEvent
//-----------------------------------------------------------------------------
bool ClientLobby::notifyEventAsynchronous(Event* event)
{
assert(m_game_setup); // assert that the setup exists
@ -265,18 +156,13 @@ bool ClientLobby::notifyEventAsynchronous(Event* event)
message_type);
switch(message_type)
{
case LE_NEW_PLAYER_CONNECTED: newPlayer(event); break;
case LE_PLAYER_DISCONNECTED : disconnectedPlayer(event); break;
case LE_START_RACE: startGame(event); break;
case LE_CONNECTION_REFUSED: connectionRefused(event); break;
case LE_CONNECTION_ACCEPTED: connectionAccepted(event); break;
case LE_KART_SELECTION_REFUSED: kartSelectionRefused(event); break;
case LE_VOTE_MAJOR : playerMajorVote(event); break;
case LE_VOTE_RACE_COUNT: playerRaceCountVote(event); break;
case LE_VOTE_MINOR: playerMinorVote(event); break;
case LE_VOTE_TRACK: playerTrackVote(event); break;
case LE_VOTE_REVERSE: playerReversedVote(event); break;
case LE_VOTE_LAPS: playerLapsVote(event); break;
case LE_VOTE: displayPlayerVote(event); break;
case LE_SERVER_OWNERSHIP: becomingServerOwner(); break;
default: break;
} // switch
return true;
@ -289,6 +175,22 @@ bool ClientLobby::notifyEventAsynchronous(Event* event)
// the ProtocolManager, which might already have been deleted.
// So only signal that STKHost should exit, which will be tested
// from the main thread.
STKHost::get()->disconnectAllPeers(false/*timeout_waiting*/);
switch(event->getPeerDisconnectInfo())
{
case PDI_TIMEOUT:
STKHost::get()->setErrorMessage(
_("Server connection timed out."));
break;
case PDI_NORMAL:
STKHost::get()->setErrorMessage(
_("Server has been shut down."));
break;
case PDI_KICK:
STKHost::get()->setErrorMessage(
_("You were kicked from the server."));
break;
} // switch
STKHost::get()->requestShutdown();
return true;
} // disconnection
@ -296,7 +198,51 @@ bool ClientLobby::notifyEventAsynchronous(Event* event)
} // notifyEventAsynchronous
//-----------------------------------------------------------------------------
void ClientLobby::addAllPlayers(Event* event)
{
if (!checkDataSize(event, 1))
{
// If recieved invalid message for players leave now
STKHost::get()->disconnectAllPeers(false/*timeout_waiting*/);
STKHost::get()->requestShutdown();
return;
}
NetworkString& data = event->data();
std::string track_name;
data.decodeString(&track_name);
uint8_t lap = data.getUInt8();
uint8_t reverse = data.getUInt8();
m_game_setup->setRace(track_name, lap, reverse == 1);
std::shared_ptr<STKPeer> peer = event->getPeerSP();
peer->cleanPlayerProfiles();
std::vector<std::shared_ptr<NetworkPlayerProfile> > players;
unsigned player_count = data.getUInt8();
assert(m_game_setup->getPlayerCount() == 0);
for (unsigned i = 0; i < player_count; i++)
{
core::stringw player_name;
data.decodeStringW(&player_name);
uint32_t host_id = data.getUInt32();
float kart_color = data.getFloat();
uint32_t online_id = data.getUInt32();
PerPlayerDifficulty ppd = (PerPlayerDifficulty)data.getUInt8();
auto player = std::make_shared<NetworkPlayerProfile>(peer, player_name,
host_id, kart_color, online_id, ppd);
std::string kart_name;
data.decodeString(&kart_name);
player->setKartName(kart_name);
peer->addPlayer(player);
m_game_setup->addPlayer(player);
players.push_back(player);
}
configRemoteKart(players);
loadWorld();
} // addAllPlayers
//-----------------------------------------------------------------------------
void ClientLobby::update(int ticks)
{
switch (m_state)
@ -309,20 +255,38 @@ void ClientLobby::update(int ticks)
break;
case LINKED:
{
core::stringw name;
if(PlayerManager::getCurrentOnlineState()==PlayerProfile::OS_SIGNED_IN)
name = PlayerManager::getCurrentOnlineUserName();
else
name = PlayerManager::getCurrentPlayer()->getName();
std::string name_u8 = StringUtils::wideToUtf8(name);
const std::string &password = NetworkConfig::get()->getPassword();
NetworkString *ns = getNetworkString(6+1+name_u8.size()
+1+password.size());
// 4 (size of id), global id
ns->addUInt8(LE_CONNECTION_REQUESTED).encodeString(name)
.encodeString(NetworkConfig::get()->getPassword());
NetworkString *ns = getNetworkString();
ns->addUInt8(LE_CONNECTION_REQUESTED)
.encodeString(NetworkConfig::get()->getPassword());
assert(!NetworkConfig::get()->isAddingNetworkPlayers());
ns->addUInt8(
(uint8_t)NetworkConfig::get()->getNetworkPlayers().size());
// Only first player has online name and profile
bool first_player = true;
for (auto& p : NetworkConfig::get()->getNetworkPlayers())
{
core::stringw name;
PlayerProfile* player = std::get<1>(p);
if (PlayerManager::getCurrentOnlineState() ==
PlayerProfile::OS_SIGNED_IN && first_player)
{
name = PlayerManager::getCurrentOnlineUserName();
}
else
{
name = player->getName();
}
std::string name_u8 = StringUtils::wideToUtf8(name);
ns->encodeString(name_u8).addFloat(player->getDefaultKartColor());
Online::OnlinePlayerProfile* opp =
dynamic_cast<Online::OnlinePlayerProfile*>(player);
ns->addUInt32(first_player && opp && opp->getProfile() ?
opp->getProfile()->getID() : 0);
// Per-player handicap
ns->addUInt8(std::get<2>(p));
first_player = false;
}
auto all_k = kart_properties_manager->getAllAvailableKarts();
auto all_t = track_manager->getAllTrackIdentifiers();
if (all_k.size() >= 65536)
@ -350,14 +314,30 @@ void ClientLobby::update(int ticks)
break;
case KART_SELECTION:
{
// In case the user opened a user info dialog
GUIEngine::ModalDialog::dismiss();
NetworkKartSelectionScreen* screen =
NetworkKartSelectionScreen::getInstance();
NetworkKartSelectionScreen::getInstance();
screen->setAvailableKartsFromServer(m_available_karts);
screen->push();
// In case of auto-connect, use random karts (or previous kart) from
// server and go to track selection (or grand prix later)
if (NetworkConfig::get()->isAutoConnect())
{
input_manager->setMasterPlayerOnly(true);
for (auto& p : NetworkConfig::get()->getNetworkPlayers())
{
StateManager::get()
->createActivePlayer(std::get<1>(p), std::get<0>(p));
}
input_manager->getDeviceManager()->setAssignMode(ASSIGN);
TracksScreen::getInstance()->setNetworkTracks();
TracksScreen::getInstance()->push();
}
else
{
screen->push();
}
m_state = SELECTING_KARTS;
std::make_shared<LatencyProtocol>()->requestStart();
Log::info("LobbyProtocol", "LatencyProtocol started.");
}
break;
case SELECTING_KARTS:
@ -376,50 +356,36 @@ void ClientLobby::update(int ticks)
} // update
//-----------------------------------------------------------------------------
/*! \brief Called when a new player is connected to the server
* \param event : Event providing the information.
*
* Format of the data :
* Byte 0 1 2
* -------------------------------------
* Size | 1 | 1 | |
* Data | player_id | hostid | player name |
* -------------------------------------
*/
void ClientLobby::newPlayer(Event* event)
void ClientLobby::displayPlayerVote(Event* event)
{
if (!checkDataSize(event, 2)) return;
const NetworkString &data = event->data();
uint8_t player_id = data.getUInt8();
uint8_t host_id = data.getUInt8();
core::stringw name;
data.decodeStringW(&name);
// FIXME need adjusting when splitscreen is used/
if(STKHost::get()->getGameSetup()->isLocalMaster(player_id))
{
Log::error("ClientLobby",
"The server notified me that I'm a new player in the "
"room (not normal).");
}
else if (m_game_setup->getProfile(player_id) == NULL)
{
Log::verbose("ClientLobby", "New player connected.");
NetworkPlayerProfile* profile =
new NetworkPlayerProfile(name, player_id, host_id);
m_game_setup->addPlayer(profile);
NetworkingLobby::getInstance()->addPlayer(profile);
}
else
{
Log::error("ClientLobby",
"One of the player notified in the list is myself.");
}
} // newPlayer
if (!checkDataSize(event, 4)) return;
// Get the player name who voted
NetworkString& data = event->data();
float timeout = data.getFloat();
TracksScreen::getInstance()->setVoteTimeout(timeout);
std::string player_name;
data.decodeString(&player_name);
uint32_t host_id = data.getUInt32();
player_name += ": ";
std::string track_name;
data.decodeString(&track_name);
Track* track = track_manager->getTrack(track_name);
if (!track)
Log::fatal("ClientLobby", "Missing track %s", track_name.c_str());
core::stringw track_readable = track->getName();
int lap = data.getUInt8();
int rev = data.getUInt8();
core::stringw yes = _("Yes");
core::stringw no = _("No");
//I18N: Vote message in network game from a player
core::stringw vote_msg = _("Track: %s,\nlaps: %d, reversed: %s",
track_readable, lap, rev == 1 ? yes : no);
vote_msg = StringUtils::utf8ToWide(player_name) + vote_msg;
TracksScreen::getInstance()->addVoteMessage(player_name +
StringUtils::toString(host_id), vote_msg);
} // displayPlayerVote
//-----------------------------------------------------------------------------
/*! \brief Called when a new player is disconnected
* \param event : Event providing the information.
*
@ -435,97 +401,73 @@ void ClientLobby::disconnectedPlayer(Event* event)
if (!checkDataSize(event, 1)) return;
NetworkString &data = event->data();
while(data.size()>0)
unsigned disconnected_player_count = data.getUInt8();
for (unsigned i = 0; i < disconnected_player_count; i++)
{
const NetworkPlayerProfile *profile =
m_game_setup->getProfile(data.getUInt8());
if (m_game_setup->removePlayer(profile))
{
Log::info("ClientLobby",
"Player %d removed successfully.",
profile->getGlobalPlayerId());
}
else
{
Log::error("ClientLobby",
"The disconnected peer wasn't known.");
}
} // while
core::stringw player_name;
data.decodeStringW(&player_name);
core::stringw msg = _("%s disconnected.", player_name);
// Use the friend icon to avoid an error-like message
MessageQueue::add(MessageQueue::MT_FRIEND, msg);
}
STKHost::get()->removePeer(event->getPeer());
} // disconnectedPlayer
//-----------------------------------------------------------------------------
/*! \brief Called when the server accepts the connection.
* \param event : Event providing the information.
*
* Format of the data :
* Byte 0 1 2 3
* ---------------------------------------------------------
* Size | 1 | 1 | 1 | |
* Data | player_id| hostid | authorised |playernames* |
* ---------------------------------------------------------
*/
void ClientLobby::connectionAccepted(Event* event)
{
// At least 3 bytes should remain now
if(!checkDataSize(event, 3)) return;
// At least 4 byte should remain now
if (!checkDataSize(event, 4)) return;
NetworkString &data = event->data();
STKPeer* peer = event->getPeer();
// Accepted
// ========
Log::info("ClientLobby",
"The server accepted the connection.");
Log::info("ClientLobby", "The server accepted the connection.");
// self profile
irr::core::stringw name;
if (PlayerManager::getCurrentOnlineState() == PlayerProfile::OS_SIGNED_IN)
name = PlayerManager::getCurrentOnlineUserName();
else
name = PlayerManager::getCurrentPlayer()->getName();
uint8_t my_player_id = data.getUInt8();
uint8_t my_host_id = data.getUInt8();
uint8_t authorised = data.getUInt8();
// Store this client's authorisation status in the peer information
// for the server.
event->getPeer()->setAuthorised(authorised!=0);
STKHost::get()->setMyHostId(my_host_id);
NetworkPlayerProfile* profile =
new NetworkPlayerProfile(name, my_player_id, my_host_id);
STKHost::get()->getGameSetup()->setLocalMaster(my_player_id);
m_game_setup->setNumLocalPlayers(1);
STKHost::get()->setMyHostId(data.getUInt32());
assert(!NetworkConfig::get()->isAddingNetworkPlayers());
m_game_setup->setNumLocalPlayers((int)
NetworkConfig::get()->getNetworkPlayers().size());
// connection token
uint32_t token = data.getToken();
peer->setClientServerToken(token);
// Add all players
// ===============
while (data.size() > 0)
{
uint8_t player_id = data.getUInt8();
uint8_t host_id = data.getUInt8();
irr::core::stringw name;
int bytes_read = data.decodeStringW(&name);
NetworkPlayerProfile* profile2 =
new NetworkPlayerProfile(name, player_id, host_id);
m_game_setup->addPlayer(profile2);
// Inform the network lobby of all players so that the GUI can
// show all currently connected players.
NetworkingLobby::getInstance()->addPlayer(profile2);
}
// Add self after other players so that player order is identical
// on server and all clients.
m_game_setup->addPlayer(profile);
NetworkingLobby::getInstance()->addPlayer(profile);
m_server = event->getPeer();
if (!peer->isClientServerTokenSet())
peer->setClientServerToken(token);
m_state = CONNECTED;
if (NetworkConfig::get()->isAutoConnect())
} // connectionAccepted
//-----------------------------------------------------------------------------
void ClientLobby::updatePlayerList(Event* event)
{
if (!checkDataSize(event, 1)) return;
NetworkString& data = event->data();
unsigned player_count = data.getUInt8();
std::vector<std::tuple<uint32_t, uint32_t, core::stringw, int> > players;
for (unsigned i = 0; i < player_count; i++)
{
std::tuple<uint32_t, uint32_t, core::stringw, int> pl;
std::get<0>(pl) = data.getUInt32();
std::get<1>(pl) = data.getUInt32();
data.decodeStringW(&std::get<2>(pl));
// icon to be used, see NetworkingLobby::loadedFromFile
std::get<3>(pl) = data.getUInt8() == 1 /*if server owner*/ ? 0 :
std::get<1>(pl) != 0 /*if online account*/ ? 1 : 2;
players.push_back(pl);
}
NetworkingLobby::getInstance()->updatePlayers(players);
} // updatePlayerList
//-----------------------------------------------------------------------------
void ClientLobby::becomingServerOwner()
{
MessageQueue::add(MessageQueue::MT_GENERIC,
_("You are now the owner of server."));
STKHost::get()->setAuthorisedToControl(true);
if (m_state == CONNECTED && NetworkConfig::get()->isAutoConnect())
{
// Send a message to the server to start
NetworkString start(PROTOCOL_LOBBY_ROOM);
@ -533,11 +475,24 @@ void ClientLobby::connectionAccepted(Event* event)
start.addUInt8(LobbyProtocol::LE_REQUEST_BEGIN);
STKHost::get()->sendToServer(&start, true);
}
} // connectionAccepted
} // becomingServerOwner
//-----------------------------------------------------------------------------
void ClientLobby::handleChat(Event* event)
{
if (!UserConfigParams::m_lobby_chat)
return;
std::string message;
event->data().decodeString(&message);
Log::info("ClientLobby", "%s", message.c_str());
if (message.size() > 0)
{
NetworkingLobby::getInstance()->addMoreServerInfo(
StringUtils::utf8ToWide(message));
}
} // handleChat
//-----------------------------------------------------------------------------
/*! \brief Called when the server refuses the connection.
* \param event : Event providing the information.
*
@ -552,93 +507,35 @@ void ClientLobby::connectionRefused(Event* event)
{
if (!checkDataSize(event, 1)) return;
const NetworkString &data = event->data();
switch (data.getUInt8()) // the second byte
switch ((RejectReason)data.getUInt8()) // the second byte
{
case 0:
Log::info("ClientLobby",
"Connection refused : too many players.");
case RR_BUSY:
STKHost::get()->setErrorMessage(
_("Connection refused: Server is busy."));
break;
case 1:
Log::info("ClientLobby", "Connection refused : banned.");
case RR_BANNED:
STKHost::get()->setErrorMessage(
_("Connection refused: You are banned from the server."));
break;
case 2:
Log::info("ClientLobby", "Client busy.");
case RR_INCORRECT_PASSWORD:
STKHost::get()->setErrorMessage(
_("Connection refused: Server password is incorrect."));
break;
case 3:
Log::info("ClientLobby", "Having incompatible karts / tracks.");
case RR_INCOMPATIBLE_DATA:
STKHost::get()->setErrorMessage(
_("Connection refused: Game data is incompatible."));
break;
default:
Log::info("ClientLobby", "Connection refused.");
case RR_TOO_MANY_PLAYERS:
STKHost::get()->setErrorMessage(
_("Connection refused: Server is full."));
break;
}
STKHost::get()->disconnectAllPeers(false/*timeout_waiting*/);
STKHost::get()->requestShutdown();
} // connectionRefused
//-----------------------------------------------------------------------------
/*! \brief Called when the server refuses the kart selection request.
* \param event : Event providing the information.
*
* Format of the data :
* Byte 0
* ----------------
* Size | 1 |
* Data | refusal code |
* ----------------
*/
void ClientLobby::kartSelectionRefused(Event* event)
{
if(!checkDataSize(event, 1)) return;
const NetworkString &data = event->data();
switch (data.getUInt8()) // the error code
{
case 0:
Log::info("ClientLobby",
"Kart selection refused : already taken.");
break;
case 1:
Log::info("ClientLobby",
"Kart selection refused : not available.");
break;
default:
Log::info("ClientLobby", "Kart selection refused.");
break;
}
} // kartSelectionRefused
//-----------------------------------------------------------------------------
/*! \brief Called when the server tells to update a player's kart.
* \param event : Event providing the information.
*
* Format of the data :
* Byte 0 1 2 3 N+3
* --------------------------------------------------
* Size | 1 | 1 | N |
* Data | player id | N (kart name size) | kart name |
* --------------------------------------------------
*/
void ClientLobby::kartSelectionUpdate(Event* event)
{
if(!checkDataSize(event, 3)) return;
const NetworkString &data = event->data();
uint8_t player_id = data.getUInt8();
std::string kart_name;
data.decodeString(&kart_name);
if (!m_game_setup->isKartAvailable(kart_name))
{
Log::error("ClientLobby",
"The updated kart is taken already.");
}
m_game_setup->setPlayerKart(player_id, kart_name);
NetworkKartSelectionScreen::getInstance()->playerSelected(player_id,
kart_name);
} // kartSelectionUpdate
//-----------------------------------------------------------------------------
/*! \brief Called when the server broadcasts to start the race to all clients.
* \param event : Event providing the information (no additional information
* in this case).
@ -668,7 +565,6 @@ void ClientLobby::startingRaceNow()
sendToServer(ns, /*reliable*/true);
Log::verbose("ClientLobby", "StartingRaceNow at %lf",
StkTime::getRealTime());
terminateLatencyProtocol();
} // startingRaceNow
//-----------------------------------------------------------------------------
@ -750,149 +646,19 @@ void ClientLobby::raceFinished(Event* event)
*/
void ClientLobby::exitResultScreen(Event *event)
{
RaceResultGUI::getInstance()->backToLobby();
// Will be reset to linked if connected to server, see update(float dt)
m_game_setup = STKHost::get()->setupNewGame();
STKHost::get()->getServerPeerForClient()->unsetClientServerToken();
// stop race protocols
auto pm = ProtocolManager::lock();
assert(pm);
pm->findAndTerminate(PROTOCOL_CONTROLLER_EVENTS);
pm->findAndTerminate(PROTOCOL_KART_UPDATE);
pm->findAndTerminate(PROTOCOL_GAME_EVENTS);
m_state = NONE;
// Will be reset to linked if connected to server, see update(float dt)
setup();
RaceResultGUI::getInstance()->backToLobby();
} // exitResultScreen
//-----------------------------------------------------------------------------
/*! \brief Called when a player votes for a major race mode.
* \param event : Event providing the information.
*
* Format of the data :
* Byte 0 1 2
* ------------------------------
* Size | 1 | 1 |
* Data |player id | major mode vote |
* ------------------------------
*/
void ClientLobby::playerMajorVote(Event* event)
{
const NetworkString &data = event->data();
if (!checkDataSize(event, 2))
return;
uint8_t player_id = data.getUInt8();
uint8_t mode = data.getUInt8();
m_game_setup->getRaceConfig()->setPlayerMajorVote(player_id, mode);
} // playerMajorVote
//-----------------------------------------------------------------------------
/*! \brief Called when a player votes for the number of races in a GP.
* \param event : Event providing the information.
*
* Format of the data :
* Byte 0 1
* ---------------------------
* Size | 1 | 1 |
* Data | player id | races count |
* ---------------------------
*/
void ClientLobby::playerRaceCountVote(Event* event)
{
if (!checkDataSize(event, 2)) return;
const NetworkString &data = event->data();
uint8_t player_id = data.getUInt8();
uint8_t count = data.getUInt8();
m_game_setup->getRaceConfig()->setPlayerRaceCountVote(player_id, count);
} // playerRaceCountVote
//-----------------------------------------------------------------------------
/*! \brief Called when a player votes for a minor race mode.
* \param event : Event providing the information.
*
* Format of the data :
* Byte 0 1
* -------------------------------
* Size | 1 | 4 |
* Data | player id | minor mode vote |
* -------------------------------
*/
void ClientLobby::playerMinorVote(Event* event)
{
if (!checkDataSize(event, 2)) return;
const NetworkString &data = event->data();
uint8_t player_id = data.getUInt8();
uint8_t minor = data.getUInt8();
m_game_setup->getRaceConfig()->setPlayerMinorVote(player_id, minor);
} // playerMinorVote
//-----------------------------------------------------------------------------
/*! \brief Called when a player votes for a track.
* \param event : Event providing the information.
*
* Format of the data :
* Byte 0 1 2 3
* --------------------------------------------------
* Size | 1 | 1 | 1 | N |
* Data | player id | track number (gp) | N | track name |
* --------------------------------------------------
*/
void ClientLobby::playerTrackVote(Event* event)
{
if (!checkDataSize(event, 3)) return;
const NetworkString &data = event->data();
std::string track_name;
uint8_t player_id = data.getUInt8();
uint8_t number = data.getUInt8();
int N = data.decodeString(&track_name);
m_game_setup->getRaceConfig()->setPlayerTrackVote(player_id, track_name,
number);
} // playerTrackVote
//-----------------------------------------------------------------------------
/*! \brief Called when a player votes for the reverse mode of a race
* \param event : Event providing the information.
*
* Format of the data :
* Byte 0 1 2
* -------------------------------------------
* Size | 1 | 1 | 1 |
* Data | player id |reversed | track number (gp) |
* -------------------------------------------
*/
void ClientLobby::playerReversedVote(Event* event)
{
if (!checkDataSize(event, 3)) return;
const NetworkString &data = event->data();
uint8_t player_id = data.getUInt8();
uint8_t reversed = data.getUInt8();
uint8_t number = data.getUInt8();
m_game_setup->getRaceConfig()->setPlayerReversedVote(player_id, reversed!=0,
number);
} // playerReversedVote
//-----------------------------------------------------------------------------
/*! \brief Called when a player votes for a major race mode.
* \param event : Event providing the information.
*
* Format of the data :
* Byte 0 1 2
* ----------------------------------------
* Size | 1 | 1 | 1 |
* Data | player id | laps | track number (gp) |
* ----------------------------------------
*/
void ClientLobby::playerLapsVote(Event* event)
{
if (!checkDataSize(event, 3)) return;
const NetworkString &data = event->data();
uint8_t player_id = data.getUInt8();
uint8_t laps = data.getUInt8();
uint8_t number = data.getUInt8();
m_game_setup->getRaceConfig()->setPlayerLapsVote(player_id, laps, number);
} // playerLapsVote
//-----------------------------------------------------------------------------
/** Callback when the world is loaded. The client will inform the server
* that the players on this host are ready to start the race. It is called by
@ -900,19 +666,8 @@ void ClientLobby::playerLapsVote(Event* event)
*/
void ClientLobby::finishedLoadingWorld()
{
assert(STKHost::get()->getPeerCount() == 1);
std::vector<NetworkPlayerProfile*> players =
STKHost::get()->getMyPlayerProfiles();
NetworkString *ns = getNetworkString(2);
NetworkString* ns = getNetworkString(1);
ns->addUInt8(LE_CLIENT_LOADED_WORLD);
ns->addUInt8( uint8_t(players.size()) ) ;
for (unsigned int i = 0; i < players.size(); i++)
{
ns->addUInt8(players[i]->getGlobalPlayerId());
Log::info("ClientLobby",
"Player %d ready, notifying server.",
players[i]->getGlobalPlayerId());
} // for i < players.size()
sendToServer(ns, /*reliable*/true);
sendToServer(ns, true);
delete ns;
} // finishedLoadingWorld

View File

@ -6,33 +6,26 @@
#include "utils/cpp2011.hpp"
#include <set>
class STKPeer;
class ClientLobby : public LobbyProtocol
{
private:
void newPlayer(Event* event);
void disconnectedPlayer(Event* event);
void connectionAccepted(Event* event); //!< Callback function on connection acceptation
void connectionRefused(Event* event); //!< Callback function on connection refusal
void kartSelectionRefused(Event* event);
void kartSelectionUpdate(Event* event);
void startGame(Event* event);
void startSelection(Event* event);
void raceFinished(Event* event);
void exitResultScreen(Event *event);
// race votes
void playerMajorVote(Event* event);
void playerRaceCountVote(Event* event);
void playerMinorVote(Event* event);
void playerTrackVote(Event* event);
void playerReversedVote(Event* event);
void playerLapsVote(Event* event);
void displayPlayerVote(Event* event);
void updatePlayerList(Event* event);
void handleChat(Event* event);
void becomingServerOwner();
void clearPlayers();
TransportAddress m_server_address;
STKPeer* m_server;
enum STATE
{
NONE,
@ -53,29 +46,18 @@ private:
std::set<std::string> m_available_karts;
std::set<std::string> m_available_tracks;
void addAllPlayers(Event* event);
public:
ClientLobby();
virtual ~ClientLobby();
virtual void requestKartSelection(uint8_t player_id,
const std::string &kart_name) OVERRIDE;
void setAddress(const TransportAddress &address);
void voteMajor(uint8_t player_id, uint32_t major);
void voteRaceCount(uint8_t player_id, uint8_t count);
void voteMinor(uint8_t player_id, uint32_t minor);
void voteTrack(uint8_t player_id, const std::string &track,
uint8_t track_nb = 0);
void voteReversed(uint8_t player_id, bool reversed, uint8_t track_nb = 0);
void voteLaps(uint8_t player_id, uint8_t laps, uint8_t track_nb = 0);
void doneWithResults();
void startingRaceNow();
void leave();
const std::set<std::string>& getAvailableKarts() const
{ return m_available_karts; }
const std::set<std::string>& getAvailableTracks() const
{ return m_available_tracks; }
virtual bool notifyEvent(Event* event) OVERRIDE;
virtual bool notifyEventAsynchronous(Event* event) OVERRIDE;
virtual void finishedLoadingWorld() OVERRIDE;

View File

@ -38,7 +38,6 @@ ConnectToPeer::ConnectToPeer(uint32_t peer_id) : Protocol(PROTOCOL_CONNECTION)
m_peer_id = peer_id;
m_state = NONE;
m_is_lan = false;
setHandleConnections(true);
} // ConnectToPeer(peer_id)
// ----------------------------------------------------------------------------
@ -53,7 +52,6 @@ ConnectToPeer::ConnectToPeer(const TransportAddress &address)
// with the state when we found the peer address.
m_state = WAIT_FOR_CONNECTION;
m_is_lan = true;
setHandleConnections(true);
} // ConnectToPeers(TransportAddress)
// ----------------------------------------------------------------------------
@ -62,17 +60,6 @@ ConnectToPeer::~ConnectToPeer()
{
} // ~ConnectToPeer
// ----------------------------------------------------------------------------
bool ConnectToPeer::notifyEventAsynchronous(Event* event)
{
if (event->getType() == EVENT_TYPE_CONNECTED)
{
Log::debug("ConnectToPeer", "Received event notifying peer connection.");
m_state = CONNECTED; // we received a message, we are connected
}
return true;
} // notifyEventAsynchronous
// ----------------------------------------------------------------------------
/** Simple finite state machine: Start a GetPeerAddress protocol. Once the
* result has been received, start a ping protocol (hoping to be able
@ -115,6 +102,14 @@ void ConnectToPeer::asynchronousUpdate()
}
case WAIT_FOR_CONNECTION:
{
if (STKHost::get()->peerExists(m_peer_address))
{
Log::info("ConnectToPeer",
"Peer %s has established a connection.",
m_peer_address.toString().c_str());
m_state = DONE;
break;
}
// Each 2 second for a ping or broadcast
if (StkTime::getRealTime() > m_timer + 2.0)
{
@ -128,13 +123,16 @@ void ConnectToPeer::asynchronousUpdate()
BareNetworkString aloha(std::string("aloha_stk"));
STKHost::get()->sendRawPacket(aloha, broadcast_address);
Log::info("ConnectToPeer", "Broadcast aloha sent.");
Log::verbose("ConnectToPeer", "Broadcast aloha sent.");
StkTime::sleep(1);
broadcast_address.setIP(0x7f000001); // 127.0.0.1 (localhost)
broadcast_address.setPort(m_peer_address.getPort());
STKHost::get()->sendRawPacket(aloha, broadcast_address);
Log::info("ConnectToPeer", "Broadcast aloha to self.");
if (m_peer_address.isPublicAddressLocalhost())
{
broadcast_address.setIP(0x7f000001); // 127.0.0.1 (localhost)
broadcast_address.setPort(m_peer_address.getPort());
STKHost::get()->sendRawPacket(aloha, broadcast_address);
Log::verbose("ConnectToPeer", "Broadcast aloha to self.");
}
// 20 seconds timeout
if (m_tried_connection++ > 10)
@ -142,23 +140,13 @@ void ConnectToPeer::asynchronousUpdate()
// Not much we can do about if we don't receive the client
// connection - it could have stopped, lost network, ...
// Terminate this protocol.
Log::error("ConnectToPeer", "Time out trying to connect to %s",
Log::warn("ConnectToPeer", "Time out trying to connect to %s",
m_peer_address.toString().c_str());
requestTerminate();
m_state = DONE;
}
}
break;
}
case CONNECTING: // waiting for the peer to connect
// If we receive a 'connected' event from enet, our
// notifyEventAsynchronous is called, which will move
// the FSM to the next state CONNECTED
break;
case CONNECTED:
{
m_state = DONE;
break;
}
case DONE:
m_state = EXITING;
requestTerminate();

View File

@ -23,8 +23,6 @@
#include "network/transport_address.hpp"
#include "utils/cpp2011.hpp"
#include <chrono>
/** One instance of this is started for every peer who tries to
* connect to this server.
*/
@ -55,8 +53,6 @@ protected:
NONE,
RECEIVED_PEER_ADDRESS,
WAIT_FOR_CONNECTION,
CONNECTING,
CONNECTED,
DONE,
EXITING
} m_state;
@ -66,7 +62,6 @@ public:
ConnectToPeer(const TransportAddress &address);
virtual ~ConnectToPeer();
virtual bool notifyEventAsynchronous(Event* event) OVERRIDE;
virtual void setup() OVERRIDE {}
virtual void update(int ticks) OVERRIDE {}
virtual void asynchronousUpdate() OVERRIDE;

View File

@ -18,7 +18,9 @@
#include "network/protocols/connect_to_server.hpp"
#include "config/user_config.hpp"
#include "network/event.hpp"
#include "network/network.hpp"
#include "network/network_config.hpp"
#include "network/protocols/get_peer_address.hpp"
#include "network/protocols/hide_public_address.hpp"
@ -33,6 +35,8 @@
#include "utils/time.hpp"
#include "utils/log.hpp"
#include <algorithm>
// ----------------------------------------------------------------------------
/** Specify server to connect to.
* \param server Server to connect to (if nullptr than we use quick play).
@ -40,7 +44,11 @@
ConnectToServer::ConnectToServer(std::shared_ptr<Server> server)
: Protocol(PROTOCOL_CONNECTION)
{
m_server = server;
if (server)
{
m_server = server;
m_server_address.copy(m_server->getAddress());
}
setHandleConnections(true);
} // ConnectToServer(server, host)
@ -78,17 +86,26 @@ void ConnectToServer::asynchronousUpdate()
StkTime::sleep(1);
while (!ServersManager::get()->listUpdated())
StkTime::sleep(1);
if (!ServersManager::get()->getServers().empty())
auto servers = std::move(ServersManager::get()->getServers());
// Remove password protected servers
servers.erase(std::remove_if(servers.begin(), servers.end(), []
(const std::shared_ptr<Server> a)->bool
{
return a->isPasswordProtected();
}), servers.end());
if (!servers.empty())
{
// For quick play we choose the server with the least player
ServersManager::get()->sortServers([]
std::sort(servers.begin(), servers.end(), []
(const std::shared_ptr<Server> a,
const std::shared_ptr<Server> b)->bool
{
return a->getCurrentPlayers() < b->getCurrentPlayers();
});
m_server = ServersManager::get()->getServers()[0];
ServersManager::get()->cleanUpServers();
m_server = servers[0];
m_server_address.copy(m_server->getAddress());
}
else
{
@ -99,7 +116,11 @@ void ConnectToServer::asynchronousUpdate()
m_state = EXITING;
return;
}
servers.clear();
}
if (handleDirectConnect())
return;
STKHost::get()->setPublicAddress();
// Set to DONE will stop STKHost is not connected
m_state = STKHost::get()->getPublicAddress().isUnset() ?
@ -115,7 +136,6 @@ void ConnectToServer::asynchronousUpdate()
case GOT_SERVER_ADDRESS:
{
assert(m_server);
m_server_address.copy(m_server->getAddress());
Log::info("ConnectToServer", "Server's address known");
m_state = REQUESTING_CONNECTION;
auto request_connection =
@ -146,11 +166,15 @@ void ConnectToServer::asynchronousUpdate()
m_current_protocol = hide_address;
return;
}
if (m_tried_connection++ > 10)
if (m_tried_connection++ > 7)
{
Log::error("ConnectToServer", "Timeout waiting for aloha");
m_state = NetworkConfig::get()->isWAN() ?
HIDING_ADDRESS : DONE;
Log::warn("ConnectToServer", "Timeout waiting for"
" aloha, trying to connect anyway.");
m_state = CONNECTING;
// Reset timer for next usage
m_timer = 0.0;
m_tried_connection = 0;
return;
}
if ((!NetworkConfig::m_disable_lan &&
m_server_address.getIP() ==
@ -183,10 +207,12 @@ void ConnectToServer::asynchronousUpdate()
if (StkTime::getRealTime() > m_timer + 5.0)
{
m_timer = StkTime::getRealTime();
STKHost::get()->stopListening();
STKHost::get()->connect(m_server_address);
STKHost::get()->startListening();
Log::info("ConnectToServer", "Trying to connect to %s",
m_server_address.toString().c_str());
if (m_tried_connection++ > 3)
if (m_tried_connection++ > 1)
{
Log::error("ConnectToServer", "Timeout connect to %s",
m_server_address.toString().c_str());
@ -201,7 +227,8 @@ void ConnectToServer::asynchronousUpdate()
Log::info("ConnectToServer", "Connected");
// LAN networking does not use the stk server tables.
if (NetworkConfig::get()->isWAN() &&
!STKHost::get()->isClientServer())
!STKHost::get()->isClientServer() &&
!STKHost::get()->getPublicAddress().isUnset())
{
auto hide_address = std::make_shared<HidePublicAddress>();
hide_address->requestStart();
@ -239,7 +266,7 @@ void ConnectToServer::update(int ticks)
{
// lobby room protocol if we're connected only
if (STKHost::get()->getPeerCount() > 0 &&
STKHost::get()->getPeers()[0]->isConnected() &&
STKHost::get()->getServerPeerForClient()->isConnected() &&
!m_server_address.isUnset())
{
// Let main thread create ClientLobby for better
@ -264,6 +291,44 @@ void ConnectToServer::update(int ticks)
}
} // update
// ----------------------------------------------------------------------------
bool ConnectToServer::handleDirectConnect()
{
// Direct connection to server should only possbile if public and private
// ports of server are the same
if (NetworkConfig::get()->isWAN() &&
m_server->getPrivatePort() == m_server->getAddress().getPort() &&
!STKHost::get()->isClientServer())
{
ENetEvent event;
ENetAddress ea;
ea.host = STKHost::HOST_ANY;
ea.port = STKHost::PORT_ANY;
Network* dc = new Network(/*peer_count*/1, /*channel_limit*/2,
/*max_in_bandwidth*/0, /*max_out_bandwidth*/0, &ea,
true/*change_port_if_bound*/);
assert(dc);
ENetPeer* p = dc->connectTo(m_server_address);
if (p)
{
while (enet_host_service(dc->getENetHost(), &event, 2000) != 0)
{
if (event.type == ENET_EVENT_TYPE_CONNECT)
{
Log::info("ConnectToServer",
"Direct connection to %s succeed",
m_server_address.toString().c_str());
STKHost::get()->replaceNetwork(event, dc);
m_state = DONE;
return true;
}
}
}
delete dc;
}
return false;
} // handleDirectConnect
// ----------------------------------------------------------------------------
/** Register this client with the STK server.
*/
@ -313,16 +378,14 @@ void ConnectToServer::waitingAloha(bool is_wan)
{
// just send a broadcast packet, the client will know our
// ip address and will connect
STKHost* host = STKHost::get();
host->stopListening(); // stop the listening
STKHost::get()->stopListening(); // stop the listening
Log::info("ConnectToServer", "Waiting broadcast message.");
TransportAddress sender;
// get the sender
const int LEN=256;
char buffer[LEN];
int len = host->receiveRawPacket(buffer, LEN, &sender, 2000);
int len = STKHost::get()->receiveRawPacket(buffer, LEN, &sender, 2000);
if(len<0)
{
Log::warn("ConnectToServer",
@ -333,7 +396,6 @@ void ConnectToServer::waitingAloha(bool is_wan)
BareNetworkString message(buffer, len);
std::string received;
message.decodeString(&received);
host->startListening(); // start listening again
std::string aloha("aloha_stk");
if (received==aloha)
{
@ -360,7 +422,9 @@ bool ConnectToServer::notifyEventAsynchronous(Event* event)
{
Log::info("ConnectToServer", "The Connect To Server protocol has "
"received an event notifying that he's connected to the peer.");
m_state = CONNECTED; // we received a message, we are connected
// We received a message and connected, no need to check for address
// as only 1 peer possible in client
m_state = CONNECTED;
}
return true;
} // notifyEventAsynchronous

View File

@ -56,7 +56,7 @@ private:
void registerWithSTKServer();
void waitingAloha(bool is_wan);
bool handleDirectConnect();
public:
ConnectToServer(std::shared_ptr<Server> server);
virtual ~ConnectToServer();

View File

@ -8,7 +8,7 @@
#include "modes/world.hpp"
#include "network/event.hpp"
#include "network/game_setup.hpp"
#include "network/protocol_manager.hpp"
#include "network/network_config.hpp"
#include "network/stk_host.hpp"
#include "network/stk_peer.hpp"
@ -82,30 +82,23 @@ bool GameEventsProtocol::notifyEvent(Event* event)
*/
void GameEventsProtocol::collectedItem(Item* item, AbstractKart* kart)
{
GameSetup* setup = STKHost::get()->getGameSetup();
assert(setup);
NetworkString *ns = getNetworkString(7);
ns->setSynchronous(true);
// Item picked : send item id, powerup type and kart race id
uint8_t powerup = 0;
if (item->getType() == Item::ITEM_BANANA)
powerup = (int)(kart->getAttachment()->getType());
else if (item->getType() == Item::ITEM_BONUS_BOX)
powerup = (((int)(kart->getPowerup()->getType()) << 4) & 0xf0)
+ (kart->getPowerup()->getNum() & 0x0f);
const std::vector<STKPeer*> &peers = STKHost::get()->getPeers();
for (unsigned int i = 0; i < peers.size(); i++)
{
NetworkString *ns = getNetworkString(7);
ns->setSynchronous(true);
// Item picked : send item id, powerup type and kart race id
uint8_t powerup = 0;
if (item->getType() == Item::ITEM_BANANA)
powerup = (int)(kart->getAttachment()->getType());
else if (item->getType() == Item::ITEM_BONUS_BOX)
powerup = (((int)(kart->getPowerup()->getType()) << 4) & 0xf0)
+ (kart->getPowerup()->getNum() & 0x0f);
ns->addUInt8(GE_ITEM_COLLECTED).addUInt32(item->getItemId())
.addUInt8(powerup).addUInt8(kart->getWorldKartId());
peers[i]->sendPacket(ns, /*reliable*/true);
delete ns;
Log::info("GameEventsProtocol",
"Notified a peer that a kart collected item %d.",
(int)(kart->getPowerup()->getType()));
}
ns->addUInt8(GE_ITEM_COLLECTED).addUInt32(item->getItemId())
.addUInt8(powerup).addUInt8(kart->getWorldKartId());
Log::info("GameEventsProtocol",
"Notified a peer that a kart collected item %d.",
(int)(kart->getPowerup()->getType()));
STKHost::get()->sendPacketToAllPeers(ns, /*reliable*/true);
delete ns;
} // collectedItem
// ----------------------------------------------------------------------------
@ -183,7 +176,7 @@ void GameEventsProtocol::clientHasStarted()
* ready set go. */
void GameEventsProtocol::receivedClientHasStarted(Event *event)
{
assert(NetworkConfig::get()->isServer());
/* assert(NetworkConfig::get()->isServer());
m_count_ready_clients++;
Log::verbose("GameEvent",
"Host %d has started ready-set-go: %d out of %d done",
@ -196,5 +189,5 @@ void GameEventsProtocol::receivedClientHasStarted(Event *event)
// SIgnal the server to start now - since it is now behind the client
// times by the latency of the 'slowest' client.
World::getWorld()->startReadySetGo();
}
}*/
} // receivedClientHasStarted

View File

@ -22,7 +22,6 @@
#include "karts/controller/player_controller.hpp"
#include "network/event.hpp"
#include "network/network_config.hpp"
#include "network/network_player_profile.hpp"
#include "network/game_setup.hpp"
#include "network/network_config.hpp"
#include "network/network_string.hpp"

View File

@ -1,141 +0,0 @@
#include "network/protocols/latency_protocol.hpp"
#include "network/event.hpp"
#include "network/network_config.hpp"
#include "network/stk_host.hpp"
#include "network/stk_peer.hpp"
#include "utils/time.hpp"
//-----------------------------------------------------------------------------
/** This protocol tries to determine the average latency between client and
* server. While this information is not used atm, it might be useful for
* the server to determine how much behind the clients it should start.
* FIXME: ATM the main thread will load the world as part of an update
* of the ProtocolManager (which updates the protocols). Since all protocols
* are locked dusing this update, the synchronisation protocol is actually
* delayed from starting while world is loading (since finding the protocol
* for a message requires the protocol lock) - causing at least two frames
* of significanlty delayed pings :(
*/
LatencyProtocol::LatencyProtocol()
: Protocol(PROTOCOL_SYNCHRONIZATION)
{
unsigned int size = STKHost::get()->getPeerCount();
m_pings.resize(size, std::map<uint32_t,double>());
m_successed_pings.resize(size, 0);
m_total_diff.resize(size, 0);
m_average_ping.resize(size, 0);
m_pings_count = 0;
m_last_time = 0.0;
} // LatencyProtocol
//-----------------------------------------------------------------------------
LatencyProtocol::~LatencyProtocol()
{
} // ~LatencyProtocol
//-----------------------------------------------------------------------------
void LatencyProtocol::setup()
{
Log::info("LatencyProtocol", "Ready !");
} // setup
//-----------------------------------------------------------------------------
/** Called when receiving a message. On the client side the message is a ping
* from the server, which is answered back. On the server the received message
* is a reply to a previous ping request. The server will keep track of
* average latency.
*/
bool LatencyProtocol::notifyEventAsynchronous(Event* event)
{
if (event->getType() != EVENT_TYPE_MESSAGE)
return true;
if(!checkDataSize(event, 5)) return true;
const NetworkString &data = event->data();
uint32_t request = data.getUInt8();
uint32_t sequence = data.getUInt32();
const std::vector<STKPeer*> &peers = STKHost::get()->getPeers();
assert(peers.size() > 0);
// Find the right peer id. The host id (i.e. each host sendings its
// host id) can not be used here, since host ids can have gaps (if a
// host should disconnect)
uint8_t peer_id = -1;
for (unsigned int i = 0; i < peers.size(); i++)
{
if (peers[i]->isSamePeer(event->getPeer()))
{
peer_id = i;
break;
}
}
if (request)
{
// Only a client should receive a request for a ping response
assert(NetworkConfig::get()->isClient());
NetworkString *response = getNetworkString(5);
// The '0' indicates a response to a ping request
response->addUInt8(0).addUInt32(sequence);
event->getPeer()->sendPacket(response, false);
delete response;
}
else // receive response to a ping request
{
// Only a server should receive this kind of message
assert(NetworkConfig::get()->isServer());
if (sequence >= m_pings[peer_id].size())
{
Log::warn("LatencyProtocol",
"The sequence# %u isn't known.", sequence);
return true;
}
double current_time = StkTime::getRealTime();
m_total_diff[peer_id] += current_time - m_pings[peer_id][sequence];
m_successed_pings[peer_id]++;
m_average_ping[peer_id] =
(int)((m_total_diff[peer_id]/m_successed_pings[peer_id])*1000.0);
Log::debug("LatencyProtocol",
"Peer %d sequence %d ping %u average %u at %lf",
peer_id, sequence,
(unsigned int)((current_time - m_pings[peer_id][sequence])*1000),
m_average_ping[peer_id],
StkTime::getRealTime());
}
return true;
} // notifyEventAsynchronous
//-----------------------------------------------------------------------------
/** Waits for the countdown to be started. On the server the start of the
* countdown is triggered by ServerLobby::finishedLoadingWorld(),
* which is called once all clients have confirmed that they are ready to
* start. The server will send a ping request to each client once a second,
* and include the information if the countdown has started (and its current
* value). On the client the countdown is started in notifyEvenAsynchronous()
* when a server ping is received that indicates that the countdown has
* started. The measured times can be used later to estimate the latency
* between server and client.
*/
void LatencyProtocol::asynchronousUpdate()
{
float current_time = float(StkTime::getRealTime());
if (NetworkConfig::get()->isServer() && current_time > m_last_time+1)
{
const std::vector<STKPeer*> &peers = STKHost::get()->getPeers();
for (unsigned int i = 0; i < peers.size(); i++)
{
NetworkString *ping_request =
getNetworkString(5);
ping_request->addUInt8(1).addUInt32((int)m_pings[i].size());
m_pings[i] [ m_pings_count ] = current_time;
peers[i]->sendPacket(ping_request, false);
delete ping_request;
} // for i M peers
m_last_time = current_time;
m_pings_count++;
} // if current_time > m_last_time + 0.1
} // asynchronousUpdate

View File

@ -1,38 +0,0 @@
#ifndef LATENCY_PROTOCOL_HPP
#define LATENCY_PROTOCOL_HPP
#include "network/protocol.hpp"
#include "utils/cpp2011.hpp"
#include <vector>
#include <map>
class LatencyProtocol : public Protocol
{
private:
std::vector<std::map<uint32_t, double> > m_pings;
std::vector<uint32_t> m_average_ping;
/** Counts the number of pings sent. */
uint32_t m_pings_count;
std::vector<uint32_t> m_successed_pings;
std::vector<double> m_total_diff;
/** Keeps track of last time that an update was sent. */
double m_last_time;
public:
LatencyProtocol();
virtual ~LatencyProtocol();
virtual bool notifyEventAsynchronous(Event* event) OVERRIDE;
virtual void setup() OVERRIDE;
virtual void asynchronousUpdate() OVERRIDE;
// ------------------------------------------------------------------------
virtual void update(int ticks) OVERRIDE {}
}; // class LatencyProtocol
#endif // LATENCY_PROTOCOL_HPP

View File

@ -22,14 +22,12 @@
#include "input/input_manager.hpp"
#include "input/device_manager.hpp"
#include "modes/world.hpp"
#include "network/game_setup.hpp"
#include "network/network_player_profile.hpp"
#include "network/protocol_manager.hpp"
#include "network/protocols/game_protocol.hpp"
#include "network/protocols/game_events_protocol.hpp"
#include "network/protocols/latency_protocol.hpp"
#include "network/race_event_manager.hpp"
#include "network/rewind_manager.hpp"
#include "network/stk_host.hpp"
#include "race/race_manager.hpp"
#include "states_screens/state_manager.hpp"
@ -44,6 +42,8 @@ LobbyProtocol::LobbyProtocol(CallbackObject* callback_object)
// ----------------------------------------------------------------------------
LobbyProtocol::~LobbyProtocol()
{
if (m_game_setup)
delete m_game_setup;
} // ~LobbyProtocol
//-----------------------------------------------------------------------------
@ -64,20 +64,39 @@ void LobbyProtocol::loadWorld()
// This creates the network world.
RaceEventManager::getInstance<RaceEventManager>()->start();
// Make sure that if there is only a single local player this player can
// use all input devices.
StateManager::ActivePlayer *ap = race_manager->getNumLocalPlayers()>1
? NULL
: StateManager::get()->getActivePlayer(0);
input_manager->getDeviceManager()->setSinglePlayer(ap);
// Load the actual world.
m_game_setup->loadWorld();
World::getWorld()->setNetworkWorld(true);
GameProtocol::createInstance()->requestStart();
std::make_shared<GameEventsProtocol>()->requestStart();
} // loadWorld
// ----------------------------------------------------------------------------
void LobbyProtocol::configRemoteKart(
const std::vector<std::shared_ptr<NetworkPlayerProfile> >& players) const
{
// The number of karts includes the AI karts, which are not supported atm
race_manager->setNumKarts(m_game_setup->getPlayerCount());
race_manager->setNumKarts((int)players.size());
// Set number of global and local players.
race_manager->setNumPlayers(m_game_setup->getPlayerCount(),
m_game_setup->getNumLocalPlayers());
race_manager->setNumPlayers((int)players.size(),
m_game_setup->getNumLocalPlayers());
// Create the kart information for the race manager:
// -------------------------------------------------
std::vector<NetworkPlayerProfile*> players = m_game_setup->getPlayers();
int local_player_id = 0;
for (unsigned int i = 0; i < players.size(); i++)
{
NetworkPlayerProfile* profile = players[i];
std::shared_ptr<NetworkPlayerProfile> profile = players[i];
bool is_local = profile->isLocalPlayer();
// All non-local players are created here. This means all players
@ -95,12 +114,13 @@ void LobbyProtocol::loadWorld()
// corresponding device associated with it).
RemoteKartInfo rki(is_local ? local_player_id
: i - local_player_id
+ STKHost::get()->getGameSetup()->getNumLocalPlayers(),
+ m_game_setup->getNumLocalPlayers(),
profile->getKartName(),
profile->getName(),
profile->getHostId(),
!is_local);
rki.setGlobalPlayerId(profile->getGlobalPlayerId());
rki.setGlobalPlayerId(i);
rki.setDefaultKartColor(profile->getDefaultKartColor());
rki.setPerPlayerDifficulty(profile->getPerPlayerDifficulty());
if (is_local)
{
@ -111,29 +131,16 @@ void LobbyProtocol::loadWorld()
// Inform the race manager about the data for this kart.
race_manager->setPlayerKart(i, rki);
} // for i in players
// Make sure that if there is only a single local player this player can
// use all input devices.
StateManager::ActivePlayer *ap = race_manager->getNumLocalPlayers()>1
? NULL
: StateManager::get()->getActivePlayer(0);
input_manager->getDeviceManager()->setSinglePlayer(ap);
Log::info("LobbyProtocol", "Player configuration ready.");
} // configRemoteKart
// Load the actual world.
m_game_setup->getRaceConfig()->loadWorld();
World::getWorld()->setNetworkWorld(true);
GameProtocol::createInstance()->requestStart();
std::make_shared<GameEventsProtocol>()->requestStart();
} // loadWorld
// ----------------------------------------------------------------------------
/** Terminates the LatencyProtocol.
//-----------------------------------------------------------------------------
/** A previous GameSetup is deleted and a new one is created.
* \return Newly create GameSetup object.
*/
void LobbyProtocol::terminateLatencyProtocol()
void LobbyProtocol::setup()
{
ProtocolManager::lock()->findAndTerminate(PROTOCOL_SYNCHRONIZATION);
} // stopLatencyProtocol
if (m_game_setup)
delete m_game_setup;
m_game_setup = new GameSetup();
} // setupNewGame

View File

@ -20,10 +20,14 @@
#define LOBBY_PROTOCOL_HPP
#include "network/protocol.hpp"
#include "network/game_setup.hpp"
#include "network/network_string.hpp"
class GameSetup;
class NetworkPlayerProfile;
#include <memory>
#include <vector>
/*!
* \class LobbyProtocol
* \brief Base class for both client and server lobby. The lobbies are started
@ -34,15 +38,15 @@ class LobbyProtocol : public Protocol
{
public:
/** Lists all lobby events (LE). */
enum
{
enum : uint8_t
{
LE_CONNECTION_REQUESTED = 1, // a connection to the server
LE_CONNECTION_REFUSED, // Connection to server refused
LE_CONNECTION_ACCEPTED, // Connection to server accepted
LE_KART_SELECTION_UPDATE, // inform client about kart selected
LE_REQUEST_BEGIN, // begin of kart selection
LE_KART_SELECTION_REFUSED, // Client not auth. to start selection
LE_NEW_PLAYER_CONNECTED, // inform client about new player
LE_UPDATE_PLAYER_LIST, // inform client about player list update
LE_KART_SELECTION, // Player selected kart
LE_PLAYER_DISCONNECTED, // Client disconnected
LE_CLIENT_LOADED_WORLD, // Client finished loading world
@ -53,21 +57,30 @@ public:
LE_RACE_FINISHED, // race has finished, display result
LE_RACE_FINISHED_ACK, // client went back to lobby
LE_EXIT_RESULT, // Force clients to exit race result screen
LE_VOTE, // Any vote (race mode, track, ...)
LE_VOTE_MAJOR, // vote of major race mode
LE_VOTE_MINOR, // vote for minor race mode
LE_VOTE_RACE_COUNT, // vote for number of tracks
LE_VOTE_TRACK, // vote for a track
LE_VOTE_REVERSE, // vote if race in reverse
LE_VOTE_LAPS, // vote number of laps
LE_VOTE, // Track vote
LE_CHAT,
LE_SERVER_OWNERSHIP,
LE_KICK_HOST
};
enum RejectReason : uint8_t
{
RR_BUSY = 0,
RR_BANNED = 1,
RR_INCORRECT_PASSWORD = 2,
RR_INCOMPATIBLE_DATA = 3,
RR_TOO_MANY_PLAYERS = 4
};
protected:
static std::weak_ptr<LobbyProtocol> m_lobby;
/** The game setup. */
/** Stores data about the online game to play. */
GameSetup* m_game_setup;
void configRemoteKart(
const std::vector<std::shared_ptr<NetworkPlayerProfile> >& players) const;
public:
/** Creates either a client or server lobby protocol as a singleton. */
@ -101,12 +114,7 @@ public:
virtual void finishedLoadingWorld() = 0;
virtual void loadWorld();
virtual bool waitingForPlayers() const = 0;
void terminateLatencyProtocol();
virtual void requestKartSelection(uint8_t player_id,
const std::string &kart_name)
{
assert(false); // Only defined in client
};
GameSetup* getGameSetup() const { return m_game_setup; }
}; // class LobbyProtocol

View File

@ -113,7 +113,14 @@ void RequestConnection::asynchronousUpdate()
// Direct socket always listens on server discovery port
server_addr.setPort(NetworkConfig::get()
->getServerDiscoveryPort());
STKHost::get()->sendRawPacket(message, server_addr);
// Avoid possible packet loss, the connect to peer done by
// server will auto terminate if same peer from same port
// has connected already
for (int i = 0; i < 5; i++)
{
STKHost::get()->sendRawPacket(message, server_addr);
StkTime::sleep(1);
}
m_state = DONE;
}
else

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,15 @@
#include "network/protocols/lobby_protocol.hpp"
#include "utils/cpp2011.hpp"
#include "utils/synchronised.hpp"
#include <atomic>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <tuple>
class STKPeer;
class ServerLobby : public LobbyProtocol
{
@ -30,24 +35,24 @@ public:
private:
std::atomic<ServerState> m_state;
/** Hold the next connected peer for server owner if current one expired
* (disconnected). */
std::weak_ptr<STKPeer> m_server_owner;
/** Available karts and tracks for all clients, this will be initialized
* with data in server first. */
Synchronised<std::pair<std::set<std::string>,
std::set<std::string> > > m_available_kts;
/** Next id to assign to a peer. */
Synchronised<int> m_next_player_id;
std::pair<std::set<std::string>, std::set<std::string> > m_available_kts;
/** Keeps track of the server state. */
bool m_server_has_loaded_world;
std::atomic_bool m_server_has_loaded_world;
/** Counts how many clients have finished loading the world. */
Synchronised<int> m_client_ready_count;
/** Counts how many peers have finished loading the world. */
std::map<std::weak_ptr<STKPeer>, bool,
std::owner_less<std::weak_ptr<STKPeer> > > m_peers_ready;
/** For debugging: keep track of the state (ready or not) of each player,
* to make sure no client/player reports more than once. Needs to be a
* map since the client IDs can be non-consecutive. */
std::map<uint8_t, bool> m_player_states;
/** Vote from each peer. */
std::map<std::weak_ptr<STKPeer>, std::tuple<std::string, uint8_t, bool>,
std::owner_less<std::weak_ptr<STKPeer> > > m_peers_votes;
/** Keeps track of an artificial server delay (which makes sure that the
* data from all clients has arrived when the server computes a certain
@ -55,7 +60,7 @@ private:
* seconds), which is the real time at which the server should start. */
double m_server_delay;
bool m_selection_enabled;
bool m_has_created_server_id_file;
/** It indicates if this server is registered with the stk server. */
std::atomic_bool m_server_registered;
@ -63,27 +68,36 @@ private:
/** Counts how many players are ready to go on. */
int m_player_ready_counter;
/** Timeout counter for showing the result screen. */
float m_timeout;
/** Timeout counter for various state. */
std::atomic<float> m_timeout;
/** Lock this mutex whenever a client is connect / disconnect or
* starting race. */
std::mutex m_connection_mutex;
/** Ban list ip (in decimal) with online user id. */
std::map<uint32_t, uint32_t> m_ban_list;
// connection management
void clientDisconnected(Event* event);
void connectionRequested(Event* event);
// kart selection
void kartSelectionRequested(Event* event);
// race votes
void playerMajorVote(Event* event);
void playerRaceCountVote(Event* event);
void playerMinorVote(Event* event);
void playerTrackVote(Event* event);
void playerReversedVote(Event* event);
void playerLapsVote(Event* event);
// Track(s) votes
void playerVote(Event *event);
void playerFinishedResult(Event *event);
void registerServer();
void finishedLoadingWorldClient(Event *event);
void startedRaceOnClient(Event *event);
void kickHost(Event* event);
void handleChat(Event* event);
void unregisterServer();
void createServerIdFile();
void updatePlayerList();
void updateServerOwner();
bool checkPeersReady() const;
std::tuple<std::string, uint8_t, bool> handleVote();
public:
ServerLobby();
virtual ~ServerLobby();
@ -98,8 +112,9 @@ public:
void startSelection(const Event *event=NULL);
void checkIncomingConnectionRequests();
void checkRaceFinished();
void finishedLoadingWorld();
void finishedLoadingWorld() OVERRIDE;
ServerState getCurrentState() const { return m_state.load(); }
void updateBanList();
virtual bool waitingForPlayers() const OVERRIDE
{ return m_state.load() == ACCEPTING_CLIENTS; }

View File

@ -1,452 +0,0 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2014-2015 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 "network/race_config.hpp"
#include "config/user_config.hpp"
#include "network/network_config.hpp"
#include "race/race_manager.hpp"
#include "utils/log.hpp"
#include <stdexcept>
/** \brief Gets the element with the highest count in a std::map<S,int>.
* \param histogram : A pointer to the histogram.
* \return The key of type S that has the highest second value.
*/
template<typename S>
S getHighestInHistogram(std::map<S,int>* histogram, S default_value)
{
if(histogram->empty())
return default_value;
S best_item = histogram->begin()->first;
uint8_t highest_count = histogram->begin()->second;
for (typename std::map<S, int>::iterator it = histogram->begin();
it != histogram->end(); it++)
{
if (it->second > highest_count)
{
highest_count = it->second;
best_item = it->first;
}
}
return best_item;
} // getHighestInHistogram
//-----------------------------------------------------------------------------
//--------------------------------- TrackVote --------------------------------
//-----------------------------------------------------------------------------
/** Constructor for a track vote.
*/
TrackVote::TrackVote()
{
has_voted_laps = false;
has_voted_track = false;
has_voted_reversed = false;
} // TrackVote
//-----------------------------------------------------------------------------
/** Sets that this vote is for the specified track.
*/
void TrackVote::voteTrack(const std::string &track)
{
track_info.track = track;
has_voted_track = true;
} // voteTrack
//-----------------------------------------------------------------------------
/** Sets if this vote is for normal or reversed driving.
* \param reversed True if this vote is for reversed racing.
*/
void TrackVote::voteReversed(bool reversed)
{
track_info.reversed = reversed;
has_voted_reversed = true;
} // voteReversed
//-----------------------------------------------------------------------------
/** Votes for the number of laps.
* \param laps Numger of laps.
*/
void TrackVote::voteLaps(uint8_t laps)
{
track_info.laps = laps;
has_voted_laps = true;
} // voteLaps
//-----------------------------------------------------------------------------
//--------------------------------- RaceVote ---------------------------------
//-----------------------------------------------------------------------------
/** Constructor for a race vote.
*/
RaceVote::RaceVote()
{
m_has_voted_major = false;
m_has_voted_minor = false;
m_has_voted_races_count = false;
m_major_mode = 0;
m_minor_mode = 0;
m_races_count = 0;
m_tracks_vote.resize(1);
} // RaceVote
//-----------------------------------------------------------------------------
/** Sets the selected major race vote.
*/
void RaceVote::voteMajor(uint32_t major)
{
m_has_voted_major = true;
m_major_mode = major;
} // voteMajor
//-----------------------------------------------------------------------------
/** Sets the vote for race count.
*/
void RaceVote::voteRaceCount(uint8_t count)
{
m_has_voted_races_count = true;
m_races_count = count;
} // voteRaceCount
//-----------------------------------------------------------------------------
/** Sets vote for minor race mode.
*/
void RaceVote::voteMinor(uint32_t minor)
{
m_has_voted_minor = true;
m_minor_mode = minor;
} // voteMinor
//-----------------------------------------------------------------------------
/** Sets a track vote.
* \param track Name of the track.
*/
void RaceVote::voteTrack(const std::string &track, uint8_t track_number)
{
m_tracks_vote[track_number].voteTrack(track);
} // voteTrack
//-----------------------------------------------------------------------------
/** Sets a vote for reveresed racing.
*/
void RaceVote::voteReversed(bool reversed, uint8_t track_number)
{
m_tracks_vote[track_number].voteReversed(reversed);
} // voteReversed
//-----------------------------------------------------------------------------
/** Sets a vote for number of laps.
*/
void RaceVote::voteLaps(uint8_t laps, uint8_t track_number)
{
m_tracks_vote[track_number].voteLaps(laps);
} // voteLaps
//-----------------------------------------------------------------------------
bool RaceVote::hasVotedMajor() const
{
return m_has_voted_major;
} // hasVotedMajor
//-----------------------------------------------------------------------------
bool RaceVote::hasVotedRacesCount() const
{
return m_has_voted_races_count;
} // hasVotedRacesCount
//-----------------------------------------------------------------------------
bool RaceVote::hasVotedMinor() const
{
return m_has_voted_minor;
} // hasVotedMinor
//-----------------------------------------------------------------------------
bool RaceVote::hasVotedTrack(uint8_t track_number) const
{
return m_tracks_vote[track_number].has_voted_track;
} // hasVotedTrack
//-----------------------------------------------------------------------------
bool RaceVote::hasVotedReversed(uint8_t track_number) const
{
return m_tracks_vote[track_number].has_voted_reversed;
} // hasVotedReversed
//-----------------------------------------------------------------------------
bool RaceVote::hasVotedLaps(uint8_t track_number) const
{
return m_tracks_vote[track_number].has_voted_laps;
} // hasVotedLaps
//-----------------------------------------------------------------------------
uint8_t RaceVote::getMajorVote() const
{
return m_major_mode;
} // getMajorVote
//-----------------------------------------------------------------------------
uint8_t RaceVote::getRacesCountVote() const
{
return m_races_count;
} // getRacesCountVote
//-----------------------------------------------------------------------------
uint8_t RaceVote::getMinorVote() const
{
return m_minor_mode;
} // getMinorVote
//-----------------------------------------------------------------------------
const std::string &RaceVote::getTrackVote(uint8_t track_number) const
{
return m_tracks_vote[track_number].track_info.track;
} // getTrackVote
//-----------------------------------------------------------------------------
bool RaceVote::getReversedVote(uint8_t track_number) const
{
return m_tracks_vote[track_number].track_info.reversed;
} // getReversedVote
//-----------------------------------------------------------------------------
uint8_t RaceVote::getLapsVote(uint8_t track_number) const
{
return m_tracks_vote[track_number].track_info.laps;
} // getLapsVote
//-----------------------------------------------------------------------------
//--------------------------------- RaceConfig -------------------------------
//-----------------------------------------------------------------------------
RaceConfig::RaceConfig()
{
m_max_players = NetworkConfig::get()->getMaxPlayers();
} // RaceConfig
//-----------------------------------------------------------------------------
void RaceConfig::setPlayerMajorVote(uint8_t player_id, uint32_t major)
{
Log::info("RaceConfig", "Player %d voted for major %d", player_id, major);
m_votes[player_id].voteMajor(major);
} // setPlayerMajorVote
//-----------------------------------------------------------------------------
void RaceConfig::setPlayerRaceCountVote(uint8_t player_id, uint8_t count)
{
Log::info("RaceConfig", "Player %d voted for %d races in GP",
player_id, count);
m_votes[player_id].voteRaceCount(count);
} // setPlayerRaceCountVote
//-----------------------------------------------------------------------------
void RaceConfig::setPlayerMinorVote(uint8_t player_id, uint32_t minor)
{
Log::info("RaceConfig", "Player %d voted for minor %d", player_id, minor);
m_votes[player_id].voteMinor(minor);
} // setPlayerMinorVote
//-----------------------------------------------------------------------------
void RaceConfig::setPlayerTrackVote(uint8_t player_id,
const std::string &track, uint8_t track_nb)
{
Log::info("RaceConfig", "Player %d voted for track %s",
player_id, track.c_str());
m_votes[player_id].voteTrack(track, track_nb);
} // setPlayerTrackVote
//-----------------------------------------------------------------------------
void RaceConfig::setPlayerReversedVote(uint8_t player_id, bool reversed,
uint8_t track_nb)
{
if (reversed)
Log::info("RaceConfig", "Player %d voted map %d to be reversed",
player_id, track_nb);
else
Log::info("RaceConfig", "Player %d voted map %d NOT to be reversed",
player_id, track_nb);
m_votes[player_id].voteReversed(reversed, track_nb);
} // setPlayerReversedVote
//-----------------------------------------------------------------------------
void RaceConfig::setPlayerLapsVote(uint8_t player_id, uint8_t lap_count,
uint8_t track_nb)
{
Log::info("RaceConfig", "Player %d voted map %d to have %d laps",
player_id, track_nb, lap_count);
m_votes[player_id].voteLaps(lap_count, track_nb);
} // setPlayerLapsVote
//-----------------------------------------------------------------------------
/** Computes the selected race mode.
*/
void RaceConfig::computeRaceMode()
{
// calculate the race type and number of tracks (in GP mode).
std::map<int,int> major_histogram;
std::map<int,int> races_count_histogram;
std::map<int,int> minor_histogram;
for (unsigned int i = 0; i < m_max_players; i++)
{
// increase the count of votes
if (m_votes[i].hasVotedMajor())
{
try
{
major_histogram.at(m_votes[i].getMajorVote()) ++;
}
catch (const std::out_of_range&) // doesn't exist in the map
{
major_histogram[m_votes[i].getMajorVote()] = 1;
}
}
else if (m_votes[i].hasVotedRacesCount())
{
try
{
races_count_histogram.at(m_votes[i].getRacesCountVote()) ++;
}
catch (const std::out_of_range&) // doesn't exist in the map
{
races_count_histogram[m_votes[i].getRacesCountVote()] = 1;
}
}
else if (m_votes[i].hasVotedMinor())
{
try
{
minor_histogram.at(m_votes[i].getMinorVote()) ++;
}
catch (const std::out_of_range&) // doesn't exist in the map
{
minor_histogram[m_votes[i].getMinorVote()] = 1;
}
}
}
// now we know :
m_major_mode = getHighestInHistogram(&major_histogram,
(int)RaceManager::MAJOR_MODE_SINGLE);
m_minor_mode = getHighestInHistogram(&minor_histogram,
(int)RaceManager::MINOR_MODE_NORMAL_RACE);
if (m_major_mode == RaceManager::MAJOR_MODE_GRAND_PRIX)
{
m_races_count = getHighestInHistogram(&races_count_histogram, 1);
m_tracks.resize(m_races_count);
}
else
{
m_tracks.resize(1);
m_races_count = 1;
}
Log::info("RaceConfig", "Major mode will be %d with %d races. Minor is %d",
m_major_mode, m_races_count, m_minor_mode);
} // computeRaceMode
// ----------------------------------------------------------------------------
void RaceConfig::computeNextTrack()
{
for (unsigned int j = 0; j < m_races_count; j++)
{
// first create histograms of the votes
std::map<std::string,int> tracks_histogram;
std::map<bool,int> reversed_histogram;
std::map<int,int> laps_histogram;
for (unsigned int i = 0; i < m_max_players; i++)
{
// increase the count of votes
if (m_votes[i].hasVotedTrack())
{
try // maps
{
tracks_histogram.at(m_votes[i].getTrackVote()) ++;
}
catch (const std::out_of_range&) // doesn't exist in the map
{
tracks_histogram[m_votes[i].getTrackVote()] = 1;
}
}
if (m_votes[i].hasVotedReversed())
{
try // reversed
{
reversed_histogram.at(m_votes[i].getReversedVote()) ++;
}
catch (const std::out_of_range&) // doesn't exist in the map
{
reversed_histogram[m_votes[i].getReversedVote()] = 1;
}
}
if (m_votes[i].hasVotedLaps())
{
try // laps
{
laps_histogram.at(m_votes[i].getLapsVote()) ++;
}
catch (const std::out_of_range&) // doesn't exist in the mapt
{
laps_histogram[m_votes[i].getLapsVote()] = 1;
}
}
}
// now find the highest votes
m_tracks[j].track = getHighestInHistogram<std::string>(&tracks_histogram,
UserConfigParams::m_last_track);
m_tracks[j].reversed = getHighestInHistogram(&reversed_histogram, false);
m_tracks[j].laps = getHighestInHistogram<int>(&laps_histogram,
UserConfigParams::m_num_laps);
if (m_tracks[j].reversed)
Log::info("RaceConfig",
"Race %d will be on %s with %d laps and reversed",
j, m_tracks[j].track.c_str(), m_tracks[j].laps);
else
Log::info("RaceConfig", "Race %d will be on %s with %d laps",
j, m_tracks[j].track.c_str(), m_tracks[j].laps);
}
} // computeNextTrack
//-----------------------------------------------------------------------------
/** Computes the selected setting (based on the users' vote) and sets them
* in the race manager. Then it loads the world.
*/
void RaceConfig::loadWorld()
{
computeRaceMode();
computeNextTrack();
race_manager->startSingleRace(m_tracks[0].track, m_tracks[0].laps,
m_tracks[0].reversed);
} // loadWorld
//-----------------------------------------------------------------------------
const TrackInfo* RaceConfig::getNextTrackInfo() const
{
return &m_tracks[0];
} // getNextTrackInfo
//-----------------------------------------------------------------------------
int RaceConfig::getNumTrackVotes() const
{
int count = 0;
for (auto entry : m_votes)
{
if (entry.second.hasVotedTrack())
count++;
}
return count;
} // getNumTrackVotes

View File

@ -1,136 +0,0 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2014-2015 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.
#ifndef RACE_CONFIG_HPP
#define RACE_CONFIG_HPP
#include <string>
#include <vector>
#include <map>
#include "utils/types.hpp"
/** Stores the name of a track, number of laps, and reverse driving.
*/
class TrackInfo
{
public:
std::string track;
bool reversed;
uint8_t laps;
TrackInfo() { laps = 0; reversed = false; }
}; // TrackInfo
// ============================================================================
/** Stores a vote about the name of a track, number of laps, and reverse
* driving.
*/
class TrackVote
{
public:
TrackVote();
void voteTrack(const std::string &track);
void voteReversed(bool reversed);
void voteLaps(uint8_t laps);
TrackInfo track_info;
bool has_voted_track;
bool has_voted_reversed;
bool has_voted_laps;
}; // class TrackVote
// ============================================================================
class RaceVote
{
public:
RaceVote();
void voteMajor(uint32_t major);
void voteRaceCount(uint8_t count);
void voteMinor(uint32_t minor);
void voteTrack(const std::string &track, uint8_t track_number = 0);
void voteReversed(bool reversed, uint8_t track_number = 0);
void voteLaps(uint8_t laps, uint8_t track_number = 0);
bool hasVotedMajor() const;
bool hasVotedRacesCount() const;
bool hasVotedMinor() const;
bool hasVotedTrack(uint8_t track_number = 0) const;
bool hasVotedReversed(uint8_t track_number = 0) const;
bool hasVotedLaps(uint8_t track_number = 0) const;
uint8_t getMajorVote() const;
uint8_t getRacesCountVote() const;
uint8_t getMinorVote() const;
const std::string &getTrackVote(uint8_t track_number = 0) const;
bool getReversedVote(uint8_t track_number = 0) const;
uint8_t getLapsVote(uint8_t track_number = 0) const;
private:
uint32_t m_major_mode;
uint32_t m_minor_mode;
uint8_t m_races_count; //!< Stores the number of races that will be in a GP
bool m_has_voted_major;
bool m_has_voted_minor;
bool m_has_voted_races_count;
std::vector<TrackVote> m_tracks_vote;
}; // RaceVote
// ============================================================================
class RaceConfig
{
private:
void computeRaceMode();
void computeNextTrack();
public:
RaceConfig();
void setPlayerMajorVote(uint8_t player_id, uint32_t major);
void setPlayerRaceCountVote(uint8_t player_id, uint8_t count);
void setPlayerMinorVote(uint8_t player_id, uint32_t minor);
void setPlayerTrackVote(uint8_t player_id, const std::string &track,
uint8_t track_nb = 0);
void setPlayerReversedVote(uint8_t player_id, bool reversed,
uint8_t track_nb = 0);
void setPlayerLapsVote(uint8_t player_id, uint8_t lap_count,
uint8_t track_nb = 0);
void loadWorld();
const TrackInfo* getNextTrackInfo() const;
bool getReverse() const;
bool getLapCount() const;
int getNumTrackVotes() const;
const RaceVote& getRaceVote(int global_player_id) { return m_votes[global_player_id]; }
int getMaxPlayers() const { return m_max_players; }
protected:
std::vector<TrackInfo> m_tracks;
int m_minor_mode;
int m_major_mode;
unsigned int m_races_count;
/** Key: globalPlayerID */
std::map<int, RaceVote> m_votes;
uint8_t m_max_players;
}; // class RaceConfig
#endif // RACE_CONFIG_HPP

View File

@ -34,9 +34,9 @@ enum SoccerTeam
};
/** Game difficulty per player. */
enum PerPlayerDifficulty
enum PerPlayerDifficulty : uint8_t
{
PLAYER_DIFFICULTY_NORMAL,
PLAYER_DIFFICULTY_NORMAL = 0,
PLAYER_DIFFICULTY_HANDICAP,
PLAYER_DIFFICULTY_COUNT
};
@ -51,22 +51,31 @@ class RemoteKartInfo
SoccerTeam m_soccer_team;
bool m_network_player;
PerPlayerDifficulty m_difficulty;
float m_default_kart_color;
public:
RemoteKartInfo(int player_id, const std::string& kart_name,
const irr::core::stringw& user_name, int host_id,
bool network)
: m_kart_name(kart_name), m_user_name(user_name),
m_local_player_id(player_id), m_host_id(host_id),
m_soccer_team(SOCCER_TEAM_NONE), m_network_player(network),
m_difficulty(PLAYER_DIFFICULTY_NORMAL)
m_local_player_id(player_id), m_global_player_id(-1),
m_host_id(host_id), m_soccer_team(SOCCER_TEAM_NONE),
m_network_player(network),
m_difficulty(PLAYER_DIFFICULTY_NORMAL),
m_default_kart_color(0.0f)
{}
RemoteKartInfo(const std::string& kart_name) : m_kart_name(kart_name),
m_user_name(""), m_local_player_id(-1), m_host_id(-1),
m_difficulty(PLAYER_DIFFICULTY_NORMAL)
m_user_name(""), m_local_player_id(-1),
m_global_player_id(-1), m_host_id(-1),
m_soccer_team(SOCCER_TEAM_NONE), m_network_player(false),
m_difficulty(PLAYER_DIFFICULTY_NORMAL),
m_default_kart_color(0.0f)
{}
RemoteKartInfo() : m_kart_name(""), m_user_name(""), m_local_player_id(-1),
m_host_id(-1), m_difficulty(PLAYER_DIFFICULTY_NORMAL)
RemoteKartInfo() : m_kart_name(""), m_user_name(""),
m_local_player_id(-1), m_global_player_id(-1),
m_host_id(-1), m_soccer_team(SOCCER_TEAM_NONE),
m_network_player(false),
m_difficulty(PLAYER_DIFFICULTY_NORMAL),
m_default_kart_color(0.0f)
{}
void setKartName(const std::string& n) { m_kart_name = n; }
void setPlayerName(const irr::core::stringw& u) { m_user_name = u; }
@ -75,6 +84,7 @@ public:
void setGlobalPlayerId(int id) { m_global_player_id = id; }
void setSoccerTeam(SoccerTeam team) { m_soccer_team = team; }
void setNetworkPlayer(bool value) { m_network_player = value; }
void setDefaultKartColor(float value) { m_default_kart_color = value; }
void setPerPlayerDifficulty(PerPlayerDifficulty value)
{ m_difficulty = value; }
int getHostId() const { return m_host_id; }
@ -85,6 +95,7 @@ public:
const irr::core::stringw& getPlayerName() const { return m_user_name; }
SoccerTeam getSoccerTeam() const { return m_soccer_team; }
PerPlayerDifficulty getDifficulty() const { return m_difficulty; }
float getDefaultKartColor() const { return m_default_kart_color; }
bool operator<(const RemoteKartInfo& other) const
{

View File

@ -16,6 +16,10 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network/server.hpp"
#include "config/player_manager.hpp"
#include "online/online_player_profile.hpp"
#include "online/online_profile.hpp"
#include "online/profile_manager.hpp"
#include "network/network_config.hpp"
#include "io/xml_node.hpp"
#include "utils/constants.hpp"
@ -34,6 +38,7 @@ Server::Server(const XMLNode& xml)
m_server_id = 0;
m_current_players = 0;
m_max_players = 0;
m_distance = 0.0f;
unsigned server_data = 0;
xml.get("game_mode", &server_data);
m_minor_mode = NetworkConfig::get()->getLocalGameMode(server_data).first;
@ -46,7 +51,7 @@ Server::Server(const XMLNode& xml)
m_lower_case_name = StringUtils::toLowerCase(m_lower_case_name);
xml.get("id", &m_server_id);
xml.get("hostid", &m_host_id);
xml.get("host_id", &m_server_owner);
xml.get("max_players", &m_max_players);
xml.get("current_players", &m_current_players);
uint32_t ip;
@ -56,6 +61,36 @@ Server::Server(const XMLNode& xml)
xml.get("port", &port);
m_address.setPort(port);
xml.get("private_port", &m_private_port);
xml.get("password", &m_password_protected);
xml.get("distance", &m_distance);
m_server_owner_name = "-";
// Display server owner name if he's your friend or localhost
Online::OnlineProfile* opp = PlayerManager::getCurrentPlayer()->getProfile();
// Check localhost owner
if (opp && opp->getID() == m_server_owner)
{
m_server_owner_name =
StringUtils::wideToUtf8(opp->getUserName());
}
else if (opp && opp->hasFetchedFriends())
{
// Check friend(s)
for (uint32_t user_id : opp->getFriends())
{
if (user_id == m_server_owner)
{
Online::OnlineProfile* friend_profile =
Online::ProfileManager::get()->getProfileByID(user_id);
if (friend_profile)
{
m_server_owner_name =
StringUtils::wideToUtf8(friend_profile->getUserName());
}
}
}
}
} // Server(const XML&)
// ----------------------------------------------------------------------------
@ -69,16 +104,17 @@ Server::Server(const XMLNode& xml)
* \param difficulty The difficulty of server.
* \param server_mode The game modes of server (including minor and major).
* \param address IP and port of the server.
* \param password_protected True if can only be joined with a password.
*/
Server::Server(unsigned server_id, const core::stringw &name, int max_players,
int current_players, unsigned difficulty, unsigned server_mode,
const TransportAddress &address)
const TransportAddress &address, bool password_protected)
{
m_name = name;
m_lower_case_name = StringUtils::toLowerCase(StringUtils::wideToUtf8(name));
m_satisfaction_score = 0;
m_server_id = server_id;
m_host_id = 0;
m_server_owner = 0;
m_current_players = current_players;
m_max_players = max_players;
m_address.copy(address);
@ -87,6 +123,8 @@ Server::Server(unsigned server_id, const core::stringw &name, int max_players,
m_difficulty = (RaceManager::Difficulty)difficulty;
m_minor_mode = NetworkConfig::get()->getLocalGameMode(server_mode).first;
m_major_mode = NetworkConfig::get()->getLocalGameMode(server_mode).second;
m_password_protected = password_protected;
m_distance = 0.0f;
} // server(server_id, ...)
// ----------------------------------------------------------------------------

View File

@ -49,7 +49,7 @@ protected:
std::string m_lower_case_name;
uint32_t m_server_id;
uint32_t m_host_id;
uint32_t m_server_owner;
/** The maximum number of players that the server supports */
int m_max_players;
@ -74,13 +74,22 @@ protected:
RaceManager::Difficulty m_difficulty;
bool m_password_protected;
/* WAN server only, show the owner name of server, can only be seen
* for localhost or if you are friend with the server owner. */
std::string m_server_owner_name;
/* WAN server only, distance based on IP latitude and longitude. */
float m_distance;
public:
/** Initialises the object from an XML node. */
Server(const XMLNode &xml);
Server(unsigned server_id, const irr::core::stringw &name,
int max_players, int current_players, unsigned difficulty,
unsigned server_mode, const TransportAddress &address);
unsigned server_mode, const TransportAddress &address,
bool password_protected);
bool filterByWords(const irr::core::stringw words) const;
// ------------------------------------------------------------------------
/** Returns ip address and port of this server. */
@ -95,9 +104,8 @@ public:
/** Returns the ID of this server. */
const uint32_t getServerId() const { return m_server_id; }
// ------------------------------------------------------------------------
/** Returns the unique host id of this server (wan game only), which is
* the user id in STK addon server of the server owner. */
const uint32_t getHostId() const { return m_host_id; }
/** Returns the user id in STK addon server of the server owner (WAN). */
const uint32_t getServerOwner() const { return m_server_owner; }
// ------------------------------------------------------------------------
uint16_t getPrivatePort() const { return m_private_port; }
// ------------------------------------------------------------------------
@ -114,7 +122,13 @@ public:
{ return m_major_mode; }
// ------------------------------------------------------------------------
RaceManager::Difficulty getDifficulty() const { return m_difficulty; }
// ------------------------------------------------------------------------
bool isPasswordProtected() const { return m_password_protected; }
// ------------------------------------------------------------------------
const std::string& getServerOwnerName() const
{ return m_server_owner_name; }
// ------------------------------------------------------------------------
float getDistance() const { return m_distance; }
}; // Server
#endif // HEADER_SERVER_HPP

View File

@ -150,7 +150,7 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const
double start_time = StkTime::getRealTime();
const double DURATION = 1.0;
const auto& servers = ServersManager::get()->getServers();
int cur_server_id = servers.size();
int cur_server_id = (int)servers.size();
assert(cur_server_id == 0);
std::vector<std::shared_ptr<Server> > servers_now;
while (StkTime::getRealTime() - start_time < DURATION)
@ -160,6 +160,12 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const
if (len > 0)
{
BareNetworkString s(buffer, len);
uint8_t version = s.getUInt8();
if (version != NetworkConfig::m_server_version)
{
Log::verbose("ServersManager", "Skipping a server");
continue;
}
irr::core::stringw name;
// bytes_read is the number of bytes read
s.decodeStringW(&name);
@ -169,9 +175,10 @@ Online::XMLRequest* ServersManager::getLANRefreshRequest() const
uint8_t difficulty = s.getUInt8();
uint8_t mode = s.getUInt8();
sender.setPort(port);
uint8_t password = s.getUInt8();
servers_now.emplace_back(std::make_shared<Server>
(cur_server_id++, name, max_players, players,
difficulty, mode, sender));
difficulty, mode, sender, password == 1));
} // if received_data
} // while still waiting
m_success = true;
@ -229,6 +236,14 @@ void ServersManager::setWanServers(bool success, const XMLNode* input)
const XMLNode *servers_xml = input->getNode("servers");
for (unsigned int i = 0; i < servers_xml->getNumNodes(); i++)
{
unsigned version = 0;
servers_xml->getNode(i)->get("version", &version);
assert(version != 0);
if (version != NetworkConfig::m_server_version)
{
Log::verbose("ServersManager", "Skipping a server");
continue;
}
m_servers.emplace_back(
std::make_shared<Server>(*servers_xml->getNode(i)));
}

View File

@ -20,9 +20,6 @@
#define HEADER_SERVERS_MANAGER_HPP
#include <atomic>
#include <algorithm>
#include <cassert>
#include <functional>
#include <memory>
#include <string>
#include <vector>
@ -70,17 +67,9 @@ public:
// ------------------------------------------------------------------------
void cleanUpServers() { m_servers.clear(); }
// ------------------------------------------------------------------------
void sortServers(std::function<bool(const std::shared_ptr<Server> a,
const std::shared_ptr<Server> b)> sorting_function)
{
assert(m_list_updated);
std::sort(m_servers.begin(), m_servers.end(), sorting_function);
}
// ------------------------------------------------------------------------
bool refresh();
// ------------------------------------------------------------------------
const std::vector<std::shared_ptr<Server> >& getServers() const
{ return m_servers; }
std::vector<std::shared_ptr<Server> >& getServers() { return m_servers; }
// ------------------------------------------------------------------------
bool listUpdated() const { return m_list_updated; }

View File

@ -21,11 +21,11 @@
#include "config/user_config.hpp"
#include "io/file_manager.hpp"
#include "network/event.hpp"
#include "network/game_setup.hpp"
#include "network/network_config.hpp"
#include "network/network_console.hpp"
#include "network/network_string.hpp"
#include "network/protocols/connect_to_peer.hpp"
#include "network/protocols/connect_to_server.hpp"
#include "network/protocols/server_lobby.hpp"
#include "network/protocol_manager.hpp"
#include "network/stk_peer.hpp"
@ -59,8 +59,10 @@
#include <algorithm>
#include <functional>
#include <limits>
#include <random>
#include <string>
#include <utility>
STKHost *STKHost::m_stk_host = NULL;
bool STKHost::m_enable_console = false;
@ -69,7 +71,11 @@ void STKHost::create(std::shared_ptr<Server> server, SeparateProcess* p)
{
assert(m_stk_host == NULL);
if (NetworkConfig::get()->isServer())
{
std::shared_ptr<ServerLobby> sl = LobbyProtocol::create<ServerLobby>();
m_stk_host = new STKHost(NetworkConfig::get()->getServerName());
sl->requestStart();
}
else
{
m_stk_host = new STKHost(server);
@ -256,7 +262,6 @@ void STKHost::create(std::shared_ptr<Server> server, SeparateProcess* p)
*/
STKHost::STKHost(std::shared_ptr<Server> server)
{
m_next_unique_host_id = -1;
// Will be overwritten with the correct value once a connection with the
// server is made.
m_host_id = 0;
@ -276,7 +281,6 @@ STKHost::STKHost(std::shared_ptr<Server> server)
}
setPrivatePort();
std::make_shared<ConnectToServer>(server)->requestStart();
} // STKHost
// ----------------------------------------------------------------------------
@ -286,16 +290,14 @@ STKHost::STKHost(std::shared_ptr<Server> server)
STKHost::STKHost(const irr::core::stringw &server_name)
{
init();
// The host id will be increased whenever a new peer is added, so the
// first client will have host id 1 (host id 0 is the server).
m_next_unique_host_id = 0;
m_host_id = 0; // indicates a server host.
ENetAddress addr;
addr.host = STKHost::HOST_ANY;
addr.port = NetworkConfig::get()->getServerPort();
m_network= new Network(NetworkConfig::get()->getMaxPlayers(),
// Reserver 1 peer to handle full server message
m_network = new Network(NetworkConfig::get()->getMaxPlayers() + 1,
/*channel_limit*/2,
/*max_in_bandwidth*/0,
/*max_out_bandwidth*/ 0, &addr,
@ -305,11 +307,7 @@ STKHost::STKHost(const irr::core::stringw &server_name)
Log::fatal("STKHost", "An error occurred while trying to create an "
"ENet server host.");
}
setPrivatePort();
ProtocolManager::lock()
->requestStart(LobbyProtocol::create<ServerLobby>());
} // STKHost(server_name)
// ----------------------------------------------------------------------------
@ -319,11 +317,9 @@ STKHost::STKHost(const irr::core::stringw &server_name)
void STKHost::init()
{
m_shutdown = false;
m_authorised = false;
m_network = NULL;
m_game_setup = NULL;
m_exit_flag.clear();
m_exit_flag.test_and_set();
m_exit_timeout.store(std::numeric_limits<double>::max());
// Start with initialising ENet
// ============================
@ -354,18 +350,8 @@ STKHost::~STKHost()
requestShutdown();
if (m_network_console.joinable())
m_network_console.join();
// delete the game setup
if (m_game_setup)
delete m_game_setup;
m_game_setup = NULL;
// Delete all connected peers
while (!m_peers.empty())
{
delete m_peers.back();
m_peers.pop_back();
}
disconnectAllPeers(true/*timeout_waiting*/);
Network::closeLog();
stopListening();
@ -391,7 +377,6 @@ STKHost::~STKHost()
void STKHost::shutdown()
{
ProtocolManager::lock()->abort();
deleteAllPeers();
destroy();
} // shutdown
@ -400,16 +385,29 @@ void STKHost::shutdown()
*/
void STKHost::setPublicAddress()
{
std::vector<std::string> untried_server = UserConfigParams::m_stun_servers;
// Generate random list of stun servers
std::vector<std::pair<std::string, uint32_t> > untried_server;
for (auto& p : UserConfigParams::m_stun_list)
untried_server.push_back(p);
assert(untried_server.size() > 2);
// Randomly use stun servers of the low ping from top-half of the list
std::sort(untried_server.begin(), untried_server.end(),
[] (const std::pair<std::string, uint32_t>& a,
const std::pair<std::string, uint32_t>& b)->bool
{
return a.second > b.second;
});
std::random_device rd;
std::mt19937 g(rd());
std::shuffle(untried_server.begin(), untried_server.end(), g);
std::shuffle(untried_server.begin() + (untried_server.size() / 2),
untried_server.end(), g);
while (!untried_server.empty())
{
// Pick last element in untried servers
const char* server_name = untried_server.back().c_str();
Log::debug("STKHost", "Using STUN server %s", server_name);
std::string server_name = untried_server.back().first.c_str();
UserConfigParams::m_stun_list[server_name] = (uint32_t)-1;
Log::debug("STKHost", "Using STUN server %s", server_name.c_str());
struct addrinfo hints, *res;
@ -418,11 +416,11 @@ void STKHost::setPublicAddress()
hints.ai_socktype = SOCK_STREAM;
// Resolve the stun server name so we can send it a STUN request
int status = getaddrinfo(server_name, NULL, &hints, &res);
int status = getaddrinfo(server_name.c_str(), NULL, &hints, &res);
if (status != 0)
{
Log::error("STKHost", "Error in getaddrinfo for stun server"
" %s: %s", server_name, gai_strerror(status));
" %s: %s", server_name.c_str(), gai_strerror(status));
untried_server.pop_back();
continue;
}
@ -453,6 +451,7 @@ void STKHost::setPublicAddress()
}
m_network->sendRawPacket(s, m_stun_address);
double ping = StkTime::getRealTime();
freeaddrinfo(res);
// Recieve now
@ -460,6 +459,7 @@ void STKHost::setPublicAddress()
const int LEN = 2048;
char buffer[LEN];
int len = m_network->receiveRawPacket(buffer, LEN, &sender, 2000);
ping = StkTime::getRealTime() - ping;
if (sender.getIP() != m_stun_address.getIP())
{
@ -597,6 +597,9 @@ void STKHost::setPublicAddress()
Log::warn("STKHost", "Only non xor-mapped address returned.");
m_public_address.copy(non_xor_addr);
}
// Succeed, save ping
UserConfigParams::m_stun_list[server_name] =
(uint32_t)(ping * 1000.0);
untried_server.clear();
}
}
@ -618,44 +621,22 @@ void STKHost::setPrivatePort()
} // setPrivatePort
//-----------------------------------------------------------------------------
/** A previous GameSetup is deletea and a new one is created.
* \return Newly create GameSetup object.
*/
GameSetup* STKHost::setupNewGame()
{
if (m_game_setup)
delete m_game_setup;
m_game_setup = new GameSetup();
return m_game_setup;
} // setupNewGame
//-----------------------------------------------------------------------------
/** Called when you leave a server.
/** Disconnect all connected peers.
*/
void STKHost::deleteAllPeers()
void STKHost::disconnectAllPeers(bool timeout_waiting)
{
// remove all peers
for (unsigned int i = 0; i < m_peers.size(); i++)
std::lock_guard<std::mutex> lock(m_peers_mutex);
if (!m_peers.empty() && timeout_waiting)
{
delete m_peers[i];
m_peers[i] = NULL;
for (auto peer : m_peers)
peer.second->disconnect();
// Wait for at most 2 seconds for disconnect event to be generated
m_exit_timeout.store(StkTime::getRealTime() + 2.0);
}
m_peers.clear();
} // deleteAllPeers
} // disconnectAllPeers
// ----------------------------------------------------------------------------
/** Called when STK exits. It stops the listening thread and the
* ProtocolManager.
*/
void STKHost::abort()
{
// Finish protocol manager first, to avoid that it access data
// in STKHost.
ProtocolManager::lock()->abort();
stopListening();
} // abort
// --------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** Sets an error message for the gui.
*/
void STKHost::setErrorMessage(const irr::core::stringw &message)
@ -668,13 +649,14 @@ void STKHost::setErrorMessage(const irr::core::stringw &message)
m_error_message = message;
} // setErrorMessage
// --------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** \brief Try to establish a connection to a given transport address.
* \param peer : The transport address which you want to connect to.
* \return True if we're successfully connected. False elseway.
*/
bool STKHost::connect(const TransportAddress& address)
{
assert(NetworkConfig::get()->isClient());
if (peerExists(address))
return isConnectedTo(address);
@ -696,8 +678,7 @@ bool STKHost::connect(const TransportAddress& address)
*/
void STKHost::startListening()
{
m_exit_flag.clear();
m_exit_flag.test_and_set();
m_exit_timeout.store(std::numeric_limits<double>::max());
m_listening_thread = std::thread(std::bind(&STKHost::mainLoop, this));
} // startListening
@ -707,28 +688,12 @@ void STKHost::startListening()
*/
void STKHost::stopListening()
{
m_exit_flag.clear();
if (m_exit_timeout.load() == std::numeric_limits<double>::max())
m_exit_timeout.store(0.0);
if (m_listening_thread.joinable())
m_listening_thread.join();
} // stopListening
// ----------------------------------------------------------------------------
/** Returns true if this client instance is allowed to control the server.
* A client can authorise itself by providing the server's password. It is
* then allowed to control the server (e.g. start kart selection).
* The information if this client was authorised by the server is actually
* stored in the peer (which is the server peer on a client).
*/
bool STKHost::isAuthorisedToControl() const
{
assert(NetworkConfig::get()->isClient());
// If we are not properly connected (i.e. only enet connection, but not
// stk logic), no peer is authorised.
if(m_peers.size()==0)
return false;
return m_peers[0]->isAuthorised();
} // isAuthorisedToControl
// ----------------------------------------------------------------------------
/** \brief Thread function checking if data is received.
* This function tries to get data from network low-level functions as
@ -739,12 +704,14 @@ bool STKHost::isAuthorisedToControl() const
void STKHost::mainLoop()
{
VS::setThreadName("STKHost");
Log::info("STKHost", "Listening has been started.");
ENetEvent event;
ENetHost* host = m_network->getENetHost();
const bool is_server = NetworkConfig::get()->isServer();
// A separate network connection (socket) to handle LAN requests.
Network* direct_socket = NULL;
if ((NetworkConfig::get()->isLAN() && NetworkConfig::get()->isServer()) ||
if ((NetworkConfig::get()->isLAN() && is_server) ||
NetworkConfig::get()->isPublicServer())
{
TransportAddress address(0,
@ -760,50 +727,158 @@ void STKHost::mainLoop()
}
}
while (m_exit_flag.test_and_set())
while (m_exit_timeout.load() > StkTime::getRealTime())
{
auto sl = LobbyProtocol::get<ServerLobby>();
if (direct_socket && sl && sl->waitingForPlayers())
{
handleDirectSocketRequest(direct_socket);
handleDirectSocketRequest(direct_socket, sl);
} // if discovery host
while (enet_host_service(host, &event, 20) != 0)
if (is_server)
{
std::unique_lock<std::mutex> peer_lock(m_peers_mutex);
// Remove any peer which has no token for 7 seconds
// The token is set when the first connection request has happened
for (auto it = m_peers.begin(); it != m_peers.end();)
{
if (!it->second->isClientServerTokenSet() &&
(float)StkTime::getRealTime() >
it->second->getConnectedTime() + 7.0f)
{
Log::info("STKHost", "%s has no token for more than 7"
" seconds, disconnect it by force.",
it->second->getAddress().toString().c_str());
enet_host_flush(host);
enet_peer_reset(it->first);
it = m_peers.erase(it);
}
else
{
it++;
}
}
peer_lock.unlock();
}
std::list<std::tuple<ENetPeer*, ENetPacket*, uint32_t,
ENetCommandType> > copied_list;
std::unique_lock<std::mutex> lock(m_enet_cmd_mutex);
std::swap(copied_list, m_enet_cmd);
lock.unlock();
for (auto& p : copied_list)
{
switch (std::get<3>(p))
{
case ECT_SEND_PACKET:
enet_peer_send(std::get<0>(p), (uint8_t)std::get<2>(p),
std::get<1>(p));
break;
case ECT_DISCONNECT:
enet_peer_disconnect(std::get<0>(p), std::get<2>(p));
break;
case ECT_RESET:
// Flush enet before reset (so previous command is send)
enet_host_flush(host);
enet_peer_reset(std::get<0>(p));
// Remove the stk peer of it
std::lock_guard<std::mutex> lock(m_peers_mutex);
m_peers.erase(std::get<0>(p));
break;
}
}
while (enet_host_service(host, &event, 0) != 0)
{
if (event.type == ENET_EVENT_TYPE_NONE)
continue;
auto pm = ProtocolManager::lock();
if (!pm || pm->isExiting())
Event* stk_event = NULL;
if (event.type == ENET_EVENT_TYPE_CONNECT)
{
// Don't create more event if no protocol manager or it will
// be exiting
auto stk_peer = std::make_shared<STKPeer>
(event.peer, this, m_next_unique_host_id++);
std::unique_lock<std::mutex> lock(m_peers_mutex);
m_peers[event.peer] = stk_peer;
lock.unlock();
stk_event = new Event(&event, stk_peer);
TransportAddress addr(event.peer->address);
Log::info("STKHost", "%s has just connected. There are "
"now %u peers.", addr.toString().c_str(), getPeerCount());
} // ENET_EVENT_TYPE_CONNECT
else if (event.type == ENET_EVENT_TYPE_DISCONNECT)
{
Log::flushBuffers();
// If used a timeout waiting disconnect, exit now
if (m_exit_timeout.load() !=
std::numeric_limits<double>::max())
{
m_exit_timeout.store(0.0);
break;
}
// Use the previous stk peer so protocol can see the network
// profile and handle it for disconnection
if (m_peers.find(event.peer) != m_peers.end())
{
stk_event = new Event(&event, m_peers.at(event.peer));
std::lock_guard<std::mutex> lock(m_peers_mutex);
m_peers.erase(event.peer);
}
TransportAddress addr(event.peer->address);
Log::info("STKHost", "%s has just disconnected. There are "
"now %u peers.", addr.toString().c_str(), getPeerCount());
} // ENET_EVENT_TYPE_DISCONNECT
if (!stk_event && m_peers.find(event.peer) != m_peers.end())
{
auto& peer = m_peers.at(event.peer);
unsigned token = 0;
// Token is after the protocol type (1 byte) in stk network
// string (network order)
token += event.packet->data[1];
token <<= 8;
token += event.packet->data[2];
token <<= 8;
token += event.packet->data[3];
token <<= 8;
token += event.packet->data[4];
if (is_server && ((!peer->isClientServerTokenSet() &&
!isConnectionRequestPacket(event.packet->data,
(int)event.packet->dataLength)) ||
(token != peer->getClientServerToken())))
{
// For server discard all events from wrong or unset token
// peers if that is not a connection request
if (token != peer->getClientServerToken())
{
Log::error("STKHost", "Received event with invalid token!");
Log::error("STKHost", "HostID %d Token %d message token %d",
peer->getHostId(), peer->getClientServerToken(), token);
NetworkString wrong_event(event.packet->data,
(int)event.packet->dataLength);
Log::error("STKHost", wrong_event.getLogMessage().c_str());
peer->unsetClientServerToken();
}
enet_packet_destroy(event.packet);
continue;
}
stk_event = new Event(&event, peer);
}
else if (!stk_event)
{
enet_packet_destroy(event.packet);
continue;
}
// Create an STKEvent with the event data. This will also
// create the peer if it doesn't exist already
Event* stk_event = new Event(&event);
STKPeer* peer = stk_event->getPeer();
if (stk_event->getType() == EVENT_TYPE_CONNECTED)
{
Log::info("STKHost", "A client has just connected. There are "
"now %lu peers.", m_peers.size());
Log::debug("STKHost", "Addresses are : %lx, %lx",
stk_event->getPeer(), peer);
} // EVENT_TYPE_CONNECTED
else if (stk_event->getType() == EVENT_TYPE_DISCONNECTED)
{
Log::info("STKHost", "A client has just disconnected.");
Log::flushBuffers();
} // EVENT_TYPE_CONNECTED
else if (stk_event->getType() == EVENT_TYPE_MESSAGE)
if (stk_event->getType() == EVENT_TYPE_MESSAGE)
{
Network::logPacket(stk_event->data(), true);
TransportAddress stk_addr(peer->getAddress());
#ifdef DEBUG_MESSAGE_CONTENT
Log::verbose("NetworkManager",
"Message, Sender : %s time %f message:",
stk_addr.toString(/*show port*/false).c_str(),
stk_event->getPeer()->getAddress()
.toString(/*show port*/false).c_str(),
StkTime::getRealTime());
Log::verbose("NetworkManager", "%s",
stk_event->data().getLogMessage().c_str());
@ -811,12 +886,16 @@ void STKHost::mainLoop()
} // if message event
// notify for the event now.
pm->propagateEvent(stk_event);
auto pm = ProtocolManager::lock();
if (pm && !pm->isExiting())
pm->propagateEvent(stk_event);
else
delete stk_event;
} // while enet_host_service
} // while m_exit_flag.test_and_set()
StkTime::sleep(10);
} // while m_exit_timeout.load() > StkTime::getRealTime()
delete direct_socket;
Log::info("STKHost", "Listening has been stopped");
Log::info("STKHost", "Listening has been stopped.");
} // mainLoop
// ----------------------------------------------------------------------------
@ -828,7 +907,8 @@ void STKHost::mainLoop()
* message is received, will answer with a message containing server details
* (and sender IP address and port).
*/
void STKHost::handleDirectSocketRequest(Network* direct_socket)
void STKHost::handleDirectSocketRequest(Network* direct_socket,
std::shared_ptr<ServerLobby> sl)
{
const int LEN=2048;
char buffer[LEN];
@ -853,18 +933,18 @@ void STKHost::handleDirectSocketRequest(Network* direct_socket)
name = name.substr(0, 255);
// Send the answer, consisting of server name, max players,
// current players, and the client's ip address and port
// number (which solves the problem which network interface
// might be the right one if there is more than one).
// current players
BareNetworkString s((int)name.size()+1+11);
s.addUInt8(NetworkConfig::m_server_version);
s.encodeString(name);
s.addUInt8(NetworkConfig::get()->getMaxPlayers());
s.addUInt8(0); // FIXME: current number of connected players
s.addUInt8((uint8_t)sl->getGameSetup()->getPlayerCount());
s.addUInt16(m_private_port);
s.addUInt8((uint8_t)race_manager->getDifficulty());
s.addUInt8((uint8_t)
NetworkConfig::get()->getServerGameMode(race_manager->getMinorMode(),
race_manager->getMajorMode()));
s.addUInt8(!NetworkConfig::get()->getPassword().empty());
direct_socket->sendRawPacket(s, sender);
} // if message is server-requested
else if (command == connection_cmd)
@ -906,66 +986,29 @@ void STKHost::handleDirectSocketRequest(Network* direct_socket)
*/
bool STKHost::peerExists(const TransportAddress& peer)
{
ENetHost *host = m_network->getENetHost();
for (unsigned int i = 0; i < host->peerCount ; i++)
std::lock_guard<std::mutex> lock(m_peers_mutex);
for (auto p : m_peers)
{
if (host->peers[i].address.host == ntohl(peer.getIP()) &&
host->peers[i].address.port == peer.getPort() )
{
auto stk_peer = p.second;
if (stk_peer->getAddress() == peer ||
((stk_peer->getAddress().isPublicAddressLocalhost() ||
peer.isPublicAddressLocalhost()) &&
stk_peer->getAddress().getPort() == peer.getPort()))
return true;
}
}
return false;
} // peerExists
// ----------------------------------------------------------------------------
std::vector<NetworkPlayerProfile*> STKHost::getMyPlayerProfiles()
{
return m_game_setup->getAllPlayersOnHost(m_host_id);
} // getMyPlayerProfiles
// ----------------------------------------------------------------------------
/** Returns the STK peer belonging to the given enet_peer. If no STKPeer
* exists, create a new STKPeer.
* \param enet_peer The EnetPeer.
*/
STKPeer* STKHost::getPeer(ENetPeer *enet_peer)
{
for(unsigned int i=0; i<m_peers.size(); i++)
{
if(m_peers[i]->isSamePeer(enet_peer))
return m_peers[i];
}
// Make sure that a client only adds one other peer (=the server).
if(NetworkConfig::get()->isClient() && m_peers.size()>0)
{
Log::error("STKHost",
"Client is adding more than one server, ignored for now.");
}
//FIXME Should we check #clients here? It might be easier to only
// handle this at connect time, not in all getPeer calls.
STKPeer *peer = new STKPeer(enet_peer);
Log::debug("getPeer",
"Creating a new peer, address are STKPeer:%p, Peer:%p",
peer, enet_peer);
m_peers.push_back(peer);
m_next_unique_host_id ++;
return peer;
} // getPeer
// ----------------------------------------------------------------------------
/** \brief Return the only server peer for client.
* \return STKPeer the STKPeer of server.
*/
STKPeer* STKHost::getServerPeerForClient() const
std::shared_ptr<STKPeer> STKHost::getServerPeerForClient() const
{
assert(m_peers.size() == 1);
assert(NetworkConfig::get()->isClient());
return m_peers[0];
if (m_peers.size() != 1)
return nullptr;
return m_peers.begin()->second;
} // getServerPeerForClient
// ----------------------------------------------------------------------------
@ -986,43 +1029,20 @@ bool STKHost::isConnectedTo(const TransportAddress& peer)
return false;
} // isConnectedTo
// ----------------------------------------------------------------------------
void STKHost::removePeer(const STKPeer* peer)
//-----------------------------------------------------------------------------
/** Sends data to all peers
* \param data Data to sent.
* \param reliable If the data should be sent reliable or now.
*/
void STKHost::sendPacketToAllPeers(NetworkString *data, bool reliable)
{
if (!peer || !peer->exists()) // peer does not exist (already removed)
return;
TransportAddress addr(peer->getAddress());
Log::debug("STKHost", "Disconnected host: %s", addr.toString().c_str());
// remove the peer:
bool removed = false;
for (unsigned int i = 0; i < m_peers.size(); i++)
std::lock_guard<std::mutex> lock(m_peers_mutex);
for (auto p : m_peers)
{
if (m_peers[i]->isSamePeer(peer) && !removed) // remove only one
{
delete m_peers[i];
m_peers.erase(m_peers.begin() + i, m_peers.begin() + i + 1);
Log::verbose("NetworkManager",
"The peer has been removed from the Network Manager.");
removed = true;
}
else if (m_peers[i]->isSamePeer(peer))
{
Log::fatal("NetworkManager",
"Multiple peers match the disconnected one.");
}
} // for i < m_peers.size()
if (!removed)
Log::warn("NetworkManager", "The peer that has been disconnected was "
"not registered by the Network Manager.");
Log::info("NetworkManager",
"Somebody is now disconnected. There are now %lu peers.",
m_peers.size());
} // removePeer
if (p.second->isClientServerTokenSet())
p.second->sendPacket(data, reliable);
}
} // sendPacketExcept
//-----------------------------------------------------------------------------
/** Sends data to all peers except the specified one.
@ -1033,13 +1053,79 @@ void STKHost::removePeer(const STKPeer* peer)
void STKHost::sendPacketExcept(STKPeer* peer, NetworkString *data,
bool reliable)
{
for (unsigned int i = 0; i < m_peers.size(); i++)
std::lock_guard<std::mutex> lock(m_peers_mutex);
for (auto p : m_peers)
{
STKPeer* p = m_peers[i];
if (!p->isSamePeer(peer))
STKPeer* stk_peer = p.second.get();
if (!stk_peer->isSamePeer(peer) && p.second->isClientServerTokenSet())
{
p->sendPacket(data, reliable);
stk_peer->sendPacket(data, reliable);
}
}
} // sendPacketExcept
//-----------------------------------------------------------------------------
/** Sends a message from a client to the server. */
void STKHost::sendToServer(NetworkString *data, bool reliable)
{
std::lock_guard<std::mutex> lock(m_peers_mutex);
if (m_peers.empty())
return;
assert(NetworkConfig::get()->isClient());
m_peers.begin()->second->sendPacket(data, reliable);
} // sendToServer
//-----------------------------------------------------------------------------
std::vector<std::shared_ptr<NetworkPlayerProfile> >
STKHost::getAllPlayerProfiles() const
{
std::vector<std::shared_ptr<NetworkPlayerProfile> > p;
std::unique_lock<std::mutex> lock(m_peers_mutex);
for (auto peer : m_peers)
{
auto peer_profile = peer.second->getPlayerProfiles();
p.insert(p.end(), peer_profile.begin(), peer_profile.end());
}
lock.unlock();
return p;
} // getAllPlayerProfiles
//-----------------------------------------------------------------------------
std::shared_ptr<STKPeer> STKHost::findPeerByHostId(uint32_t id) const
{
std::lock_guard<std::mutex> lock(m_peers_mutex);
auto ret = std::find_if(m_peers.begin(), m_peers.end(),
[id](const std::pair<ENetPeer*, std::shared_ptr<STKPeer> >& p)
{
return p.second->getHostId() == id;
});
return ret != m_peers.end() ? ret->second : nullptr;
} // findPeerByHostId
//-----------------------------------------------------------------------------
void STKHost::replaceNetwork(ENetEvent& event, Network* network)
{
assert(NetworkConfig::get()->isClient());
assert(!m_listening_thread.joinable());
assert(network->getENetHost()->peerCount == 1);
delete m_network;
m_network = network;
auto stk_peer = std::make_shared<STKPeer>(event.peer, this,
m_next_unique_host_id++);
m_peers[event.peer] = stk_peer;
setPrivatePort();
startListening();
auto pm = ProtocolManager::lock();
if (pm && !pm->isExiting())
pm->propagateEvent(new Event(&event, stk_peer));
} // replaceNetwork
//-----------------------------------------------------------------------------
bool STKHost::isConnectionRequestPacket(unsigned char* data, int length)
{
if (length < 6)
return false;
// Connection request is not synchronous
return (uint8_t)data[0] == PROTOCOL_LOBBY_ROOM &&
(uint8_t)data[5] == LobbyProtocol::LE_CONNECTION_REQUESTED;
} // isConnectionRequestPacket

View File

@ -23,11 +23,8 @@
#define STK_HOST_HPP
#include "network/network.hpp"
#include "network/network_config.hpp"
#include "network/network_string.hpp"
#include "network/stk_peer.hpp"
#include "network/transport_address.hpp"
#include "utils/synchronised.hpp"
#include "irrString.h"
@ -38,13 +35,26 @@
#include <enet/enet.h>
#include <atomic>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <thread>
#include <tuple>
class GameSetup;
class NetworkPlayerProfile;
class Server;
class ServerLobby;
class SeparateProcess;
enum ENetCommandType : unsigned int
{
ECT_SEND_PACKET = 0,
ECT_DISCONNECT = 1,
ECT_RESET = 2
};
class STKHost
{
public:
@ -58,9 +68,6 @@ public:
PORT_ANY = 0 //!< Any port.
};
friend class STKPeer; // allow direct enet modifications in implementations
private:
/** Singleton pointer to the instance. */
static STKHost* m_stk_host;
@ -74,20 +81,29 @@ private:
/** Network console thread */
std::thread m_network_console;
/** Make sure the removing or adding a peer is thread-safe. */
mutable std::mutex m_peers_mutex;
/** Let (atm enet_peer_send and enet_peer_disconnect) run in the listening
* thread. */
std::list<std::tuple</*peer receive*/ENetPeer*,
/*packet to send*/ENetPacket*, /*integer data*/uint32_t,
ENetCommandType> > m_enet_cmd;
/** Protect \ref m_enet_cmd from multiple threads usage. */
std::mutex m_enet_cmd_mutex;
/** The list of peers connected to this instance. */
std::vector<STKPeer*> m_peers;
std::map<ENetPeer*, std::shared_ptr<STKPeer> > m_peers;
/** Next unique host id. It is increased whenever a new peer is added (see
* getPeer()), but not decreased whena host (=peer) disconnects. This
* results in a unique host id for each host, even when a host should
* disconnect and then reconnect. */
int m_next_unique_host_id;
uint32_t m_next_unique_host_id = 0;
/** Host id of this host. */
uint8_t m_host_id;
/** Stores data about the online game to play. */
GameSetup* m_game_setup;
uint32_t m_host_id = 0;
/** Id of thread listening to enet events. */
std::thread m_listening_thread;
@ -96,8 +112,11 @@ private:
* triggers a shutdown of the STKHost (and the Protocolmanager). */
std::atomic_bool m_shutdown;
/** Atomic flag used to stop this thread. */
std::atomic_flag m_exit_flag = ATOMIC_FLAG_INIT;
/** True if this local host is authorised to control a server. */
std::atomic_bool m_authorised;
/** Use as a timeout to waiting a disconnect event when exiting. */
std::atomic<double> m_exit_timeout;
/** An error message, which is set by a protocol to be displayed
* in the GUI. */
@ -112,23 +131,26 @@ private:
/** The private port enet socket is bound. */
uint16_t m_private_port;
/** An error message, which is set by a protocol to be displayed
* in the GUI. */
STKHost(std::shared_ptr<Server> server);
STKHost(const irr::core::stringw &server_name);
virtual ~STKHost();
// ------------------------------------------------------------------------
STKHost(std::shared_ptr<Server> server);
// ------------------------------------------------------------------------
STKHost(const irr::core::stringw &server_name);
// ------------------------------------------------------------------------
~STKHost();
// ------------------------------------------------------------------------
void init();
void handleDirectSocketRequest(Network* direct_socket);
// ------------------------------------------------------------------------
void handleDirectSocketRequest(Network* direct_socket,
std::shared_ptr<ServerLobby> sl);
// ------------------------------------------------------------------------
void mainLoop();
// ------------------------------------------------------------------------
bool isConnectionRequestPacket(unsigned char* data, int length);
public:
/** If a network console should be started. Note that the console can cause
* a crash in release mode on windows (see #1529). */
/** If a network console should be started. */
static bool m_enable_console;
/** Creates the STKHost. It takes all confifguration parameters from
* NetworkConfig. This STKHost can either be a client or a server.
*/
@ -156,21 +178,17 @@ public:
const TransportAddress& getPublicAddress() const
{ return m_public_address; }
// ------------------------------------------------------------------------
const TransportAddress& getStunAddress() const
{ return m_stun_address; }
const TransportAddress& getStunAddress() const { return m_stun_address; }
// ------------------------------------------------------------------------
uint16_t getPrivatePort() const
{ return m_private_port; }
uint16_t getPrivatePort() const { return m_private_port; }
// ------------------------------------------------------------------------
void setPrivatePort();
// ------------------------------------------------------------------------
void setPublicAddress();
// ------------------------------------------------------------------------
virtual GameSetup* setupNewGame();
void abort();
void deleteAllPeers();
void disconnectAllPeers(bool timeout_waiting = false);
// ------------------------------------------------------------------------
bool connect(const TransportAddress& peer);
//-------------------------------------------------------------------------
/** Requests that the network infrastructure is to be shut down. This
* function is called from a thread, but the actual shutdown needs to be
@ -185,24 +203,48 @@ public:
} // requestExit
//-------------------------------------------------------------------------
void shutdown();
void sendPacketExcept(STKPeer* peer,
NetworkString *data,
//-------------------------------------------------------------------------
void sendPacketToAllPeers(NetworkString *data, bool reliable = true);
// ------------------------------------------------------------------------
/** Returns true if this client instance is allowed to control the server.
* It will auto transfer ownership if previous server owner disconnected.
*/
bool isAuthorisedToControl() const { return m_authorised.load(); }
// ------------------------------------------------------------------------
/** Sets if this local host is authorised to control the server. */
void setAuthorisedToControl(bool authorised)
{ m_authorised.store(authorised); }
// ------------------------------------------------------------------------
std::vector<std::shared_ptr<NetworkPlayerProfile> >
getAllPlayerProfiles() const;
// ------------------------------------------------------------------------
std::shared_ptr<STKPeer> findPeerByHostId(uint32_t id) const;
// ------------------------------------------------------------------------
void sendPacketExcept(STKPeer* peer, NetworkString *data,
bool reliable = true);
void setupClient(int peer_count, int channel_limit,
uint32_t max_incoming_bandwidth,
uint32_t max_outgoing_bandwidth);
void startListening();
void stopListening();
bool peerExists(const TransportAddress& peer_address);
void removePeer(const STKPeer* peer);
bool isConnectedTo(const TransportAddress& peer_address);
STKPeer *getPeer(ENetPeer *enet_peer);
STKPeer *getServerPeerForClient() const;
std::vector<NetworkPlayerProfile*> getMyPlayerProfiles();
void setErrorMessage(const irr::core::stringw &message);
bool isAuthorisedToControl() const;
// ------------------------------------------------------------------------
void setupClient(int peer_count, int channel_limit,
uint32_t max_incoming_bandwidth,
uint32_t max_outgoing_bandwidth);
// ------------------------------------------------------------------------
void startListening();
// ------------------------------------------------------------------------
void stopListening();
// ------------------------------------------------------------------------
bool peerExists(const TransportAddress& peer_address);
// ------------------------------------------------------------------------
bool isConnectedTo(const TransportAddress& peer_address);
// ------------------------------------------------------------------------
std::shared_ptr<STKPeer> getServerPeerForClient() const;
// ------------------------------------------------------------------------
void setErrorMessage(const irr::core::stringw &message);
// ------------------------------------------------------------------------
void addEnetCommand(ENetPeer* peer, ENetPacket* packet, uint32_t i,
ENetCommandType ect)
{
std::lock_guard<std::mutex> lock(m_enet_cmd_mutex);
m_enet_cmd.emplace_back(peer, packet, i, ect);
}
// ------------------------------------------------------------------------
/** Returns the last error (or "" if no error has happened). */
const irr::core::stringw& getErrorMessage() const
@ -212,9 +254,6 @@ public:
* requested. */
bool requestedShutdown() const { return m_shutdown.load(); }
// ------------------------------------------------------------------------
/** Returns the current game setup. */
GameSetup* getGameSetup() { return m_game_setup; }
// ------------------------------------------------------------------------
int receiveRawPacket(char *buffer, int buffer_len,
TransportAddress* sender, int max_tries = -1)
{
@ -228,8 +267,17 @@ public:
m_network->sendRawPacket(buffer, dst);
} // sendRawPacket
// ------------------------------------------------------------------------
/** Returns a const reference to the list of peers. */
const std::vector<STKPeer*> &getPeers() { return m_peers; }
/** Returns a copied list of peers. */
std::vector<std::shared_ptr<STKPeer> > getPeers() const
{
std::lock_guard<std::mutex> lock(m_peers_mutex);
std::vector<std::shared_ptr<STKPeer> > peers;
for (auto p : m_peers)
{
peers.push_back(p.second);
}
return peers;
}
// ------------------------------------------------------------------------
/** Returns the next (unique) host id. */
unsigned int getNextHostId() const
@ -239,24 +287,25 @@ public:
}
// ------------------------------------------------------------------------
/** Returns the number of currently connected peers. */
unsigned int getPeerCount() { return (int)m_peers.size(); }
unsigned int getPeerCount() const
{
std::lock_guard<std::mutex> lock(m_peers_mutex);
return (unsigned)m_peers.size();
}
// ------------------------------------------------------------------------
/** Sets the global host id of this host. */
void setMyHostId(uint8_t my_host_id) { m_host_id = my_host_id; }
/** Sets the global host id of this host (client use). */
void setMyHostId(uint32_t my_host_id) { m_host_id = my_host_id; }
// ------------------------------------------------------------------------
/** Returns the host id of this host. */
uint8_t getMyHostId() const { return m_host_id; }
uint32_t getMyHostId() const { return m_host_id; }
// ------------------------------------------------------------------------
/** Sends a message from a client to the server. */
void sendToServer(NetworkString *data, bool reliable = true)
{
assert(NetworkConfig::get()->isClient());
m_peers[0]->sendPacket(data, reliable);
} // sendToServer
void sendToServer(NetworkString *data, bool reliable = true);
// ------------------------------------------------------------------------
/** True if this is a client and server in graphics mode made by server
* creation screen. */
bool isClientServer() const { return m_separate_process != NULL; }
// ------------------------------------------------------------------------
void replaceNetwork(ENetEvent& event, Network* network);
}; // class STKHost

View File

@ -17,6 +17,7 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network/stk_peer.hpp"
#include "config/user_config.hpp"
#include "network/game_setup.hpp"
#include "network/network_string.hpp"
#include "network/network_player_profile.hpp"
@ -29,32 +30,50 @@
/** Constructor for an empty peer.
*/
STKPeer::STKPeer(ENetPeer *enet_peer)
STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
: m_peer_address(enet_peer->address), m_host(host)
{
m_enet_peer = enet_peer;
m_is_authorised = false;
m_client_server_token = 0;
m_host_id = 0;
m_token_set = false;
m_host_id = host_id;
m_connected_time = (float)StkTime::getRealTime();
m_token_set.store(false);
m_client_server_token.store(0);
} // STKPeer
//-----------------------------------------------------------------------------
/** Destructor.
*/
STKPeer::~STKPeer()
{
m_enet_peer = NULL;
m_client_server_token = 0;
} // ~STKPeer
//-----------------------------------------------------------------------------
/** Disconnect from the server.
*/
void STKPeer::disconnect()
{
enet_peer_disconnect(m_enet_peer, 0);
TransportAddress a(m_enet_peer->address);
if (m_enet_peer->state != ENET_PEER_STATE_CONNECTED ||
a != m_peer_address)
return;
m_host->addEnetCommand(m_enet_peer, NULL, PDI_NORMAL, ECT_DISCONNECT);
} // disconnect
//-----------------------------------------------------------------------------
/** Kick this peer (used by server).
*/
void STKPeer::kick()
{
TransportAddress a(m_enet_peer->address);
if (m_enet_peer->state != ENET_PEER_STATE_CONNECTED ||
a != m_peer_address)
return;
m_host->addEnetCommand(m_enet_peer, NULL, PDI_KICK, ECT_DISCONNECT);
} // kick
//-----------------------------------------------------------------------------
/** Forcefully disconnects a peer (used by server).
*/
void STKPeer::reset()
{
TransportAddress a(m_enet_peer->address);
if (m_enet_peer->state != ENET_PEER_STATE_CONNECTED ||
a != m_peer_address)
return;
m_host->addEnetCommand(m_enet_peer, NULL, 0, ECT_RESET);
} // reset
//-----------------------------------------------------------------------------
/** Sends a packet to this host.
* \param data The data to send.
@ -62,50 +81,32 @@ void STKPeer::disconnect()
*/
void STKPeer::sendPacket(NetworkString *data, bool reliable)
{
data->setToken(m_client_server_token);
TransportAddress a(m_enet_peer->address);
// Enet will reuse a disconnected peer so we check here to avoid sending
// to wrong peer
if (m_enet_peer->state != ENET_PEER_STATE_CONNECTED ||
a != m_peer_address)
return;
data->setToken(m_client_server_token);
Log::verbose("STKPeer", "sending packet of size %d to %s at %f",
data->size(), a.toString().c_str(),StkTime::getRealTime());
ENetPacket* packet = enet_packet_create(data->getData(),
data->getTotalSize(),
(reliable ? ENET_PACKET_FLAG_RELIABLE
: ENET_PACKET_FLAG_UNSEQUENCED));
enet_peer_send(m_enet_peer, 0, packet);
m_host->addEnetCommand(m_enet_peer, packet, 0, ECT_SEND_PACKET);
} // sendPacket
//-----------------------------------------------------------------------------
/** Returns the IP address (in host format) of this client.
*/
uint32_t STKPeer::getAddress() const
{
return ntohl(m_enet_peer->address.host);
} // getAddress
//-----------------------------------------------------------------------------
/** Returns the port of this peer.
*/
uint16_t STKPeer::getPort() const
{
return m_enet_peer->address.port;
}
//-----------------------------------------------------------------------------
/** Returns if the peer is connected or not.
*/
bool STKPeer::isConnected() const
{
Log::info("STKPeer", "The peer state is %i", m_enet_peer->state);
Log::debug("STKPeer", "The peer state is %i", m_enet_peer->state);
return (m_enet_peer->state == ENET_PEER_STATE_CONNECTED);
} // isConnected
//-----------------------------------------------------------------------------
bool STKPeer::exists() const
{
return (m_enet_peer != NULL); // assert that the peer exists
}
//-----------------------------------------------------------------------------
/** Returns if this STKPeer is the same as the given peer.
*/
@ -123,12 +124,12 @@ bool STKPeer::isSamePeer(const ENetPeer* peer) const
} // isSamePeer
//-----------------------------------------------------------------------------
/** Returns the list of all player profiles connected to this peer. Note that
* this function is somewhat expensive (it loops over all network profiles
* to find the ones with the same host id as this peer.
/** Returns the ping to this peer from host, it waits for 15 seconds for a
* stable ping returned by enet measured in ms.
*/
std::vector<NetworkPlayerProfile*> STKPeer::getAllPlayerProfiles()
uint32_t STKPeer::getPing() const
{
return STKHost::get()->getGameSetup()->getAllPlayersOnHost(getHostId());
} // getAllPlayerProfiles
if ((float)StkTime::getRealTime() - m_connected_time < 15.0f)
return 0;
return m_enet_peer->lastRoundTripTime;
} // getPing

View File

@ -23,17 +23,28 @@
#ifndef STK_PEER_HPP
#define STK_PEER_HPP
#include "network/transport_address.hpp"
#include "utils/no_copy.hpp"
#include "utils/types.hpp"
#include <enet/enet.h>
#include <atomic>
#include <memory>
#include <vector>
class NetworkPlayerProfile;
class NetworkString;
class STKHost;
class TransportAddress;
enum PeerDisconnectInfo : unsigned int
{
PDI_TIMEOUT = 0, //!< Timeout disconnected (default in enet).
PDI_NORMAL = 1, //!< Normal disconnction with acknowledgement
PDI_KICK = 2, //!< Kick disconnection
}; // PeerDisconnectInfo
/*! \class STKPeer
* \brief Represents a peer.
* This class is used to interface the ENetPeer structure.
@ -45,61 +56,78 @@ protected:
ENetPeer* m_enet_peer;
/** The token of this client. */
uint32_t m_client_server_token;
std::atomic<uint32_t> m_client_server_token;
/** True if the token for this peer has been set. */
bool m_token_set;
std::atomic_bool m_token_set;
/** Host id of this peer. */
int m_host_id;
/** True if this peer is authorised to control a server. */
bool m_is_authorised;
public:
STKPeer(ENetPeer *enet_peer);
virtual ~STKPeer();
TransportAddress m_peer_address;
virtual void sendPacket(NetworkString *data,
bool reliable = true);
STKHost* m_host;
std::vector<std::shared_ptr<NetworkPlayerProfile> > m_players;
float m_connected_time;
public:
STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id);
~STKPeer() {}
// ------------------------------------------------------------------------
void sendPacket(NetworkString *data, bool reliable = true);
// ------------------------------------------------------------------------
void disconnect();
// ------------------------------------------------------------------------
void kick();
// ------------------------------------------------------------------------
void reset();
// ------------------------------------------------------------------------
bool isConnected() const;
bool exists() const;
uint32_t getAddress() const;
uint16_t getPort() const;
const TransportAddress& getAddress() const { return m_peer_address; }
bool isSamePeer(const STKPeer* peer) const;
bool isSamePeer(const ENetPeer* peer) const;
std::vector<NetworkPlayerProfile*> getAllPlayerProfiles();
// ------------------------------------------------------------------------
std::vector<std::shared_ptr<NetworkPlayerProfile> >& getPlayerProfiles()
{ return m_players; }
// ------------------------------------------------------------------------
bool hasPlayerProfiles() const { return !m_players.empty(); }
// ------------------------------------------------------------------------
void cleanPlayerProfiles() { m_players.clear(); }
// ------------------------------------------------------------------------
void addPlayer(std::shared_ptr<NetworkPlayerProfile> p)
{ m_players.push_back(p); }
// ------------------------------------------------------------------------
/** Sets the token for this client. */
void setClientServerToken(const uint32_t& token)
void setClientServerToken(const uint32_t token)
{
m_client_server_token = token;
m_token_set = true;
m_client_server_token.store(token);
m_token_set.store(true);
} // setClientServerToken
// ------------------------------------------------------------------------
void unsetClientServerToken() { m_token_set = false; }
/** Unsets the token for this client. (used in server to invalidate peer)
*/
void unsetClientServerToken()
{
m_client_server_token.store(0);
m_token_set.store(false);
}
// ------------------------------------------------------------------------
/** Returns the token of this client. */
uint32_t getClientServerToken() const { return m_client_server_token; }
uint32_t getClientServerToken() const
{ return m_client_server_token.load(); }
// ------------------------------------------------------------------------
/** Returns if the token for this client is known. */
bool isClientServerTokenSet() const { return m_token_set; }
// ------------------------------------------------------------------------
/** Sets the host if of this peer. */
void setHostId(int host_id) { m_host_id = host_id; }
bool isClientServerTokenSet() const { return m_token_set.load(); }
// ------------------------------------------------------------------------
/** Returns the host id of this peer. */
int getHostId() const { return m_host_id; }
uint32_t getHostId() const { return m_host_id; }
// ------------------------------------------------------------------------
/** Sets if this peer is authorised to control the server. */
void setAuthorised(bool authorised) { m_is_authorised = authorised; }
float getConnectedTime() const { return m_connected_time; }
// ------------------------------------------------------------------------
/** Returns if this peer is authorised to control the server. The server
* uses this to check if a peer is allowed certain commands; and a client
* uses this function (in which case this peer is actually the server
* peer) to see if this client is allowed certain command (i.e. to
* display additional GUI elements). */
bool isAuthorised() const { return m_is_authorised; }
uint32_t getPing() const;
}; // STKPeer
#endif // STK_PEER_HPP

View File

@ -96,10 +96,6 @@ namespace Online
return m_online_state;
} // getOnlineState
// ----------------------------------------------------------------
/** Returns a pointer to the profile associated with the current user. */
OnlineProfile* getProfile() const { return m_profile; }
// ----------------------------------------------------------------
/** Returns the session token of the signed in user. */
const std::string& getToken() const { return m_token; }
@ -113,6 +109,9 @@ namespace Online
OnlinePlayerProfile(const core::stringw &name, bool is_guest = false);
virtual ~OnlinePlayerProfile() {}
// ----------------------------------------------------------------
/** Returns a pointer to the profile associated with the current user. */
OnlineProfile* getProfile() const { return m_profile; }
// ----------------------------------------------------------------
}; // class OnlinePlayerProfile
} // namespace Online
#endif // HEADER_CURRENT_ONLINE_USER_HPP

View File

@ -65,7 +65,7 @@ OnlineProfile::OnlineProfile(const uint32_t & userid,
m_id = userid;
m_is_current_user = is_current_user;
m_username = username;
m_has_fetched_friends = false;
m_has_fetched_friends.store(false);
m_has_fetched_achievements = false;
m_relation_info = NULL;
m_is_friend = false;
@ -85,7 +85,7 @@ OnlineProfile::OnlineProfile(const XMLNode * xml, ConstructorType type)
m_relation_info = NULL;
m_is_friend = false;
m_cache_bit = true;
m_has_fetched_friends = false;
m_has_fetched_friends.store(false);
m_has_fetched_achievements = false;
if (type == C_RELATION_INFO)
{
@ -184,7 +184,7 @@ void OnlineProfile::storeAchievements(const XMLNode * input)
void OnlineProfile::fetchFriends()
{
assert(PlayerManager::isCurrentLoggedIn());
if (m_has_fetched_friends)
if (m_has_fetched_friends.load())
return;
m_state = State(m_state | S_FETCHING_FRIENDS);
@ -238,8 +238,8 @@ void OnlineProfile::storeFriends(const XMLNode * input)
ProfileManager::get()->addToCache(profile);
}
} // for i in nodes
m_has_fetched_friends = true;
m_state = State(m_state & ~S_FETCHING_FRIENDS);
m_has_fetched_friends.store(true);
} // storeFriends
// ----------------------------------------------------------------------------
@ -248,7 +248,7 @@ void OnlineProfile::storeFriends(const XMLNode * input)
*/
void OnlineProfile::removeFriend(const uint32_t id)
{
assert(m_has_fetched_friends);
assert(m_has_fetched_friends.load());
IDList::iterator iter;
for (iter = m_friends.begin(); iter != m_friends.end();)
{
@ -270,7 +270,7 @@ void OnlineProfile::removeFriend(const uint32_t id)
*/
void OnlineProfile::addFriend(const uint32_t id)
{
assert(m_has_fetched_friends);
assert(m_has_fetched_friends.load());
// find if friend id is is already in the user list
for (unsigned int i = 0; i < m_friends.size(); i++)
@ -296,7 +296,7 @@ void OnlineProfile::deleteRelationalInfo()
*/
const OnlineProfile::IDList& OnlineProfile::getFriends()
{
assert(m_has_fetched_friends &&
assert(m_has_fetched_friends.load() &&
(m_state & S_FETCHING_FRIENDS) == 0);
return m_friends;
} // getFriends
@ -322,7 +322,8 @@ void OnlineProfile::merge(OnlineProfile *profile)
assert(profile != NULL);
// profile has fetched friends, use that instead
if (!m_has_fetched_friends && profile->m_has_fetched_friends)
if (!m_has_fetched_friends.load() &&
profile->m_has_fetched_friends.load())
m_friends = profile->m_friends;
// profile has fetched achievements, use that instead

View File

@ -24,6 +24,7 @@
#include "utils/types.hpp"
#include "utils/ptr_vector.hpp"
#include <atomic>
#include <irrString.h>
#include <string>
@ -91,7 +92,7 @@ private:
/** Whether or not the user of this profile, is a friend of the current user */
bool m_is_friend;
bool m_has_fetched_friends;
std::atomic_bool m_has_fetched_friends;
/** List of user id's that are friends with the user of this profile.
* In case this profile is of the current user, this list also contains
@ -123,15 +124,15 @@ public:
// ------------------------------------------------------------------------
/** Returns true if the achievements for this profile have been fetched. */
bool hasFetchedAchievements() const { return m_has_fetched_achievements; }
bool hasFetchedAchievements() const { return m_has_fetched_achievements; }
// ------------------------------------------------------------------------
/** Unsets the flag that all friends of this profile are in cache. Used
* when a profile is pushed out of cache. */
void unsetHasFetchedFriends() { m_has_fetched_friends = false; }
void unsetHasFetchedFriends() { m_has_fetched_friends.store(false); }
// ------------------------------------------------------------------------
/** Returns true if the friend list for this profile has been fetched. */
bool hasFetchedFriends() const { return m_has_fetched_friends; }
bool hasFetchedFriends() const { return m_has_fetched_friends.load(); }
// ------------------------------------------------------------------------
/** True if the profile has fetched friends. */

View File

@ -35,8 +35,6 @@
#include <iostream>
DEFINE_SCREEN_SINGLETON( AddonsScreen );
using namespace Online;
// ----------------------------------------------------------------------------

View File

@ -37,8 +37,6 @@ using namespace GUIEngine;
using namespace irr::core;
using namespace irr::video;
DEFINE_SCREEN_SINGLETON( ArenasScreen );
static const char ALL_ARENA_GROUPS_ID[] = "all";

View File

@ -15,23 +15,16 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define DEBUG_MENU_ITEM 0
#include "states_screens/create_server_screen.hpp"
#include "audio/sfx_manager.hpp"
#include "challenges/unlock_manager.hpp"
#include "config/player_manager.hpp"
#include "config/user_config.hpp"
#include "modes/demo_world.hpp"
#include "network/protocols/lobby_protocol.hpp"
#include "network/network_config.hpp"
#include "network/server.hpp"
#include "network/stk_host.hpp"
#include "states_screens/state_manager.hpp"
#include "states_screens/dialogs/message_dialog.hpp"
#include "states_screens/networking_lobby.hpp"
#include "states_screens/dialogs/server_info_dialog.hpp"
#include "utils/separate_process.hpp"
#include "utils/translation.hpp"
@ -43,8 +36,6 @@
using namespace GUIEngine;
DEFINE_SCREEN_SINGLETON( CreateServerScreen );
// ----------------------------------------------------------------------------
CreateServerScreen::CreateServerScreen() : Screen("online/create_server.stkgui")
@ -62,7 +53,7 @@ void CreateServerScreen::loadedFromFile()
assert(m_max_players_widget != NULL);
int max = UserConfigParams::m_server_max_players.getDefaultValue();
m_max_players_widget->setMax(max);
if (UserConfigParams::m_server_max_players > max)
UserConfigParams::m_server_max_players = max;
@ -71,8 +62,15 @@ void CreateServerScreen::loadedFromFile()
m_info_widget = getWidget<LabelWidget>("info");
assert(m_info_widget != NULL);
m_more_options_text = getWidget<LabelWidget>("more-options");
assert(m_more_options_text != NULL);
m_more_options_spinner = getWidget<SpinnerWidget>("more-options-spinner");
assert(m_more_options_spinner != NULL);
m_options_widget = getWidget<RibbonWidget>("options");
assert(m_options_widget != NULL);
m_game_mode_widget = getWidget<RibbonWidget>("gamemode");
assert(m_game_mode_widget != NULL);
m_create_widget = getWidget<IconButtonWidget>("create");
assert(m_create_widget != NULL);
m_cancel_widget = getWidget<IconButtonWidget>("cancel");
@ -83,7 +81,6 @@ void CreateServerScreen::loadedFromFile()
void CreateServerScreen::init()
{
Screen::init();
DemoWorld::resetIdleTime();
m_info_widget->setText("", false);
LabelWidget *title = getWidget<LabelWidget>("title");
@ -109,6 +106,7 @@ void CreateServerScreen::init()
RibbonWidget* gamemode = getWidget<RibbonWidget>("gamemode");
assert(gamemode != NULL);
gamemode->setSelection(0, PLAYER_ID_GAME_MASTER);
updateMoreOption(0);
} // init
// ----------------------------------------------------------------------------
@ -131,8 +129,62 @@ void CreateServerScreen::eventCallback(Widget* widget, const std::string& name,
createServer();
} // is create_widget
}
else if (name == m_game_mode_widget->m_properties[PROP_ID])
{
const int selection =
m_game_mode_widget->getSelection(PLAYER_ID_GAME_MASTER);
updateMoreOption(selection);
}
} // eventCallback
// ----------------------------------------------------------------------------
void CreateServerScreen::updateMoreOption(int game_mode)
{
switch (game_mode)
{
case 0:
case 1:
{
m_more_options_text->setVisible(true);
//I18N: In the create server screen
m_more_options_text->setText(_("No. of grand prix track(s)"),
false);
m_more_options_spinner->setVisible(true);
m_more_options_spinner->clearLabels();
m_more_options_spinner->addLabel(_("Disabled"));
for (int i = 1; i <= 20; i++)
{
m_more_options_spinner->addLabel(StringUtils::toWString(i));
}
m_more_options_spinner->setValue(0);
break;
}
case 3:
{
m_more_options_text->setVisible(true);
m_more_options_spinner->setVisible(true);
m_more_options_spinner->clearLabels();
//I18N: In the create server screen
m_more_options_text->setText(_("Soccer game type"), false);
m_more_options_spinner->setVisible(true);
m_more_options_spinner->clearLabels();
//I18N: In the create server screen for soccer server
m_more_options_spinner->addLabel(_("Time limit"));
//I18N: In the create server screen for soccer server
m_more_options_spinner->addLabel(_("Goals limit"));
m_more_options_spinner->setValue(0);
break;
}
default:
{
m_more_options_text->setVisible(false);
m_more_options_spinner->setVisible(false);
break;
}
}
} // updateMoreOption
// ----------------------------------------------------------------------------
/** Called once per framce to check if the server creation request has
* finished. If so, if pushes the server creation sceen.
@ -164,18 +216,31 @@ void CreateServerScreen::createServer()
if (name.size() < 4 || name.size() > 30)
{
//I18N: In the create server screen
m_info_widget->setText(
_("Name has to be between 4 and 30 characters long!"), false);
SFXManager::get()->quickSound("anvil");
return;
}
assert(max_players > 1 &&
max_players <= UserConfigParams::m_server_max_players.getDefaultValue());
assert(max_players > 1 && max_players <=
UserConfigParams::m_server_max_players.getDefaultValue());
UserConfigParams::m_server_max_players = max_players;
core::stringw password_w = getWidget<TextBoxWidget>("password")->getText();
std::string password = StringUtils::xmlEncode(password_w);
NetworkConfig::get()->setPassword(StringUtils::wideToUtf8(password_w));
std::string password = StringUtils::wideToUtf8(getWidget<TextBoxWidget>
("password")->getText());
if ((!password.empty() != 0 &&
password.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP"
"QRSTUVWXYZ01234567890_") != std::string::npos) ||
password.size() > 255)
{
//I18N: In the create server screen
m_info_widget->setText(
_("Incorrect characters in password!"), false);
SFXManager::get()->quickSound("anvil");
return;
}
NetworkConfig::get()->setPassword(password);
if (!password.empty())
password = std::string(" --server-password=") + password;
@ -186,7 +251,7 @@ void CreateServerScreen::createServer()
max_players, /*current_player*/0, (RaceManager::Difficulty)
difficulty_widget->getSelection(PLAYER_ID_GAME_MASTER),
NetworkConfig::get()->getServerGameMode(race_manager->getMinorMode(),
race_manager->getMajorMode()), server_address);
race_manager->getMajorMode()), server_address, !password.empty());
#undef USE_GRAPHICS_SERVER
#ifdef USE_GRAPHICS_SERVER
@ -250,7 +315,7 @@ void CreateServerScreen::createServer()
new SeparateProcess(SeparateProcess::getCurrentExecutableLocation(),
server_cfg.str() + password);
STKHost::create(server, sp);
NetworkingLobby::getInstance()->setJoinedServer(server);
#endif
} // createServer

View File

@ -39,14 +39,18 @@ private:
GUIEngine::TextBoxWidget * m_name_widget;
GUIEngine::SpinnerWidget * m_max_players_widget;
GUIEngine::SpinnerWidget* m_more_options_spinner;
GUIEngine::LabelWidget * m_more_options_text;
GUIEngine::LabelWidget * m_info_widget;
GUIEngine::RibbonWidget * m_game_mode_widget;
GUIEngine::RibbonWidget * m_options_widget;
GUIEngine::IconButtonWidget * m_create_widget;
GUIEngine::IconButtonWidget * m_cancel_widget;
void createServer();
void updateMoreOption(int game_mode);
public:

View File

@ -33,8 +33,6 @@ using irr::core::stringc;
#include "utils/string_utils.hpp"
#include "utils/translation.hpp"
DEFINE_SCREEN_SINGLETON( CreditsScreen );
using namespace GUIEngine;
const float TIME_SECTION_FADE = 0.8f;
const float ENTRIES_FADE_TIME = 0.3f;

View File

@ -27,6 +27,7 @@
#include "guiengine/engine.hpp"
#include "guiengine/widgets/model_view_widget.hpp"
#include "guiengine/widgets/spinner_widget.hpp"
#include "states_screens/state_manager.hpp"
using namespace GUIEngine;
@ -36,10 +37,11 @@ KartColorSliderDialog::KartColorSliderDialog(PlayerProfile* pp)
{
loadFromFile("kart_color_slider.stkgui");
m_player_profile = pp;
getWidget<SpinnerWidget>("color-slider")->setValue(
int(pp->getDefaultKartColor() * 100.0f));
m_model_view->getModelViewRenderInfo()->setHue(
float(getWidget<SpinnerWidget>("color-slider")->getValue()) / 100.0f);
SpinnerWidget* color_slider = getWidget<SpinnerWidget>("color-slider");
color_slider->setValue(int(pp->getDefaultKartColor() * 100.0f));
m_model_view->getModelViewRenderInfo()->setHue(float(color_slider->getValue()) / 100.0f);
color_slider->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
} // KartColorSliderDialog
// ----------------------------------------------------------------------------

View File

@ -0,0 +1,125 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2018 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 "states_screens/dialogs/network_user_dialog.hpp"
#include "config/player_manager.hpp"
#include "guiengine/dialog_queue.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/widgets/icon_button_widget.hpp"
#include "guiengine/widgets/label_widget.hpp"
#include "guiengine/widgets/ribbon_widget.hpp"
#include "online/online_profile.hpp"
#include "network/protocols/lobby_protocol.hpp"
#include "network/stk_host.hpp"
#include "states_screens/state_manager.hpp"
#include "utils/translation.hpp"
#include <IGUIEnvironment.h>
using namespace GUIEngine;
using namespace irr;
using namespace irr::gui;
using namespace Online;
// ----------------------------------------------------------------------------
void NetworkUserDialog::beforeAddingWidgets()
{
m_name_widget = getWidget<LabelWidget>("name");
assert(m_name_widget != NULL);
m_name_widget->setText(m_name, false);
m_friend_widget = getWidget<IconButtonWidget>("friend");
assert(m_friend_widget != NULL);
m_friend_widget->setVisible(m_online_id != 0);
// Hide friend request button if already friend
Online::OnlineProfile* opp =
PlayerManager::getCurrentPlayer()->getProfile();
if (m_online_id != 0 && opp && opp->hasFetchedFriends())
{
for (uint32_t user_id : opp->getFriends())
{
if (user_id == m_online_id)
{
m_friend_widget->setVisible(false);
}
}
}
m_kick_widget = getWidget<IconButtonWidget>("decline");
assert(m_kick_widget != NULL);
//I18N: In the network user dialog
m_kick_widget->setText(_("Kick"));
m_kick_widget->setVisible(STKHost::get()->isAuthorisedToControl());
m_cancel_widget = getWidget<IconButtonWidget>("cancel");
assert(m_cancel_widget != NULL);
m_options_widget = getWidget<RibbonWidget>("options");
assert(m_options_widget != NULL);
m_options_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
m_options_widget->select("cancel", PLAYER_ID_GAME_MASTER);
getWidget<IconButtonWidget>("accept")->setVisible(false);
getWidget<IconButtonWidget>("remove")->setVisible(false);
getWidget<IconButtonWidget>("enter")->setVisible(false);
getWidget<LabelWidget>("info")->setVisible(false);
} // beforeAddingWidgets
// -----------------------------------------------------------------------------
GUIEngine::EventPropagation
NetworkUserDialog::processEvent(const std::string& source)
{
if (source == m_options_widget->m_properties[PROP_ID])
{
const std::string& selection = m_options_widget
->getSelectionIDString(PLAYER_ID_GAME_MASTER);
if (selection == m_cancel_widget->m_properties[PROP_ID])
{
m_self_destroy = true;
return GUIEngine::EVENT_BLOCK;
}
else if(selection == m_friend_widget->m_properties[PROP_ID])
{
XMLRequest *request = new XMLRequest();
PlayerManager::setUserDetails(request, "friend-request");
request->addParameter("friendid", m_online_id);
request->queue();
m_self_destroy = true;
return GUIEngine::EVENT_BLOCK;
}
else if(selection == m_kick_widget->m_properties[PROP_ID])
{
NetworkString kick(PROTOCOL_LOBBY_ROOM);
kick.addUInt8(LobbyProtocol::LE_KICK_HOST).addUInt32(m_host_id);
STKHost::get()->sendToServer(&kick, true/*reliable*/);
m_self_destroy = true;
return GUIEngine::EVENT_BLOCK;
}
}
return GUIEngine::EVENT_LET;
} // processEvent
// -----------------------------------------------------------------------------
bool NetworkUserDialog::onEscapePressed()
{
if (m_cancel_widget->isActivated())
m_self_destroy = true;
return false;
} // onEscapePressed

View File

@ -0,0 +1,89 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2018 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.
#ifndef HEADER_NETWORK_USER_DIALOG_HPP
#define HEADER_NETWORK_USER_DIALOG_HPP
#include "guiengine/modaldialog.hpp"
#include "utils/types.hpp"
#include <irrString.h>
namespace GUIEngine
{
class IconButtonWidget;
class LabelWidget;
class RibbonWidget;
}
/**
* \brief Dialog that handle user in network lobby
* \ingroup states_screens
*/
class NetworkUserDialog : public GUIEngine::ModalDialog
{
private:
const uint32_t m_host_id;
const uint32_t m_online_id;
const core::stringw m_name;
bool m_self_destroy;
GUIEngine::RibbonWidget * m_options_widget;
GUIEngine::LabelWidget * m_name_widget;
GUIEngine::IconButtonWidget * m_friend_widget;
GUIEngine::IconButtonWidget * m_kick_widget;
GUIEngine::IconButtonWidget * m_cancel_widget;
public:
NetworkUserDialog(uint32_t host_id, uint32_t online_id,
const core::stringw& name)
: ModalDialog(0.8f,0.8f), m_host_id(host_id), m_online_id(online_id),
m_name(name), m_self_destroy(false)
{
loadFromFile("online/user_info_dialog.stkgui");
}
// ------------------------------------------------------------------------
~NetworkUserDialog() {}
// ------------------------------------------------------------------------
virtual void beforeAddingWidgets();
// ------------------------------------------------------------------------
void onEnterPressedInternal() { m_self_destroy = true; }
// ------------------------------------------------------------------------
GUIEngine::EventPropagation processEvent(const std::string& source);
// ------------------------------------------------------------------------
virtual bool onEscapePressed();
// ------------------------------------------------------------------------
virtual void onUpdate(float dt)
{
// It's unsafe to delete from inside the event handler so we do it here
if (m_self_destroy)
{
ModalDialog::dismiss();
return;
}
}
};
#endif

View File

@ -18,8 +18,13 @@
#include "states_screens/dialogs/server_info_dialog.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/widgets/icon_button_widget.hpp"
#include "guiengine/widgets/label_widget.hpp"
#include "guiengine/widgets/ribbon_widget.hpp"
#include "guiengine/widgets/text_box_widget.hpp"
#include "network/server.hpp"
#include "network/stk_host.hpp"
#include "network/network_config.hpp"
#include "states_screens/networking_lobby.hpp"
#include "states_screens/state_manager.hpp"
#include "utils/string_utils.hpp"
@ -39,10 +44,10 @@ using namespace Online;
* server (i.e. while it is being created).
*/
ServerInfoDialog::ServerInfoDialog(std::shared_ptr<Server> server)
: ModalDialog(0.8f,0.8f), m_server(server)
: ModalDialog(0.8f,0.8f), m_server(server), m_password(NULL)
{
Log::info("ServerInfoDialog", "Server id is %d, Host id is %d",
server->getServerId(), server->getHostId());
Log::info("ServerInfoDialog", "Server id is %d, owner is %d",
server->getServerId(), server->getServerOwner());
m_self_destroy = false;
loadFromFile("online/server_info_dialog.stkgui");
@ -67,10 +72,21 @@ ServerInfoDialog::ServerInfoDialog(std::shared_ptr<Server> server)
assert(m_cancel_widget != NULL);
m_options_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
if (m_server->isPasswordProtected())
{
m_password = getWidget<TextBoxWidget>("password");
m_password->setPasswordBox(true, L'*');
assert(m_password != NULL);
}
else
{
getWidget("label_password")->setVisible(false);
getWidget("password")->setVisible(false);
}
} // ServerInfoDialog
// -----------------------------------------------------------------------------
ServerInfoDialog::~ServerInfoDialog()
{
} // ~ServerInfoDialog
@ -78,6 +94,16 @@ ServerInfoDialog::~ServerInfoDialog()
// -----------------------------------------------------------------------------
void ServerInfoDialog::requestJoin()
{
if (m_server->isPasswordProtected())
{
assert(m_password != NULL);
NetworkConfig::get()->setPassword(
StringUtils::wideToUtf8(m_password->getText()));
}
else
{
NetworkConfig::get()->setPassword("");
}
STKHost::create(m_server);
NetworkingLobby::getInstance()->setJoinedServer(m_server);
ModalDialog::dismiss();

View File

@ -20,11 +20,16 @@
#define HEADER_SERVER_INFO_DIALOG_HPP
#include "guiengine/modaldialog.hpp"
#include "guiengine/widgets/icon_button_widget.hpp"
#include "guiengine/widgets/ribbon_widget.hpp"
#include "guiengine/widgets/label_widget.hpp"
#include "utils/types.hpp"
namespace GUIEngine
{
class LabelWidget;
class RibbonWidget;
class IconButtonWidget;
class TextBoxWidget;
}
#include <memory>
#include <irrString.h>
@ -49,6 +54,8 @@ private:
/** The cancel button. */
GUIEngine::IconButtonWidget *m_cancel_widget;
/** Specify server password if needed. */
GUIEngine::TextBoxWidget* m_password;
public:
ServerInfoDialog(std::shared_ptr<Server> server);

View File

@ -0,0 +1,160 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2018 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 "states_screens/dialogs/splitscreen_player_dialog.hpp"
#include "config/player_manager.hpp"
#include "config/user_config.hpp"
#include "input/device_manager.hpp"
#include "input/input_manager.hpp"
#include "guiengine/dialog_queue.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/message_queue.hpp"
#include "guiengine/widgets/check_box_widget.hpp"
#include "guiengine/widgets/icon_button_widget.hpp"
#include "guiengine/widgets/label_widget.hpp"
#include "guiengine/widgets/ribbon_widget.hpp"
#include "guiengine/widgets/spinner_widget.hpp"
#include "network/network_config.hpp"
#include "states_screens/networking_lobby.hpp"
#include "utils/translation.hpp"
#include <IGUIEnvironment.h>
using namespace GUIEngine;
using namespace irr;
// ----------------------------------------------------------------------------
void SplitscreenPlayerDialog::beforeAddingWidgets()
{
m_profiles = getWidget<SpinnerWidget>("name-spinner");
for (unsigned i = 0; i < PlayerManager::get()->getNumNonGuestPlayers();
i++)
{
PlayerProfile* p = PlayerManager::get()->getPlayer(i);
if (!NetworkConfig::get()->playerExists(p))
{
m_profiles->addLabel(p->getName());
m_available_players.push_back(p);
}
}
m_message = getWidget<LabelWidget>("message-label");
assert(m_message != NULL);
m_handicap = getWidget<CheckBoxWidget>("handicap");
assert(m_handicap != NULL);
m_options_widget = getWidget<RibbonWidget>("options");
assert(m_options_widget != NULL);
m_add = getWidget<IconButtonWidget>("add");
assert(m_add != NULL);
m_connect = getWidget<IconButtonWidget>("connect");
assert(m_connect != NULL);
m_cancel = getWidget<IconButtonWidget>("cancel");
assert(m_cancel != NULL);
m_reset = getWidget<IconButtonWidget>("reset");
assert(m_reset != NULL);
if (NetworkConfig::get()->getNetworkPlayers().size() == MAX_PLAYER_COUNT)
{
m_available_players.clear();
}
m_options_widget->setFocusForPlayer(PLAYER_ID_GAME_MASTER);
if (m_available_players.empty())
{
getWidget("name-text")->setVisible(false);
getWidget("handicap-row")->setVisible(false);
m_add->setVisible(false);
m_profiles->setVisible(false);
m_options_widget->select("connect", PLAYER_ID_GAME_MASTER);
}
else
{
getWidget("name-text")->setVisible(true);
getWidget("handicap-row")->setVisible(true);
m_add->setVisible(true);
m_profiles->setVisible(true);
m_handicap->setState(false);
m_handicap->setActive(UserConfigParams::m_per_player_difficulty);
m_options_widget->select("add", PLAYER_ID_GAME_MASTER);
}
input_manager->getDeviceManager()->setAssignMode(NO_ASSIGN);
input_manager->getDeviceManager()->mapFireToSelect(false);
} // beforeAddingWidgets
// -----------------------------------------------------------------------------
GUIEngine::EventPropagation
SplitscreenPlayerDialog::processEvent(const std::string& source)
{
if (source == m_options_widget->m_properties[PROP_ID])
{
const std::string& selection = m_options_widget
->getSelectionIDString(PLAYER_ID_GAME_MASTER);
if (selection == m_add->m_properties[PROP_ID])
{
const unsigned pid = m_profiles->getValue();
assert(pid < PlayerManager::get()->getNumPlayers());
PlayerProfile* p = m_available_players[pid];
const bool handicap = m_handicap->getState();
if (NetworkConfig::get()->addNetworkPlayer(m_device, p, handicap))
{
NetworkingLobby::getInstance()
->addSplitscreenPlayer(p->getName());
m_self_destroy = true;
return GUIEngine::EVENT_BLOCK;
}
else
{
//I18N: in splitscreen player dialog for network game
m_message->setErrorColor();
m_message->setText(_("Input device already exists."),
false);
}
return GUIEngine::EVENT_BLOCK;
}
else if(selection == m_connect->m_properties[PROP_ID])
{
if (!NetworkConfig::get()->getNetworkPlayers().empty())
{
NetworkConfig::get()->doneAddingNetworkPlayers();
NetworkingLobby::getInstance()->finishAddingPlayers();
m_self_destroy = true;
return GUIEngine::EVENT_BLOCK;
}
//I18N: in splitscreen player dialog for network game
m_message->setErrorColor();
m_message->setText(
_("No player available for connecting to server."), false);
return GUIEngine::EVENT_BLOCK;
}
else if(selection == m_cancel->m_properties[PROP_ID])
{
m_self_destroy = true;
return GUIEngine::EVENT_BLOCK;
}
else if(selection == m_reset->m_properties[PROP_ID])
{
NetworkConfig::get()->cleanNetworkPlayers();
NetworkingLobby::getInstance()->cleanAddedPlayers();
m_self_destroy = true;
return GUIEngine::EVENT_BLOCK;
}
}
return GUIEngine::EVENT_LET;
} // processEvent

View File

@ -0,0 +1,101 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2018 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.
#ifndef HEADER_SPLITSCREEN_PLAYER_DIALOG_HPP
#define HEADER_SPLITSCREEN_PLAYER_DIALOG_HPP
#include "guiengine/modaldialog.hpp"
#include "utils/types.hpp"
#include <irrString.h>
#include <vector>
class InputDevice;
class PlayerProfile;
namespace GUIEngine
{
class CheckBoxWidget;
class IconButtonWidget;
class LabelWidget;
class RibbonWidget;
class SpinnerWidget;
}
/**
* \brief Dialog that handle user in network lobby
* \ingroup states_screens
*/
class SplitscreenPlayerDialog : public GUIEngine::ModalDialog
{
private:
InputDevice* m_device;
bool m_self_destroy;
std::vector<PlayerProfile*> m_available_players;
GUIEngine::LabelWidget* m_message;
GUIEngine::SpinnerWidget* m_profiles;
GUIEngine::CheckBoxWidget* m_handicap;
GUIEngine::RibbonWidget* m_options_widget;
GUIEngine::IconButtonWidget* m_add;
GUIEngine::IconButtonWidget* m_connect;
GUIEngine::IconButtonWidget* m_cancel;
GUIEngine::IconButtonWidget* m_reset;
public:
SplitscreenPlayerDialog(InputDevice* device)
: ModalDialog(0.8f,0.8f), m_device(device), m_self_destroy(false)
{
loadFromFile("online/splitscreen_player_dialog.stkgui");
}
// ------------------------------------------------------------------------
~SplitscreenPlayerDialog() {}
// ------------------------------------------------------------------------
virtual void beforeAddingWidgets();
// ------------------------------------------------------------------------
void onEnterPressedInternal() { m_self_destroy = true; }
// ------------------------------------------------------------------------
GUIEngine::EventPropagation processEvent(const std::string& source);
// ------------------------------------------------------------------------
virtual bool onEscapePressed()
{
m_self_destroy = true;
return false;
}
// ------------------------------------------------------------------------
virtual void onUpdate(float dt)
{
// It's unsafe to delete from inside the event handler so we do it here
if (m_self_destroy)
{
ModalDialog::dismiss();
return;
}
}
};
#endif

View File

@ -39,8 +39,6 @@ using namespace irr::video;
static const char ALL_TRACK_GROUPS_ID[] = "all";
DEFINE_SCREEN_SINGLETON( EasterEggScreen );
// -----------------------------------------------------------------------------
EasterEggScreen::EasterEggScreen() : Screen("easter_egg.stkgui")

View File

@ -32,8 +32,6 @@
using namespace GUIEngine;
DEFINE_SCREEN_SINGLETON( EditGPScreen );
// -----------------------------------------------------------------------------
EditGPScreen::EditGPScreen() : Screen("edit_gp.stkgui"), m_gp(NULL),
m_list(NULL), m_icon_bank(NULL),

View File

@ -33,8 +33,6 @@ using namespace irr::core;
const char* EditTrackScreen::ALL_TRACKS_GROUP_ID = "all";
DEFINE_SCREEN_SINGLETON( EditTrackScreen );
// -----------------------------------------------------------------------------
EditTrackScreen::EditTrackScreen()
: Screen("edit_track.stkgui"), m_track_group(ALL_TRACKS_GROUP_ID),

View File

@ -62,8 +62,6 @@ const float ANIM_TO = 7.0f;
const int GIFT_EXIT_FROM = (int)ANIM_TO;
const int GIFT_EXIT_TO = GIFT_EXIT_FROM + 7;
DEFINE_SCREEN_SINGLETON( FeatureUnlockedCutScene );
// ============================================================================
#if 0

View File

@ -27,8 +27,6 @@
using namespace GUIEngine;
DEFINE_SCREEN_SINGLETON( GhostReplaySelection );
// ----------------------------------------------------------------------------
/** Constructor, which loads the stkgui file.
*/

View File

@ -48,8 +48,6 @@
using irr::gui::IGUIStaticText;
using namespace GUIEngine;
DEFINE_SCREEN_SINGLETON( GPInfoScreen );
/** Constructor, initialised some variables which might be used before
* loadedFromFile is called.
*/
@ -234,9 +232,9 @@ void GPInfoScreen::init()
{
const int local_players = race_manager->getNumLocalPlayers();
int min_ai = 0;
int num_ai = UserConfigParams::m_num_karts_per_gamemode[RaceManager::MAJOR_MODE_GRAND_PRIX]
- local_players;
int num_ai = int(UserConfigParams::m_num_karts_per_gamemode
[RaceManager::MAJOR_MODE_GRAND_PRIX]) - local_players;
// A ftl reace needs at least three karts to make any sense
if (race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
{

View File

@ -36,9 +36,6 @@
using namespace GUIEngine;
using namespace irr::core;
DEFINE_SCREEN_SINGLETON( GrandPrixEditorScreen );
// -----------------------------------------------------------------------------
GrandPrixEditorScreen::GrandPrixEditorScreen()
: Screen("grand_prix_editor.stkgui"), m_selection(NULL),

View File

@ -69,8 +69,6 @@ const float KART_Z = 0.0f;
const int MAX_KART_COUNT = 4;
DEFINE_SCREEN_SINGLETON( GrandPrixLose );
// -------------------------------------------------------------------------------------
void GrandPrixLose::onCutsceneEnd()

View File

@ -106,8 +106,6 @@ const float PODIUMS_FINAL_Y[3] = {
const float PODIUMS_AND_KARTS_SPEED_Y[3] = { 0.9f, 1.35f, 0.45f };
DEFINE_SCREEN_SINGLETON( GrandPrixWin );
// -------------------------------------------------------------------------------------
GrandPrixWin::GrandPrixWin() : GrandPrixCutscene("grand_prix_win.stkgui")

View File

@ -36,8 +36,6 @@
using namespace GUIEngine;
DEFINE_SCREEN_SINGLETON( HelpScreen1 );
// -----------------------------------------------------------------------------
HelpScreen1::HelpScreen1() : Screen("help1.stkgui")

View File

@ -27,8 +27,6 @@
using namespace GUIEngine;
DEFINE_SCREEN_SINGLETON( HelpScreen2 );
// -----------------------------------------------------------------------------
HelpScreen2::HelpScreen2() : Screen("help2.stkgui")

View File

@ -28,8 +28,6 @@
using namespace GUIEngine;
DEFINE_SCREEN_SINGLETON( HelpScreen3 );
// -----------------------------------------------------------------------------
HelpScreen3::HelpScreen3() : Screen("help3.stkgui")

View File

@ -28,8 +28,6 @@
using namespace GUIEngine;
DEFINE_SCREEN_SINGLETON( HelpScreen4 );
// -----------------------------------------------------------------------------
HelpScreen4::HelpScreen4() : Screen("help4.stkgui")

View File

@ -27,8 +27,6 @@
using namespace GUIEngine;
DEFINE_SCREEN_SINGLETON( HelpScreen5 );
// -----------------------------------------------------------------------------
HelpScreen5::HelpScreen5() : Screen("help5.stkgui")

View File

@ -37,6 +37,7 @@
#include "karts/kart_properties.hpp"
#include "karts/kart_properties_manager.hpp"
#include "modes/overworld.hpp"
#include "network/network_config.hpp"
#include "states_screens/race_setup_screen.hpp"
#include "utils/log.hpp"
#include "utils/translation.hpp"
@ -55,7 +56,6 @@ static const char ID_DONT_USE[] = "x";
// a kart called 'locked'
static const char ID_LOCKED[] = "locked/";
//DEFINE_SCREEN_SINGLETON( KartSelectionScreen );
KartSelectionScreen* KartSelectionScreen::m_instance_ptr = NULL;
int g_root_id;
@ -320,8 +320,6 @@ void KartSelectionScreen::init()
Widget* placeholder = getWidget("playerskarts");
assert(placeholder != NULL);
// FIXME : The reserved id value is -1 when we switch from KSS to NKSS and vice-versa
m_dispatcher->setRootID(placeholder->m_reserved_id);
g_root_id = placeholder->m_reserved_id;
@ -340,8 +338,6 @@ void KartSelectionScreen::init()
tabs->setActive(true);
m_kart_widgets.clearAndDeleteAll();
StateManager::get()->resetActivePlayers();
input_manager->getDeviceManager()->setAssignMode(DETECT_NEW);
DynamicRibbonWidget* w = getWidget<DynamicRibbonWidget>("karts");
assert( w != NULL );
@ -378,26 +374,34 @@ void KartSelectionScreen::init()
}
else */
// For now this is what will happen
if (!m_multiplayer)
{
joinPlayer(input_manager->getDeviceManager()->getLatestUsedDevice());
w->updateItemDisplay();
// Player 0 select default kart
if (!w->setSelection(UserConfigParams::m_default_kart, 0, true))
{
// if kart from config not found, select the first instead
w->setSelection(0, 0, true);
}
} else
// Add multiplayer message
addMultiplayerMessage();
input_manager->getDeviceManager()->setAssignMode(DETECT_NEW);
// This flag will cause that a 'fire' event will be mapped to 'select' (if
// 'fire' is not assigned to a GUI event). This is done to support the old
// way of player joining by pressing 'fire' instead of 'select'.
input_manager->getDeviceManager()->mapFireToSelect(true);
if (!NetworkConfig::get()->isNetworking())
{
StateManager::get()->resetActivePlayers();
if (!m_multiplayer)
{
joinPlayer(input_manager->getDeviceManager()->getLatestUsedDevice(),
NULL/*player profile*/);
w->updateItemDisplay();
// Player 0 select default kart
if (!w->setSelection(UserConfigParams::m_default_kart, 0, true))
{
// if kart from config not found, select the first instead
w->setSelection(0, 0, true);
}
}
else
{
// Add multiplayer message
addMultiplayerMessage();
}
}
} // init
// ----------------------------------------------------------------------------
@ -421,8 +425,7 @@ void KartSelectionScreen::tearDown()
Screen::tearDown();
m_kart_widgets.clearAndDeleteAll();
if (m_must_delete_on_back)
GUIEngine::removeScreen(getName().c_str());
GUIEngine::removeScreen(getName().c_str());
} // tearDown
// ----------------------------------------------------------------------------
@ -435,7 +438,7 @@ void KartSelectionScreen::unloaded()
// ----------------------------------------------------------------------------
// Return true if event was handled successfully
bool KartSelectionScreen::joinPlayer(InputDevice* device)
bool KartSelectionScreen::joinPlayer(InputDevice* device, PlayerProfile* p)
{
bool first_player = m_kart_widgets.size() == 0;
@ -468,14 +471,18 @@ bool KartSelectionScreen::joinPlayer(InputDevice* device)
}
// ---- Create new active player
PlayerProfile* profile_to_use = PlayerManager::getCurrentPlayer();
PlayerProfile* profile_to_use = p == NULL ?
PlayerManager::getCurrentPlayer() : p;
// Make sure enough guest character exists. At this stage this player has
// not been added, so the number of guests requested for the first player
// is 0 --> forcing at least one real player.
PlayerManager::get()->createGuestPlayers(
StateManager::get()->activePlayerCount());
if (!first_player)
if (p == NULL)
{
PlayerManager::get()->createGuestPlayers(
StateManager::get()->activePlayerCount());
}
if (!first_player && p == NULL)
{
// Give each player a different start profile
const int num_active_players = StateManager::get()->activePlayerCount();
@ -514,6 +521,12 @@ bool KartSelectionScreen::joinPlayer(InputDevice* device)
m_kart_widgets.push_back(newPlayerWidget);
newPlayerWidget->add();
// From network kart selection, the player name is already defined
if (p != NULL)
{
newPlayerWidget->getPlayerNameSpinner()->setActive(false);
newPlayerWidget->getPlayerNameSpinner()->setCustomText(p->getName());
}
// ---- Divide screen space among all karts
const int amount = m_kart_widgets.size();
@ -522,7 +535,8 @@ bool KartSelectionScreen::joinPlayer(InputDevice* device)
// in this special case, leave room for a message on the right
if (m_multiplayer && first_player)
{
addMultiplayerMessage();
if (p == NULL)
addMultiplayerMessage();
const int splitWidth = fullarea->m_w / 2;
m_kart_widgets[0].move( fullarea->m_x, fullarea->m_y, splitWidth,
fullarea->m_h );
@ -1481,7 +1495,7 @@ void KartSelectionScreen::setKartsFromCurrentGroup()
{
const KartProperties* prop = karts.get(i);
if (PlayerManager::getCurrentPlayer()->isLocked(prop->getIdent()) &&
!m_multiplayer)
!m_multiplayer && !NetworkConfig::get()->isNetworking())
{
w->addItem(_("Locked : solve active challenges to gain access to more!"),
ID_LOCKED + prop->getIdent(),

View File

@ -38,6 +38,7 @@ namespace Online
class FocusDispatcher;
class InputDevice;
class PlayerProfile;
class KartHoverListener;
extern int g_root_id;
@ -85,7 +86,7 @@ protected:
KartSelectionScreen(const char* filename);
/** Called when all players selected their kart */
void allPlayersDone();
virtual void allPlayersDone();
/** Called when number/order of karts changed, so that all will keep
* an up-to-date ID */
@ -105,7 +106,7 @@ protected:
/** Fill the ribbon with the karts from the currently selected group */
void setKartsFromCurrentGroup();
virtual void playerConfirm(const int playerID);
void playerConfirm(const int playerID);
void updateKartStats(uint8_t widget_id,
const std::string& selection);
@ -143,14 +144,14 @@ public:
/** \brief Called when a player hits 'fire'/'select' on his device to
* join the game */
bool joinPlayer(InputDevice* device);
bool joinPlayer(InputDevice* device, PlayerProfile* p);
/**
* \brief Called when a player hits 'rescue'/'cancel' on his device
* to leave the game
* \return true if event was handled succesfully
*/
bool playerQuit(StateManager::ActivePlayer* player);
virtual bool playerQuit(StateManager::ActivePlayer* player);
/** \brief implement callback from parent class GUIEngine::Screen */
virtual void init() OVERRIDE;

Some files were not shown because too many files have changed in this diff Show More