C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Facepunch;
using Facepunch.Math;
using Network;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Rust;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
namespace Oxide.Plugins {
[Info("Guardian", "WhiteDragon", "1.7.9")]
[Description("Protects the server from various annoyances, cheats, and macro attacks.")]
class Guardian : CovalencePlugin {
[PluginReference]
private Plugin Friends, PlaytimeTracker;
private static Guardian _instance;
#region _action_queue_
private class ActionQueue {
private Queue<Action> actions;
public ActionQueue(float interval) {
actions = new Queue<Action>();
Timers.Add(interval, () => Scan());
}
public void Clear() => actions.Clear();
public void Enqueue(Action callback) {
if(callback != null) {
actions.Enqueue(callback);
}
}
private void Scan() {
if(actions.Count > 0) {
actions.Dequeue()?.Invoke();
}
}
}
#endregion _action_queue_
#region _admin_
private class Admin {
public class Settings {
public bool Broadcast;
public bool Bypass;
public Settings() {
Broadcast = true;
Bypass = true;
}
}
}
#endregion _admin_
#region _anticheat_
private class AntiCheat {
public class Settings {
public Aim.Settings Aim;
public FireRate.Settings FireRate;
public Gravity.Settings Gravity;
public MeleeRate.Settings MeleeRate;
public Recoil.Settings Recoil;
public Server.Settings Server;
public Stash.Settings Stash;
public Trajectory.Settings Trajectory;
public WallHack.Settings WallHack;
public Settings() {
Aim = new Aim.Settings();
FireRate = new FireRate.Settings();
Gravity = new Gravity.Settings();
MeleeRate = new MeleeRate.Settings();
Recoil = new Recoil.Settings();
Server = new Server.Settings();
Stash = new Stash.Settings();
Trajectory = new Trajectory.Settings();
WallHack = new WallHack.Settings();
}
public void Validate() {
Configuration.Validate(ref Aim, () => new Aim.Settings(), () => Aim.Validate());
Configuration.Validate(ref FireRate, () => new FireRate.Settings());
Configuration.Validate(ref Gravity, () => new Gravity.Settings());
Configuration.Validate(ref MeleeRate, () => new MeleeRate.Settings());
Configuration.Validate(ref Recoil, () => new Recoil.Settings());
Configuration.Validate(ref Server, () => new Server.Settings());
Configuration.Validate(ref Stash, () => new Stash.Settings());
Configuration.Validate(ref Trajectory, () => new Trajectory.Settings());
Configuration.Validate(ref WallHack, () => new WallHack.Settings());
}
}
private const Key category = Key.AntiCheat;
private const float epsilon = 9.5367431640625E-7f;
public static void Configure() {
Aim.Configure();
FireRate.Configure();
Gravity.Configure();
MeleeRate.Configure();
Recoil.Configure();
Server.Configure();
Stash.Configure();
Trajectory.Configure();
WallHack.Configure();
Gravity.Scan();
Server.Scan();
}
public static void Unload() {
Aim.Unload();
FireRate.Unload();
Gravity.Unload();
MeleeRate.Unload();
Recoil.Unload();
Server.Unload();
Stash.Unload();
Trajectory.Unload();
WallHack.Unload();
}
public class Aim {
public class Settings {
public bool Ban;
public ulong Cooldown;
public bool Enabled;
public float Sensitivity;
public AimTrigger Trigger;
public bool Warn;
public Settings() {
Ban = false;
Cooldown = 300;
Enabled = true;
Sensitivity = 0.5f;
Trigger = new AimTrigger();
Warn = true;
}
public class AimTrigger {
public bool Animal;
public bool Bradley;
public bool Helicopter;
public bool NPC;
}
public void Validate() {
Configuration.Validate(ref Trigger, () => new AimTrigger());
}
public Violation.Settings Validate(ulong max) {
Configuration.Clamp(ref Cooldown, 1ul, max);
Configuration.Clamp(ref Sensitivity, 0.0f, 1.0f);
return new Violation.Settings(Ban, Cooldown, Sensitivity, Warn);
}
}
private const Key type = Key.AntiCheatAim;
private static readonly Violation violation = new Violation(category);
private static float aim_distance;
private static float headshot_scale;
private static float hit_scale;
private static float pvp_distance;
private static float sensitivity_lo;
private static float sensitivity_hi;
private static float spin_angle;
private static float swing_angle;
private class History {
public HitArea boneArea;
public Counter headshot;
public Counter hit;
public HashSet<int> hits;
public Counter repeat;
private static readonly Dictionary<ulong, History> histories = new Dictionary<ulong, History>();
private History() {
boneArea = 0;
headshot = new Counter();
hit = new Counter();
hits = new HashSet<int>();
repeat = new Counter();
}
public static void Clear() => histories.Clear();
public static bool Contains(ulong userid) => histories.ContainsKey(userid);
public static History Get(ulong userid) {
History history;
if(!histories.TryGetValue(userid, out history)) {
histories.Add(userid, history = new History());
}
return history;
}
}
public static void Configure() {
var settings = config.AntiCheat.Aim.Validate(900);
violation.Configure(settings, 2, 8, 900000);
aim_distance = 6.00f + (6.0f * (1.0f - settings.Sensitivity));
headshot_scale = 1.50f + settings.Sensitivity;
hit_scale = 0.25f + settings.Sensitivity;
pvp_distance = 12.00f + (12.0f * (1.0f - settings.Sensitivity));
sensitivity_lo = -0.50f + (0.5f * settings.Sensitivity);
sensitivity_hi = -1.00f - (0.5f * (1.0f - settings.Sensitivity));
spin_angle = 35.00f + (35.0f * (1.0f - settings.Sensitivity));
swing_angle = 10.00f + (10.0f * (1.0f - settings.Sensitivity));
}
public static void Trigger(BaseEntity entity, HitInfo info) {
var player = info.InitiatorPlayer;
if(Permissions.Bypass.AntiCheat.Aim(player.userID)) {
return;
}
var weapon = Weapon.Get(player.userID, info.ProjectileID);
if(weapon == null) {
return;
}
_instance.NextTick(() => {
var position = info.HitPositionWorld;
var distance = Vector3.Distance(position, weapon.Position);
bool can_trigger = false, hit_location = true, pvp = false;
string target;
switch(Entity.GetType(entity, out target)) {
case Entity.Type.Animal : case Entity.Type.Bear : case Entity.Type.Boar : case Entity.Type.Chicken : case Entity.Type.Stag : can_trigger = config.AntiCheat.Aim.Trigger.Animal;
break;
case Entity.Type.Bradley : can_trigger = config.AntiCheat.Aim.Trigger.Bradley;
break;
case Entity.Type.Helicopter : can_trigger = config.AntiCheat.Aim.Trigger.Helicopter;
break;
case Entity.Type.Bot : case Entity.Type.Murderer : case Entity.Type.NPC : case Entity.Type.Scientist : can_trigger = config.AntiCheat.Aim.Trigger.NPC;
break;
case Entity.Type.Player : can_trigger = pvp = true;
break;
default : hit_location = false;
break;
}
if(can_trigger) {
can_trigger = Entity.Health.Changed(entity);
}
var history = History.Get(player.userID);
if(hit_location) {
if(info.boneArea == 0) {
history.repeat.Decrement();
}
else {
if(info.boneArea == history.boneArea) {
history.repeat.Increment();
}
else {
history.repeat.Decrement();
}
}
history.boneArea = info.boneArea;
if(pvp && (distance > pvp_distance)) {
if(history.boneArea == HitArea.Head) {
history.headshot.Increment();
}
else {
history.headshot.Decrement();
}
}
}
var range_modifier = (1.0f - (0.25f * history.repeat.Ratio())) * (1.0f - (0.025f * weapon.Speed));
var range_variance = 1.0f - (distance / (weapon.Range * range_modifier));
var angle = Vector3.Angle(weapon.AimAngle, info.ProjectileVelocity);
var angle_drop_off = 12.0f / (4.0f - (weapon.Range / weapon.Velocity));
var angle_variance = weapon.Accuracy - (angle / (angle_drop_off + weapon.AimCone));
var pvp_variance = weapon.Accuracy - (history.headshot.Ratio(headshot_scale) + history.hit.Ratio(hit_scale));
var deflection = false;
var violations = 0ul;
if(hit_location && (distance > aim_distance) && (can_trigger || (angle > spin_angle))) {
history.hits.Add(info.ProjectileID);
if(range_variance < sensitivity_lo) {
if(violation.Trigger(player.userID)) {
++violations;
}
else if((range_variance < sensitivity_hi) && violation.Trigger(player.userID)) {
++violations;
}
}
if(angle_variance < sensitivity_lo) {
BasePlayer.FiredProjectile projectile;
if(player.firedProjectiles.TryGetValue(info.ProjectileID, out projectile)) {
deflection = (projectile.ricochets > 0);
}
if(!deflection) {
var deflectors = Pool.GetList<BaseEntity>();
Vis.Entities(position, aim_distance, deflectors);
var d_squared = distance * distance;
foreach(var deflector in deflectors) {
if(deflector.SqrDistance(weapon.Position) < d_squared) {
deflection = true;
break;
}
}
Pool.FreeList(ref deflectors);
}
if(!deflection || (angle > spin_angle)) {
if(violation.Trigger(player.userID)) {
++violations;
}
else if((angle_variance < sensitivity_hi) && violation.Trigger(player.userID)) {
++violations;
}
if((angle > spin_angle) && violation.Trigger(player.userID)) {
++violations;
}
}
}
if(pvp && (distance > pvp_distance) && (pvp_variance < sensitivity_lo)) {
if(violation.Trigger(player.userID)) {
++violations;
}
else if((pvp_variance < sensitivity_hi) && violation.Trigger(player.userID)) {
++violations;
}
}
if((violations > 2) && (angle <= spin_angle)) {
violations = 2;
}
}
var bodypart = Text.BodyPart(info.boneArea);
if(config.Log.AntiCheat.Aim) {
Log.Console(Key.LogAntiCheatAim, new Dictionary<string, string> {
{
"angle_variance", angle_variance.ToString("F6")
}
, {
"bodypart", bodypart
}
, {
"distance", distance.ToString("F1")
}
, {
"playerid", player.UserIDString
}
, {
"playername", Text.Sanitize(player.displayName)
}
, {
"pvp_variance", pvp_variance.ToString("F6")
}
, {
"range_variance", range_variance.ToString("F6")
}
, {
"speed", weapon.Speed.ToString("F1")
}
, {
"target", target
}
, {
"weapon", $"{weapon.Name}{(deflection ? " (deflection)" : string.Empty)}"
}
}
);
}
Projectile.Log.SetAim(player.userID, info.ProjectileID, angle_variance, pvp_variance, range_variance, pvp, deflection);
Projectile.Log.SetVictim(player.userID, info.ProjectileID, target);
Projectile.Log.SetHit(player.userID, info.ProjectileID, distance, info.boneArea);
if(config.AntiCheat.Aim.Enabled && (violations > 0)) {
Projectile.Log.SetAimViolations(player.userID, info.ProjectileID, violations);
var hit_angle = angle.ToString("F1");
var hit_distance = distance.ToString("F1");
violation.Trigger(player.userID, type, Text.GetPlain(Key.ViolationAim, new Dictionary<string, string> {
{
"angle", hit_angle
}
, {
"bodypart", bodypart
}
, {
"distance", hit_distance
}
, {
"target", target
}
, {
"weapon", weapon.Name
}
}
), violations, false, new Dictionary<string, string> {
{
"headshot_percent", history.headshot.Percent().ToString()
}
, {
"hip_fire", (weapon.Zoom == 1.0f).ToString()
}
, {
"hit_angle", hit_angle
}
, {
"hit_area", Text.BodyPart(info.boneArea, "en")
}
, {
"hit_distance", hit_distance
}
, {
"hit_percent", history.hit.Percent().ToString()
}
, {
"movement_speed", weapon.Speed.ToString("F1")
}
, {
"projectile_id", info.ProjectileID.ToString()
}
, {
"ricochet", deflection.ToString()
}
, {
"swing_angle", "0.0"
}
, {
"violation_id", weapon.Fired.Ticks.ToString()
}
, {
"weapon_ammo", weapon.AmmoName
}
, {
"weapon_attachments", string.Join(", ", weapon.Attachments)
}
, {
"weapon_type", weapon.ShortName
}
}
);
}
}
);
}
public static void Unload() {
History.Clear();
violation.Clear();
}
public static void Update(Weapon weapon) {
var player = weapon.Player;
if(!History.Contains(player.userID)) {
return;
}
var history = History.Get(player.userID);
foreach(var entry in weapon.Projectiles) {
if(history.hits.Contains(entry)) {
history.hits.Remove(entry);
history.hit.Increment();
var angle = weapon.Swing;
if(config.AntiCheat.Aim.Enabled && (angle > swing_angle) && Projectile.Log.GetAimPvp(player.userID, entry)) {
var distance = Projectile.Log.GetHitDistance(player.userID, entry);
if(distance > pvp_distance) {
if(violation.Trigger(player.userID)) {
Projectile.Log.SetAimViolations(player.userID, entry, 1ul, true);
var hit_angle = Projectile.Log.GetAimAngle(player.userID, entry).ToString("F1");
var hit_area = Projectile.Log.GetHitLocation(player.userID, entry);
var hit_distance = distance.ToString("F1");
var swing_angle = angle.ToString("F1");
violation.Trigger(player.userID, type, Text.GetPlain(Key.ViolationAim, new Dictionary<string, string> {
{
"angle", $"{hit_angle}-{swing_angle}"
}
, {
"bodypart", Text.BodyPart(hit_area)
}
, {
"distance", hit_distance
}
, {
"target", Projectile.Log.GetVictim(player.userID, entry)
}
, {
"weapon", weapon.Name
}
}
), 1ul, false, new Dictionary<string, string> {
{
"headshot_percent", history.headshot.Percent().ToString()
}
, {
"hip_fire", (weapon.Zoom == 1.0f).ToString()
}
, {
"hit_angle", hit_angle
}
, {
"hit_area", Text.BodyPart(hit_area, "en")
}
, {
"hit_distance", hit_distance
}
, {
"hit_percent", history.hit.Percent().ToString()
}
, {
"movement_speed", weapon.Speed.ToString("F1")
}
, {
"projectile_id", entry.ToString()
}
, {
"ricochet", Projectile.Log.GetRicochet(player.userID, entry).ToString()
}
, {
"swing_angle", swing_angle
}
, {
"violation_id", weapon.Fired.Ticks.ToString()
}
, {
"weapon_ammo", weapon.AmmoName
}
, {
"weapon_attachments", string.Join(", ", weapon.Attachments)
}
, {
"weapon_type", weapon.ShortName
}
}
);
}
}
}
}
else {
history.hit.Decrement();
}
}
}
}
public class FireRate {
public class Settings {
public bool Ban;
public ulong Cooldown;
public bool Enabled;
public float Sensitivity;
public bool Warn;
public Settings() {
Ban = false;
Cooldown = 10;
Enabled = true;
Sensitivity = 0.5f;
Warn = true;
}
public Violation.Settings Validate(ulong max) {
Configuration.Clamp(ref Cooldown, 1ul, max);
Configuration.Clamp(ref Sensitivity, 0.0f, 1.0f);
return new Violation.Settings(Ban, Cooldown, Sensitivity, Warn);
}
}
private const Key type = Key.AntiCheatFireRate;
private static readonly Violation violation = new Violation(category);
private static float sensitivity;
private class History {
public int entity;
public DateTime fired;
public float repeat;
private static readonly Dictionary<ulong, History> histories = new Dictionary<ulong, History>();
private History() {
entity = 0;
fired = DateTime.MinValue;
repeat = 10.0f;
}
public static void Clear() => histories.Clear();
public static History Get(ulong userid) {
History history;
if(!histories.TryGetValue(userid, out history)) {
histories.Add(userid, history = new History());
}
return history;
}
}
public static void Configure() {
var settings = config.AntiCheat.FireRate.Validate(30);
violation.Configure(settings, 2, 8, 5000);
sensitivity = 0.3f + (0.3f * settings.Sensitivity);
}
private static uint Percent(float min, float delay, float max) => (uint)(100.0f * (max - delay) / (max - min));
public static void Trigger(BaseEntity entity, HitInfo info) {
var player = info.InitiatorPlayer;
if(Permissions.Bypass.AntiCheat.FireRate(player.userID)) {
return;
}
var weapon = Weapon.Get(player.userID, info.ProjectileID);
if(weapon == null) {
return;
}
_instance.NextTick(() => {
var current = entity.GetInstanceID();
var history = History.Get(player.userID);
if((weapon.Repeat == history.repeat) && (current == history.entity)) {
var delay = (float)weapon.Fired.Subtract(history.fired).TotalSeconds;
var max = weapon.Repeat * sensitivity;
var min = max * 0.1f;
if(config.AntiCheat.FireRate.Enabled && (min <= delay) && (delay <= max) && violation.Trigger(player.userID)) {
Projectile.Log.SetFireRateViolations(player.userID, info.ProjectileID, 1);
var attack_rate = delay.ToString("F3");
violation.Trigger(player.userID, type, Text.GetPlain(Key.ViolationFireRate, new Dictionary<string, string> {
{
"delay", attack_rate
}
, {
"weapon", weapon.Name
}
}
), false, new Dictionary<string, string> {
{
"attack_rate", attack_rate
}
, {
"movement_speed", weapon.Speed.ToString("F1")
}
, {
"projectile_id", info.ProjectileID.ToString()
}
, {
"rate_percent", Percent(min, delay, max).ToString()
}
, {
"violation_id", weapon.Fired.Ticks.ToString()
}
, {
"weapon_ammo", weapon.AmmoName
}
, {
"weapon_attachments", string.Join(", ", weapon.Attachments)
}
, {
"weapon_type", weapon.ShortName
}
}
);
}
}
history.entity = current;
history.fired = weapon.Fired;
history.repeat = weapon.Repeat;
}
);
}
public static void Unload() {
History.Clear();
violation.Clear();
}
}
public class Gravity {
public class Settings {
public bool Ban;
public ulong Cooldown;
public bool Enabled;
public float Sensitivity;
public bool Warn;
public Settings() {
Ban = false;
Cooldown = 10;
Enabled = true;
Sensitivity = 0.5f;
Warn = true;
}
public Violation.Settings Validate(ulong max) {
Configuration.Clamp(ref Cooldown, 1ul, max);
Configuration.Clamp(ref Sensitivity, 0.0f, 1.0f);
return new Violation.Settings(Ban, Cooldown, Sensitivity, Warn);
}
}
private const Key type = Key.AntiCheatGravity;
private static readonly Violation violation = new Violation(category);
private static float sensitivity_lo;
private static float sensitivity_md;
private static float sensitivity_hi;
private class History {
private static readonly Dictionary<ulong, float> histories = new Dictionary<ulong, float>();
public static void Clear() => histories.Clear();
public static float Set(ulong userid, float new_value) {
float old_value;
if(!histories.TryGetValue(userid, out old_value)) {
histories.Add(userid, new_value);
return float.MaxValue;
}
histories[userid] = new_value;
return old_value;
}
}
public static void Configure() {
var settings = config.AntiCheat.Gravity.Validate(30);
violation.Configure(settings, 2, 8, 500);
sensitivity_lo = 1.55f + 1.5f * (1.0f - settings.Sensitivity);
sensitivity_md = sensitivity_lo * 2.0f;
sensitivity_hi = sensitivity_md * 2.0f;
}
private static bool Check(BasePlayer player) {
var position = player.transform.position;
position.y += 0.05f;
if((History.Set(player.userID, position.y) >= position.y) || player.HasParent()) {
return false;
}
else if(Physics.Raycast(position, Vector3.down, sensitivity_md)) {
return false;
}
else if(position.y <= (Map.Terrain.Height(position) + sensitivity_lo)) {
return false;
}
bool flying = true;
var entities = Pool.GetList<BaseEntity>();
Vis.Entities(position, sensitivity_hi, entities);
foreach(var entity in entities) {
if(player.IsStandingOnEntity(entity, -1)) {
flying = false;
break;
}
switch(entity.ShortPrefabName) {
case "cave_lift" : case "elevator_lift" : case "floor.ladder.hatch" : case "floor.triangle.ladder.hatch" : case "hopperoutput" : case "miningquarry" : case "miningquarry_static" : case "watchtower.wood" : if(player.Distance(entity.bounds.ClosestPoint(position)) <= sensitivity_lo) {
flying = false;
}
break;
default : if((entity is BaseVehicle) || (entity is BaseVehicleSeat)) {
if(player.Distance(entity.bounds.ClosestPoint(position)) <= sensitivity_lo) {
flying = false;
}
}
else if(entity.ShortPrefabName == "ladder.wooden.wall") {
if(player.Distance(entity.bounds.ClosestPoint(position)) <= sensitivity_md) {
flying = false;
}
}
else if((entity is BaseHelicopterVehicle) || (entity is SupplyDrop) || (entity is TreeEntity)) {
flying = false;
}
else {
var top = entity.bounds.max.y;
if((position.y >= top) && ((position.y - top) <= sensitivity_lo)) {
var bot = new Vector3(position.x, position.y - sensitivity_lo, position.z);
if(Physics.CheckCapsule(position, bot, 0.25f)) {
flying = false;
}
}
}
break;
}
if(!flying) {
break;
}
}
if(flying && (entities.Count == 0) && Vis.AnyColliders(position, sensitivity_lo)) {
flying = false;
}
Pool.FreeList(ref entities);
return flying;
}
public static void Scan() {
Timers.Add(1.0f, () => {
if(config.AntiCheat.Gravity.Enabled) {
foreach(var player in BasePlayer.activePlayerList) {
if(User.ShouldIgnore(player) || User.CanFly(player)) {
continue;
}
if(player.isMounted || player.HasParent() || player.IsSwimming() || player.IsOnGround()) {
continue;
}
var position = player.transform.position;
if(Map.Water.IsSurface(position) && !Map.Monument.IsNearby(position) && !Map.Entities.InRange(position)) {
if((History.Set(player.userID, position.y) <= position.y)) {
Trigger(player, position.y, true);
}
}
}
}
}
);
}
public static void Trigger(BasePlayer player, float amount, bool scanned = false) {
if(Permissions.Bypass.AntiCheat.Gravity(player.userID)) {
return;
}
if(config.Log.AntiCheat.Gravity) {
var position = player.transform.position;
Log.Console(Key.LogAntiCheatGravity, new Dictionary<string, string> {
{
"amount", amount.ToString("F6")
}
, {
"playerid", player.UserIDString
}
, {
"playername", Text.Sanitize(player.displayName)
}
, {
"position", $"({(int)position.x},{(int)position.y},{(int)position.z})"
}
}
);
}
if(config.AntiCheat.Gravity.Enabled && (scanned || Check(player)) && violation.Trigger(player.userID)) {
violation.Trigger(player.userID, type, Text.GetPlain(Key.ViolationGravity, new Dictionary<string, string> {
{
"amount", amount.ToString("F6")
}
}
), false, new Dictionary<string, string> {
{
"elevation", player.transform.position.y.ToString("F1")
}
, {
"movement_speed", player.estimatedSpeed.ToString("F1")
}
, {
"violation_id", DateTime.UtcNow.Ticks.ToString()
}
}
);
}
}
public static void Unload() {
History.Clear();
violation.Clear();
}
}
public class MeleeRate {
public class Settings {
public bool Ban;
public ulong Cooldown;
public bool Enabled;
public float Sensitivity;
public bool Warn;
public Settings() {
Ban = false;
Cooldown = 300;
Enabled = true;
Sensitivity = 0.5f;
Warn = true;
}
public Violation.Settings Validate(ulong max) {
Configuration.Clamp(ref Cooldown, 1ul, max);
Configuration.Clamp(ref Sensitivity, 0.0f, 1.0f);
return new Violation.Settings(Ban, Cooldown, Sensitivity, Warn);
}
}
private const Key type = Key.AntiCheatMeleeRate;
private static readonly Violation violation = new Violation(category);
private static float sensitivity;
private class History {
private static readonly Dictionary<ulong, DateTime> histories = new Dictionary<ulong, DateTime>();
public static void Clear() => histories.Clear();
public static float Set(ulong userid, DateTime new_value) {
DateTime old_value;
if(!histories.TryGetValue(userid, out old_value)) {
histories.Add(userid, new_value);
return float.MaxValue;
}
histories[userid] = new_value;
return (float)new_value.Subtract(old_value).TotalSeconds;
}
}
public static void Configure() {
var settings = config.AntiCheat.MeleeRate.Validate(900);
violation.Configure(settings, 2, 8, 15000);
sensitivity = 0.4f + (0.3f * settings.Sensitivity);
}
private static uint Percent(float delay, float max) => (uint)(100.0f * (max - delay) / max);
public static void Trigger(BasePlayer player, HitInfo info) {
if(Permissions.Bypass.AntiCheat.MeleeRate(player.userID)) {
return;
}
BaseMelee melee = info?.Weapon?.GetComponent<BaseMelee>();
if(melee == null) {
return;
}
var item = info.Weapon.GetItem();
if(item == null) {
return;
}
var current = DateTime.UtcNow;
_instance.NextTick(() => {
var itemid = item.info.itemid;
var delay = History.Set(player.userID, current);
string target = Entity.GetName(info.HitEntity);
var weapon = item.info.displayName.translated ?? "null";
if(config.Log.AntiCheat.MeleeRate) {
var delay_string = (delay >= (melee.repeatDelay * 2.0f)) ? Text.Get(Key.idle) : $"{delay:0.00}{Text.Get(Key.DurationSecondsUnit)}";
Log.Console(Key.LogAntiCheatMeleeRate, new Dictionary<string, string> {
{
"delay", delay_string
}
, {
"playerid", player.UserIDString
}
, {
"playername", Text.Sanitize(player.displayName)
}
, {
"target", target
}
, {
"weapon", weapon
}
}
);
}
var chainsaw = (itemid == 1104520648);
var jackhammer = (itemid == 1488979457);
var max = melee.repeatDelay * sensitivity;
if((delay <= max) && !(jackhammer || chainsaw)) {
if(config.AntiCheat.MeleeRate.Enabled && violation.Trigger(player.userID)) {
var attack_rate = delay.ToString("F3");
violation.Trigger(player.userID, type, Text.GetPlain(Key.ViolationMeleeRate, new Dictionary<string, string> {
{
"delay", attack_rate
}
, {
"target", target
}
, {
"weapon", weapon
}
}
), false, new Dictionary<string, string> {
{
"attack_rate", attack_rate
// ... [Code preview truncated due to length. Full source in attached file] ...