Implement auto-kick high ping player (default off)

This commit is contained in:
Benau 2018-08-03 19:50:49 +08:00
parent 14475762b8
commit 4d2e79da4c
9 changed files with 121 additions and 51 deletions

View File

@ -727,7 +727,7 @@ namespace UserConfigParams
PARAM_DEFAULT(FloatUserConfigParam(20.0f, "validation-timeout", PARAM_DEFAULT(FloatUserConfigParam(20.0f, "validation-timeout",
&m_network_group, "Timeout in seconds for validation of clients.")); &m_network_group, "Timeout in seconds for validation of clients."));
PARAM_PREFIX IntUserConfigParam m_server_max_players PARAM_PREFIX IntUserConfigParam m_server_max_players
PARAM_DEFAULT(IntUserConfigParam(8, "server_max_players", PARAM_DEFAULT(IntUserConfigParam(8, "server-max-players",
&m_network_group, "Maximum number of players on the server.")); &m_network_group, "Maximum number of players on the server."));
PARAM_PREFIX BoolUserConfigParam m_firewalled_server PARAM_PREFIX BoolUserConfigParam m_firewalled_server
PARAM_DEFAULT(BoolUserConfigParam(true, "firewalled-server", PARAM_DEFAULT(BoolUserConfigParam(true, "firewalled-server",
@ -750,6 +750,15 @@ namespace UserConfigParams
"from this IP will be banned.", "from this IP will be banned.",
{ { "0.0.0.0", 0u } } { { "0.0.0.0", 0u } }
)); ));
PARAM_PREFIX IntUserConfigParam m_max_ping
PARAM_DEFAULT(IntUserConfigParam(300, "max-ping",
&m_network_group, "Maximum ping allowed for a player (in ms)."));
PARAM_PREFIX IntUserConfigParam m_jitter_tolerance
PARAM_DEFAULT(IntUserConfigParam(100, "jitter-tolerance",
&m_network_group, "Tolerance of jitter in network allowed (in ms)."));
PARAM_PREFIX BoolUserConfigParam m_kick_high_ping_players
PARAM_DEFAULT(BoolUserConfigParam(false, "kick-high-ping-players",
&m_network_group, "Kick players whose ping is above max-ping."));
// ---- Gamemode setup // ---- Gamemode setup
PARAM_PREFIX UIntToUIntUserConfigParam m_num_karts_per_gamemode PARAM_PREFIX UIntToUIntUserConfigParam m_num_karts_per_gamemode

View File

@ -167,6 +167,7 @@ bool ClientLobby::notifyEventAsynchronous(Event* event)
case LE_VOTE: displayPlayerVote(event); break; case LE_VOTE: displayPlayerVote(event); break;
case LE_SERVER_OWNERSHIP: becomingServerOwner(); break; case LE_SERVER_OWNERSHIP: becomingServerOwner(); break;
case LE_BAD_TEAM: handleBadTeam(); break; case LE_BAD_TEAM: handleBadTeam(); break;
case LE_BAD_CONNECTION: handleBadConnection(); break;
default: break; default: break;
} // switch } // switch
@ -195,6 +196,10 @@ bool ClientLobby::notifyEventAsynchronous(Event* event)
STKHost::get()->setErrorMessage( STKHost::get()->setErrorMessage(
_("You were kicked from the server.")); _("You were kicked from the server."));
break; break;
case PDI_BAD_CONNECTION:
STKHost::get()->setErrorMessage(
_("Bad network connection is detected."));
break;
} // switch } // switch
STKHost::get()->requestShutdown(); STKHost::get()->requestShutdown();
return true; return true;
@ -624,6 +629,14 @@ void ClientLobby::handleBadTeam()
MessageQueue::add(MessageQueue::MT_ERROR, msg); MessageQueue::add(MessageQueue::MT_ERROR, msg);
} // handleBadTeam } // handleBadTeam
//-----------------------------------------------------------------------------
void ClientLobby::handleBadConnection()
{
SFXManager::get()->quickSound("anvil");
core::stringw msg = _("Bad network connection is detected.");
MessageQueue::add(MessageQueue::MT_ERROR, msg);
} // handleBadConnection
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ClientLobby::becomingServerOwner() void ClientLobby::becomingServerOwner()
{ {

View File

@ -46,6 +46,7 @@ private:
void handleChat(Event* event); void handleChat(Event* event);
void handleServerInfo(Event* event); void handleServerInfo(Event* event);
void handleBadTeam(); void handleBadTeam();
void handleBadConnection();
void becomingServerOwner(); void becomingServerOwner();
void clearPlayers(); void clearPlayers();

View File

@ -61,7 +61,8 @@ public:
LE_SERVER_OWNERSHIP, LE_SERVER_OWNERSHIP,
LE_KICK_HOST, LE_KICK_HOST,
LE_CHANGE_TEAM, LE_CHANGE_TEAM,
LE_BAD_TEAM LE_BAD_TEAM,
LE_BAD_CONNECTION
}; };
enum RejectReason : uint8_t enum RejectReason : uint8_t

