Initial work on lobby redesign

This commit is contained in:
Benau 2018-03-12 00:18:53 +08:00
parent 4dea283965
commit 3bbec8aa27
16 changed files with 461 additions and 392 deletions

View File

@ -25,13 +25,13 @@
#include "network/race_config.hpp" #include "network/race_config.hpp"
#include "network/remote_kart_info.hpp" #include "network/remote_kart_info.hpp"
#include <vector> #include <memory>
#include <mutex>
#include <string> #include <string>
#include <vector>
namespace Online { class OnlineProfile; }
class NetworkPlayerProfile; class NetworkPlayerProfile;
// ============================================================================ // ============================================================================
/*! \class GameSetup /*! \class GameSetup
* \brief Used to store the needed data about the players that join a game. * \brief Used to store the needed data about the players that join a game.

View File

@ -19,31 +19,6 @@
#include "network/network_player_profile.hpp" #include "network/network_player_profile.hpp"
#include "network/stk_host.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 /** 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 * is done by comparing the host id of this player with the host id of this

View File

@ -26,10 +26,11 @@
#include "utils/types.hpp" #include "utils/types.hpp"
#include "irrString.h" #include "irrString.h"
#include <memory>
#include <string> #include <string>
namespace Online { class OnlineProfile; } #include <tuple>
class STKPeer;
/*! \class NetworkPlayerProfile /*! \class NetworkPlayerProfile
* \brief Contains the profile of a player. * \brief Contains the profile of a player.
@ -37,31 +38,51 @@ namespace Online { class OnlineProfile; }
class NetworkPlayerProfile class NetworkPlayerProfile
{ {
private: 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 /** 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 * by the server (and it might not be the index of this player in the
* peer list. */ * peer list. */
uint8_t m_global_player_id; 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). */ /** The kart id in the World class (pointer to AbstractKart). */
uint8_t m_world_kart_id; uint8_t m_world_kart_id;
/** Per player difficulty. */
PerPlayerDifficulty m_per_player_difficulty;
public: public:
NetworkPlayerProfile(const irr::core::stringw &name, NetworkPlayerProfile(std::shared_ptr<STKPeer> peer,
int global_player_id, int host_id); const irr::core::stringw &name, uint32_t host_id,
~NetworkPlayerProfile(); 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; bool isLocalPlayer() const;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Sets the global player id of this player. */ /** Sets the global player id of this player. */
void setGlobalPlayerId(int player_id) { m_global_player_id = player_id; } void setGlobalPlayerId(int player_id) { m_global_player_id = player_id; }
@ -93,6 +114,15 @@ public:
/** Returns the name of this player. */ /** Returns the name of this player. */
const irr::core::stringw& getName() const { return m_player_name; } 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; }
// ------------------------------------------------------------------------
std::shared_ptr<STKPeer> getPeer() const { return m_peer.lock(); }
// ------------------------------------------------------------------------
/** Returns the minimun info for networking lobby screen. */
std::tuple<uint32_t, uint32_t, irr::core::stringw> toTuple() const
{ return std::make_tuple(m_host_id, m_online_id, m_player_name); }
}; // class NetworkPlayerProfile }; // class NetworkPlayerProfile

View File

