Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bugs with click limiter and add configurable semi-auto CPS limit to dedicated server config #270

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ Configuration example:
// 2 - essential game parameters must match (blue P symbol in Pure Faction)
// 3 - client-side mods are disallowed (gold P symbol in Pure Faction)
//$DF Anticheat Level: 0
// Set click limit for semi-automatic weapons (pistol and precision rifle). Bullets fired quicker than this rate will be ignored. Can be used to lessen impact of autoclicker cheats.
$DF Semi Auto Click Limit: 20.0
GooberRF marked this conversation as resolved.
Show resolved Hide resolved
// If true and server is using a mod (-mod command line argument) then client is required to use the same mod
// Can be disabled to allow publicly available modded servers
$DF Require Client Mod: true
Expand Down
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ Version 1.9.0 (not released yet)
- Add Kill Reward settings for dedicated servers
- Do not load unnecessary VPPs in dedicated server mode
- Add level filename to "Level Initializing" console message
- Fixed some fire packets being wrongfully dropped by click limiter (DF bug)
- Add `$DF Semi Auto Click Limit` option in dedicated server config

Version 1.8.0 (released 2022-09-17)
-----------------------------------
Expand Down
32 changes: 25 additions & 7 deletions game_patch/multi/multi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <patch_common/CodeInjection.h>
#include "multi.h"
#include "multi_private.h"
#include "server_internal.h"
#include "../misc/misc.h"
#include "../rf/os/os.h"
#include "../rf/os/timer.h"
Expand Down Expand Up @@ -245,14 +246,27 @@ bool is_entity_out_of_ammo(rf::Entity *entity, int weapon_type, bool alt_fire)
return clip_ammo == 0;
}

std::pair<bool, float> multi_is_cps_above_limit(rf::Player* pp, float max_cps)
std::pair<bool, float> multi_is_cps_above_limit(rf::Player* pp, float max_cps, int weapon_type, bool alt_fire)
{
constexpr int num_samples = 4;
int player_id = pp->net_data->player_id;
static int last_weapon_id[rf::multi_max_player_id]; // Keep a record of last weapon used
static bool last_weapon_fire_mode[rf::multi_max_player_id]; // Keep a record of last fire mode used
GooberRF marked this conversation as resolved.
Show resolved Hide resolved
static int last_weapon_fire[rf::multi_max_player_id][num_samples];
int now = rf::timer_get(1000);
// If the weapon or fire mode has changed, zero out the saved array of timestamps. Avoids dropping legit fire packets based on cps from other weapon/firemode
if (last_weapon_id[player_id] != weapon_type || last_weapon_fire_mode[player_id] != alt_fire) {
GooberRF marked this conversation as resolved.
Show resolved Hide resolved
xlog::debug("Player %i swapped weapon to %i", player_id, weapon_type);
xlog::debug("Player %i swapped fire mode to %i", player_id, alt_fire);
for (int i = 0; i < num_samples; ++i) {
last_weapon_fire[player_id][i] = 0;
}
last_weapon_id[player_id] = weapon_type; // Update the last weapon used
last_weapon_fire_mode[player_id] = alt_fire; // Update the last fire mode used
}
float avg_dt_secs = (now - last_weapon_fire[player_id][0]) / static_cast<float>(num_samples) / 1000.0f;
float cps = 1.0f / avg_dt_secs;
xlog::debug("Current cps %f, based on current avg_dt_secs %f", cps, avg_dt_secs);
if (cps > max_cps) {
return {true, cps};
}
Expand All @@ -266,10 +280,14 @@ std::pair<bool, float> multi_is_cps_above_limit(rf::Player* pp, float max_cps)
float multi_get_max_cps(int weapon_type, bool alt_fire)
{
if (rf::weapon_is_semi_automatic(weapon_type)) {
// most people cannot do much more than 10 clicks per second so 20 should be safe
return 20.0f;
// since semi-auto weapons can be fired as quickly as click input, limit max allowed rate to value set by server, default 20/sec
return g_additional_server_config.click_limiter_max_cps;
}
if (rf::weapon_is_riot_stick(weapon_type)) {
// double (+ 10%) baton max fire rate to fix legit fire requests being dropped when holding primary fire
GooberRF marked this conversation as resolved.
Show resolved Hide resolved
return 2.2f * (1.0f / ((rf::weapon_get_fire_wait_ms(weapon_type, alt_fire) / 1000.0f)));
}
int fire_wait_ms = rf::weapon_get_fire_wait_ms(weapon_type, alt_fire);
int fire_wait_ms = rf::weapon_get_fire_wait_ms(weapon_type, alt_fire); // for most automatic weapons, using fire wait value in weapons.tbl is fine
float fire_wait_secs = fire_wait_ms / 1000.0f;
float fire_rate = 1.0f / fire_wait_secs;
// allow 10% more to make sure we do not skip any legit packets
Expand All @@ -279,10 +297,10 @@ float multi_get_max_cps(int weapon_type, bool alt_fire)
bool multi_check_cps(rf::Player* pp, int weapon_type, bool alt_fire)
{
float max_cps = multi_get_max_cps(weapon_type, alt_fire);
auto [above_limit, cps] = multi_is_cps_above_limit(pp, max_cps);
auto [above_limit, cps] = multi_is_cps_above_limit(pp, max_cps, weapon_type, alt_fire);
if (above_limit) {
xlog::info("Player %s is shooting too fast: cps %.2f is greater than allowed %.2f",
pp->name.c_str(), cps, max_cps);
xlog::debug("Cancelled fire request from player %s for weapon %i. They are shooting too fast: cps %.2f is greater than allowed %.2f",
pp->name.c_str(), weapon_type, cps, max_cps);
}
return above_limit;
}
Expand Down
4 changes: 4 additions & 0 deletions game_patch/multi/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ void load_additional_server_config(rf::Parser& parser)
g_additional_server_config.anticheat_level = parser.parse_int();
}

if (parser.parse_optional("$DF Semi Auto Click Limit:")) {
g_additional_server_config.click_limiter_max_cps = parser.parse_float();
}

if (parser.parse_optional("$DF Require Client Mod:")) {
g_additional_server_config.require_client_mod = parser.parse_bool();
}
Expand Down
1 change: 1 addition & 0 deletions game_patch/multi/server_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ struct ServerAdditionalConfig
std::optional<int> force_player_character;
std::optional<float> max_fov;
int anticheat_level = 0;
float click_limiter_max_cps = 20.0f;
bool stats_message_enabled = true;
std::string welcome_message;
bool weapon_items_give_full_ammo = false;
Expand Down