View File

@ -199,7 +199,8 @@ void ServerLobby::setup()
// the server are ready: // the server are ready:
resetPeersReady(); resetPeersReady();
m_peers_votes.clear(); m_peers_votes.clear();
m_server_delay = 0.0; m_server_delay = std::numeric_limits<double>::max();
m_server_max_ping = std::numeric_limits<double>::max();
m_timeout.store(std::numeric_limits<float>::max()); m_timeout.store(std::numeric_limits<float>::max());
m_waiting_for_reset = false; m_waiting_for_reset = false;
@ -428,12 +429,34 @@ void ServerLobby::asynchronousUpdate()
// Reset for next state usage // Reset for next state usage
resetPeersReady(); resetPeersReady();
signalRaceStartToClients(); signalRaceStartToClients();
m_server_max_ping = StkTime::getRealTime() +
((double)UserConfigParams::m_max_ping / 1000.0);
break; break;
} }
case WAIT_FOR_RACE_STARTED: case WAIT_FOR_RACE_STARTED:
// The function startedRaceOnClient() will trigger the {
// next state. const bool ping_timed_out =
m_server_max_ping < StkTime::getRealTime();
if (checkPeersReady() || ping_timed_out)
{
for (auto p : m_peers_ready)
{
auto cur_peer = p.first.lock();
if (!cur_peer)
continue;
if (ping_timed_out && p.second.second > m_server_max_ping)
sendBadConnectionMessageToPeer(cur_peer);
}
m_state = DELAY_SERVER;
const double jt =
(double)UserConfigParams::m_jitter_tolerance / 1000.0;
m_server_delay = StkTime::getRealTime() + jt;
Log::verbose("ServerLobby",
"Started delay at %lf to %lf with jitter tolerance %lf.",
StkTime::getRealTime(), m_server_delay, jt);
}
break; break;
}
case DELAY_SERVER: case DELAY_SERVER:
if (m_server_delay < StkTime::getRealTime()) if (m_server_delay < StkTime::getRealTime())
{ {
@ -501,6 +524,19 @@ void ServerLobby::asynchronousUpdate()
} // asynchronousUpdate } // asynchronousUpdate
//-----------------------------------------------------------------------------
void ServerLobby::sendBadConnectionMessageToPeer(std::shared_ptr<STKPeer> p)
{
const unsigned max_ping = UserConfigParams::m_max_ping;
Log::warn("ServerLobby", "Peer %s cannot catch up with max ping %d, it"
" started at %lf.", p->getAddress().toString().c_str(), max_ping,
StkTime::getRealTime());
NetworkString* msg = getNetworkString();
msg->addUInt8(LE_BAD_CONNECTION);
p->sendPacket(msg, /*reliable*/true);
delete msg;
} // sendBadConnectionMessageToPeer
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
/** Simple finite state machine. Once this /** Simple finite state machine. Once this
* is known, register the server and its address with the stk server so that * is known, register the server and its address with the stk server so that
@ -1783,53 +1819,22 @@ void ServerLobby::finishedLoadingWorldClient(Event *event)
/** Called when a notification from a client is received that the client has /** Called when a notification from a client is received that the client has
* started the race. Once all clients have informed the server that they * started the race. Once all clients have informed the server that they
* have started the race, the server can start. This makes sure that the * have started the race, the server can start. This makes sure that the
* server's local time is behind all clients by (at least) their latency, * server's local time is behind all clients by at most max ping,
* which in turn means that when the server simulates local time T all * which in turn means that when the server simulates local time T all
* messages from clients at their local time T should have arrived at * messages from clients at their local time T should have arrived at
* the server, which creates smoother play experience. * the server, which creates smoother play experience.
*/ */
void ServerLobby::startedRaceOnClient(Event *event) void ServerLobby::startedRaceOnClient(Event *event)
{ {
if (m_state.load() != WAIT_FOR_RACE_STARTED)
{
sendBadConnectionMessageToPeer(event->getPeerSP());
return;
}
std::shared_ptr<STKPeer> peer = event->getPeerSP(); std::shared_ptr<STKPeer> peer = event->getPeerSP();
m_peers_ready.at(peer) = std::make_pair(true, StkTime::getRealTime()); m_peers_ready.at(peer) = std::make_pair(true, StkTime::getRealTime());
Log::info("ServerLobby", "Peer %d has started race at %lf", Log::info("ServerLobby", "Peer %s has started race at %lf",
peer->getHostId(), StkTime::getRealTime()); peer->getAddress().toString().c_str(), StkTime::getRealTime());
if (checkPeersReady())
{
std::vector<std::pair<STKPeer*, double> > mapping;
for (auto p : m_peers_ready)
{
auto peer = p.first.lock();
if (!peer)
continue;
mapping.emplace_back(peer.get(), p.second.second);
}
std::sort(mapping.begin(), mapping.end(),
[](const std::pair<STKPeer*, double>& a,
const std::pair<STKPeer*, double>& b)->bool
{
return a.second > b.second;
});
for (unsigned i = 0; i < mapping.size(); i++)
{
// Server delay is 0.1, so it's around 12 ticks
// (0.1 * 120 (physics fps)) for the highest ping client
if (i == 0)
GameProtocol::lock()->addInitialTicks(mapping[0].first, 12);
else
{
const double diff = mapping[0].second - mapping[i].second;
assert(diff >= 0.0);
GameProtocol::lock()->addInitialTicks(mapping[i].first,
12 + stk_config->time2Ticks((float)diff));
}
}
m_state = DELAY_SERVER;
m_server_delay = StkTime::getRealTime() + 0.1;
Log::verbose("ServerLobby", "Started delay at %lf set delay to %lf",
StkTime::getRealTime(), m_server_delay);
}
} // startedRaceOnClient } // startedRaceOnClient
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -90,12 +90,12 @@ private:
std::map<std::weak_ptr<STKPeer>, std::tuple<std::string, uint8_t, bool>, std::map<std::weak_ptr<STKPeer>, std::tuple<std::string, uint8_t, bool>,
std::owner_less<std::weak_ptr<STKPeer> > > m_peers_votes; std::owner_less<std::weak_ptr<STKPeer> > > m_peers_votes;
/** Keeps track of an artificial server delay (which makes sure that the /** Keeps track of an artificial server delay, which is used to compensate
* data from all clients has arrived when the server computes a certain * for network jitter. */
* timestep.(. It stores the real time since epoch + delta (atm 0.1
* seconds), which is the real time at which the server should start. */
double m_server_delay; double m_server_delay;
double m_server_max_ping;
bool m_has_created_server_id_file; bool m_has_created_server_id_file;
/** It indicates if this server is unregistered with the stk server. */ /** It indicates if this server is unregistered with the stk server. */
@ -218,6 +218,7 @@ private:
double getModeSpread(); double getModeSpread();
double scalingValueForTime(double time); double scalingValueForTime(double time);
void checkRaceFinished(); void checkRaceFinished();
void sendBadConnectionMessageToPeer(std::shared_ptr<STKPeer> p);
public: public:
ServerLobby(); ServerLobby();

View File

@ -753,6 +753,20 @@ void STKHost::mainLoop()
{ {
m_peer_pings.getData()[p.second->getHostId()] = m_peer_pings.getData()[p.second->getHostId()] =
p.second->getPing(); p.second->getPing();
const unsigned ap = p.second->getAveragePing();
const unsigned max_ping = UserConfigParams::m_max_ping;
if (UserConfigParams::m_kick_high_ping_players &&
p.second->isValidated() && ap > max_ping)
{
Log::info("STKHost", "%s with ping %d is higher than"
" %d ms, kick.",
p.second->getAddress().toString().c_str(),
ap, max_ping);
std::lock_guard<std::mutex> lock(m_enet_cmd_mutex);
m_enet_cmd.emplace_back(p.second->getENetPeer(),
(ENetPacket*)NULL, PDI_BAD_CONNECTION,
ECT_DISCONNECT);
}
} }
} }
for (auto it = m_peers.begin(); it != m_peers.end();) for (auto it = m_peers.begin(); it != m_peers.end();)

View File

@ -17,9 +17,11 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network/stk_peer.hpp" #include "network/stk_peer.hpp"
#include "config/stk_config.hpp"
#include "config/user_config.hpp" #include "config/user_config.hpp"
#include "network/crypto.hpp" #include "network/crypto.hpp"
#include "network/event.hpp" #include "network/event.hpp"
#include "network/network_config.hpp"
#include "network/network_string.hpp" #include "network/network_string.hpp"
#include "network/stk_host.hpp" #include "network/stk_host.hpp"
#include "network/transport_address.hpp" #include "network/transport_address.hpp"
@ -37,6 +39,7 @@ STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
m_host_id = host_id; m_host_id = host_id;
m_connected_time = (float)StkTime::getRealTime(); m_connected_time = (float)StkTime::getRealTime();
m_validated.store(false); m_validated.store(false);
m_average_ping = 0;
} // STKPeer } // STKPeer
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -150,10 +153,23 @@ bool STKPeer::isSamePeer(const ENetPeer* peer) const
/** Returns the ping to this peer from host, it waits for 3 seconds for a /** Returns the ping to this peer from host, it waits for 3 seconds for a
* stable ping returned by enet measured in ms. * stable ping returned by enet measured in ms.
*/ */
uint32_t STKPeer::getPing() const uint32_t STKPeer::getPing()
{ {
if ((float)StkTime::getRealTime() - m_connected_time < 3.0f) if ((float)StkTime::getRealTime() - m_connected_time < 3.0f)
return 0; return 0;
if (NetworkConfig::get()->isServer())
{
// Average ping in 5 seconds
const unsigned ap = stk_config->m_network_state_frequeny * 5;
m_previous_pings.push_back(m_enet_peer->roundTripTime);
while (m_previous_pings.size() > ap)
{
m_previous_pings.pop_front();
m_average_ping =
(uint32_t)(std::accumulate(m_previous_pings.begin(),
m_previous_pings.end(), 0) / m_previous_pings.size());
}
}
return m_enet_peer->roundTripTime; return m_enet_peer->roundTripTime;
} // getPing } // getPing

View File

@ -30,7 +30,9 @@
#include <enet/enet.h> #include <enet/enet.h>
#include <atomic> #include <atomic>
#include <deque>
#include <memory> #include <memory>
#include <numeric>
#include <set> #include <set>
#include <vector> #include <vector>
@ -45,6 +47,7 @@ enum PeerDisconnectInfo : unsigned int
PDI_TIMEOUT = 0, //!< Timeout disconnected (default in enet). PDI_TIMEOUT = 0, //!< Timeout disconnected (default in enet).
PDI_NORMAL = 1, //!< Normal disconnction with acknowledgement PDI_NORMAL = 1, //!< Normal disconnction with acknowledgement
PDI_KICK = 2, //!< Kick disconnection PDI_KICK = 2, //!< Kick disconnection
PDI_BAD_CONNECTION = 3, //!< Bad connection disconnection
}; // PeerDisconnectInfo }; // PeerDisconnectInfo
/*! \class STKPeer /*! \class STKPeer
@ -76,6 +79,10 @@ protected:
std::unique_ptr<Crypto> m_crypto; std::unique_ptr<Crypto> m_crypto;
std::deque<uint32_t> m_previous_pings;
uint32_t m_average_ping;
public: public:
STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id); STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -152,12 +159,15 @@ public:
void setPingInterval(uint32_t interval) void setPingInterval(uint32_t interval)
{ enet_peer_ping_interval(m_enet_peer, interval); } { enet_peer_ping_interval(m_enet_peer, interval); }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
uint32_t getPing() const; uint32_t getPing();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
Crypto* getCrypto() const { return m_crypto.get(); } Crypto* getCrypto() const { return m_crypto.get(); }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void setCrypto(std::unique_ptr<Crypto>&& c); void setCrypto(std::unique_ptr<Crypto>&& c);
// ------------------------------------------------------------------------
uint32_t getAveragePing() const { return m_average_ping; }
// ------------------------------------------------------------------------
ENetPeer* getENetPeer() const { return m_enet_peer; }
}; // STKPeer }; // STKPeer
#endif // STK_PEER_HPP #endif // STK_PEER_HPP