@ -20,6 +20,7 @@
#include "config/player_manager.hpp" #include "config/player_manager.hpp"
#include "karts/kart_properties_manager.hpp" #include "karts/kart_properties_manager.hpp"
#include "guiengine/message_queue.hpp"
#include "modes/world_with_rank.hpp" #include "modes/world_with_rank.hpp"
#include "network/event.hpp" #include "network/event.hpp"
#include "network/network_config.hpp" #include "network/network_config.hpp"
@ -29,6 +30,7 @@
#include "network/race_event_manager.hpp" #include "network/race_event_manager.hpp"
#include "network/stk_host.hpp" #include "network/stk_host.hpp"
#include "network/stk_peer.hpp" #include "network/stk_peer.hpp"
#include "online/online_player_profile.hpp"
#include "online/online_profile.hpp" #include "online/online_profile.hpp"
#include "states_screens/networking_lobby.hpp" #include "states_screens/networking_lobby.hpp"
#include "states_screens/network_kart_selection.hpp" #include "states_screens/network_kart_selection.hpp"
@ -201,13 +203,6 @@ void ClientLobby::voteLaps(uint8_t player_id, uint8_t laps,
delete request; delete request;
} // voteLaps } // voteLaps
//-----------------------------------------------------------------------------
/** Called when a client selects to exit a server.
*/
void ClientLobby::leave()
{
} // leave
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Called from the gui when a client clicked on 'continue' on the race result /** 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 * screen. It notifies the server that this client has exited the screen and
@ -239,6 +234,7 @@ bool ClientLobby::notifyEvent(Event* event)
case LE_LOAD_WORLD: loadWorld(); break; case LE_LOAD_WORLD: loadWorld(); break;
case LE_RACE_FINISHED: raceFinished(event); break; case LE_RACE_FINISHED: raceFinished(event); break;
case LE_EXIT_RESULT: exitResultScreen(event); break; case LE_EXIT_RESULT: exitResultScreen(event); break;
case LE_UPDATE_PLAYER_LIST: updatePlayerList(event); break;
default: default:
return false; return false;
break; break;
@ -261,7 +257,6 @@ bool ClientLobby::notifyEventAsynchronous(Event* event)
message_type); message_type);
switch(message_type) switch(message_type)
{ {
case LE_NEW_PLAYER_CONNECTED: newPlayer(event); break;
case LE_PLAYER_DISCONNECTED : disconnectedPlayer(event); break; case LE_PLAYER_DISCONNECTED : disconnectedPlayer(event); break;
case LE_START_RACE: startGame(event); break; case LE_START_RACE: startGame(event); break;
case LE_CONNECTION_REFUSED: connectionRefused(event); break; case LE_CONNECTION_REFUSED: connectionRefused(event); break;
@ -273,6 +268,7 @@ bool ClientLobby::notifyEventAsynchronous(Event* event)
case LE_VOTE_TRACK: playerTrackVote(event); break; case LE_VOTE_TRACK: playerTrackVote(event); break;
case LE_VOTE_REVERSE: playerReversedVote(event); break; case LE_VOTE_REVERSE: playerReversedVote(event); break;
case LE_VOTE_LAPS: playerLapsVote(event); break; case LE_VOTE_LAPS: playerLapsVote(event); break;
case LE_AUTHORISED: becomingServerOwner(); break;
} // switch } // switch
return true; return true;
@ -321,20 +317,31 @@ void ClientLobby::update(float dt)
break; break;
case LINKED: case LINKED:
{ {
core::stringw name; // atm assume only 1 local player (no split screen yet)
if(PlayerManager::getCurrentOnlineState()==PlayerProfile::OS_SIGNED_IN) NetworkString *ns = getNetworkString();
name = PlayerManager::getCurrentOnlineUserName(); ns->addUInt8(LE_CONNECTION_REQUESTED)
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()); .encodeString(NetworkConfig::get()->getPassword());
uint8_t num_player = 1;
ns->addUInt8(num_player);
for (unsigned i = 0; i < num_player; i++)
{
core::stringw name;
PlayerProfile* player = PlayerManager::getCurrentPlayer();
if (PlayerManager::getCurrentOnlineState() ==
PlayerProfile::OS_SIGNED_IN)
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(opp && opp->getProfile() ?
opp->getProfile()->getID() : 0);
// Assume no handicap player now
ns->addUInt8(PLAYER_DIFFICULTY_NORMAL);
}
auto all_k = kart_properties_manager->getAllAvailableKarts(); auto all_k = kart_properties_manager->getAllAvailableKarts();
auto all_t = track_manager->getAllTrackIdentifiers(); auto all_t = track_manager->getAllTrackIdentifiers();
if (all_k.size() >= 65536) if (all_k.size() >= 65536)
@ -389,49 +396,6 @@ void ClientLobby::update(float dt)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/*! \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)
{
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
//-----------------------------------------------------------------------------
/*! \brief Called when a new player is disconnected /*! \brief Called when a new player is disconnected
* \param event : Event providing the information. * \param event : Event providing the information.
* *
@ -447,95 +411,67 @@ void ClientLobby::disconnectedPlayer(Event* event)
if (!checkDataSize(event, 1)) return; if (!checkDataSize(event, 1)) return;
NetworkString &data = event->data(); 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 = core::stringw player_name;
m_game_setup->getProfile(data.getUInt8()); data.decodeStringW(&player_name);
if (m_game_setup->removePlayer(profile)) core::stringw msg = _("%s disconnected.", player_name);
{ // Use the friend icon to avoid an error-like message
Log::info("ClientLobby", MessageQueue::add(MessageQueue::MT_FRIEND, msg);
"Player %d removed successfully.",
profile->getGlobalPlayerId());
} }
else
{
Log::error("ClientLobby",
"The disconnected peer wasn't known.");
}
} // while
} // disconnectedPlayer } // disconnectedPlayer
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/*! \brief Called when the server accepts the connection. /*! \brief Called when the server accepts the connection.
* \param event : Event providing the information. * \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) void ClientLobby::connectionAccepted(Event* event)
{ {
// At least 3 bytes should remain now // At least 4 byte should remain now
if(!checkDataSize(event, 3)) return; if (!checkDataSize(event, 4)) return;
NetworkString &data = event->data(); NetworkString &data = event->data();
STKPeer* peer = event->getPeer(); STKPeer* peer = event->getPeer();
// Accepted // Accepted
// ======== // ========
Log::info("ClientLobby", Log::info("ClientLobby", "The server accepted the connection.");
"The server accepted the connection.");
// self profile STKHost::get()->setMyHostId(data.getUInt32());
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); m_game_setup->setNumLocalPlayers(1);
// connection token // connection token
uint32_t token = data.getToken(); uint32_t token = data.getToken();
peer->setClientServerToken(token); 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;
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_state = CONNECTED; 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();
NetworkingLobby::getInstance()->cleanPlayers();
for (unsigned i = 0; i < player_count; i++)
{
std::tuple<uint32_t, uint32_t, core::stringw, bool> pl;
std::get<0>(pl) = data.getUInt32();
std::get<1>(pl) = data.getUInt32();
data.decodeStringW(&std::get<2>(pl));
std::get<3>(pl) = data.getUInt8() == 1;
NetworkingLobby::getInstance()->addPlayer(pl);
}
} // 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 // Send a message to the server to start
NetworkString start(PROTOCOL_LOBBY_ROOM); NetworkString start(PROTOCOL_LOBBY_ROOM);
@ -543,8 +479,7 @@ void ClientLobby::connectionAccepted(Event* event)
start.addUInt8(LobbyProtocol::LE_REQUEST_BEGIN); start.addUInt8(LobbyProtocol::LE_REQUEST_BEGIN);
STKHost::get()->sendToServer(&start, true); STKHost::get()->sendToServer(&start, true);
} }
} // becomingServerOwner
} // connectionAccepted
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -562,26 +497,31 @@ void ClientLobby::connectionRefused(Event* event)
{ {
if (!checkDataSize(event, 1)) return; if (!checkDataSize(event, 1)) return;
const NetworkString &data = event->data(); const NetworkString &data = event->data();
switch ((RejectReason)data.getUInt8()) // the second byte
switch (data.getUInt8()) // the second byte
{ {
case 0: case RR_BUSY:
Log::info("ClientLobby", STKHost::get()->setErrorMessage(
"Connection refused : too many players."); _("Connection refused: Server is busy."));
break; break;
case 1: case RR_BANNED:
Log::info("ClientLobby", "Connection refused : banned."); STKHost::get()->setErrorMessage(
_("Connection refused: You are banned from the server."));
break; break;
case 2: case RR_INCORRECT_PASSWORD:
Log::info("ClientLobby", "Client busy."); STKHost::get()->setErrorMessage(
_("Connection refused: Server password is incorrect."));
break; break;
case 3: case RR_INCOMPATIBLE_DATA:
Log::info("ClientLobby", "Having incompatible karts / tracks."); STKHost::get()->setErrorMessage(
_("Connection refused: Game data is incompatible."));
break; break;
default: case RR_TOO_MANY_PLAYERS:
Log::info("ClientLobby", "Connection refused."); STKHost::get()->setErrorMessage(
_("Connection refused: Server is full."));
break; break;
} }
STKHost::get()->disconnectAllPeers(false/*timeout_waiting*/);
STKHost::get()->requestShutdown();
} // connectionRefused } // connectionRefused
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -9,7 +9,6 @@
class ClientLobby : public LobbyProtocol class ClientLobby : public LobbyProtocol
{ {
private: private:
void newPlayer(Event* event);
void disconnectedPlayer(Event* event); void disconnectedPlayer(Event* event);
void connectionAccepted(Event* event); //!< Callback function on connection acceptation void connectionAccepted(Event* event); //!< Callback function on connection acceptation
void connectionRefused(Event* event); //!< Callback function on connection refusal void connectionRefused(Event* event); //!< Callback function on connection refusal
@ -26,6 +25,8 @@ private:
void playerTrackVote(Event* event); void playerTrackVote(Event* event);
void playerReversedVote(Event* event); void playerReversedVote(Event* event);
void playerLapsVote(Event* event); void playerLapsVote(Event* event);
void updatePlayerList(Event* event);
void becomingServerOwner();
TransportAddress m_server_address; TransportAddress m_server_address;
@ -65,7 +66,6 @@ public:
void voteLaps(uint8_t player_id, uint8_t laps, uint8_t track_nb = 0); void voteLaps(uint8_t player_id, uint8_t laps, uint8_t track_nb = 0);
void doneWithResults(); void doneWithResults();
void startingRaceNow(); void startingRaceNow();
void leave();
const std::set<std::string>& getAvailableKarts() const const std::set<std::string>& getAvailableKarts() const
{ return m_available_karts; } { return m_available_karts; }

View File

@ -42,7 +42,7 @@ public:
LE_KART_SELECTION_UPDATE, // inform client about kart selected LE_KART_SELECTION_UPDATE, // inform client about kart selected
LE_REQUEST_BEGIN, // begin of kart selection LE_REQUEST_BEGIN, // begin of kart selection
LE_KART_SELECTION_REFUSED, // Client not auth. to start 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_KART_SELECTION, // Player selected kart
LE_PLAYER_DISCONNECTED, // Client disconnected LE_PLAYER_DISCONNECTED, // Client disconnected
LE_CLIENT_LOADED_WORLD, // Client finished loading world LE_CLIENT_LOADED_WORLD, // Client finished loading world
@ -60,6 +60,18 @@ public:
LE_VOTE_TRACK, // vote for a track LE_VOTE_TRACK, // vote for a track
LE_VOTE_REVERSE, // vote if race in reverse LE_VOTE_REVERSE, // vote if race in reverse
LE_VOTE_LAPS, // vote number of laps LE_VOTE_LAPS, // vote number of laps
LE_CHAT,
LE_FINAL_PLAYER_LIST,
LE_AUTHORISED
};
enum RejectReason : uint8_t
{
RR_BUSY = 0,
RR_BANNED = 1,
RR_INCORRECT_PASSWORD = 2,
RR_INCOMPATIBLE_DATA = 3,
RR_TOO_MANY_PLAYERS = 4
}; };
protected: protected:

View File

@ -41,6 +41,7 @@
#include "utils/random_generator.hpp" #include "utils/random_generator.hpp"
#include "utils/time.hpp" #include "utils/time.hpp"
#include <algorithm>
#include <fstream> #include <fstream>
/** This is the central game setup protocol running in the server. It is /** This is the central game setup protocol running in the server. It is
@ -84,18 +85,9 @@
*/ */
ServerLobby::ServerLobby() : LobbyProtocol(NULL) ServerLobby::ServerLobby() : LobbyProtocol(NULL)
{ {
m_has_created_server_id_file = false;
setHandleDisconnections(true); setHandleDisconnections(true);
m_state = SET_PUBLIC_ADDRESS; m_state = SET_PUBLIC_ADDRESS;
// We use maximum 16bit unsigned limit
auto all_k = kart_properties_manager->getAllAvailableKarts();
auto all_t = track_manager->getAllTrackIdentifiers();
if (all_k.size() >= 65536)
all_k.resize(65535);
if (all_t.size() >= 65536)
all_t.resize(65535);
m_available_kts.getData().first = { all_k.begin(), all_k.end() };
m_available_kts.getData().second = { all_t.begin(), all_t.end() };
} // ServerLobby } // ServerLobby
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -113,6 +105,16 @@ ServerLobby::~ServerLobby()
void ServerLobby::setup() void ServerLobby::setup()
{ {
// We use maximum 16bit unsigned limit
auto all_k = kart_properties_manager->getAllAvailableKarts();
auto all_t = track_manager->getAllTrackIdentifiers();
if (all_k.size() >= 65536)
all_k.resize(65535);
if (all_t.size() >= 65536)
all_t.resize(65535);
m_available_kts.first = { all_k.begin(), all_k.end() };
m_available_kts.second = { all_t.begin(), all_t.end() };
m_server_registered = false; m_server_registered = false;
m_game_setup = STKHost::get()->setupNewGame(); m_game_setup = STKHost::get()->setupNewGame();
m_game_setup->setNumLocalPlayers(0); // no local players on a server m_game_setup->setNumLocalPlayers(0); // no local players on a server
@ -197,11 +199,12 @@ bool ServerLobby::notifyEventAsynchronous(Event* event)
void ServerLobby::createServerIdFile() void ServerLobby::createServerIdFile()
{ {
const std::string& sid = NetworkConfig::get()->getServerIdFile(); const std::string& sid = NetworkConfig::get()->getServerIdFile();
if (!sid.empty()) if (!sid.empty() && !m_has_created_server_id_file)
{ {
std::fstream fs; std::fstream fs;
fs.open(sid, std::ios::out); fs.open(sid, std::ios::out);
fs.close(); fs.close();
m_has_created_server_id_file = true;
} }
} // createServerIdFile } // createServerIdFile
@ -265,6 +268,7 @@ void ServerLobby::asynchronousUpdate()
default: default:
break; break;
} }
} // asynchronousUpdate } // asynchronousUpdate
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -274,6 +278,9 @@ void ServerLobby::asynchronousUpdate()
*/ */
void ServerLobby::update(float dt) void ServerLobby::update(float dt)
{ {
// Check if server owner has left
updateServerOwner();
switch (m_state.load()) switch (m_state.load())
{ {
case SET_PUBLIC_ADDRESS: case SET_PUBLIC_ADDRESS:
@ -463,12 +470,6 @@ void ServerLobby::signalRaceStartToClients()
void ServerLobby::startSelection(const Event *event) void ServerLobby::startSelection(const Event *event)
{ {
std::lock_guard<std::mutex> lock(m_connection_mutex); std::lock_guard<std::mutex> lock(m_connection_mutex);
if (NetworkConfig::get()->isWAN())
{
assert(m_server_registered);
unregisterServer();
m_server_registered = false;
}
if (m_state != ACCEPTING_CLIENTS) if (m_state != ACCEPTING_CLIENTS)
{ {
@ -477,21 +478,28 @@ void ServerLobby::startSelection(const Event *event)
m_state.load()); m_state.load());
return; return;
} }
if(event && !event->getPeer()->isAuthorised()) if (event->getPeerSP() != m_server_owner.lock())
{ {
Log::warn("ServerLobby", Log::warn("ServerLobby",
"Client %lx is not authorised to start selection.", "Client %lx is not authorised to start selection.",
event->getPeer()); event->getPeer());
return; return;
} }
if (NetworkConfig::get()->isWAN())
{
assert(m_server_registered);
unregisterServer();
m_server_registered = false;
}
NetworkString *ns = getNetworkString(1); NetworkString *ns = getNetworkString(1);
// Start selection - must be synchronous since the receiver pushes // Start selection - must be synchronous since the receiver pushes
// a new screen, which must be donefrom the main thread. // a new screen, which must be donefrom the main thread.
ns->setSynchronous(true); ns->setSynchronous(true);
ns->addUInt8(LE_START_SELECTION); ns->addUInt8(LE_START_SELECTION);
m_available_kts.lock(); const auto& all_k = m_available_kts.first;
const auto& all_k = m_available_kts.getData().first; const auto& all_t = m_available_kts.second;
const auto& all_t = m_available_kts.getData().second;
ns->addUInt16((uint16_t)all_k.size()).addUInt16((uint16_t)all_t.size()); ns->addUInt16((uint16_t)all_k.size()).addUInt16((uint16_t)all_t.size());
for (const std::string& kart : all_k) for (const std::string& kart : all_k)
{ {
@ -501,7 +509,6 @@ void ServerLobby::startSelection(const Event *event)
{ {
ns->encodeString(track); ns->encodeString(track);
} }
m_available_kts.unlock();
sendMessageToPeersChangingToken(ns, /*reliable*/true); sendMessageToPeersChangingToken(ns, /*reliable*/true);
delete ns; delete ns;
@ -625,23 +632,21 @@ void ServerLobby::checkIncomingConnectionRequests()
*/ */
void ServerLobby::clientDisconnected(Event* event) void ServerLobby::clientDisconnected(Event* event)
{ {
std::vector<NetworkPlayerProfile*> players_on_host = std::lock_guard<std::mutex> lock(m_connection_mutex);
event->getPeer()->getAllPlayerProfiles(); auto players_on_peer = event->getPeer()->getPlayerProfiles();
if (players_on_peer.empty())
return;
NetworkString *msg = getNetworkString(2); NetworkString* msg = getNetworkString(2);
msg->addUInt8(LE_PLAYER_DISCONNECTED); msg->addUInt8(LE_PLAYER_DISCONNECTED);
msg->addUInt8((uint8_t)players_on_peer.size());
for(unsigned int i=0; i<players_on_host.size(); i++) for (auto p : players_on_peer)
{ {
msg->addUInt8(players_on_host[i]->getGlobalPlayerId()); msg->encodeString(p->getName());
Log::info("ServerLobby", "Player disconnected : id %d",
players_on_host[i]->getGlobalPlayerId());
m_game_setup->removePlayer(players_on_host[i]);
} }
sendMessageToPeersChangingToken(msg, /*reliable*/true); sendMessageToPeersChangingToken(msg, /*reliable*/true);
updatePlayerList();
delete msg; delete msg;
} // clientDisconnected } // clientDisconnected
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -659,34 +664,62 @@ void ServerLobby::clientDisconnected(Event* event)
void ServerLobby::connectionRequested(Event* event) void ServerLobby::connectionRequested(Event* event)
{ {
std::lock_guard<std::mutex> lock(m_connection_mutex); std::lock_guard<std::mutex> lock(m_connection_mutex);
STKPeer* peer = event->getPeer(); std::shared_ptr<STKPeer> peer = event->getPeerSP();
peer->cleanPlayerProfiles();
const NetworkString &data = event->data(); const NetworkString &data = event->data();
// can we add the player ? // can we add the player ?
if (m_game_setup->getPlayerCount() >= NetworkConfig::get()->getMaxPlayers() || if (m_game_setup->getPlayerCount() >=
m_state!=ACCEPTING_CLIENTS ) NetworkConfig::get()->getMaxPlayers() ||
m_state != ACCEPTING_CLIENTS)
{ {
NetworkString *message = getNetworkString(2); NetworkString *message = getNetworkString(2);
// Len, error code: 2 = busy, 0 = too many players
message->addUInt8(LE_CONNECTION_REFUSED) message->addUInt8(LE_CONNECTION_REFUSED)
.addUInt8(m_state!=ACCEPTING_CLIENTS ? 2 : 0); .addUInt8(m_state != ACCEPTING_CLIENTS ?
RR_BUSY : RR_TOO_MANY_PLAYERS);
// send only to the peer that made the request // send only to the peer that made the request and disconect it now
peer->sendPacket(message); peer->sendPacket(message);
peer->reset();
delete message; delete message;
Log::verbose("ServerLobby", "Player refused"); Log::verbose("ServerLobby", "Player refused");
return; return;
} }
// Check for password
std::string password;
data.decodeString(&password);
if (password != NetworkConfig::get()->getPassword())
{
NetworkString *message = getNetworkString(2);
message->addUInt8(LE_CONNECTION_REFUSED)
.addUInt8(RR_INCORRECT_PASSWORD);
// send only to the peer that made the request and disconect it now
peer->sendPacket(message);
peer->reset();
delete message;
Log::verbose("ServerLobby", "Player refused: incorrect password");
return;
}
// Connection accepted. // Connection accepted.
// ==================== // ====================
uint8_t player_count = data.getUInt8();
for (unsigned i = 0; i < player_count; i++)
{
std::string name_u8; std::string name_u8;
data.decodeString(&name_u8); data.decodeString(&name_u8);
core::stringw name = StringUtils::utf8ToWide(name_u8); core::stringw name = StringUtils::utf8ToWide(name_u8);
std::string password; float default_kart_color = data.getFloat();
data.decodeString(&password); uint32_t online_id = data.getUInt32();
bool is_authorised = (password==NetworkConfig::get()->getPassword()); PerPlayerDifficulty per_player_difficulty =
(PerPlayerDifficulty)data.getUInt8();
peer->addPlayer(std::make_shared<NetworkPlayerProfile>
(peer, name, peer->getHostId(), default_kart_color, online_id,
per_player_difficulty));
}
std::set<std::string> client_karts, client_tracks; std::set<std::string> client_karts, client_tracks;
const unsigned kart_num = data.getUInt16(); const unsigned kart_num = data.getUInt16();
const unsigned track_num = data.getUInt16(); const unsigned track_num = data.getUInt16();
@ -707,14 +740,14 @@ void ServerLobby::connectionRequested(Event* event)
// so that in the end the server has a list of all karts/tracks available // so that in the end the server has a list of all karts/tracks available
// on all clients // on all clients
std::set<std::string> karts_erase, tracks_erase; std::set<std::string> karts_erase, tracks_erase;
for (const std::string& server_kart : m_available_kts.getData().first) for (const std::string& server_kart : m_available_kts.first)
{ {
if (client_karts.find(server_kart) == client_karts.end()) if (client_karts.find(server_kart) == client_karts.end())
{ {
karts_erase.insert(server_kart); karts_erase.insert(server_kart);
} }
} }
for (const std::string& server_track : m_available_kts.getData().second) for (const std::string& server_track : m_available_kts.second)
{ {
if (client_tracks.find(server_track) == client_tracks.end()) if (client_tracks.find(server_track) == client_tracks.end())
{ {
@ -724,48 +757,27 @@ void ServerLobby::connectionRequested(Event* event)
// Drop this player if he doesn't have at least 1 kart / track the same // Drop this player if he doesn't have at least 1 kart / track the same
// from server // from server
if (karts_erase.size() == m_available_kts.getData().first.size() || if (karts_erase.size() == m_available_kts.first.size() ||
tracks_erase.size() == m_available_kts.getData().second.size()) tracks_erase.size() == m_available_kts.second.size())
{ {
NetworkString *message = getNetworkString(2); NetworkString *message = getNetworkString(2);
message->addUInt8(LE_CONNECTION_REFUSED).addUInt8(3); message->addUInt8(LE_CONNECTION_REFUSED)
.addUInt8(RR_INCOMPATIBLE_DATA);
peer->sendPacket(message); peer->sendPacket(message);
peer->reset();
delete message; delete message;
Log::verbose("ServerLobby", "Player has incompatible karts / tracks"); Log::verbose("ServerLobby", "Player has incompatible karts / tracks");
m_available_kts.unlock();
return; return;
} }
for (const std::string& kart_erase : karts_erase) for (const std::string& kart_erase : karts_erase)
{ {
m_available_kts.getData().first.erase(kart_erase); m_available_kts.first.erase(kart_erase);
} }
for (const std::string& track_erase : tracks_erase) for (const std::string& track_erase : tracks_erase)
{ {
m_available_kts.getData().second.erase(track_erase); m_available_kts.second.erase(track_erase);
} }
m_available_kts.unlock();
// Get the unique global ID for this player.
m_next_player_id.lock();
m_next_player_id.getData()++;
int new_player_id = m_next_player_id.getData();
m_next_player_id.unlock();
if(m_game_setup->getLocalMasterID()==0)
m_game_setup->setLocalMaster(new_player_id);
// The host id has already been incremented when the peer
// was added, so it is the right id now.
int new_host_id = STKHost::get()->getNextHostId();
// Notify everybody that there is a new player
// -------------------------------------------
NetworkString *message = getNetworkString(3+1+name_u8.size());
// size of id -- id -- size of local id -- local id;
message->addUInt8(LE_NEW_PLAYER_CONNECTED).addUInt8(new_player_id)
.addUInt8(new_host_id).encodeString(name_u8);
STKHost::get()->sendPacketExcept(peer, message);
delete message;
// Now answer to the peer that just connected // Now answer to the peer that just connected
// ------------------------------------------ // ------------------------------------------
@ -777,38 +789,83 @@ void ServerLobby::connectionRequested(Event* event)
(token_generator.get(RAND_MAX) & 0xff)); (token_generator.get(RAND_MAX) & 0xff));
peer->setClientServerToken(token); peer->setClientServerToken(token);
peer->setAuthorised(is_authorised);
peer->setHostId(new_host_id);
const std::vector<NetworkPlayerProfile*> &players = m_game_setup->getPlayers();
// send a message to the one that asked to connect // send a message to the one that asked to connect
// Estimate 10 as average name length NetworkString *message_ack = getNetworkString(4);
NetworkString *message_ack = getNetworkString(4 + players.size() * (2+10)); // connection success -- return the host id of peer
// connection success -- size of token -- token message_ack->addUInt8(LE_CONNECTION_ACCEPTED).addUInt32(peer->getHostId());
message_ack->addUInt8(LE_CONNECTION_ACCEPTED).addUInt8(new_player_id)
.addUInt8(new_host_id).addUInt8(is_authorised);
// Add all players so that this user knows (this new player is only added
// to the list of players later, so the new player's info is not included)
for (unsigned int i = 0; i < players.size(); i++)
{
message_ack->addUInt8(players[i]->getGlobalPlayerId())
.addUInt8(players[i]->getHostId())
.encodeString(players[i]->getName());
}
peer->sendPacket(message_ack); peer->sendPacket(message_ack);
delete message_ack; delete message_ack;
NetworkPlayerProfile* profile = updatePlayerList();
new NetworkPlayerProfile(name, new_player_id, new_host_id);
m_game_setup->addPlayer(profile);
NetworkingLobby::getInstance()->addPlayer(profile);
Log::verbose("ServerLobby", "New player."); Log::verbose("ServerLobby", "New player.");
} // connectionRequested } // connectionRequested
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ServerLobby::updatePlayerList()
{
if (m_state.load() != ACCEPTING_CLIENTS)
return;
auto all_profiles = STKHost::get()->getAllPlayerProfiles();
NetworkString* pl = getNetworkString();
pl->setSynchronous(true);
pl->addUInt8(LE_UPDATE_PLAYER_LIST).addUInt8((uint8_t)all_profiles.size());
for (auto profile : all_profiles)
{
pl->addUInt32(profile->getHostId()).addUInt32(profile->getOnlineID())
.encodeString(profile->getName());
uint8_t server_owner = 0;
if (m_server_owner.lock() == profile->getPeer())
server_owner = 1;
pl->addUInt8(server_owner);
}
sendMessageToPeersChangingToken(pl);
delete pl;
} // updatePlayerList
//-----------------------------------------------------------------------------
void ServerLobby::updateServerOwner()
{
if (m_state.load() < ACCEPTING_CLIENTS ||
m_state.load() > RESULT_DISPLAY)
return;
if (!m_server_owner.expired())
return;
auto peers = STKHost::get()->getPeers();
if (peers.empty())
return;
std::sort(peers.begin(), peers.end(), [](const std::shared_ptr<STKPeer> a,
const std::shared_ptr<STKPeer> b)->bool
{
return a->getHostId() < b->getHostId();
});
std::shared_ptr<STKPeer> owner;
std::lock_guard<std::mutex> lock(m_connection_mutex);
// Make sure no one access the weak pointer or adding player to peers
for (auto peer: peers)
{
// Only 127.0.0.1 can be server owner in case of graphics-client-server
if (peer->hasPlayerProfiles() &&
(NetworkConfig::get()->getServerIdFile().empty() ||
peer->getAddress().getIP() == 0x7f000001))
{
owner = peer;
break;
}
}
if (owner)
{
NetworkString* ns = getNetworkString();
ns->addUInt8(LE_AUTHORISED);
owner->sendPacket(ns);
delete ns;
m_server_owner = owner;
updatePlayerList();
}
} // updateServerOwner
//-----------------------------------------------------------------------------
/*! \brief Called when a player asks to select a kart. /*! \brief Called when a player asks to select a kart.
* \param event : Event providing the information. * \param event : Event providing the information.
* *

View File

@ -6,9 +6,12 @@
#include "utils/synchronised.hpp" #include "utils/synchronised.hpp"
#include <atomic> #include <atomic>
#include <memory>
#include <mutex> #include <mutex>
#include <set> #include <set>
class STKPeer;
class ServerLobby : public LobbyProtocol class ServerLobby : public LobbyProtocol
{ {
public: public:
@ -31,10 +34,13 @@ public:
private: private:
std::atomic<ServerState> m_state; 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 /** Available karts and tracks for all clients, this will be initialized
* with data in server first. */ * with data in server first. */
Synchronised<std::pair<std::set<std::string>, std::pair<std::set<std::string>, std::set<std::string> > m_available_kts;
std::set<std::string> > > m_available_kts;
/** Next id to assign to a peer. */ /** Next id to assign to a peer. */
Synchronised<int> m_next_player_id; Synchronised<int> m_next_player_id;
@ -58,6 +64,8 @@ private:
bool m_selection_enabled; bool m_selection_enabled;
bool m_has_created_server_id_file;
/** It indicates if this server is registered with the stk server. */ /** It indicates if this server is registered with the stk server. */
std::atomic_bool m_server_registered; std::atomic_bool m_server_registered;
@ -89,6 +97,9 @@ private:
void startedRaceOnClient(Event *event); void startedRaceOnClient(Event *event);
void unregisterServer(); void unregisterServer();
void createServerIdFile(); void createServerIdFile();
void updatePlayerList();
void updateServerOwner();
public: public:
ServerLobby(); ServerLobby();
virtual ~ServerLobby(); virtual ~ServerLobby();

View File

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

View File

@ -262,7 +262,6 @@ void STKHost::create(std::shared_ptr<Server> server, SeparateProcess* p)
*/ */
STKHost::STKHost(std::shared_ptr<Server> server) 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 // Will be overwritten with the correct value once a connection with the
// server is made. // server is made.
m_host_id = 0; m_host_id = 0;
@ -291,16 +290,14 @@ STKHost::STKHost(std::shared_ptr<Server> server)
STKHost::STKHost(const irr::core::stringw &server_name) STKHost::STKHost(const irr::core::stringw &server_name)
{ {
init(); 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. m_host_id = 0; // indicates a server host.
ENetAddress addr; ENetAddress addr;
addr.host = STKHost::HOST_ANY; addr.host = STKHost::HOST_ANY;
addr.port = NetworkConfig::get()->getServerPort(); 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, /*channel_limit*/2,
/*max_in_bandwidth*/0, /*max_in_bandwidth*/0,
/*max_out_bandwidth*/ 0, &addr, /*max_out_bandwidth*/ 0, &addr,
@ -320,6 +317,7 @@ STKHost::STKHost(const irr::core::stringw &server_name)
void STKHost::init() void STKHost::init()
{ {
m_shutdown = false; m_shutdown = false;
m_authorised = false;
m_network = NULL; m_network = NULL;
m_game_setup = NULL; m_game_setup = NULL;
m_exit_timeout.store(std::numeric_limits<double>::max()); m_exit_timeout.store(std::numeric_limits<double>::max());
@ -627,13 +625,14 @@ GameSetup* STKHost::setupNewGame()
void STKHost::disconnectAllPeers(bool timeout_waiting) void STKHost::disconnectAllPeers(bool timeout_waiting)
{ {
std::lock_guard<std::mutex> lock(m_peers_mutex); std::lock_guard<std::mutex> lock(m_peers_mutex);
if (!m_peers.empty()) if (!m_peers.empty() && timeout_waiting)
{ {
for (auto peer : m_peers)
peer.second->disconnect();
// Wait for at most 2 seconds for disconnect event to be generated // Wait for at most 2 seconds for disconnect event to be generated
if (timeout_waiting)
m_exit_timeout.store(StkTime::getRealTime() + 2.0); m_exit_timeout.store(StkTime::getRealTime() + 2.0);
m_peers.clear();
} }
m_peers.clear();
} // disconnectAllPeers } // disconnectAllPeers
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -694,23 +693,6 @@ void STKHost::stopListening()
m_listening_thread.join(); m_listening_thread.join();
} // stopListening } // 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.begin()->second->isAuthorised();
} // isAuthorisedToControl
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** \brief Thread function checking if data is received. /** \brief Thread function checking if data is received.
* This function tries to get data from network low-level functions as * This function tries to get data from network low-level functions as
@ -767,6 +749,14 @@ void STKHost::mainLoop()
case ECT_DISCONNECT: case ECT_DISCONNECT:
enet_peer_disconnect(std::get<0>(p), std::get<2>(p)); enet_peer_disconnect(std::get<0>(p), std::get<2>(p));
break; 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;
} }
} }
@ -778,8 +768,8 @@ void STKHost::mainLoop()
Event* stk_event = NULL; Event* stk_event = NULL;
if (event.type == ENET_EVENT_TYPE_CONNECT) if (event.type == ENET_EVENT_TYPE_CONNECT)
{ {
auto stk_peer = auto stk_peer = std::make_shared<STKPeer>
std::make_shared<STKPeer>(event.peer, this); (event.peer, this, m_next_unique_host_id++);
std::unique_lock<std::mutex> lock(m_peers_mutex); std::unique_lock<std::mutex> lock(m_peers_mutex);
m_peers[event.peer] = stk_peer; m_peers[event.peer] = stk_peer;
lock.unlock(); lock.unlock();
@ -1026,3 +1016,30 @@ void STKHost::sendToServer(NetworkString *data, bool reliable)
assert(NetworkConfig::get()->isClient()); assert(NetworkConfig::get()->isClient());
m_peers.begin()->second->sendPacket(data, reliable); m_peers.begin()->second->sendPacket(data, reliable);
} // sendToServer } // 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

View File

@ -50,7 +50,8 @@ class SeparateProcess;
enum ENetCommandType : unsigned int enum ENetCommandType : unsigned int
{ {
ECT_SEND_PACKET = 0, ECT_SEND_PACKET = 0,
ECT_DISCONNECT = 1 ECT_DISCONNECT = 1,
ECT_RESET = 2
}; };
class STKHost class STKHost
@ -80,7 +81,7 @@ private:
std::thread m_network_console; std::thread m_network_console;
/** Make sure the removing or adding a peer is thread-safe. */ /** Make sure the removing or adding a peer is thread-safe. */
std::mutex m_peers_mutex; mutable std::mutex m_peers_mutex;
/** Let (atm enet_peer_send and enet_peer_disconnect) run in the listening /** Let (atm enet_peer_send and enet_peer_disconnect) run in the listening
* thread. */ * thread. */
@ -98,10 +99,10 @@ private:
* getPeer()), but not decreased whena host (=peer) disconnects. This * getPeer()), but not decreased whena host (=peer) disconnects. This
* results in a unique host id for each host, even when a host should * results in a unique host id for each host, even when a host should
* disconnect and then reconnect. */ * disconnect and then reconnect. */
int m_next_unique_host_id; uint32_t m_next_unique_host_id = 0;
/** Host id of this host. */ /** Host id of this host. */
uint8_t m_host_id; uint32_t m_host_id = 0;
/** Stores data about the online game to play. */ /** Stores data about the online game to play. */
GameSetup* m_game_setup; GameSetup* m_game_setup;
@ -113,6 +114,9 @@ private:
* triggers a shutdown of the STKHost (and the Protocolmanager). */ * triggers a shutdown of the STKHost (and the Protocolmanager). */
std::atomic_bool m_shutdown; std::atomic_bool m_shutdown;
/** 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. */ /** Use as a timeout to waiting a disconnect event when exiting. */
std::atomic<double> m_exit_timeout; std::atomic<double> m_exit_timeout;
@ -142,7 +146,6 @@ public:
* a crash in release mode on windows (see #1529). */ * a crash in release mode on windows (see #1529). */
static bool m_enable_console; static bool m_enable_console;
/** Creates the STKHost. It takes all confifguration parameters from /** Creates the STKHost. It takes all confifguration parameters from
* NetworkConfig. This STKHost can either be a client or a server. * NetworkConfig. This STKHost can either be a client or a server.
*/ */
@ -199,7 +202,21 @@ public:
void shutdown(); void shutdown();
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
void sendPacketToAllPeers(NetworkString *data, bool reliable = true); 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, void sendPacketExcept(STKPeer* peer,
NetworkString *data, NetworkString *data,
bool reliable = true); bool reliable = true);
@ -213,8 +230,6 @@ public:
std::shared_ptr<STKPeer> getServerPeerForClient() const; std::shared_ptr<STKPeer> getServerPeerForClient() const;
std::vector<NetworkPlayerProfile*> getMyPlayerProfiles(); std::vector<NetworkPlayerProfile*> getMyPlayerProfiles();
void setErrorMessage(const irr::core::stringw &message); void setErrorMessage(const irr::core::stringw &message);
bool isAuthorisedToControl() const;
//-------------------------------------------------------------------------
void addEnetCommand(ENetPeer* peer, ENetPacket* packet, uint32_t i, void addEnetCommand(ENetPeer* peer, ENetPacket* packet, uint32_t i,
ENetCommandType ect) ENetCommandType ect)
{ {
@ -247,7 +262,7 @@ public:
} // sendRawPacket } // sendRawPacket
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns a copied list of peers. */ /** Returns a copied list of peers. */
std::vector<std::shared_ptr<STKPeer> > getPeers() std::vector<std::shared_ptr<STKPeer> > getPeers() const
{ {
std::lock_guard<std::mutex> lock(m_peers_mutex); std::lock_guard<std::mutex> lock(m_peers_mutex);
std::vector<std::shared_ptr<STKPeer> > peers; std::vector<std::shared_ptr<STKPeer> > peers;
@ -266,17 +281,17 @@ public:
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the number of currently connected peers. */ /** Returns the number of currently connected peers. */
unsigned int getPeerCount() unsigned int getPeerCount() const
{ {
std::lock_guard<std::mutex> lock(m_peers_mutex); std::lock_guard<std::mutex> lock(m_peers_mutex);
return m_peers.size(); return m_peers.size();
} }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Sets the global host id of this host. */ /** Sets the global host id of this host (client use). */
void setMyHostId(uint8_t my_host_id) { m_host_id = my_host_id; } void setMyHostId(uint32_t my_host_id) { m_host_id = my_host_id; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the host id of this host. */ /** Returns the host id of this host. */
uint8_t getMyHostId() const { return m_host_id; } uint32_t getMyHostId() const { return m_host_id; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void sendToServer(NetworkString *data, bool reliable = true); void sendToServer(NetworkString *data, bool reliable = true);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -29,27 +29,24 @@
/** Constructor for an empty peer. /** Constructor for an empty peer.
*/ */
STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host) STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
: m_peer_address(enet_peer->address), m_host(host) : m_peer_address(enet_peer->address), m_host(host)
{ {
m_enet_peer = enet_peer; m_enet_peer = enet_peer;
m_is_authorised = false;
m_client_server_token = 0; m_client_server_token = 0;
m_host_id = 0; m_host_id = host_id;
m_token_set = false; m_token_set = false;
} // STKPeer } // STKPeer
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Destructor. void STKPeer::disconnect()
*/
STKPeer::~STKPeer()
{ {
TransportAddress a(m_enet_peer->address); TransportAddress a(m_enet_peer->address);
if (m_enet_peer->state != ENET_PEER_STATE_CONNECTED || if (m_enet_peer->state != ENET_PEER_STATE_CONNECTED ||
a != m_peer_address) a != m_peer_address)
return; return;
m_host->addEnetCommand(m_enet_peer, NULL, PDI_NORMAL, ECT_DISCONNECT); m_host->addEnetCommand(m_enet_peer, NULL, PDI_NORMAL, ECT_DISCONNECT);
} // ~STKPeer } // disconnect
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Kick this peer (used by server). /** Kick this peer (used by server).
@ -63,6 +60,18 @@ void STKPeer::kick()
m_host->addEnetCommand(m_enet_peer, NULL, PDI_KICK, ECT_DISCONNECT); m_host->addEnetCommand(m_enet_peer, NULL, PDI_KICK, ECT_DISCONNECT);
} // kick } // 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. /** Sends a packet to this host.
* \param data The data to send. * \param data The data to send.
@ -111,14 +120,3 @@ bool STKPeer::isSamePeer(const ENetPeer* peer) const
{ {
return peer==m_enet_peer; return peer==m_enet_peer;
} // isSamePeer } // 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.
*/
std::vector<NetworkPlayerProfile*> STKPeer::getAllPlayerProfiles()
{
return m_host->getGameSetup()->getAllPlayersOnHost(getHostId());
} // getAllPlayerProfiles

View File

@ -29,6 +29,7 @@
#include <enet/enet.h> #include <enet/enet.h>
#include <memory>
#include <vector> #include <vector>
class NetworkPlayerProfile; class NetworkPlayerProfile;
@ -62,26 +63,38 @@ protected:
/** Host id of this peer. */ /** Host id of this peer. */
int m_host_id; int m_host_id;
/** True if this peer is authorised to control a server. */
bool m_is_authorised;
TransportAddress m_peer_address; TransportAddress m_peer_address;
STKHost* m_host; STKHost* m_host;
std::vector<std::shared_ptr<NetworkPlayerProfile> > m_players;
public: public:
STKPeer(ENetPeer *enet_peer, STKHost* host); STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id);
~STKPeer(); ~STKPeer() {}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void sendPacket(NetworkString *data, bool reliable = true); void sendPacket(NetworkString *data, bool reliable = true);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void disconnect();
// ------------------------------------------------------------------------
void kick(); void kick();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void reset();
// ------------------------------------------------------------------------
bool isConnected() const; bool isConnected() const;
const TransportAddress& getAddress() const { return m_peer_address; } const TransportAddress& getAddress() const { return m_peer_address; }
bool isSamePeer(const STKPeer* peer) const; bool isSamePeer(const STKPeer* peer) const;
bool isSamePeer(const ENetPeer* peer) const; bool isSamePeer(const ENetPeer* peer) const;
std::vector<NetworkPlayerProfile*> getAllPlayerProfiles(); // ------------------------------------------------------------------------
std::vector<std::shared_ptr<NetworkPlayerProfile> >
getPlayerProfiles() const { 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. */ /** Sets the token for this client. */
void setClientServerToken(const uint32_t& token) void setClientServerToken(const uint32_t& token)
@ -90,7 +103,11 @@ public:
m_token_set = true; m_token_set = true;
} // setClientServerToken } // setClientServerToken
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void unsetClientServerToken() { m_token_set = false; } void unsetClientServerToken()
{
m_token_set = false;
m_client_server_token = 0;
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the token of this client. */ /** Returns the token of this client. */
uint32_t getClientServerToken() const { return m_client_server_token; } uint32_t getClientServerToken() const { return m_client_server_token; }
@ -98,21 +115,9 @@ public:
/** Returns if the token for this client is known. */ /** Returns if the token for this client is known. */
bool isClientServerTokenSet() const { return m_token_set; } bool isClientServerTokenSet() const { return m_token_set; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Sets the host if of this peer. */
void setHostId(int host_id) { m_host_id = host_id; }
// ------------------------------------------------------------------------
/** Returns the host id of this peer. */ /** 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; }
// ------------------------------------------------------------------------
/** 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; }
}; // STKPeer }; // STKPeer
#endif // STK_PEER_HPP #endif // STK_PEER_HPP

View File

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

View File

@ -34,7 +34,6 @@
#include "input/input_manager.hpp" #include "input/input_manager.hpp"
#include "io/file_manager.hpp" #include "io/file_manager.hpp"
#include "network/network_config.hpp" #include "network/network_config.hpp"
#include "network/network_player_profile.hpp"
#include "network/protocols/client_lobby.hpp" #include "network/protocols/client_lobby.hpp"
#include "network/protocols/server_lobby.hpp" #include "network/protocols/server_lobby.hpp"
#include "network/server.hpp" #include "network/server.hpp"
@ -262,16 +261,25 @@ void NetworkingLobby::onDialogClose()
} // onDialogClose() } // onDialogClose()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void NetworkingLobby::addPlayer(NetworkPlayerProfile *profile) void NetworkingLobby::addPlayer(const std::tuple<uint32_t, uint32_t,
core::stringw, bool>& p)
{ {
// In GUI-less server this function will be called without proper // In GUI-less server this function will be called without proper
// initialisation // initialisation
if(m_player_list) if (m_player_list)
m_player_list->addItem(StringUtils::toString(profile->getGlobalPlayerId()), {
profile->getName()); const std::string internal_name =
StringUtils::toString(std::get<0>(p)) + "_" +
StringUtils::toString(std::get<1>(p));
m_player_list->addItem(internal_name, std::get<2>(p));
if (std::get<3>(p))
m_player_list->markItemRed(internal_name, true);
}
} // addPlayer } // addPlayer
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void NetworkingLobby::removePlayer(NetworkPlayerProfile *profile) void NetworkingLobby::cleanPlayers()
{ {
} // removePlayer if (m_player_list)
m_player_list->clear();
} // cleanPlayers

View File

@ -20,6 +20,7 @@
#include "guiengine/screen.hpp" #include "guiengine/screen.hpp"
#include <memory> #include <memory>
#include <tuple>
class Server; class Server;
@ -32,8 +33,6 @@ namespace GUIEngine
class TextBoxWidget; class TextBoxWidget;
} }
class NetworkPlayerProfile;
/** /**
* \brief Handles the main menu * \brief Handles the main menu
* \ingroup states_screens * \ingroup states_screens
@ -91,8 +90,9 @@ public:
/** Used to insert each client chat message (reserved). */ /** Used to insert each client chat message (reserved). */
void addMoreServerInfo(const core::stringw& info); void addMoreServerInfo(const core::stringw& info);
void setJoinedServer(std::shared_ptr<Server> server); void setJoinedServer(std::shared_ptr<Server> server);
void addPlayer(NetworkPlayerProfile *profile); void addPlayer(const std::tuple<uint32_t/*host id*/, uint32_t/*online id*/,
void removePlayer(NetworkPlayerProfile *profile); core::stringw/*player name*/, bool/*is server owner*/>& p);
void cleanPlayers();
}; // class NetworkingLobby }; // class NetworkingLobby
#endif #endif