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",
&m_network_group, "Timeout in seconds for validation of clients."));
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."));
PARAM_PREFIX BoolUserConfigParam m_firewalled_server
PARAM_DEFAULT(BoolUserConfigParam(true, "firewalled-server",
@ -750,6 +750,15 @@ namespace UserConfigParams
"from this IP will be banned.",
{ { "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
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_SERVER_OWNERSHIP: becomingServerOwner(); break;
case LE_BAD_TEAM: handleBadTeam(); break;
case LE_BAD_CONNECTION: handleBadConnection(); break;
default: break;
} // switch
@ -195,6 +196,10 @@ bool ClientLobby::notifyEventAsynchronous(Event* event)
STKHost::get()->setErrorMessage(
_("You were kicked from the server."));
break;
case PDI_BAD_CONNECTION:
STKHost::get()->setErrorMessage(
_("Bad network connection is detected."));
break;
} // switch
STKHost::get()->requestShutdown();
return true;
@ -624,6 +629,14 @@ void ClientLobby::handleBadTeam()
MessageQueue::add(MessageQueue::MT_ERROR, msg);
} // 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()
{

View File

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

View File

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

View File

@ -199,7 +199,8 @@ void ServerLobby::setup()
// the server are ready:
resetPeersReady();
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_waiting_for_reset = false;
@ -428,12 +429,34 @@ void ServerLobby::asynchronousUpdate()
// Reset for next state usage
resetPeersReady();
signalRaceStartToClients();
m_server_max_ping = StkTime::getRealTime() +
((double)UserConfigParams::m_max_ping / 1000.0);
break;
}
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;
}
case DELAY_SERVER:
if (m_server_delay < StkTime::getRealTime())
{
@ -501,6 +524,19 @@ void ServerLobby::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
* 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
* 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
* 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
* messages from clients at their local time T should have arrived at
* the server, which creates smoother play experience.
*/
void ServerLobby::startedRaceOnClient(Event *event)
{
if (m_state.load() != WAIT_FOR_RACE_STARTED)
{
sendBadConnectionMessageToPeer(event->getPeerSP());
return;
}
std::shared_ptr<STKPeer> peer = event->getPeerSP();
m_peers_ready.at(peer) = std::make_pair(true, StkTime::getRealTime());
Log::info("ServerLobby", "Peer %d has started race at %lf",
peer->getHostId(), 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);
}
Log::info("ServerLobby", "Peer %s has started race at %lf",
peer->getAddress().toString().c_str(), StkTime::getRealTime());
} // startedRaceOnClient
//-----------------------------------------------------------------------------

View File

@ -90,12 +90,12 @@ private:
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
* timestep.(. It stores the real time since epoch + delta (atm 0.1
* seconds), which is the real time at which the server should start. */
/** Keeps track of an artificial server delay, which is used to compensate
* for network jitter. */
double m_server_delay;
double m_server_max_ping;
bool m_has_created_server_id_file;
/** It indicates if this server is unregistered with the stk server. */
@ -218,6 +218,7 @@ private:
double getModeSpread();
double scalingValueForTime(double time);
void checkRaceFinished();
void sendBadConnectionMessageToPeer(std::shared_ptr<STKPeer> p);
public:
ServerLobby();

View File

@ -753,6 +753,20 @@ void STKHost::mainLoop()
{
m_peer_pings.getData()[p.second->getHostId()] =
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();)

View File

@ -17,9 +17,11 @@
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "network/stk_peer.hpp"
#include "config/stk_config.hpp"
#include "config/user_config.hpp"
#include "network/crypto.hpp"
#include "network/event.hpp"
#include "network/network_config.hpp"
#include "network/network_string.hpp"
#include "network/stk_host.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_connected_time = (float)StkTime::getRealTime();
m_validated.store(false);
m_average_ping = 0;
} // 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
* 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)
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;
} // getPing

View File

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