Implement auto-kick high ping player (default off)
This commit is contained in:
parent
14475762b8
commit
4d2e79da4c
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -46,6 +46,7 @@ private:
|
||||
void handleChat(Event* event);
|
||||
void handleServerInfo(Event* event);
|
||||
void handleBadTeam();
|
||||
void handleBadConnection();
|
||||
void becomingServerOwner();
|
||||
|
||||
void clearPlayers();
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -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();
|
||||
|
@ -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();)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user