Merge remote-tracking branch 'origin/kobato' into game_protocol
This commit is contained in:
commit
1b72d88593
@ -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
BIN
data/gui/crown.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
@ -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>
|
@ -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>
|
||||
|
@ -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%">
|
||||
|
@ -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">
|
||||
|
@ -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"/>
|
||||
|
@ -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>
|
||||
|
46
data/gui/online/splitscreen_player_dialog.stkgui
Normal file
46
data/gui/online/splitscreen_player_dialog.stkgui
Normal 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>
|
@ -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>
|
@ -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 ************ -->
|
||||
|
@ -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>
|
||||
|
@ -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/*")
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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") );
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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); }
|
||||
};
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 ++;
|
||||
|
@ -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
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -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. */
|
||||
|
@ -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)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 = "";
|
||||
|
@ -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; }
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -56,7 +56,7 @@ private:
|
||||
|
||||
void registerWithSTKServer();
|
||||
void waitingAloha(bool is_wan);
|
||||
|
||||
bool handleDirectConnect();
|
||||
public:
|
||||
ConnectToServer(std::shared_ptr<Server> server);
|
||||
virtual ~ConnectToServer();
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
@ -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; }
|
||||
|
||||
|
@ -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
|
@ -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
|
@ -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
|
||||
{
|
||||
|
@ -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, ...)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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
|
||||
|
@ -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)));
|
||||
}
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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. */
|
||||
|
@ -35,8 +35,6 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
DEFINE_SCREEN_SINGLETON( AddonsScreen );
|
||||
|
||||
using namespace Online;
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -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";
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
125
src/states_screens/dialogs/network_user_dialog.cpp
Normal file
125
src/states_screens/dialogs/network_user_dialog.cpp
Normal 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
|
89
src/states_screens/dialogs/network_user_dialog.hpp
Normal file
89
src/states_screens/dialogs/network_user_dialog.hpp
Normal 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
|
@ -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();
|
||||
|
@ -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);
|
||||
|
160
src/states_screens/dialogs/splitscreen_player_dialog.cpp
Normal file
160
src/states_screens/dialogs/splitscreen_player_dialog.cpp
Normal 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
|
101
src/states_screens/dialogs/splitscreen_player_dialog.hpp
Normal file
101
src/states_screens/dialogs/splitscreen_player_dialog.hpp
Normal 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
|
@ -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")
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -27,8 +27,6 @@
|
||||
|
||||
using namespace GUIEngine;
|
||||
|
||||
DEFINE_SCREEN_SINGLETON( GhostReplaySelection );
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
/** Constructor, which loads the stkgui 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)
|
||||
{
|
||||
|
@ -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),
|
||||
|
@ -69,8 +69,6 @@ const float KART_Z = 0.0f;
|
||||
|
||||
const int MAX_KART_COUNT = 4;
|
||||
|
||||
DEFINE_SCREEN_SINGLETON( GrandPrixLose );
|
||||
|
||||
// -------------------------------------------------------------------------------------
|
||||
|
||||
void GrandPrixLose::onCutsceneEnd()
|
||||
|
@ -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")
|
||||
|
@ -36,8 +36,6 @@
|
||||
|
||||
using namespace GUIEngine;
|
||||
|
||||
DEFINE_SCREEN_SINGLETON( HelpScreen1 );
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
HelpScreen1::HelpScreen1() : Screen("help1.stkgui")
|
||||
|
@ -27,8 +27,6 @@
|
||||
|
||||
using namespace GUIEngine;
|
||||
|
||||
DEFINE_SCREEN_SINGLETON( HelpScreen2 );
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
HelpScreen2::HelpScreen2() : Screen("help2.stkgui")
|
||||
|
@ -28,8 +28,6 @@
|
||||
|
||||
using namespace GUIEngine;
|
||||
|
||||
DEFINE_SCREEN_SINGLETON( HelpScreen3 );
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
HelpScreen3::HelpScreen3() : Screen("help3.stkgui")
|
||||
|
@ -28,8 +28,6 @@
|
||||
|
||||
using namespace GUIEngine;
|
||||
|
||||
DEFINE_SCREEN_SINGLETON( HelpScreen4 );
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
HelpScreen4::HelpScreen4() : Screen("help4.stkgui")
|
||||
|
@ -27,8 +27,6 @@
|
||||
|
||||
using namespace GUIEngine;
|
||||
|
||||
DEFINE_SCREEN_SINGLETON( HelpScreen5 );
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
HelpScreen5::HelpScreen5() : Screen("help5.stkgui")
|
||||
|
@ -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(),
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user