Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ===== Guardian 1.7.10 PASTEBIN PART 1 of 2 =====
- // Free Pastebin limit is 512 KB. This is the first half (through #endregion _log_).
- // Join: paste PART 1, then paste PART 2 immediately below it. Delete these header lines (optional; they are comments).
- 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 / Gattaca", "1.7.10")]
- [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)
- {
- // Facepunch removed Pool.GetList<T>() / Pool.FreeList (CS0117).
- // Current Facepunch.Pool API: Get<List<T>>() rents a pooled list,
- // FreeUnmanaged<T>(ref list) returns it. Same Vis.Entities usage.
- var deflectors = Pool.Get<List<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.FreeUnmanaged<BaseEntity>(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;
- // Facepunch removed Pool.GetList<T>() (CS0117). Use Pool.Get<List<T>>() instead.
- var entities = Pool.Get<List<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:
- // Facepunch removed BaseHelicopterVehicle (CS0246). Replacement is
- // PlayerHelicopter (minicopter / scrap heli / attack heli). That type
- // now inherits BaseVehicle, so it MUST be tested first: the old
- // BaseHelicopterVehicle branch skipped the distance gate. If we left
- // it after `is BaseVehicle`, player helis would take the tighter
- // sensitivity_lo check and false-flag gravity near a heli.
- if((entity is PlayerHelicopter) || (entity is SupplyDrop) || (entity is TreeEntity))
- {
- flying = false;
- }
- else 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
- {
- 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;
- }
- // Pair of Get<List<T>> above. FreeList is gone; FreeUnmanaged<T> is the current return path.
- Pool.FreeUnmanaged<BaseEntity>(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 },
- { "movement_speed", player.estimatedSpeed.ToString("F1") },
- { "rate_percent", Percent(delay, max).ToString() },
- { "violation_id", DateTime.UtcNow.Ticks.ToString() },
- { "weapon_type", item.info.shortname }
- });
- }
- }
- });
- }
- public static void Unload()
- {
- History.Clear();
- violation.Clear();
- }
- }
- public class Recoil
- {
- 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 float latency_min = 0.075f;
- private const float latency_max = 0.175f;
- private const float recoil_max = 9.765625e-4f;
- private const float recoil_range = recoil_max - epsilon;
- private const float recoil_scale = 100.0f / recoil_range;
- private const float repeat_interval = 1.25f;
- private const Key type = Key.AntiCheatRecoil;
- private static readonly Violation violation = new Violation(category);
- private static ulong reset_limit;
- private static float sensitivity;
- private class History
- {
- public Counter count_r;
- public Counter count_x;
- public Counter count_y;
- public DateTime fired;
- public ulong repeats;
- private static readonly Dictionary<ulong, History> histories = new Dictionary<ulong, History>();
- private History()
- {
- count_r = new Counter();
- count_x = new Counter();
- count_y = new Counter();
- fired = DateTime.MinValue;
- repeats = 0;
- }
- public static void Clear() => histories.Clear();
- public static History Get(BasePlayer player)
- {
- History history;
- if(!histories.TryGetValue(player.userID, out history))
- {
- histories.Add(player.userID, history = new History());
- }
- return history;
- }
- }
- public static void Configure()
- {
- var settings = config.AntiCheat.Recoil.Validate(30);
- violation.Configure(settings, 2, 8, 1000);
- reset_limit = settings.Count(12, 20);
- sensitivity = 0.00001f + (0.00009f * settings.Sensitivity);
- }
- private static float Latency(BasePlayer player, Weapon weapon)
- {
- var connection = player?.net?.connection;
- var latency = 0.0f;
- if(connection != null)
- {
- latency = 0.001f * (Net.sv.GetAveragePing(connection) >> 1);
- }
- if(latency < weapon.Repeat)
- {
- latency = weapon.Repeat;
- }
- return Generic.Clamp(latency, latency_min, latency_max);
- }
- private static uint Percent(float value) =>
- (uint)((recoil_range - Generic.Clamp(value, epsilon, recoil_max)) * recoil_scale);
- public static void Trigger(BasePlayer player, Weapon weapon)
- {
- if(Permissions.Bypass.AntiCheat.Recoil(player.userID))
- {
- return;
- }
- if(weapon == null)
- {
- return;
- }
- _instance.timer.In(Latency(player, weapon), () =>
- {
- var current = player.eyes.HeadRay().direction;
- weapon.SetSwing(Vector3.Angle(current, weapon.AimAngle));
- var x = Math.Abs(current.x - weapon.AimAngle.x) / weapon.Yaw;
- var y = Math.Abs(current.y - weapon.AimAngle.y) / weapon.Pitch;
- var history = History.Get(player);
- x *= 0.25f + history.count_x.Ratio(true);
- y *= 0.25f + history.count_y.Ratio(true);
- var delay = weapon.Fired.Subtract(history.fired).TotalSeconds;
- var repeat = delay <= (weapon.Repeat * repeat_interval);
- var reset = false;
- var reset_full = false;
- if(Math.Abs(current.y) > 0.85f)
- {
- reset = true; y = 1.0f;
- }
- else if((x < epsilon) && (y < epsilon))
- {
- if(reset = (history.repeats == 0) || (history.repeats >= (weapon.Automatic ? 1ul : 2ul)))
- {
- reset_full = true;
- }
- }
- var violations = 0ul;
- if(reset_full && (history.repeats > 0))
- {
- history.count_r.Increment();
- if(history.count_r.Total() >= reset_limit)
- {
- if(violation.Trigger(player.userID))
- {
- ++violations;
- }
- }
- }
- else
- {
- history.count_r.Decrement();
- }
- var can_trigger = false;
- if(repeat)
- {
- ++history.repeats;
- can_trigger = !(reset || weapon.Shell);
- }
- else
- {
- history.repeats = 0;
- }
- if(can_trigger)
- {
- if(x <= sensitivity)
- {
- history.count_x.Increment();
- if(violation.Trigger(player.userID))
- {
- ++violations;
- }
- }
- if(y <= sensitivity)
- {
- history.count_y.Increment();
- if(violation.Trigger(player.userID))
- {
- ++violations;
- }
- }
- }
- else if(!reset)
- {
- history.count_x.Decrement();
- history.count_y.Decrement();
- }
- history.fired = weapon.Fired;
- var playername = Text.Sanitize(player.displayName);
- if(config.Log.AntiCheat.Recoil)
- {
- Log.Console(Key.LogAntiCheatRecoil, new Dictionary<string, string>
- {
- { "count_x", history.count_x.Total().ToString("D2") },
- { "count_y", history.count_y.Total().ToString("D2") },
- { "pitch", y.ToString("F6") },
- { "playerid", player.UserIDString },
- { "playername", playername },
- { "swing", weapon.Swing.ToString("F1") },
- { "weapon", $"{weapon.Name}[{history.repeats}{(reset ? $" (reset)" : string.Empty)}]" },
- { "yaw", x.ToString("F6") }
- });
- }
- foreach(var projectileid in weapon.Projectiles)
- {
- Projectile.Log.SetAttacker(player.userID, projectileid, playername, player.userID);
- Projectile.Log.SetRecoil(player.userID, projectileid, y, history.repeats, x, weapon.Swing);
- Projectile.Log.SetWeapon(player.userID, projectileid, weapon.Speed, weapon.Name);
- }
- if(config.AntiCheat.Recoil.Enabled && (violations > 0))
- {
- foreach(var projectileid in weapon.Projectiles)
- {
- Projectile.Log.SetRecoilViolations(player.userID, projectileid, violations);
- }
- violation.Trigger(player.userID, type, Text.GetPlain(Key.ViolationRecoil, new Dictionary<string, string>
- {
- { "pitch", y.ToString("F6") },
- { "weapon", weapon.Name },
- { "yaw", x.ToString("F6") }
- }), violations, false, new Dictionary<string, string>
- {
- { "movement_speed", weapon.Speed.ToString("F1") },
- { "recoil_pitch", Percent(y).ToString() },
- { "recoil_repeats", history.repeats.ToString() },
- { "recoil_yaw", Percent(x).ToString() },
- { "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 class Server
- {
- 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 static Vector3 sensitivity = Vector3.zero;
- private const Key type = Key.Server;
- private static readonly Violation violation = new Violation(category);
- public static void Configure()
- {
- var settings = config.AntiCheat.Server.Validate(900);
- sensitivity = new Vector3(0.0f, 0.125f + 0.25f * (1.0f - config.AntiCheat.Server.Sensitivity), 0.0f);
- violation.Configure(settings, 4, 16, 60000);
- }
- public static void Scan()
- {
- Timers.Add(1.0f, () =>
- {
- if(config.AntiCheat.Server.Enabled)
- {
- foreach(var player in BasePlayer.activePlayerList)
- {
- if(User.ShouldIgnore(player) || User.CanFly(player))
- {
- continue;
- }
- if(player.isMounted || player.HasParent() || player.IsSwimming())
- {
- continue;
- }
- if(!player.IsFlying)
- {
- player.SendConsoleCommand("noclip");
- player.SendConsoleCommand("debugcamera");
- player.SendConsoleCommand("camspeed", "0");
- }
- var position = player.transform.position;
- var highspot = position + sensitivity;
- if(!Map.Cave.IsInside(highspot))
- {
- if(Map.Terrain.IsInside(highspot, false))
- {
- _instance.OnPlayerViolation(player, AntiHackType.InsideTerrain, position.y);
- }
- else if(Map.Rock.IsInside(highspot, false))
- {
- _instance.OnPlayerViolation(player, AntiHackType.NoClip, position.y);
- }
- }
- if(Map.Building.InFoundation(highspot))
- {
- _instance.OnPlayerViolation(player, AntiHackType.NoClip, position.y);
- }
- }
- }
- });
- }
- public static void Trigger(BasePlayer player, AntiHackType ahtype, float amount)
- {
- if(Permissions.Bypass.AntiCheat.Server(player.userID))
- {
- return;
- }
- if(Math.Abs(amount) < epsilon)
- {
- return;
- }
- var position = player.transform.position;
- if(config.Log.AntiCheat.Server)
- {
- var colliders = string.Empty;
- if((ahtype == AntiHackType.InsideTerrain) || (ahtype == AntiHackType.NoClip))
- {
- colliders = Map.Collider.Info(position);
- }
- Log.Console(Key.LogAntiCheatServer, new Dictionary<string, string>
- {
- { "amount", amount.ToString("F6") },
- { "colliders", colliders },
- { "playerid", player.UserIDString },
- { "playername", Text.Sanitize(player.displayName) },
- { "position", $"({(int)position.x},{(int)position.y},{(int)position.z})" },
- { "type", ahtype.ToString() }
- });
- }
- if(violation.Trigger(player.userID))
- {
- var antihack_amount = amount.ToString("F6");
- var antihack_type = ahtype.ToString();
- violation.Trigger(player.userID, type, $"{antihack_type}({antihack_amount})", false, new Dictionary<string, string>
- {
- { "antihack_amount", antihack_amount },
- { "antihack_type", antihack_type },
- { "movement_speed", player.estimatedSpeed.ToString("F1") },
- { "violation_id", DateTime.UtcNow.Ticks.ToString() }
- });
- }
- }
- public static void Unload() => violation.Clear();
- }
- public class Stash
- {
- public class Settings
- {
- public bool Ban;
- public ulong Cooldown;
- public bool Enabled;
- public float Sensitivity;
- public bool Warn;
- public Settings()
- {
- Ban = false;
- Cooldown = 3600;
- 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.AntiCheatStash;
- private static readonly Violation violation = new Violation(category);
- private class History
- {
- private static readonly Dictionary<ulong, List<Vector3>> histories = new Dictionary<ulong, List<Vector3>>();
- public static void Clear() => histories.Clear();
- public static float Set(ulong userid, Vector3 position)
- {
- List<Vector3> history;
- if(!histories.TryGetValue(userid, out history))
- {
- histories.Add(userid, history = new List<Vector3> { position });
- return 0.0f;
- }
- var minimum = float.MaxValue;
- var nearest = Vector3.zero;
- foreach(var found in history)
- {
- var delta = (found - position).sqrMagnitude;
- if(delta < minimum)
- {
- minimum = delta;
- nearest = found;
- }
- }
- if(!Map.InRange2D(position, nearest, 3.0f))
- {
- history.Add(position);
- }
- return (float)Math.Sqrt(minimum);
- }
- }
- private static float sensitivity;
- public static void Configure()
- {
- var settings = config.AntiCheat.Stash.Validate(14400);
- violation.Configure(settings, 1, 1, 3600000);
- sensitivity = 3.0f + 18.0f * (1.0f - settings.Sensitivity);
- }
- public static void Subscribe()
- {
- if(config.AntiCheat.Stash.Enabled)
- {
- Hooks.Subscribe(nameof(CanSeeStash));
- }
- else
- {
- Hooks.Unsubscribe(nameof(CanSeeStash));
- }
- }
- public static void Trigger(BasePlayer player, StashContainer stash)
- {
- if(Permissions.Bypass.AntiCheat.Stash(player.userID))
- {
- return;
- }
- if((player.userID == stash.OwnerID) || User.IsTeamMate(player, stash.OwnerID) || User.IsFriend(player, stash.OwnerID))
- {
- return;
- }
- _instance.NextTick(() =>
- {
- var position = stash.transform.position;
- var grid = Map.Grid(position);
- if(config.Log.AntiCheat.Stash)
- {
- Log.Console(Key.LogAntiCheatStash, new Dictionary<string, string>
- {
- { "grid", grid },
- { "ownerid", stash.OwnerID.ToString() },
- { "playerid", player.UserIDString },
- { "playername", Text.Sanitize(player.displayName) },
- { "position", $"({(int)position.x},{(int)position.y},{(int)position.z})" }
- });
- }
- float distance = History.Set(player.userID, position);
- if(config.AntiCheat.Stash.Enabled && (distance >= sensitivity))
- {
- var triggered = violation.Trigger(player.userID);
- if(triggered || config.AntiCheat.Stash.Warn)
- {
- var details = Text.GetPlain(Key.ViolationStash, new Dictionary<string, string>
- {
- { "grid", grid },
- { "position", $"({(int)position.x},{(int)position.y},{(int)position.z})" }
- });
- var hook_details = new Dictionary<string, string>
- {
- { "stash_distance", distance.ToString("F1") },
- { "movement_speed", player.estimatedSpeed.ToString("F1") },
- { "violation_id", DateTime.UtcNow.Ticks.ToString() }
- };
- if(triggered)
- {
- violation.Trigger(player.userID, type, details, false, hook_details);
- }
- else
- {
- violation.Warning(player.userID, type, details, hook_details);
- }
- }
- }
- });
- }
- public static void Unload()
- {
- History.Clear();
- violation.Clear();
- }
- }
- public class Trajectory
- {
- public class Settings
- {
- public bool Ban;
- public ulong Cooldown;
- public bool Enabled;
- public float Sensitivity;
- public bool Warn;
- public Settings()
- {
- Ban = false;
- Cooldown = 90;
- 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.AntiCheatTrajectory;
- private static readonly Violation violation = new Violation(category);
- private static float sensitivity;
- private static float sensitivity_lo;
- private static float sensitivity_hi;
- public static void Configure()
- {
- var settings = config.AntiCheat.Trajectory.Validate(300);
- violation.Configure(settings, 2, 8, 1000);
- sensitivity = 5.0f + (5.0f * (1.0f - settings.Sensitivity));
- sensitivity_lo = 0.0625f + 0.125f * settings.Sensitivity;
- sensitivity_hi = 0.5f * sensitivity_lo;
- }
- private static float Ratio(float a, float b) => (a + sensitivity) / (b + sensitivity);
- private static uint Percent(float value) =>
- (uint)(100.0f * (1.0f - value));
- public static void Trigger(BaseCombatEntity entity, HitInfo info)
- {
- var player = info.InitiatorPlayer;
- if(Permissions.Bypass.AntiCheat.Trajectory(player.userID))
- {
- return;
- }
- var weapon = Weapon.Get(player.userID, info.ProjectileID);
- if(weapon == null)
- {
- return;
- }
- var d_actual = Vector3.Distance(entity.transform.position, weapon.Position);
- var d_server = info.ProjectileDistance;
- _instance.NextTick(() =>
- {
- var variance = (d_actual < d_server) ? Ratio(d_actual, d_server) : Ratio(d_server, d_actual);
- if(float.IsNaN(variance) || float.IsInfinity(variance))
- {
- variance = 1.0f;
- }
- else
- {
- variance = Math.Abs(variance);
- }
- ulong violations = 0;
- if(variance < sensitivity_lo)
- {
- if(violation.Trigger(player.userID))
- {
- ++violations;
- }
- if((variance < sensitivity_hi) && violation.Trigger(player.userID))
- {
- ++violations;
- }
- }
- if(config.Log.AntiCheat.Trajectory)
- {
- Log.Console(Key.LogAntiCheatTrajectory, new Dictionary<string, string>
- {
- { "distance", d_actual.ToString("F1") },
- { "playerid", player.UserIDString },
- { "playername", Text.Sanitize(player.displayName) },
- { "reported", d_server.ToString("F1") },
- { "weapon", $"{weapon.Name}[{Enum.GetName(typeof(DamageType), entity.lastDamage)}]" }
- });
- }
- Projectile.Log.SetTrajectory(player.userID, info.ProjectileID, variance);
- if(player.isMounted || User.HasParent<CargoShip>(player) || User.HasParent<HotAirBalloon>(player))
- {
- return;
- }
- if(config.AntiCheat.Trajectory.Enabled && (violations > 0))
- {
- Projectile.Log.SetTrajectoryViolations(player.userID, info.ProjectileID, violations);
- var hit_distance = d_actual.ToString("F1");
- var projectile_distance = d_server.ToString("F1");
- violation.Trigger(player.userID, type, Text.GetPlain(Key.ViolationTrajectory, new Dictionary<string, string>
- {
- { "distance", hit_distance },
- { "reported", projectile_distance },
- { "weapon", weapon.Name }
- }), violations, false, new Dictionary<string, string>
- {
- { "hit_distance", hit_distance },
- { "movement_speed", weapon.Speed.ToString("F1") },
- { "projectile_distance", projectile_distance },
- { "projectile_id", info.ProjectileID.ToString() },
- { "trajectory_percent", Percent(variance).ToString() },
- { "violation_id", weapon.Fired.Ticks.ToString() },
- { "weapon_ammo", weapon.AmmoName },
- { "weapon_attachments", string.Join(", ", weapon.Attachments) },
- { "weapon_type", weapon.ShortName }
- });
- }
- });
- }
- public static void Unload() => violation.Clear();
- }
- public class WallHack
- {
- 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.AntiCheatWallHack;
- private static readonly Violation violation = new Violation(category);
- public static void Configure()
- {
- var settings = config.AntiCheat.WallHack.Validate(900);
- violation.Configure(settings, 2, 8, 15000);
- }
- public static void Trigger(BasePlayer player, HitInfo info)
- {
- if(Permissions.Bypass.AntiCheat.WallHack(player.userID))
- {
- return;
- }
- var victim = info.HitEntity as BasePlayer;
- if((victim == null) || !victim.userID.IsSteamId())
- {
- return;
- }
- var weapon = Weapon.Get(player.userID, info.ProjectileID);
- if((weapon == null) || weapon.Spread)
- {
- return;
- }
- _instance.NextTick(() =>
- {
- if(Physics.Linecast(info.PointStart, info.HitPositionWorld, Layers.Mask.Construction))
- {
- if(config.AntiCheat.WallHack.Enabled && violation.Trigger(player.userID))
- {
- Projectile.Log.SetWallHackViolations(player.userID, info.ProjectileID, 1);
- violation.Trigger(player.userID, type, Text.GetPlain(Key.ViolationWallHack, new Dictionary<string, string>
- {
- { "target", Text.Sanitize(victim.displayName) },
- { "weapon", weapon.Name }
- }), false, new Dictionary<string, string>
- {
- { "hit_distance", Vector3.Distance(victim.transform.position, weapon.Position).ToString("F1") },
- { "movement_speed", weapon.Speed.ToString("F1") },
- { "projectile_id", info.ProjectileID.ToString() },
- { "violation_id", weapon.Fired.Ticks.ToString() },
- { "weapon_ammo", weapon.AmmoName },
- { "weapon_attachments", string.Join(", ", weapon.Attachments) },
- { "weapon_type", weapon.ShortName }
- });
- }
- }
- });
- }
- public static void Unload() => violation.Clear();
- }
- }
- #endregion _anticheat_
- #region _antiflood_
- private class AntiFlood
- {
- public class Settings
- {
- public Chat.Settings Chat;
- public Command.Settings Command;
- public ItemDrop.Settings ItemDrop;
- public Settings()
- {
- Chat = new Chat.Settings();
- Command = new Command.Settings();
- ItemDrop = new ItemDrop.Settings();
- }
- public void Validate()
- {
- Configuration.Validate(ref Chat, () => new Chat.Settings());
- Configuration.Validate(ref Command, () => new Command.Settings());
- Configuration.Validate(ref ItemDrop, () => new ItemDrop.Settings());
- }
- }
- private const Key category = Key.AntiFlood;
- public static void Configure()
- {
- Chat.Configure();
- Command.Configure();
- ItemDrop.Configure();
- }
- public static void Unload()
- {
- Chat.Unload();
- Command.Unload();
- ItemDrop.Unload();
- }
- public class Chat
- {
- public class Settings
- {
- public bool Ban;
- public ulong Cooldown;
- public bool Enabled;
- public float Sensitivity;
- public bool Warn;
- public Settings()
- {
- Ban = false;
- Cooldown = 30;
- Enabled = true;
- Sensitivity = 0.5f;
- Warn = false;
- }
- 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.AntiFloodChat;
- private static readonly Violation violation = new Violation(category);
- public static void Configure()
- {
- var settings = config.AntiFlood.Chat.Validate(3600);
- violation.Configure(settings, 5, 15, 1000);
- }
- public static ulong Cooldown(ulong playerid)
- {
- return violation.Cooldown(playerid);
- }
- public static void Subscribe()
- {
- if(config.AntiFlood.Chat.Enabled)
- {
- Hooks.Subscribe(nameof(OnPlayerChat));
- }
- else
- {
- Hooks.Unsubscribe(nameof(OnPlayerChat));
- }
- }
- public static bool Trigger(ulong playerid)
- {
- return violation.Trigger(playerid);
- }
- public static void Unload() => violation.Clear();
- public static void Violation(BasePlayer player, string details)
- {
- if(config.AntiFlood.Chat.Enabled)
- {
- violation.Trigger(player.userID, type, details);
- }
- }
- }
- public class Command
- {
- public class Settings
- {
- public bool Ban;
- public ulong Cooldown;
- public bool Enabled;
- public float Sensitivity;
- public bool Warn;
- public Settings()
- {
- Ban = false;
- Cooldown = 30;
- Enabled = true;
- Sensitivity = 0.5f;
- Warn = false;
- }
- 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.AntiFloodCommand;
- private static readonly Violation violation = new Violation(category);
- public static void Configure()
- {
- var settings = config.AntiFlood.Command.Validate(60);
- violation.Configure(settings, 5, 15, 125);
- }
- public static ulong Cooldown(ulong playerid)
- {
- return violation.Cooldown(playerid);
- }
- public static void Subscribe()
- {
- if(config.AntiFlood.Command.Enabled)
- {
- Hooks.Subscribe(nameof(OnServerCommand));
- }
- else
- {
- Hooks.Unsubscribe(nameof(OnServerCommand));
- }
- }
- public static bool Trigger(ulong playerid)
- {
- return violation.Trigger(playerid);
- }
- public static void Unload() => violation.Clear();
- public static void Violation(BasePlayer player, string details)
- {
- if(config.AntiFlood.Command.Enabled)
- {
- violation.Trigger(player.userID, type, details);
- }
- }
- }
- public class ItemDrop
- {
- 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 = false;
- }
- 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.AntiFloodItemDrop;
- private static readonly Violation violation = new Violation(category);
- private static readonly Dictionary<ulong, int> history = new Dictionary<ulong, int>();
- public static void Configure()
- {
- var settings = config.AntiFlood.ItemDrop.Validate(900);
- violation.Configure(settings, 3, 7, 30000);
- }
- public static ulong CoolDown(ulong playerid, int itemid)
- {
- history[playerid] = itemid;
- return violation.Cooldown(playerid);
- }
- public static void Subscribe()
- {
- if(config.AntiFlood.ItemDrop.Enabled)
- {
- Hooks.Subscribe(nameof(CanCraft));
- Hooks.Subscribe(nameof(OnItemDropped));
- }
- else
- {
- Hooks.Unsubscribe(nameof(CanCraft));
- Hooks.Unsubscribe(nameof(OnItemDropped));
- }
- }
- public static bool Trigger(ulong playerid, int itemid)
- {
- if(history.ContainsKey(playerid))
- {
- if(history[playerid] == itemid)
- {
- if(config.Log.AntiFlood.ItemDrop)
- {
- var playername = Text.Sanitize(BasePlayer.FindByID(playerid)?.displayName ?? Text.Get(Key.unknown));
- var itemtext = ItemManager.FindItemDefinition(itemid)?.displayName;
- var itemname = itemtext?.translated ?? itemtext?.english ?? Text.Get(Key.unknown);
- Log.Console(Key.LogAntiSpamItemDrop, new Dictionary<string, string>
- {
- { "itemid", itemid.ToString() },
- { "itemname", itemname },
- { "playerid", playerid.ToString() },
- { "playername", playername }
- });
- }
- return config.AntiFlood.ItemDrop.Enabled && violation.Trigger(playerid);
- }
- }
- return false;
- }
- public static void Unload()
- {
- history.Clear();
- violation.Clear();
- }
- public static void Violation(BasePlayer player, string details)
- {
- violation.Trigger(player.userID, type, details);
- }
- }
- }
- #endregion _antiflood_
- #region _api_
- public class API
- {
- public class Settings
- {
- public string ApiKey;
- public bool Enabled;
- public Settings()
- {
- ApiKey = string.Empty;
- Enabled = false;
- }
- public void Validate()
- {
- Configuration.Validate(ref ApiKey, () => { return string.Empty; });
- }
- }
- }
- #endregion _api_
- #region _ban_
- public class Ban
- {
- public class Settings
- {
- public bool Inherit;
- public bool Teleport;
- public BanTime Time;
- public Settings()
- {
- Inherit = true;
- Teleport = false;
- Time = new BanTime();
- }
- public class BanTime
- {
- public bool Enforce;
- public bool Multiply;
- public ulong Seconds;
- }
- public void Validate()
- {
- Configuration.Validate(ref Time, () => new BanTime());
- }
- }
- }
- #endregion _ban_
- #region _chat_
- private class Chat
- {
- public static void Admin(Key key, Dictionary<string, string> parameters = null)
- {
- Log.Console(key, parameters);
- if(config.Admin.Broadcast)
- {
- foreach(var player in BasePlayer.activePlayerList)
- {
- if(Permissions.Admin(player.userID))
- {
- Send(player, key, parameters);
- }
- }
- }
- }
- public static void Broadcast(Key key, Dictionary<string, string> parameters = null)
- {
- foreach(var player in BasePlayer.activePlayerList)
- {
- Send(player, key, parameters);
- }
- }
- public static void Console(BasePlayer player, Key key, Dictionary<string, string> parameters = null)
- {
- if((player == null) || !player.IsConnected)
- {
- return;
- }
- player.ConsoleMessage(Text.GetPlain(key, player, parameters));
- }
- public static void Reply(IPlayer iplayer, Key key, Dictionary<string, string> parameters = null)
- {
- if((iplayer == null) || iplayer.IsServer)
- {
- Log.Console(key, parameters);
- }
- else
- {
- if(iplayer.LastCommand == CommandType.Console)
- {
- iplayer.Reply(Text.GetPlain(key, iplayer, parameters));
- }
- else
- {
- Send(iplayer.Object as BasePlayer, key, parameters);
- }
- }
- }
- public static void Send(BasePlayer player, Key key, Dictionary<string, string> parameters = null)
- {
- if((player == null) || !player.IsConnected)
- {
- return;
- }
- var message = Text.Get(key, player, parameters);
- player.SendConsoleCommand("chat.add", 0, 76561199125814167UL, message);
- }
- }
- #endregion _chat_
- #region _command_
- private void CommandReceive(IPlayer iplayer, string command, string[] args) => Command.Receive(iplayer, command, args);
- private class Command
- {
- private static readonly Dictionary<string, Info> commands = new Dictionary<string, Info>();
- private static List<Info> info;
- private class Info
- {
- public Action<IPlayer, string, string[]> Action { get; protected set; }
- public List<string> Aliases { get; protected set; }
- public Key Title { get; protected set; }
- public Info(Action<IPlayer, string, string[]> action, Key title, params string[] aliases)
- {
- Action = action;
- Aliases = new List<string>(aliases);
- Title = title;
- foreach(var alias in aliases)
- {
- commands.Add(alias, this);
- }
- }
- }
- public static void Load()
- {
- info = new List<Info>
- {
- new Info(Config, Key.CommandConfigTitle, "g.config", "guardian.config"),
- new Info(Help, Key.CommandHelpTitle, "g.help", "guardian", "guardian.help"),
- new Info(Ip, Key.CommandIpTitle, "g.ip", "guardian.ip"),
- new Info(Log, Key.CommandLogTitle, "g.log", "guardian.log"),
- new Info(Server, Key.CommandServerTitle, "g.server", "guardian.server"),
- new Info(Teleport, Key.CommandTeleportTitle, "g.tp", "g.tpv", "guardian.tp", "guardian.tpv"),
- new Info(Users, Key.CommandUserTitle, "g.user", "guardian.user"),
- new Info(Vpn, Key.CommandVpnTitle, "g.vpn", "guardian.vpn")
- };
- foreach(var command in commands)
- {
- _instance.AddCovalenceCommand(command.Key, nameof(CommandReceive));
- }
- }
- public static void Receive(IPlayer iplayer, string command, string[] args)
- {
- Info entry;
- if(commands.TryGetValue(command = command.ToLower(), out entry))
- {
- entry.Action(iplayer, command, args);
- }
- else
- {
- Chat.Reply(iplayer, Key.CommandUnknown, new Dictionary<string, string>
- {
- { "command", (iplayer.LastCommand == CommandType.Console) ? command : ("/" + command) }
- });
- }
- }
- public static void Unload()
- {
- commands.Clear();
- info.Clear();
- info = null;
- }
- private static void Config(IPlayer iplayer, string command, string[] args)
- {
- var command_replace = new Dictionary<string, string>
- {
- { "command", (iplayer.LastCommand == CommandType.Console) ? command : ("/" + command) }
- };
- BasePlayer player = iplayer.IsServer ? null : iplayer.Object as BasePlayer;
- if((player != null) && !Permissions.Command.Config(player.userID))
- {
- Chat.Reply(iplayer, Key.CommandNoPermission);
- return;
- }
- else if(args.Length == 0)
- {
- goto syntax_error;
- }
- var subsection = args[0].ToLower();
- var subcommand = subsection.Split('.')[0];
- bool success = false;
- switch(subcommand)
- {
- case "admin":
- switch(subsection)
- {
- case "admin.broadcast": success = Config(iplayer, args, subsection, ref config.Admin.Broadcast); break;
- case "admin.bypass": success = Config(iplayer, args, subsection, ref config.Admin.Bypass); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAdminSyntax, command_replace);
- return;
- case "anticheat":
- switch(Config(subsection))
- {
- case "anticheat.aim":
- switch(subsection)
- {
- case "anticheat.aim.ban": success = Config(iplayer, args, subsection, ref config.AntiCheat.Aim.Ban, AntiCheat.Aim.Configure); break;
- case "anticheat.aim.cooldown": success = Config(iplayer, args, subsection, ref config.AntiCheat.Aim.Cooldown, AntiCheat.Aim.Configure); break;
- case "anticheat.aim.enabled": success = Config(iplayer, args, subsection, ref config.AntiCheat.Aim.Enabled); break;
- case "anticheat.aim.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiCheat.Aim.Sensitivity, AntiCheat.Aim.Configure); break;
- case "anticheat.aim.trigger.animal": success = Config(iplayer, args, subsection, ref config.AntiCheat.Aim.Trigger.Animal); break;
- case "anticheat.aim.trigger.bradley": success = Config(iplayer, args, subsection, ref config.AntiCheat.Aim.Trigger.Bradley); break;
- case "anticheat.aim.trigger.helicopter": success = Config(iplayer, args, subsection, ref config.AntiCheat.Aim.Trigger.Helicopter); break;
- case "anticheat.aim.trigger.npc": success = Config(iplayer, args, subsection, ref config.AntiCheat.Aim.Trigger.NPC); break;
- case "anticheat.aim.warn": success = Config(iplayer, args, subsection, ref config.AntiCheat.Aim.Warn, AntiCheat.Aim.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiCheatAimSyntax, command_replace);
- return;
- case "anticheat.firerate":
- switch(subsection)
- {
- case "anticheat.firerate.ban": success = Config(iplayer, args, subsection, ref config.AntiCheat.FireRate.Ban, AntiCheat.FireRate.Configure); break;
- case "anticheat.firerate.cooldown": success = Config(iplayer, args, subsection, ref config.AntiCheat.FireRate.Cooldown, AntiCheat.FireRate.Configure); break;
- case "anticheat.firerate.enabled": success = Config(iplayer, args, subsection, ref config.AntiCheat.FireRate.Enabled); break;
- case "anticheat.firerate.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiCheat.FireRate.Sensitivity, AntiCheat.FireRate.Configure); break;
- case "anticheat.firerate.warn": success = Config(iplayer, args, subsection, ref config.AntiCheat.FireRate.Warn, AntiCheat.FireRate.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiCheatFireRateSyntax, command_replace);
- return;
- case "anticheat.gravity":
- switch(subsection)
- {
- case "anticheat.gravity.ban": success = Config(iplayer, args, subsection, ref config.AntiCheat.Gravity.Ban, AntiCheat.Gravity.Configure); break;
- case "anticheat.gravity.cooldown": success = Config(iplayer, args, subsection, ref config.AntiCheat.Gravity.Cooldown, AntiCheat.Gravity.Configure); break;
- case "anticheat.gravity.enabled": success = Config(iplayer, args, subsection, ref config.AntiCheat.Gravity.Enabled); break;
- case "anticheat.gravity.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiCheat.Gravity.Sensitivity, AntiCheat.Gravity.Configure); break;
- case "anticheat.gravity.warn": success = Config(iplayer, args, subsection, ref config.AntiCheat.Gravity.Warn, AntiCheat.Gravity.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiCheatGravitySyntax, command_replace);
- return;
- case "anticheat.meleerate":
- switch(subsection)
- {
- case "anticheat.meleerate.ban": success = Config(iplayer, args, subsection, ref config.AntiCheat.MeleeRate.Ban, AntiCheat.MeleeRate.Configure); break;
- case "anticheat.meleerate.cooldown": success = Config(iplayer, args, subsection, ref config.AntiCheat.MeleeRate.Cooldown, AntiCheat.MeleeRate.Configure); break;
- case "anticheat.meleerate.enabled": success = Config(iplayer, args, subsection, ref config.AntiCheat.MeleeRate.Enabled); break;
- case "anticheat.meleerate.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiCheat.MeleeRate.Sensitivity, AntiCheat.MeleeRate.Configure); break;
- case "anticheat.meleerate.warn": success = Config(iplayer, args, subsection, ref config.AntiCheat.MeleeRate.Warn, AntiCheat.MeleeRate.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiCheatMeleeRateSyntax, command_replace);
- return;
- case "anticheat.recoil":
- switch(subsection)
- {
- case "anticheat.recoil.ban": success = Config(iplayer, args, subsection, ref config.AntiCheat.Recoil.Ban, AntiCheat.Recoil.Configure); break;
- case "anticheat.recoil.cooldown": success = Config(iplayer, args, subsection, ref config.AntiCheat.Recoil.Cooldown, AntiCheat.Recoil.Configure); break;
- case "anticheat.recoil.enabled": success = Config(iplayer, args, subsection, ref config.AntiCheat.Recoil.Enabled); break;
- case "anticheat.recoil.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiCheat.Recoil.Sensitivity, AntiCheat.Recoil.Configure); break;
- case "anticheat.recoil.warn": success = Config(iplayer, args, subsection, ref config.AntiCheat.Recoil.Warn, AntiCheat.Recoil.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiCheatRecoilSyntax, command_replace);
- return;
- case "anticheat.server":
- switch(subsection)
- {
- case "anticheat.server.ban": success = Config(iplayer, args, subsection, ref config.AntiCheat.Server.Ban, AntiCheat.Server.Configure); break;
- case "anticheat.server.cooldown": success = Config(iplayer, args, subsection, ref config.AntiCheat.Server.Cooldown, AntiCheat.Server.Configure); break;
- case "anticheat.server.enabled": success = Config(iplayer, args, subsection, ref config.AntiCheat.Server.Enabled); break;
- case "anticheat.server.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiCheat.Server.Sensitivity, AntiCheat.Server.Configure); break;
- case "anticheat.server.warn": success = Config(iplayer, args, subsection, ref config.AntiCheat.Server.Warn, AntiCheat.Server.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiCheatServerSyntax, command_replace);
- return;
- case "anticheat.stash":
- switch(subsection)
- {
- case "anticheat.stash.ban": success = Config(iplayer, args, subsection, ref config.AntiCheat.Stash.Ban, AntiCheat.Stash.Configure); break;
- case "anticheat.stash.cooldown": success = Config(iplayer, args, subsection, ref config.AntiCheat.Stash.Cooldown, AntiCheat.Stash.Configure); break;
- case "anticheat.stash.enabled": success = Config(iplayer, args, subsection, ref config.AntiCheat.Stash.Enabled, AntiCheat.Stash.Subscribe); break;
- case "anticheat.stash.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiCheat.Stash.Sensitivity, AntiCheat.Stash.Configure); break;
- case "anticheat.stash.warn": success = Config(iplayer, args, subsection, ref config.AntiCheat.Stash.Warn, AntiCheat.Stash.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiCheatStashSyntax, command_replace);
- return;
- case "anticheat.trajectory":
- switch(subsection)
- {
- case "anticheat.trajectory.ban": success = Config(iplayer, args, subsection, ref config.AntiCheat.Trajectory.Ban, AntiCheat.Trajectory.Configure); break;
- case "anticheat.trajectory.cooldown": success = Config(iplayer, args, subsection, ref config.AntiCheat.Trajectory.Cooldown, AntiCheat.Trajectory.Configure); break;
- case "anticheat.trajectory.enabled": success = Config(iplayer, args, subsection, ref config.AntiCheat.Trajectory.Enabled); break;
- case "anticheat.trajectory.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiCheat.Trajectory.Sensitivity, AntiCheat.Trajectory.Configure); break;
- case "anticheat.trajectory.warn": success = Config(iplayer, args, subsection, ref config.AntiCheat.Trajectory.Warn, AntiCheat.Trajectory.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiCheatTrajectorySyntax, command_replace);
- return;
- case "anticheat.wallhack":
- switch(subsection)
- {
- case "anticheat.wallhack.ban": success = Config(iplayer, args, subsection, ref config.AntiCheat.WallHack.Ban, AntiCheat.WallHack.Configure); break;
- case "anticheat.wallhack.cooldown": success = Config(iplayer, args, subsection, ref config.AntiCheat.WallHack.Cooldown, AntiCheat.WallHack.Configure); break;
- case "anticheat.wallhack.enabled": success = Config(iplayer, args, subsection, ref config.AntiCheat.WallHack.Enabled); break;
- case "anticheat.wallhack.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiCheat.WallHack.Sensitivity, AntiCheat.WallHack.Configure); break;
- case "anticheat.wallhack.warn": success = Config(iplayer, args, subsection, ref config.AntiCheat.WallHack.Warn, AntiCheat.WallHack.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiCheatWallHackSyntax, command_replace);
- return;
- }
- Chat.Reply(iplayer, Key.CommandConfigAntiCheatSyntax, command_replace);
- return;
- case "antiflood":
- switch(Config(subsection))
- {
- case "antiflood.chat":
- switch(subsection)
- {
- case "antiflood.chat.ban": success = Config(iplayer, args, subsection, ref config.AntiFlood.Chat.Ban, AntiFlood.Chat.Configure); break;
- case "antiflood.chat.cooldown": success = Config(iplayer, args, subsection, ref config.AntiFlood.Chat.Cooldown, AntiFlood.Chat.Configure); break;
- case "antiflood.chat.enabled": success = Config(iplayer, args, subsection, ref config.AntiFlood.Chat.Enabled, AntiFlood.Chat.Subscribe); break;
- case "antiflood.chat.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiFlood.Chat.Sensitivity, AntiFlood.Chat.Configure); break;
- case "antiflood.chat.warn": success = Config(iplayer, args, subsection, ref config.AntiFlood.Chat.Warn, AntiFlood.Chat.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiFloodChatSyntax, command_replace);
- return;
- case "antiflood.command":
- switch(subsection)
- {
- case "antiflood.command.ban": success = Config(iplayer, args, subsection, ref config.AntiFlood.Command.Ban, AntiFlood.Command.Configure); break;
- case "antiflood.command.cooldown": success = Config(iplayer, args, subsection, ref config.AntiFlood.Command.Cooldown, AntiFlood.Command.Configure); break;
- case "antiflood.command.enabled": success = Config(iplayer, args, subsection, ref config.AntiFlood.Command.Enabled, AntiFlood.Command.Subscribe); break;
- case "antiflood.command.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiFlood.Command.Sensitivity, AntiFlood.Command.Configure); break;
- case "antiflood.command.warn": success = Config(iplayer, args, subsection, ref config.AntiFlood.Command.Warn, AntiFlood.Command.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiFloodCommandSyntax, command_replace);
- return;
- case "antiflood.itemdrop":
- switch(subsection)
- {
- case "antiflood.itemdrop.ban": success = Config(iplayer, args, subsection, ref config.AntiFlood.ItemDrop.Ban, AntiFlood.ItemDrop.Configure); break;
- case "antiflood.itemdrop.cooldown": success = Config(iplayer, args, subsection, ref config.AntiFlood.ItemDrop.Cooldown, AntiFlood.ItemDrop.Configure); break;
- case "antiflood.itemdrop.enabled": success = Config(iplayer, args, subsection, ref config.AntiFlood.ItemDrop.Enabled, AntiFlood.ItemDrop.Subscribe); break;
- case "antiflood.itemdrop.sensitivity": success = Config(iplayer, args, subsection, ref config.AntiFlood.ItemDrop.Sensitivity, AntiFlood.ItemDrop.Configure); break;
- case "antiflood.itemdrop.warn": success = Config(iplayer, args, subsection, ref config.AntiFlood.ItemDrop.Warn, AntiFlood.ItemDrop.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigAntiFloodItemDropSyntax, command_replace);
- return;
- }
- Chat.Reply(iplayer, Key.CommandConfigAntiFloodSyntax, command_replace);
- return;
- case "ban":
- switch(subsection)
- {
- case "ban.inherit": success = Config(iplayer, args, subsection, ref config.Ban.Inherit); break;
- case "ban.teleport": success = Config(iplayer, args, subsection, ref config.Ban.Teleport); break;
- case "ban.time.enforce": success = Config(iplayer, args, subsection, ref config.Ban.Time.Enforce); break;
- case "ban.time.multiply": success = Config(iplayer, args, subsection, ref config.Ban.Time.Multiply); break;
- case "ban.time.seconds": success = Config(iplayer, args, subsection, ref config.Ban.Time.Seconds); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigBanSyntax, command_replace);
- return;
- case "cripple":
- switch(subsection)
- {
- case "cripple.heal": success = Config(iplayer, args, subsection, ref config.Cripple.Heal); break;
- case "cripple.inherit": success = Config(iplayer, args, subsection, ref config.Cripple.Inherit); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigCrippleSyntax, command_replace);
- return;
- case "discord":
- switch(subsection)
- {
- case "discord.enabled": success = Config(iplayer, args, subsection, ref config.Discord.Enabled, () => Discord.Subscribe()); break;
- case "discord.webhook": success = Config(iplayer, args, subsection, ref config.Discord.WebHook); break;
- case "discord.filters.anticheat.enabled": success = Config(iplayer, args, subsection, ref config.Discord.Filters.AntiCheat.Enabled); break;
- case "discord.filters.anticheat.webhook": success = Config(iplayer, args, subsection, ref config.Discord.Filters.AntiCheat.WebHook); break;
- case "discord.filters.antiflood.enabled": success = Config(iplayer, args, subsection, ref config.Discord.Filters.AntiFlood.Enabled); break;
- case "discord.filters.antiflood.webhook": success = Config(iplayer, args, subsection, ref config.Discord.Filters.AntiFlood.WebHook); break;
- case "discord.filters.ip.enabled": success = Config(iplayer, args, subsection, ref config.Discord.Filters.IP.Enabled); break;
- case "discord.filters.ip.webhook": success = Config(iplayer, args, subsection, ref config.Discord.Filters.IP.WebHook); break;
- case "discord.filters.steam.enabled": success = Config(iplayer, args, subsection, ref config.Discord.Filters.Steam.Enabled); break;
- case "discord.filters.steam.webhook": success = Config(iplayer, args, subsection, ref config.Discord.Filters.Steam.WebHook); break;
- case "discord.filters.vpn.enabled": success = Config(iplayer, args, subsection, ref config.Discord.Filters.VPN.Enabled); break;
- case "discord.filters.vpn.webhook": success = Config(iplayer, args, subsection, ref config.Discord.Filters.VPN.WebHook); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigDiscordSyntax, command_replace);
- return;
- case "entity":
- switch(subsection)
- {
- case "entity.damage.animal": success = Config(iplayer, args, subsection, ref config.Entity.Damage.Animal, () => config.Entity.Damage.Validate()); break;
- case "entity.damage.bradley": success = Config(iplayer, args, subsection, ref config.Entity.Damage.Bradley, () => config.Entity.Damage.Validate()); break;
- case "entity.damage.building": success = Config(iplayer, args, subsection, ref config.Entity.Damage.Building, () => config.Entity.Damage.Validate()); break;
- case "entity.damage.entity": success = Config(iplayer, args, subsection, ref config.Entity.Damage.Entity, () => config.Entity.Damage.Validate()); break;
- case "entity.damage.friend": success = Config(iplayer, args, subsection, ref config.Entity.Damage.Friend, () => config.Entity.Damage.Validate()); break;
- case "entity.damage.helicopter": success = Config(iplayer, args, subsection, ref config.Entity.Damage.Helicopter, () => config.Entity.Damage.Validate()); break;
- case "entity.damage.npc": success = Config(iplayer, args, subsection, ref config.Entity.Damage.NPC, () => config.Entity.Damage.Validate()); break;
- case "entity.damage.player": success = Config(iplayer, args, subsection, ref config.Entity.Damage.Player, () => config.Entity.Damage.Validate()); break;
- case "entity.damage.team": success = Config(iplayer, args, subsection, ref config.Entity.Damage.Team, () => config.Entity.Damage.Validate()); break;
- case "entity.damage.trap": success = Config(iplayer, args, subsection, ref config.Entity.Damage.Trap, () => config.Entity.Damage.Validate()); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigEntitySyntax, command_replace);
- return;
- case "ip":
- switch(subsection)
- {
- case "ip.filter.cooldown": success = Config(iplayer, args, subsection, ref config.IP.Filter.Cooldown, IP.Configure); break;
- case "ip.violation.ban": success = Config(iplayer, args, subsection, ref config.IP.Violation.Ban); break;
- case "ip.violation.enabled": success = Config(iplayer, args, subsection, ref config.IP.Violation.Enabled); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigIpSyntax, command_replace);
- return;
- case "log":
- switch(subsection)
- {
- case "log.anticheat.aim": success = Config(iplayer, args, subsection, ref config.Log.AntiCheat.Aim); break;
- case "log.anticheat.gravity": success = Config(iplayer, args, subsection, ref config.Log.AntiCheat.Gravity); break;
- case "log.anticheat.meleerate": success = Config(iplayer, args, subsection, ref config.Log.AntiCheat.MeleeRate); break;
- case "log.anticheat.recoil": success = Config(iplayer, args, subsection, ref config.Log.AntiCheat.Recoil); break;
- case "log.anticheat.server": success = Config(iplayer, args, subsection, ref config.Log.AntiCheat.Server); break;
- case "log.anticheat.stash": success = Config(iplayer, args, subsection, ref config.Log.AntiCheat.Stash); break;
- case "log.anticheat.trajectory": success = Config(iplayer, args, subsection, ref config.Log.AntiCheat.Trajectory); break;
- case "log.antiflood.itemdrop": success = Config(iplayer, args, subsection, ref config.Log.AntiFlood.ItemDrop); break;
- case "log.ip.filter": success = Config(iplayer, args, subsection, ref config.Log.IP.Filter); break;
- case "log.projectile.collapse": success = Config(iplayer, args, subsection, ref config.Log.Projectile.Collapse); break;
- case "log.projectile.verbose": success = Config(iplayer, args, subsection, ref config.Log.Projectile.Verbose); break;
- case "log.user.bypass": success = Config(iplayer, args, subsection, ref config.Log.User.Bypass); break;
- case "log.user.connect": success = Config(iplayer, args, subsection, ref config.Log.User.Connect); break;
- case "log.vpn.check": success = Config(iplayer, args, subsection, ref config.Log.VPN.Check); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigLogSyntax, command_replace);
- return;
- case "save":
- if((args.Length == 1) && (subsection == "save"))
- {
- Configuration.Save();
- Chat.Reply(iplayer, Key.CommandConfig, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.Saved, iplayer) },
- { "info", Text.GetPlain(Key.file, iplayer) }
- });
- return;
- }
- Chat.Reply(iplayer, Key.CommandConfigSaveSyntax, command_replace);
- return;
- case "steam":
- switch(subsection)
- {
- case "steam.api.apikey": success = Config(iplayer, args, subsection, ref config.Steam.API.ApiKey, Steam.Configure); break;
- case "steam.api.enabled": success = Config(iplayer, args, subsection, ref config.Steam.API.Enabled, Steam.Configure); break;
- case "steam.ban.active": success = Config(iplayer, args, subsection, ref config.Steam.Ban.Active, Steam.Configure); break;
- case "steam.ban.community": success = Config(iplayer, args, subsection, ref config.Steam.Ban.Community, Steam.Configure); break;
- case "steam.ban.days": success = Config(iplayer, args, subsection, ref config.Steam.Ban.Days, Steam.Configure); break;
- case "steam.ban.economy": success = Config(iplayer, args, subsection, ref config.Steam.Ban.Economy, Steam.Configure); break;
- case "steam.ban.game": success = Config(iplayer, args, subsection, ref config.Steam.Ban.Game, Steam.Configure); break;
- case "steam.ban.vac": success = Config(iplayer, args, subsection, ref config.Steam.Ban.VAC, Steam.Configure); break;
- case "steam.game.count": success = Config(iplayer, args, subsection, ref config.Steam.Game.Count, Steam.Configure); break;
- case "steam.game.hours": success = Config(iplayer, args, subsection, ref config.Steam.Game.Hours, Steam.Configure); break;
- case "steam.profile.invalid": success = Config(iplayer, args, subsection, ref config.Steam.Profile.Invalid, Steam.Configure); break;
- case "steam.profile.limited": success = Config(iplayer, args, subsection, ref config.Steam.Profile.Limited, Steam.Configure); break;
- case "steam.profile.private": success = Config(iplayer, args, subsection, ref config.Steam.Profile.Private, Steam.Configure); break;
- case "steam.share.family": success = Config(iplayer, args, subsection, ref config.Steam.Share.Family, Steam.Configure); break;
- case "steam.violation.ban": success = Config(iplayer, args, subsection, ref config.Steam.Violation.Ban); break;
- case "steam.violation.enabled": success = Config(iplayer, args, subsection, ref config.Steam.Violation.Enabled); break;
- case "steam.violation.warn": success = Config(iplayer, args, subsection, ref config.Steam.Violation.Warn, Steam.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigSteamSyntax, command_replace);
- return;
- case "user":
- switch(subsection)
- {
- case "user.bypass.dayssincelastban": success = Config(iplayer, args, subsection, ref config.User.Bypass.DaysSinceBan); break;
- case "user.bypass.enabled": success = Config(iplayer, args, subsection, ref config.User.Bypass.Enabled); break;
- case "user.bypass.hoursplayed": success = Config(iplayer, args, subsection, ref config.User.Bypass.HoursPlayed); break;
- case "user.bypass.multiply": success = Config(iplayer, args, subsection, ref config.User.Bypass.Multiply); break;
- case "user.friend.damage": success = Config(iplayer, args, subsection, ref config.User.Friend.Damage); break;
- case "user.team.damage": success = Config(iplayer, args, subsection, ref config.User.Team.Damage); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigUserSyntax, command_replace);
- return;
- case "violation":
- switch(subsection)
- {
- case "violation.ban": success = Config(iplayer, args, subsection, ref config.Violation.Ban, Violation.Configure); break;
- case "violation.cooldown": success = Config(iplayer, args, subsection, ref config.Violation.Cooldown, Violation.Configure); break;
- case "violation.sensitivity": success = Config(iplayer, args, subsection, ref config.Violation.Sensitivity, Violation.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigViolationSyntax, command_replace);
- return;
- case "vpn":
- switch(subsection)
- {
- case "vpn.api.getipintel.apikey": success = Config(iplayer, args, subsection, ref config.VPN.API.GetIpIntel.ApiKey); break;
- case "vpn.api.getipintel.enabled": success = Config(iplayer, args, subsection, ref config.VPN.API.GetIpIntel.Enabled); break;
- case "vpn.api.ipapi.enabled": success = Config(iplayer, args, subsection, ref config.VPN.API.IpApi.Enabled); break;
- case "vpn.api.iphub.apikey": success = Config(iplayer, args, subsection, ref config.VPN.API.IpHub.ApiKey, VPN.API.IpHub.Configure); break;
- case "vpn.api.iphub.enabled": success = Config(iplayer, args, subsection, ref config.VPN.API.IpHub.Enabled); break;
- case "vpn.api.ipqualityscore.apikey": success = Config(iplayer, args, subsection, ref config.VPN.API.IpQualityScore.ApiKey); break;
- case "vpn.api.ipqualityscore.enabled": success = Config(iplayer, args, subsection, ref config.VPN.API.IpQualityScore.Enabled); break;
- case "vpn.cache.hours": success = Config(iplayer, args, subsection, ref config.VPN.Cache.Hours); break;
- case "vpn.check.enabled": success = Config(iplayer, args, subsection, ref config.VPN.Check.Enabled); break;
- case "vpn.check.strict": success = Config(iplayer, args, subsection, ref config.VPN.Check.Strict); break;
- case "vpn.violation.ban": success = Config(iplayer, args, subsection, ref config.VPN.Violation.Ban); break;
- case "vpn.violation.enabled": success = Config(iplayer, args, subsection, ref config.VPN.Violation.Enabled); break;
- case "vpn.violation.warn": success = Config(iplayer, args, subsection, ref config.VPN.Violation.Warn, VPN.Configure); break;
- }
- if(!success) Chat.Reply(iplayer, Key.CommandConfigVpnSyntax, command_replace);
- return;
- }
- syntax_error:
- Chat.Reply(iplayer, Key.CommandConfigSyntax, command_replace);
- }
- private static string Config(string subsection)
- {
- var split = subsection.Split('.');
- if(split.Length >= 2)
- {
- return split[0] + "." + split[1];
- }
- return subsection;
- }
- private static bool Config<T>(IPlayer iplayer, string[] args, string setting, ref T value, Action callback = null)
- {
- bool changed = false, success = false;
- if(args.Length == 1)
- {
- success = true;
- }
- else if(args.Length == 2)
- {
- if(typeof(T) == typeof(bool))
- {
- bool new_value;
- if(bool.TryParse(args[1], out new_value))
- {
- if(!value.Equals(new_value))
- {
- changed = true; value = (T)Convert.ChangeType(new_value, typeof(T));
- }
- success = true;
- }
- }
- else if(typeof(T) == typeof(float))
- {
- float new_value;
- if(float.TryParse(args[1], out new_value))
- {
- if(!value.Equals(new_value))
- {
- changed = true; value = (T)Convert.ChangeType(new_value, typeof(T));
- }
- success = true;
- }
- }
- else if(typeof(T) == typeof(string))
- {
- string new_value = args[1];
- if(!value.Equals(new_value))
- {
- changed = true; value = (T)Convert.ChangeType(new_value, typeof(T));
- }
- success = true;
- }
- else if(typeof(T) == typeof(ulong))
- {
- ulong new_value;
- if(ulong.TryParse(args[1], out new_value))
- {
- if(!value.Equals(new_value))
- {
- changed = true; value = (T)Convert.ChangeType(new_value, typeof(T));
- }
- success = true;
- }
- }
- else
- {
- throw new NotImplementedException($"{_instance.Name}.Command.Config<T>: No conversion case exists for type T={typeof(T).Name}.");
- }
- }
- if(success)
- {
- if(changed)
- {
- Configuration.SetDirty();
- callback?.Invoke();
- }
- Chat.Reply(iplayer, Key.CommandConfig, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(changed ? Key.Changed : Key.Current, iplayer) },
- { "info", $"{setting}={value}" }
- });
- }
- return success;
- }
- private static void Help(IPlayer iplayer, string command, string[] args)
- {
- BasePlayer player = iplayer.IsServer ? null : iplayer.Object as BasePlayer;
- if((player != null) && !Permissions.Admin(player.userID))
- {
- Chat.Reply(iplayer, Key.CommandNoPermission);
- return;
- }
- var console = iplayer.IsServer || (iplayer.LastCommand == CommandType.Console);
- StringBuilder aliases = new StringBuilder(), entries = new StringBuilder();
- foreach(var entry in info)
- {
- foreach(var alias in entry.Aliases)
- {
- aliases.Append(console ? "\n " : "\n /").Append(alias);
- }
- entries.Append(Text.Get(Key.CommandHelpEntry, iplayer, new Dictionary<string, string>
- {
- { "aliases", aliases.ToString() },
- { "title", Text.GetPlain(entry.Title, iplayer) }
- }));
- aliases.Clear();
- }
- Chat.Reply(iplayer, Key.CommandHelp, new Dictionary<string, string>
- {
- { "entries", entries.ToString() },
- { "name", _instance.Name },
- { "version", Version.String }
- });
- }
- private static void Ip(IPlayer iplayer, string command, string[] args)
- {
- BasePlayer player = iplayer.IsServer ? null : iplayer.Object as BasePlayer;
- if((player != null) && !Permissions.Command.Ip(player.userID))
- {
- Chat.Reply(iplayer, Key.CommandNoPermission);
- return;
- }
- else if(args.Length == 0)
- {
- goto syntax_error;
- }
- switch(args[0].ToLower())
- {
- case "allow":
- if(args.Length == 1)
- {
- Ip(iplayer, Key.Allowed, IP.GetAllows());
- return;
- }
- else if(args.Length == 2)
- {
- var network = IP.Network(args[1]);
- if(network?.Address != null)
- {
- Ip(iplayer, Key.Allowed, IP.GetAllows(network.Address));
- return;
- }
- }
- else if(args.Length == 3)
- {
- var network = IP.Network(args[2]);
- if(network?.Address != null)
- {
- switch(args[1].ToLower())
- {
- case "add":
- Ip(iplayer, Key.Allowed, network, true);
- return;
- case "remove":
- Ip(iplayer, Key.Allowed, network, false);
- return;
- }
- }
- }
- break;
- case "block":
- if((args.Length == 2) && IP.IsValid(args[1]))
- {
- IP.Block(args[1]);
- Chat.Reply(iplayer, Key.CommandIp, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.Blocked, iplayer) },
- { "info", args[1] }
- });
- return;
- }
- break;
- case "bypass":
- if((args.Length == 2) && IP.IsValid(args[1]))
- {
- IP.Bypass(args[1]);
- Chat.Reply(iplayer, Key.CommandIp, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.Bypassed, iplayer) },
- { "info", args[1] }
- });
- return;
- }
- break;
- case "deny":
- if(args.Length == 1)
- {
- Ip(iplayer, Key.Denied, IP.GetDenies());
- return;
- }
- else if(args.Length == 2)
- {
- var network = IP.Network(args[1]);
- if(network?.Address != null)
- {
- Ip(iplayer, Key.Denied, IP.GetDenies(network.Address));
- return;
- }
- }
- else if(args.Length == 3)
- {
- var network = IP.Network(args[2]);
- if(network?.Address != null)
- {
- switch(args[1].ToLower())
- {
- case "add":
- Ip(iplayer, Key.Denied, network, true);
- return;
- case "remove":
- Ip(iplayer, Key.Denied, network, false);
- return;
- }
- }
- }
- break;
- case "save":
- if(args.Length == 1)
- {
- IP.Save();
- Chat.Reply(iplayer, Key.CommandIp, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.Saved, iplayer) },
- { "info", Text.GetPlain(Key.data, iplayer) }
- });
- return;
- }
- break;
- case "unblock":
- if((args.Length == 2) && IP.IsValid(args[1]))
- {
- IP.Unblock(args[1]);
- Chat.Reply(iplayer, Key.CommandIp, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.Unblocked, iplayer) },
- { "info", args[1] }
- });
- return;
- }
- break;
- }
- syntax_error:
- Chat.Reply(iplayer, Key.CommandIpSyntax, new Dictionary<string, string>
- {
- { "command", (iplayer.LastCommand == CommandType.Console) ? command : ("/" + command) }
- });
- }
- private static void Ip(IPlayer iplayer, Key key, IP.NetworkInfo network, bool add)
- {
- bool success;
- if(key == Key.Allowed)
- {
- if(success = IP.SetAllow(network, add))
- {
- IP.Bypass(network);
- }
- }
- else
- {
- if(success = IP.SetDeny(network, add))
- {
- IP.Block(network);
- }
- }
- if(success)
- {
- Chat.Reply(iplayer, Key.CommandIpEntry, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(add ? Key.Added : Key.Removed, iplayer) },
- { "entry", $"{network.Address}/{network.Bits}" }
- });
- }
- else
- {
- Chat.Reply(iplayer, Key.CommandIpEntryFailed, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(add ? Key.add : Key.remove, iplayer) },
- { "entry", $"{network.Address}/{network.Bits}" }
- });
- }
- }
- private static void Ip(IPlayer iplayer, Key key, List<string> list)
- {
- StringBuilder addresses = new StringBuilder();
- if(list.Count == 0)
- {
- addresses.Append(Text.GetPlain(Key.empty, iplayer));
- }
- else
- {
- foreach(var address in list)
- {
- addresses.Append("\n ").Append(address);
- }
- }
- Chat.Reply(iplayer, Key.CommandIpList, new Dictionary<string, string>
- {
- { "addresses", addresses.ToString() },
- { "type", Text.GetPlain(key, iplayer) }
- });
- }
- private static void Log(IPlayer iplayer, string command, string[] args)
- {
- BasePlayer player = iplayer.IsServer ? null : iplayer.Object as BasePlayer;
- if((player != null) && !Permissions.Admin(player.userID))
- {
- Chat.Reply(iplayer, Key.CommandNoPermission);
- return;
- }
- else if((args.Length == 0) || (args.Length > 2))
- {
- goto syntax_error;
- }
- var userids = User.Find(args[0]);
- if(userids.Count == 0)
- {
- Chat.Reply(iplayer, Key.CommandUserNotFound);
- return;
- }
- else if(userids.Count == 1)
- {
- int lines = 0;
- if(args.Length == 2)
- {
- int.TryParse(args[1], out lines);
- }
- if((lines = Generic.Clamp(lines, 0, 64)) == 0)
- {
- lines = 20;
- }
- foreach(var userid in userids)
- {
- Projectile.Log.Get(iplayer, userid, lines);
- return;
- }
- }
- else
- {
- Users(iplayer, Key.CommandUserTooMany, userids);
- return;
- }
- syntax_error:
- Chat.Reply(iplayer, Key.CommandLogSyntax, new Dictionary<string, string>
- {
- { "command", (iplayer.LastCommand == CommandType.Console) ? command : ("/" + command) }
- });
- }
- private static void Server(IPlayer iplayer, string command, string[] args)
- {
- BasePlayer player = iplayer.IsServer ? null : iplayer.Object as BasePlayer;
- if((player != null) && !Permissions.Command.Server(player.userID))
- {
- Chat.Reply(iplayer, Key.CommandNoPermission);
- return;
- }
- else if(args.Length != 1)
- {
- goto syntax_error;
- }
- switch(args[0].ToLower())
- {
- case "pardon": User.Pardon(iplayer); return;
- case "unban": User.Unban(iplayer); return;
- case "uncripple": User.Uncripple(iplayer); return;
- }
- syntax_error:
- Chat.Reply(iplayer, Key.CommandServerSyntax, new Dictionary<string, string>
- {
- { "command", (iplayer.LastCommand == CommandType.Console) ? command : ("/" + command) }
- });
- }
- private static void Teleport(IPlayer iplayer, string command, string[] args)
- {
- BasePlayer player = iplayer.IsServer ? null : iplayer.Object as BasePlayer;
- if((player != null) && !Permissions.Command.Tp(player.userID))
- {
- Chat.Reply(iplayer, Key.CommandNoPermission);
- return;
- }
- else if(args.Length != 1)
- {
- goto syntax_error;
- }
- var userids = User.Find(args[0]);
- if(userids.Count == 0)
- {
- Chat.Reply(iplayer, Key.CommandUserNotFound);
- return;
- }
- else if(userids.Count > 1)
- {
- Users(iplayer, Key.CommandUserTooMany, userids);
- return;
- }
- var position = Vector3.zero;
- if(command.EndsWith(".tp"))
- {
- foreach(var userid in userids)
- {
- position = User.GetLastSeenPosition(userid);
- }
- }
- else
- {
- foreach(var userid in userids)
- {
- position = User.GetViolationPosition(userid);
- }
- }
- var found = position != Vector3.zero;
- var parameters = new Dictionary<string, string>
- {
- { "position", found ? $"({position.x:0.0} {position.y:0.0} {position.z:0.0})" : Text.GetPlain(Key.unknown, iplayer) }
- };
- if(command.EndsWith(".tp"))
- {
- Chat.Reply(iplayer, Key.CommandTeleport, parameters);
- }
- else
- {
- Chat.Reply(iplayer, Key.CommandTeleportViolation, parameters);
- }
- if(found && !iplayer.IsServer)
- {
- iplayer.Teleport(position.x, position.y, position.z);
- }
- return;
- syntax_error:
- Chat.Reply(iplayer, Key.CommandTeleportSyntax, new Dictionary<string, string>
- {
- { "command", (iplayer.LastCommand == CommandType.Console) ? command : ("/" + command) }
- });
- }
- private static void Users(IPlayer iplayer, string command, string[] args)
- {
- BasePlayer player = iplayer.IsServer ? null : iplayer.Object as BasePlayer;
- if((player != null) && !Permissions.Admin(player.userID))
- {
- Chat.Reply(iplayer, Key.CommandNoPermission);
- return;
- }
- else if(args.Length == 0)
- {
- goto syntax_error;
- }
- var is_ip = IP.IsValid(args[0]);
- var userids = User.Find(args[0]);
- if(userids.Count == 0)
- {
- Chat.Reply(iplayer, Key.CommandUserNotFound);
- return;
- }
- else if(userids.Count == 1)
- {
- if(args.Length == 1)
- {
- foreach(var userid in userids)
- {
- Chat.Reply(iplayer, Key.CommandUserInfo, new Dictionary<string, string>
- {
- { "info", User.InfoText(userid, iplayer) }
- });
- return;
- }
- }
- }
- else if(args.Length == 1)
- {
- Users(iplayer, Key.CommandUserTooMany, userids);
- return;
- }
- else if(!is_ip)
- {
- goto syntax_error;
- }
- ulong duration = 0; string duration_string = null, reason = null;
- if(args.Length == 3)
- {
- if(!Text.ParseTime(args[2], out duration))
- {
- reason = string.IsNullOrEmpty(args[2]) ? null : args[2];
- }
- }
- else if(args.Length == 4)
- {
- reason = string.IsNullOrEmpty(args[2]) ? null : args[2];
- if(!Text.ParseTime(args[3], out duration))
- {
- goto syntax_error;
- }
- }
- else if(args.Length > 4)
- {
- goto syntax_error;
- }
- if(duration == 0)
- {
- duration_string = Text.GetPlain(Key.permanently, iplayer);
- }
- else
- {
- duration_string = $"{Text.GetPlain(Key.For, iplayer)} {Text.Duration.Short(TimeSpan.FromSeconds(duration), iplayer)}";
- }
- var subcommand = args[1].ToLower();
- if(subcommand.StartsWith("team"))
- {
- var dot = subcommand.IndexOf('.') + 1;
- if(subcommand.Length > dot)
- {
- subcommand = subcommand.Substring(dot);
- }
- var team = new HashSet<ulong>();
- foreach(var userid in userids)
- {
- foreach(var entry in User.Team(userid))
- {
- team.Add(entry);
- }
- }
- foreach(var entry in team)
- {
- userids.Add(entry);
- }
- }
- switch(subcommand)
- {
- case "ban":
- foreach(var userid in userids)
- {
- User.Ban(userid, reason, duration, iplayer);
- Chat.Reply(iplayer, Key.CommandUser, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.Banned, iplayer) },
- { "duration", duration_string },
- { "playername", Text.Sanitize(User.Name(userid)) },
- { "playerid", userid.ToString() },
- { "reason", reason }
- });
- }
- return;
- case "ban.reset":
- if(args.Length != 2)
- {
- goto syntax_error;
- }
- foreach(var userid in userids)
- {
- User.BanReset(userid, iplayer);
- Chat.Reply(iplayer, Key.CommandUserAction, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.BanReset, iplayer) },
- { "playername", Text.Sanitize(User.Name(userid)) },
- { "playerid", userid.ToString() }
- });
- }
- return;
- case "cripple":
- foreach(var userid in userids)
- {
- User.Cripple(userid, reason, duration, iplayer);
- Chat.Reply(iplayer, Key.CommandUser, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.Crippled, iplayer) },
- { "duration", duration_string },
- { "playername", Text.Sanitize(User.Name(userid)) },
- { "playerid", userid.ToString() },
- { "reason", reason }
- });
- }
- return;
- case "cripple.reset":
- if(args.Length != 2)
- {
- goto syntax_error;
- }
- foreach(var userid in userids)
- {
- User.CrippleReset(userid, iplayer);
- Chat.Reply(iplayer, Key.CommandUserAction, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.CrippleReset, iplayer) },
- { "playername", Text.Sanitize(User.Name(userid)) },
- { "playerid", userid.ToString() }
- });
- }
- return;
- case "kick":
- if((args.Length > 3) || ((args.Length == 3) && (reason == null)))
- {
- goto syntax_error;
- }
- foreach(var userid in userids)
- {
- User.Kick(userid, reason, iplayer);
- Chat.Reply(iplayer, Key.CommandUserKick, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.Kicked, iplayer) },
- { "playername", Text.Sanitize(User.Name(userid)) },
- { "playerid", userid.ToString() },
- { "reason", reason }
- });
- }
- return;
- case "pardon":
- if(args.Length != 2)
- {
- goto syntax_error;
- }
- foreach(var userid in userids)
- {
- User.Pardon(userid, iplayer);
- Chat.Reply(iplayer, Key.CommandUserAction, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.Pardoned, iplayer) },
- { "playername", Text.Sanitize(User.Name(userid)) },
- { "playerid", userid.ToString() }
- });
- }
- return;
- case "team":
- if(args.Length != 2)
- {
- goto syntax_error;
- }
- Users(iplayer, Key.CommandUserTeam, userids);
- return;
- case "unban":
- if(args.Length != 2)
- {
- goto syntax_error;
- }
- foreach(var userid in userids)
- {
- User.Unban(userid, true, iplayer);
- Chat.Reply(iplayer, Key.CommandUserAction, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.Unbanned, iplayer) },
- { "playername", Text.Sanitize(User.Name(userid)) },
- { "playerid", userid.ToString() }
- });
- }
- return;
- case "uncripple":
- if(args.Length != 2)
- {
- goto syntax_error;
- }
- foreach(var userid in userids)
- {
- User.Uncripple(userid, true, iplayer);
- Chat.Reply(iplayer, Key.CommandUserAction, new Dictionary<string, string>
- {
- { "action", Text.GetPlain(Key.Uncrippled, iplayer) },
- { "playername", Text.Sanitize(User.Name(userid)) },
- { "playerid", userid.ToString() }
- });
- }
- return;
- }
- syntax_error:
- Chat.Reply(iplayer, Key.CommandUserSyntax, new Dictionary<string, string>
- {
- { "command", (iplayer.LastCommand == CommandType.Console) ? command : ("/" + command) }
- });
- }
- private static void Users(IPlayer iplayer, Key key, HashSet<ulong> userids)
- {
- var users = new StringBuilder();
- foreach(var userid in userids)
- {
- users.Append("\n ");
- users.Append(userid.ToString());
- users.Append(" - ");
- users.Append(User.Name(userid));
- users.Append(" (");
- users.Append(User.StatusText(userid, iplayer));
- users.Append(')');
- }
- Chat.Reply(iplayer, key, new Dictionary<string, string>
- {
- { "users", users.ToString() }
- });
- users.Clear();
- }
- private static void Vpn(IPlayer iplayer, string command, string[] args)
- {
- BasePlayer player = iplayer.IsServer ? null : iplayer.Object as BasePlayer;
- if((player != null) && !Permissions.Command.Vpn(player.userID))
- {
- Chat.Reply(iplayer, Key.CommandNoPermission);
- return;
- }
- else if(args.Length != 2)
- {
- goto syntax_error;
- }
- var address = args[1];
- if(!IP.IsValid(address))
- {
- goto syntax_error;
- }
- switch(args[0].ToLower())
- {
- case "bypass":
- VPN.Bypass(address);
- IP.Unblock(address);
- Vpn(iplayer, Key.Bypassed, address);
- return;
- case "status":
- Vpn(iplayer, Key.Current, address);
- return;
- case "unblock":
- VPN.Unblock(address);
- IP.Unblock(address);
- Vpn(iplayer, Key.Unblocked, address);
- return;
- }
- syntax_error:
- Chat.Reply(iplayer, Key.CommandVpnSyntax, new Dictionary<string, string>
- {
- { "command", (iplayer.LastCommand == CommandType.Console) ? command : ("/" + command) }
- });
- }
- private static void Vpn(IPlayer iplayer, Key key, string address)
- {
- var info = VPN.IsBlocked(address) ? Text.GetPlain(Key.Blocked, iplayer) :
- VPN.IsBypassed(address) ? Text.GetPlain(Key.Bypassed, iplayer) :
- Text.GetPlain(Key.Unblocked, iplayer);
- Chat.Reply(iplayer, Key.CommandVpn, new Dictionary<string, string>
- {
- { "action", Text.Get(key, iplayer) },
- { "info", $"{info}({address})" }
- });
- }
- }
- #endregion _command_
- #region _configuration_
- private static Configuration config;
- private class Configuration
- {
- public Admin.Settings Admin;
- public AntiCheat.Settings AntiCheat;
- public AntiFlood.Settings AntiFlood;
- public Ban.Settings Ban;
- public Cripple.Settings Cripple;
- public Discord.Settings Discord;
- public Entity.Settings Entity;
- public IP.Settings IP;
- public Log.Settings Log;
- public Steam.Settings Steam;
- public User.Settings User;
- public Version.Settings Version;
- public Violation.Settings Violation;
- public VPN.Settings VPN;
- private static bool corrupt = false;
- private static bool dirty = false;
- private static bool upgraded = false;
- public static void Clamp<T>(ref T value, T min, T max) where T : IComparable<T>
- {
- T clamped = Generic.Clamp(value, min, max);
- if(!value.Equals(clamped))
- {
- dirty = true; value = clamped;
- }
- }
- public static void Load()
- {
- dirty = false;
- try
- {
- config = _instance.Config.ReadObject<Configuration>();
- config.Version.Compare(0, 0, 0);
- }
- catch(NullReferenceException)
- {
- Guardian.Log.Warning("Configuration: Created new configuration with default settings.");
- dirty = true; config = new Configuration();
- }
- catch(JsonException e)
- {
- Guardian.Log.Error("Configuration: Using default settings. Delete the configuration file, or fix the following error, and reload; " + e.ToString());
- corrupt = true; config = new Configuration();
- }
- Validate();
- }
- public static void Save()
- {
- if(dirty && !corrupt)
- {
- dirty = false;
- _instance.Config.WriteObject(config);
- }
- }
- public static void SetDirty() => dirty = true;
- public static void SetUpgrade(bool upgrade = true) => upgraded = upgrade;
- public static void Unload()
- {
- Save();
- config = null;
- }
- public static bool Upgraded() => upgraded;
- public static void Validate<T>(ref T value, Func<T> initializer, Action validator = null)
- {
- if(value == null)
- {
- dirty = true; value = initializer();
- }
- else
- {
- validator?.Invoke();
- }
- }
- private static void Validate()
- {
- Validate(ref config.Admin, () => new Admin.Settings());
- Validate(ref config.AntiCheat, () => new AntiCheat.Settings(), () => config.AntiCheat.Validate());
- Validate(ref config.AntiFlood, () => new AntiFlood.Settings(), () => config.AntiFlood.Validate());
- Validate(ref config.Ban, () => new Ban.Settings(), () => config.Ban.Validate());
- Validate(ref config.Cripple, () => new Cripple.Settings());
- Validate(ref config.Discord, () => new Discord.Settings(), () => config.Discord.Validate());
- Validate(ref config.Entity, () => new Entity.Settings(), () => config.Entity.Validate());
- Validate(ref config.IP, () => new IP.Settings(), () => config.IP.Validate());
- Validate(ref config.Log, () => new Log.Settings(), () => config.Log.Validate());
- Validate(ref config.Steam, () => new Steam.Settings(), () => config.Steam.Validate());
- Validate(ref config.User, () => new User.Settings(), () => config.User.Validate());
- Validate(ref config.Version, () => new Version.Settings());
- Validate(ref config.Violation, () => new Violation.Settings());
- Validate(ref config.VPN, () => new VPN.Settings(), () => config.VPN.Validate());
- config.Version.Validate();
- }
- }
- #endregion _conifguration_
- #region _counter_
- private class Counter
- {
- private uint count;
- private uint delay;
- public Counter()
- {
- count = delay = 0;
- }
- public void Decrement()
- {
- if((delay >> 31) == 1)
- {
- --count;
- }
- delay <<= 1;
- }
- public void Increment()
- {
- if((delay >> 31) == 0)
- {
- ++count;
- }
- delay = (delay << 1) + 1;
- }
- public uint Percent()
- {
- return (100u * count) >> 5;
- }
- public float Ratio(bool inverse = false)
- {
- return inverse ? (1.0f - (count * 0.03125f)) : (count * 0.03125f);
- }
- public float Ratio(float scale, bool inverse = false)
- {
- return (inverse ? (1.0f - (count * 0.03125f)) : (count * 0.03125f)) * scale;
- }
- public ulong Total()
- {
- return count;
- }
- }
- #endregion _counter_
- #region _cripple_
- public class Cripple
- {
- public class Settings
- {
- public bool Heal;
- public bool Inherit;
- public Settings()
- {
- Heal = false;
- Inherit = true;
- }
- }
- }
- #endregion _cripple_
- #region _data_
- private class Data
- {
- private static DataFileSystem data = null;
- private static string path = null;
- public static void Close()
- {
- data = null;
- path = null;
- }
- public static bool Exists(string name)
- {
- return data.ExistsDatafile(name);
- }
- public static void Open()
- {
- path = $"{Interface.Oxide.DataDirectory}\\{_instance.Name}";
- data = new DataFileSystem(path);
- }
- public static T ReadObject<T>(string name)
- {
- if(!string.IsNullOrEmpty(name) && data.ExistsDatafile(name))
- {
- try
- {
- return data.ReadObject<T>(name);
- }
- catch(JsonException)
- {
- Log.Warning($"Data: recreating corrupted file \'{path}\\{name}.json\'.");
- }
- }
- return default(T);
- }
- public static void WriteObject<T>(string name, T value)
- {
- data.WriteObject(name, value);
- }
- }
- #endregion _data_
- #region _data_file_
- private class DataFile<TKey, TValue>
- {
- private readonly Dictionary<TKey, TValue> data = new Dictionary<TKey, TValue>();
- private bool dirty;
- private string name;
- public DataFile(string name = null)
- {
- dirty = false; this.name = name;
- }
- public TValue this[TKey key]
- {
- get
- {
- return data[key];
- }
- set
- {
- data[key] = value;
- SetDirty();
- }
- }
- public void Add(TKey key, TValue value)
- {
- if(!data.ContainsKey(key))
- {
- this[key] = value;
- }
- }
- public bool Contains(TKey key)
- {
- return data.ContainsKey(key);
- }
- public void Clear()
- {
- data.Clear();
- SetDirty();
- }
- public bool Exists()
- {
- if(!string.IsNullOrEmpty(name))
- {
- return Data.Exists(name);
- }
- return false;
- }
- public void ForEach(Action<TKey, TValue> action)
- {
- foreach(var entry in data)
- {
- action(entry.Key, entry.Value);
- }
- }
- public TValue Get(TKey key, TValue default_value)
- {
- TValue value;
- if(data.TryGetValue(key, out value))
- {
- return value;
- }
- return default_value;
- }
- public bool IsDirty() => dirty;
- public bool IsEmpty() => data.IsEmpty();
- public void Load()
- {
- if(string.IsNullOrEmpty(name))
- {
- return;
- }
- try
- {
- SetDirty(false);
- var file = Data.ReadObject<Dictionary<TKey, TValue>>(name);
- foreach(var entry in file)
- {
- data[entry.Key] = entry.Value;
- }
- }
- catch
- {
- SetDirty();
- }
- Save();
- }
- public void Load(string name)
- {
- SetName(name);
- Load();
- }
- public void Remove(TKey key)
- {
- if(data.ContainsKey(key))
- {
- data.Remove(key);
- SetDirty();
- }
- }
- public void Save()
- {
- if(IsDirty())
- {
- if(!string.IsNullOrEmpty(name))
- {
- Data.WriteObject(name, data);
- }
- SetDirty(false);
- }
- }
- public void Save(string name)
- {
- SetName(name);
- Save();
- }
- public void SetDirty(bool value = true) => dirty = value;
- public void SetName(string name)
- {
- this.name = name;
- SetDirty();
- }
- public void Unload()
- {
- Save();
- data.Clear();
- }
- }
- #endregion _data_file_
- #region _discord_
- private class Discord
- {
- public class Settings
- {
- public bool Enabled;
- public string WebHook;
- public DiscordFilters Filters;
- public Settings()
- {
- Enabled = false;
- WebHook = string.Empty;
- Filters = new DiscordFilters();
- }
- public class DiscordFilter
- {
- public bool Enabled;
- public string WebHook;
- public DiscordFilter()
- {
- Enabled = true;
- WebHook = string.Empty;
- }
- public string URL() => Enabled ? WebHook : null;
- public void Validate()
- {
- Configuration.Validate(ref WebHook, () => { return string.Empty; });
- }
- }
- public class DiscordFilters
- {
- public DiscordFilter AntiCheat;
- public DiscordFilter AntiFlood;
- public DiscordFilter IP;
- public DiscordFilter Steam;
- public DiscordFilter VPN;
- public DiscordFilters()
- {
- AntiCheat = new DiscordFilter();
- AntiFlood = new DiscordFilter();
- IP = new DiscordFilter();
- Steam = new DiscordFilter();
- VPN = new DiscordFilter();
- }
- public void Validate()
- {
- Configuration.Validate(ref AntiCheat, () => new DiscordFilter(), () => AntiCheat.Validate());
- Configuration.Validate(ref AntiFlood, () => new DiscordFilter(), () => AntiFlood.Validate());
- Configuration.Validate(ref IP, () => new DiscordFilter(), () => IP.Validate());
- Configuration.Validate(ref Steam, () => new DiscordFilter(), () => Steam.Validate());
- Configuration.Validate(ref VPN, () => new DiscordFilter(), () => VPN.Validate());
- }
- }
- public void Validate()
- {
- Configuration.Validate(ref WebHook, () => { return string.Empty; });
- Configuration.Validate(ref Filters, () => new DiscordFilters(), () => Filters.Validate());
- }
- }
- public static void Send(string category, Dictionary<string, object> message)
- {
- if(!config.Discord.Enabled || (message == null))
- {
- return;
- }
- if((message.Count == 0) || !(message.ContainsKey("content") || message.ContainsKey("embeds")))
- {
- Log.Console(Key.LogDiscordMessage);
- return;
- }
- var url = Select(category);
- if(string.IsNullOrWhiteSpace(url))
- {
- if(url != null)
- {
- Log.Console(Key.LogDiscordConfig);
- }
- return;
- }
- WebHook.Send(url, "Discord", JsonConvert.SerializeObject(message));
- }
- private static string Select(string category)
- {
- string url;
- switch(Violation.Category(category))
- {
- case Key.AntiCheat: url = config.Discord.Filters.AntiCheat.URL(); break;
- case Key.AntiFlood: url = config.Discord.Filters.AntiFlood.URL(); break;
- case Key.IP: url = config.Discord.Filters.IP.URL(); break;
- case Key.Steam: url = config.Discord.Filters.Steam.URL(); break;
- case Key.VPN: url = config.Discord.Filters.VPN.URL(); break;
- default: url = null; break;
- }
- if((url != null) && string.IsNullOrWhiteSpace(url))
- {
- url = config.Discord.WebHook;
- }
- return url;
- }
- public static void Subscribe()
- {
- if(config.Discord.Enabled)
- {
- Hooks.Subscribe(nameof(OnGuardianViolation));
- }
- else
- {
- Hooks.Unsubscribe(nameof(OnGuardianViolation));
- }
- }
- }
- #endregion _discord_
- #region _entity_
- private class Entity
- {
- public class Settings
- {
- public EntityDamage Damage;
- public Settings()
- {
- Damage = new EntityDamage();
- }
- public class EntityDamage
- {
- public float Animal;
- public float Bradley;
- public float Building;
- public float Entity;
- public float Friend;
- public float Helicopter;
- public float NPC;
- public float Player;
- public float Team;
- public float Trap;
- public EntityDamage()
- {
- Animal = 1.0f;
- Bradley = 1.0f;
- Building = 1.0f;
- Entity = 1.0f;
- Friend = 1.0f;
- Helicopter = 1.0f;
- NPC = 1.0f;
- Player = 1.0f;
- Team = 1.0f;
- Trap = 1.0f;
- }
- public void Validate()
- {
- Configuration.Clamp(ref Animal, 0.0f, 100.0f);
- Configuration.Clamp(ref Bradley, 0.0f, 100.0f);
- Configuration.Clamp(ref Building, 0.0f, 100.0f);
- Configuration.Clamp(ref Entity, 0.0f, 100.0f);
- Configuration.Clamp(ref Friend, 0.0f, 100.0f);
- Configuration.Clamp(ref Helicopter, 0.0f, 100.0f);
- Configuration.Clamp(ref NPC, 0.0f, 100.0f);
- Configuration.Clamp(ref Player, 0.0f, 100.0f);
- Configuration.Clamp(ref Team, 0.0f, 100.0f);
- Configuration.Clamp(ref Trap, 0.0f, 100.0f);
- }
- }
- public void Validate()
- {
- Configuration.Validate(ref Damage, () => new EntityDamage(), () => Damage.Validate());
- }
- }
- public enum Type
- {
- Animal,
- AutoTurret,
- Bear,
- Boar,
- Bot,
- Bradley,
- Building,
- Chicken,
- Entity,
- FlameTurret,
- GunTrap,
- Helicopter,
- Murderer,
- NPC,
- Player,
- SAMSite,
- Scientist,
- Sentry,
- Stag,
- TC,
- NULL
- }
- public class Damage
- {
- private static readonly DamageTypeList cleared = new DamageTypeList();
- public static void Cancel(HitInfo info)
- {
- info.damageTypes = cleared;
- info.HitMaterial = 0;
- info.PointStart = Vector3.zero;
- info.HitEntity = null;
- }
- public static object Scale(HitInfo info, BasePlayer attacker, BaseCombatEntity victim)
- {
- switch(Entity.GetType(info.HitEntity))
- {
- case Type.Animal: return Scale(info, config.Entity.Damage.Animal);
- case Type.AutoTurret: return Scale(info, config.Entity.Damage.Trap);
- case Type.Bear: return Scale(info, config.Entity.Damage.Animal);
- case Type.Boar: return Scale(info, config.Entity.Damage.Animal);
- case Type.Bot: return Scale(info, config.Entity.Damage.NPC);
- case Type.Bradley: return Scale(info, config.Entity.Damage.Bradley);
- case Type.Building: return Scale(info, config.Entity.Damage.Building);
- case Type.Chicken: return Scale(info, config.Entity.Damage.Animal);
- case Type.Entity: return Scale(info, config.Entity.Damage.Entity);
- case Type.FlameTurret: return Scale(info, config.Entity.Damage.Trap);
- case Type.GunTrap: return Scale(info, config.Entity.Damage.Trap);
- case Type.Helicopter: return Scale(info, config.Entity.Damage.Helicopter);
- case Type.Murderer: return Scale(info, config.Entity.Damage.NPC);
- case Type.NPC: return Scale(info, config.Entity.Damage.NPC);
- case Type.Player: return Scale(info, attacker, victim.ToPlayer());
- case Type.SAMSite: return Scale(info, config.Entity.Damage.Trap);
- case Type.Scientist: return Scale(info, config.Entity.Damage.NPC);
- case Type.Sentry: return Scale(info, config.Entity.Damage.Trap);
- case Type.Stag: return Scale(info, config.Entity.Damage.Animal);
- case Type.TC: return Scale(info, config.Entity.Damage.Building);
- }
- return null;
- }
- private static object Scale(HitInfo info, BasePlayer attacker, BasePlayer victim)
- {
- if(victim == attacker)
- {
- return null;
- }
- var scale = 1.0f;
- if((config.Entity.Damage.Friend != 1.0f) && User.IsFriend(attacker, victim))
- {
- scale = Math.Min(scale, config.Entity.Damage.Friend);
- }
- if((config.Entity.Damage.Team != 1.0f) && User.IsTeamMate(attacker, victim))
- {
- scale = Math.Min(scale, config.Entity.Damage.Team);
- }
- return Scale(info, scale * config.Entity.Damage.Player);
- }
- private static object Scale(HitInfo info, float scale)
- {
- if(scale == 1.0f)
- {
- return null;
- }
- if(scale == 0.0f)
- {
- Cancel(info);
- }
- else
- {
- info.damageTypes.ScaleAll(scale);
- }
- return true;
- }
- }
- public static BasePlayer GetAttacker(BaseCombatEntity entity, HitInfo info)
- {
- var attacker = info?.Initiator ?? entity?.lastAttacker;
- if(attacker is FireBall)
- {
- return Fire.Initiator(attacker as FireBall);
- }
- return attacker as BasePlayer;
- }
- public static string GetName(BaseEntity entity)
- {
- return GetName(entity, GetType(entity));
- }
- private static string GetName(BaseEntity entity, Type type)
- {
- switch(type)
- {
- case Type.Animal: return $"{Text.GetPlain(Key.EntityAnimal)}({entity.GetType().Name})";
- case Type.AutoTurret: return $"{Text.GetPlain(Key.EntityTrap)}({Text.GetPlain(Key.EntityAutoTurret)})";
- case Type.Bear: return $"{Text.GetPlain(Key.EntityAnimal)}({Text.GetPlain(Key.EntityBear)})";
- case Type.Boar: return $"{Text.GetPlain(Key.EntityAnimal)}({Text.GetPlain(Key.EntityBoar)})";
- case Type.Bot: return $"{Text.GetPlain(Key.EntityNPC)}({Text.Sanitize((entity as BasePlayer).displayName)})";
- case Type.Bradley: return $"{Text.GetPlain(Key.EntityNPC)}({Text.GetPlain(Key.EntityBradley)})";
- case Type.Building: return $"{Text.GetPlain(Key.EntityBuilding)}({GetPrefabName(entity)})";
- case Type.Chicken: return $"{Text.GetPlain(Key.EntityAnimal)}({Text.GetPlain(Key.EntityChicken)})";
- case Type.Entity: return $"{Text.GetPlain(Key.Entity)}({GetPrefabName(entity)})";
- case Type.FlameTurret: return $"{Text.GetPlain(Key.EntityTrap)}({Text.GetPlain(Key.EntityFlameTurret)})";
- case Type.GunTrap: return $"{Text.GetPlain(Key.EntityTrap)}({Text.GetPlain(Key.EntityGunTrap)})";
- case Type.Helicopter: return $"{Text.GetPlain(Key.EntityNPC)}({Text.GetPlain(Key.EntityHelicopter)})";
- case Type.Murderer: return $"{Text.GetPlain(Key.EntityNPC)}({Text.GetPlain(Key.EntityMurderer)})";
- case Type.NPC: return $"{Text.GetPlain(Key.EntityNPC)}({entity.GetType().Name})";
- case Type.Player: return $"{Text.GetPlain(Key.EntityPlayer)}({Text.Sanitize((entity as BasePlayer).displayName)})";
- case Type.SAMSite: return $"{Text.GetPlain(Key.EntityTrap)}({Text.GetPlain(Key.EntitySAMSite)})";
- case Type.Scientist: return $"{Text.GetPlain(Key.EntityNPC)}({Text.GetPlain(Key.EntityScientist)})";
- case Type.Sentry: return $"{Text.GetPlain(Key.EntityNPC)}({Text.GetPlain(Key.EntityAutoTurret)})";
- case Type.Stag: return $"{Text.GetPlain(Key.EntityAnimal)}({Text.GetPlain(Key.EntityStag)})";
- case Type.TC: return $"{Text.GetPlain(Key.EntityBuilding)}({Text.GetPlain(Key.EntityTC)})";
- }
- return $"{Text.GetPlain(Key.Entity)}({Text.GetPlain(Key.NULL)})";
- }
- private static string GetPrefabName(BaseEntity entity)
- {
- if(string.IsNullOrEmpty(entity.ShortPrefabName))
- {
- return entity.GetType().Name;
- }
- return entity.ShortPrefabName.Split('.')[0];
- }
- public static Type GetType(BaseEntity entity, out string name)
- {
- var type = GetType(entity);
- name = GetName(entity, type);
- return type;
- }
- public static Type GetType(BaseEntity entity)
- {
- if(entity == null)
- {
- return Type.NULL;
- }
- switch(entity.GetType().Name)
- {
- case "AutoTurret": return Type.AutoTurret;
- case "BaseHelicopter": return Type.Helicopter;
- case "BasePlayer": return (entity as BasePlayer).userID.IsSteamId() ? Type.Player : Type.Bot;
- case "Bear": return Type.Bear;
- case "Boar": return Type.Boar;
- case "BradleyAPC": return Type.Bradley;
- case "Chicken": return Type.Chicken;
- case "FlameTurret": return Type.FlameTurret;
- case "GunTrap": return Type.GunTrap;
- case "HTNPlayer": return Type.Scientist;
- case "NPCAutoTurret": return Type.Sentry;
- case "NPCMurderer": return Type.Murderer;
- case "SamSite": return Type.SAMSite;
- case "Scientist": return Type.Scientist;
- case "ScientistNPC": return Type.Scientist;
- case "Stag": return Type.Stag;
- }
- if(entity is BasePlayer)
- {
- return Type.NPC;
- }
- else if(entity is BaseAnimalNPC)
- {
- return Type.Animal;
- }
- else if(entity is BuildingBlock)
- {
- return Type.Building;
- }
- else if(entity is BuildingPrivlidge)
- {
- return Type.TC;
- }
- return Type.Entity;
- }
- public class Health
- {
- private static readonly Dictionary<int, float> health = new Dictionary<int, float>();
- public static bool Changed(BaseEntity entity)
- {
- if(entity != null)
- {
- var instanceid = entity.GetInstanceID();
- float current = entity.Health(), previous;
- if(!health.TryGetValue(instanceid, out previous))
- {
- previous = float.MaxValue;
- }
- health[instanceid] = current;
- return previous != current;
- }
- return false;
- }
- public static void Clear() => health.Clear();
- public static void Clear(BaseEntity entity)
- {
- if(entity != null)
- {
- health.Remove(entity.GetInstanceID());
- }
- }
- }
- public static void Unload() => Health.Clear();
- }
- #endregion _entity_
- #region _fire_
- private class Fire
- {
- private static Dictionary<int, BasePlayer> fires = new Dictionary<int, BasePlayer>();
- public static void Ignite(FireBall fire, BasePlayer initiator)
- {
- if((fire != null) && (initiator?.userID.IsSteamId() ?? false))
- {
- fires[fire.GetInstanceID()] = initiator;
- }
- }
- public static BasePlayer Initiator(FireBall fire)
- {
- if(fire != null)
- {
- BasePlayer initiator;
- if(fires.TryGetValue(fire.GetInstanceID(), out initiator))
- {
- return initiator;
- }
- }
- return null;
- }
- public static void Spread(FireBall fire, FireBall spread)
- {
- if((fire != null) || (spread != null))
- {
- BasePlayer initiator;
- if(fires.TryGetValue(fire.GetInstanceID(), out initiator))
- {
- fires.Add(spread.GetInstanceID(), initiator);
- }
- }
- }
- public static void Quench(FireBall fire)
- {
- if(fire != null)
- {
- fires.Remove(fire.GetInstanceID());
- }
- }
- public static void Unload() => fires.Clear();
- }
- #endregion _fire_
- #region _generic_
- private class Generic
- {
- public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
- {
- if(value.CompareTo(min) < 0)
- {
- return min;
- }
- else if(value.CompareTo(max) > 0)
- {
- return max;
- }
- return value;
- }
- }
- #endregion _generic_
- #region _hooks_
- private new class Hooks
- {
- private static HashSet<string> subscribed;
- private static HashSet<string> unsubscribed;
- public class Base
- {
- public static void Subscribe()
- {
- Hooks.Subscribe(nameof(OnUserBanned));
- Hooks.Subscribe(nameof(OnUserUnbanned));
- }
- }
- public class Core
- {
- public static void Subscribe()
- {
- Hooks.Subscribe(nameof(CanBypassQueue));
- Hooks.Subscribe(nameof(CanLootEntity));
- Hooks.Subscribe(nameof(CanLootPlayer));
- Hooks.Subscribe(nameof(CanUserLogin));
- Hooks.Subscribe(nameof(OnEntityDeath));
- Hooks.Subscribe(nameof(OnEntityKill));
- Hooks.Subscribe(nameof(OnEntityTakeDamage));
- Hooks.Subscribe(nameof(OnFireBallDamage));
- Hooks.Subscribe(nameof(OnFireBallSpread));
- Hooks.Subscribe(nameof(OnFlameExplosion));
- Hooks.Subscribe(nameof(OnFlameThrowerBurn));
- Hooks.Subscribe(nameof(OnGroupPermissionGranted));
- Hooks.Subscribe(nameof(OnGroupPermissionRevoked));
- Hooks.Subscribe(nameof(OnLootEntity));
- Hooks.Subscribe(nameof(OnLootPlayer));
- Hooks.Subscribe(nameof(OnPlayerAttack));
- Hooks.Subscribe(nameof(OnPlayerConnected));
- Hooks.Subscribe(nameof(OnPlayerDisconnected));
- Hooks.Subscribe(nameof(OnPlayerViolation));
- Hooks.Subscribe(nameof(OnRocketLaunched));
- Hooks.Subscribe(nameof(OnUserPermissionGranted));
- Hooks.Subscribe(nameof(OnUserPermissionRevoked));
- Hooks.Subscribe(nameof(OnWeaponFired));
- }
- }
- public class Dynamic
- {
- public static void Subscribe()
- {
- AntiCheat.Stash.Subscribe();
- AntiFlood.Chat.Subscribe();
- AntiFlood.Command.Subscribe();
- AntiFlood.ItemDrop.Subscribe();
- Discord.Subscribe();
- }
- }
- public static void Load()
- {
- subscribed = new HashSet<string>
- {
- nameof(CanBypassQueue),
- nameof(CanCraft),
- nameof(CanLootEntity),
- nameof(CanLootPlayer),
- nameof(CanSeeStash),
- nameof(CanUserLogin),
- nameof(OnEntityDeath),
- nameof(OnEntityKill),
- nameof(OnEntityTakeDamage),
- nameof(OnFireBallDamage),
- nameof(OnFireBallSpread),
- nameof(OnFlameExplosion),
- nameof(OnFlameThrowerBurn),
- nameof(OnGuardianViolation),
- nameof(OnGroupPermissionGranted),
- nameof(OnGroupPermissionRevoked),
- nameof(OnItemDropped),
- nameof(OnLootEntity),
- nameof(OnLootPlayer),
- nameof(OnPlayerAttack),
- nameof(OnPlayerChat),
- nameof(OnPlayerConnected),
- nameof(OnPlayerDisconnected),
- nameof(OnPlayerViolation),
- nameof(OnRocketLaunched),
- nameof(OnServerCommand),
- nameof(OnUserBanned),
- nameof(OnUserPermissionGranted),
- nameof(OnUserPermissionRevoked),
- nameof(OnUserUnbanned),
- nameof(OnWeaponFired)
- };
- unsubscribed = new HashSet<string>();
- Unsubscribe();
- }
- public static void Subscribe(string hook)
- {
- if(unsubscribed.Contains(hook))
- {
- unsubscribed.Remove(hook);
- _instance.Subscribe(hook);
- subscribed.Add(hook);
- }
- }
- public static void Unload()
- {
- Unsubscribe();
- subscribed = null;
- unsubscribed.Clear();
- unsubscribed = null;
- }
- private static void Unsubscribe()
- {
- foreach(var hook in subscribed)
- {
- _instance.Unsubscribe(hook);
- unsubscribed.Add(hook);
- }
- subscribed.Clear();
- }
- public static void Unsubscribe(string hook)
- {
- if(subscribed.Contains(hook))
- {
- subscribed.Remove(hook);
- _instance.Unsubscribe(hook);
- unsubscribed.Add(hook);
- }
- }
- }
- #region _hooks_lifecycle_
- private void Init()
- {
- _instance = this;
- Data.Open();
- Configuration.Load();
- Hooks.Load();
- Permissions.Load();
- Text.Load();
- IP.Load();
- User.Load();
- Projectile.Load();
- Steam.Load();
- Violation.Load();
- VPN.Load();
- Weapon.Load();
- AntiCheat.Configure();
- AntiFlood.Configure();
- Command.Load();
- Configuration.Save();
- }
- private void Loaded()
- {
- Hooks.Base.Subscribe();
- }
- protected override void LoadDefaultConfig() { }
- protected override void LoadDefaultMessages() { }
- private void OnServerInitialized()
- {
- Hooks.Core.Subscribe();
- Hooks.Dynamic.Subscribe();
- Map.Load();
- User.Update();
- }
- private void OnServerSave()
- {
- Configuration.Save();
- IP.Save();
- User.Save();
- VPN.Save();
- }
- private void Unload()
- {
- Hooks.Unload();
- Timers.Destroy();
- Map.Unload();
- Fire.Unload();
- Entity.Unload();
- Command.Unload();
- AntiFlood.Unload();
- AntiCheat.Unload();
- Weapon.Unload();
- VPN.Unload();
- Violation.Unload();
- Steam.Unload();
- Projectile.Unload();
- User.Unload();
- IP.Unload();
- Text.Unload();
- Permissions.Unload();
- Configuration.Unload();
- Data.Close();
- _instance = null;
- }
- #endregion _hooks_lifecycle_
- #region _hooks_other_
- private object CanBypassQueue(Connection connection)
- {
- if(Permissions.Ignore(connection.userid, true))
- {
- return true;
- }
- else if(config.Admin.Bypass && Permissions.Admin(connection.userid, true))
- {
- return true;
- }
- return null;
- }
- private object CanCraft(ItemCrafter crafter, ItemBlueprint bp, int amount)
- {
- if(!config.AntiFlood.ItemDrop.Enabled)
- {
- return null;
- }
- var player = crafter.gameObject.GetComponent<BasePlayer>();
- if(User.ShouldIgnore(player))
- {
- return null;
- }
- var cooldown = AntiFlood.ItemDrop.CoolDown(player.userID, bp.targetItem.itemid);
- if(cooldown == 0)
- {
- return null;
- }
- Chat.Send(player, Key.Cooldown, new Dictionary<string, string>
- {
- { "cooldown", cooldown.ToString() },
- { "type", Text.Get(Key.Crafting) }
- });
- AntiFlood.ItemDrop.Violation(player, bp.targetItem.displayName.english);
- return false;
- }
- private object CanLootEntity(BasePlayer looter, DroppedItemContainer target) => User.CanLoot(looter, target.playerSteamID);
- private object CanLootEntity(BasePlayer looter, LootableCorpse target) => User.CanLoot(looter, target.playerSteamID);
- private object CanLootPlayer(BasePlayer target, BasePlayer looter) => User.CanLoot(looter, target.userID);
- private object CanSeeStash(BasePlayer player, StashContainer stash)
- {
- if(!config.AntiCheat.Stash.Enabled)
- {
- return null;
- }
- if(User.ShouldIgnore(player))
- {
- return null;
- }
- AntiCheat.Stash.Trigger(player, stash);
- return null;
- }
- private object CanUserLogin(string name, string id, string address)
- {
- return User.CanConnect(name, id, address);
- }
- private void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
- {
- Entity.Health.Clear(entity);
- var victim = entity.ToPlayer();
- if(User.ShouldIgnore(victim))
- {
- return;
- }
- var attacker = Entity.GetAttacker(entity, info);
- if(!User.ShouldIgnore(attacker))
- {
- User.AssignAttacker(attacker, victim);
- }
- User.AssignVictim(victim);
- }
- void OnEntityKill(BaseNetworkable entity)
- {
- if(entity is FireBall)
- {
- Fire.Quench(entity as FireBall);
- }
- }
- private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
- {
- var attacker = Entity.GetAttacker(entity, info);
- if(User.ShouldIgnore(attacker))
- {
- return null;
- }
- if(info.IsProjectile())
- {
- AntiCheat.Aim.Trigger(entity, info);
- AntiCheat.FireRate.Trigger(entity, info);
- AntiCheat.Trajectory.Trigger(entity, info);
- }
- if((attacker.net?.connection?.authLevel ?? 0) > 0)
- {
- return null;
- }
- var victim = entity.ToPlayer();
- if(!User.ShouldIgnore(victim))
- {
- User.AssignAttacker(attacker, victim);
- if(User.IsCrippled(attacker.userID) && (victim != attacker))
- {
- if(victim.IsWounded())
- {
- victim.StopWounded();
- }
- Entity.Damage.Cancel(info);
- if(config.Cripple.Heal)
- {
- _instance.NextFrame(() =>
- {
- victim.Heal(100.0f);
- });
- }
- return true;
- }
- }
- return Entity.Damage.Scale(info, attacker, entity);
- }
- private void OnFireBallDamage(FireBall fireball, BaseCombatEntity target, HitInfo info)
- {
- info.Initiator = fireball;
- }
- private void OnFireBallSpread(FireBall fireball, BaseEntity entity)
- {
- Fire.Spread(fireball, entity as FireBall);
- }
- private void OnFlameExplosion(FlameExplosive explosive, BaseEntity entity)
- {
- if(explosive.creatorEntity is BasePlayer)
- {
- var fire = entity as FireBall;
- var initiator = explosive.creatorEntity as BasePlayer;
- if(initiator.userID.IsSteamId() && (fire != null))
- {
- Fire.Ignite(fire, initiator);
- }
- }
- }
- private void OnFlameThrowerBurn(FlameThrower flamethrower, BaseEntity entity)
- {
- var initiator = flamethrower.GetOwnerPlayer();
- if(initiator != null)
- {
- var fire = entity as FireBall;
- if(initiator.userID.IsSteamId() && (fire != null))
- {
- Fire.Ignite(fire, initiator);
- }
- }
- }
- private void OnGroupPermissionGranted(string name, string perm)
- {
- if(Permissions.HasPrefix(perm))
- {
- foreach(var id in permission.GetUsersInGroup(name))
- {
- var player = BasePlayer.FindAwakeOrSleeping(id);
- if(player != null)
- {
- Permissions.Update(player.userID);
- }
- }
- }
- }
- private void OnGroupPermissionRevoked(string name, string perm) => OnGroupPermissionGranted(name, perm);
- private void OnGuardianViolation(string playerid, Dictionary<string, string> details)
- {
- Discord.Send(details["category"], new Dictionary<string, object>
- {
- {
- "embeds", new List<object>
- {
- new Dictionary<string, object>
- {
- { "color", int.Parse(details["color"]) },
- { "description",
- $"{details["actionicon"]} {details["action"]} [{details["playername"]}]" +
- $"(https://steamcommunity.com/profiles/{playerid}) {playerid}\n" +
- $"{details["categoryicon"]} {details["category"]} - {details["type"]}: {details["details"]}"
- }
- }
- }
- }
- });
- }
- private void OnItemDropped(Item item, BaseEntity entity)
- {
- if(!config.AntiFlood.ItemDrop.Enabled)
- {
- return;
- }
- var player = item?.GetOwnerPlayer();
- if(User.ShouldIgnore(player))
- {
- return;
- }
- if(AntiFlood.ItemDrop.Trigger(player.userID, item.info.itemid))
- {
- entity?.Kill();
- }
- }
- private void OnLootEntity(BasePlayer looter, BaseEntity target)
- {
- if(target is LootableCorpse)
- {
- User.OnLoot(looter, (target as LootableCorpse).playerSteamID);
- }
- else if(target is DroppedItemContainer)
- {
- User.OnLoot(looter, (target as DroppedItemContainer).playerSteamID);
- }
- }
- private void OnLootPlayer(BasePlayer looter, BasePlayer target) => User.OnLoot(looter, target.userID);
- private object OnPlayerAttack(BasePlayer player, HitInfo info)
- {
- if(User.ShouldIgnore(player) || (info.Weapon == null))
- {
- return null;
- }
- if(info.IsProjectile())
- {
- AntiCheat.WallHack.Trigger(player, info);
- }
- else
- {
- AntiCheat.MeleeRate.Trigger(player, info);
- }
- return null;
- }
- private object OnPlayerChat(BasePlayer player, string message, ConVar.Chat.ChatChannel channel)
- {
- if(!config.AntiFlood.Chat.Enabled)
- {
- return null;
- }
- if(User.ShouldIgnore(player))
- {
- return null;
- }
- if(!AntiFlood.Chat.Trigger(player.userID))
- {
- return null;
- }
- Chat.Send(player, Key.Cooldown, new Dictionary<string, string>
- {
- { "cooldown", AntiFlood.Chat.Cooldown(player.userID).ToString() },
- { "type", Text.Get(Key.Chat) }
- });
- AntiFlood.Chat.Violation(player, Enum.GetName(typeof(ConVar.Chat.ChatChannel), channel));
- return true;
- }
- private void OnPlayerConnected(BasePlayer player)
- {
- User.OnConnected(player);
- }
- private void OnPlayerDisconnected(BasePlayer player)
- {
- User.OnDisconnected(player);
- }
- private object OnPlayerViolation(BasePlayer player, AntiHackType ahtype, float amount)
- {
- if(User.ShouldIgnore(player) || User.IsInactive(player))
- {
- return true;
- }
- if(ahtype == AntiHackType.FlyHack)
- {
- if(config.AntiCheat.Gravity.Enabled)
- {
- NextFrame(() => AntiCheat.Gravity.Trigger(player, amount));
- return true;
- }
- }
- else
- {
- if(config.AntiCheat.Server.Enabled)
- {
- NextFrame(() => AntiCheat.Server.Trigger(player, ahtype, amount));
- return true;
- }
- }
- return null;
- }
- private void OnRocketLaunched(BasePlayer player, BaseEntity entity)
- {
- if(User.ShouldIgnore(player))
- {
- return;
- }
- var projectile = player.GetActiveItem().GetHeldEntity() as BaseProjectile;
- if(projectile == null)
- {
- return;
- }
- var time = UnityEngine.Time.realtimeSinceStartup;
- var ammo = projectile.primaryMagazine.ammoType;
- var item = projectile.GetItem();
- var itemmod = ammo.GetComponent<ItemModProjectile>();
- var projectiles = Pool.Get<ProtoBuf.ProjectileShoot>();
- projectiles.ammoType = ammo.itemid;
- projectiles.projectiles = Pool.Get<List<ProtoBuf.ProjectileShoot.Projectile>>();
- if((item?.info.shortname ?? string.Empty) != "pistol.eoka")
- {
- foreach(var fired in player.firedProjectiles)
- {
- if((fired.Value.weaponSource as BaseProjectile) != projectile)
- {
- continue;
- }
- if((time - fired.Value.firedTime) > 0.01)
- {
- continue;
- }
- var entry = Pool.Get<ProtoBuf.ProjectileShoot.Projectile>();
- entry.projectileID = fired.Key;
- entry.startPos = fired.Value.initialPosition;
- entry.startVel = fired.Value.initialVelocity;
- projectiles.projectiles.Add(entry);
- }
- }
- OnWeaponFired(projectile, player, itemmod, projectiles);
- projectiles.ResetToPool();
- }
- private object OnServerCommand(ConsoleSystem.Arg arg)
- {
- if(!config.AntiFlood.Command.Enabled)
- {
- return null;
- }
- var player = arg.Player();
- if(User.ShouldIgnore(player) || (arg.cmd.FullName == "craft.add"))
- {
- return null;
- }
- if(!AntiFlood.Command.Trigger(player.userID))
- {
- return null;
- }
- Chat.Send(player, Key.Cooldown, new Dictionary<string, string>
- {
- { "cooldown", AntiFlood.Command.Cooldown(player.userID).ToString() },
- { "type", Text.Get(Key.Command) }
- });
- AntiFlood.Command.Violation(player, arg.cmd.FullName);
- return true;
- }
- private void OnUserBanned(string playername, string playerid, string playerip, string reason)
- {
- ulong userid;
- if(ulong.TryParse(playerid, out userid))
- {
- if((userid != 0) && userid.IsSteamId())
- {
- NextTick(() => User.OnBanned(userid, true));
- }
- }
- }
- private void OnUserPermissionGranted(string id, string perm)
- {
- if(Permissions.HasPrefix(perm))
- {
- var player = BasePlayer.FindAwakeOrSleeping(id);
- if(player != null)
- {
- Permissions.Update(player.userID);
- }
- }
- }
- private void OnUserPermissionRevoked(string id, string perm) => OnUserPermissionGranted(id, perm);
- private void OnUserUnbanned(string playername, string playerid, string playerip)
- {
- ulong userid;
- if(ulong.TryParse(playerid, out userid))
- {
- if((userid != 0) && userid.IsSteamId())
- {
- NextTick(() => User.OnBanned(userid, false));
- }
- }
- }
- private void OnWeaponFired(BaseProjectile projectile, BasePlayer player, ItemModProjectile itemmod, ProtoBuf.ProjectileShoot projectiles)
- {
- if(User.ShouldIgnore(player))
- {
- return;
- }
- AntiCheat.Recoil.Trigger(player, Weapon.Get(projectile, player, projectiles));
- }
- #endregion _hooks_other_
- #endregion _hooks_
- #region _ip_
- private class IP
- {
- public class Settings
- {
- public IpFilter Filter;
- public IpViolation Violation;
- public Settings()
- {
- Filter = new IpFilter();
- Violation = new IpViolation();
- }
- public class IpFilter
- {
- public ulong Cooldown;
- public IpFilter()
- {
- Cooldown = 30ul;
- }
- public void Validate()
- {
- Configuration.Clamp(ref Cooldown, 0ul, 60ul);
- }
- }
- public class IpViolation
- {
- public bool Ban;
- public bool Enabled;
- public IpViolation()
- {
- Ban = false;
- Enabled = true;
- }
- }
- public void Validate()
- {
- Configuration.Validate(ref Filter, () => new IpFilter(), () => Filter.Validate());
- Configuration.Validate(ref Violation, () => new IpViolation());
- }
- }
- private static readonly DataFile<string, uint> allows = new DataFile<string, uint>("ip_allows");
- private static readonly DataFile<string, bool> blocks = new DataFile<string, bool>();
- private static readonly DataFile<string, uint> denies = new DataFile<string, uint>("ip_denies");
- private static readonly HashSet<ulong> empty = new HashSet<ulong>();
- private static readonly DataFile<string, DateTime> filter = new DataFile<string, DateTime>();
- private static TimeSpan filter_cooldown;
- private static readonly DataFile<string, HashSet<ulong>> users = new DataFile<string, HashSet<ulong>>("ip_users");
- private static readonly Violation violation = new Violation(Key.IP);
- public class NetworkInfo
- {
- public string Address { get; protected set; }
- public uint Bits { get; protected set; }
- public NetworkInfo(string address, uint bits)
- {
- Address = address;
- Bits = bits;
- }
- }
- private static uint Bits(string address)
- {
- uint bits = 0;
- var octets = address.Split('.');
- foreach(var octet in octets)
- {
- if(octet == "*")
- {
- return bits;
- }
- else
- {
- bits += 8;
- }
- }
- return bits;
- }
- public static void Block(NetworkInfo network)
- {
- foreach(var address in Match(network))
- {
- if(!IsAllowed(address))
- {
- Block(address);
- }
- }
- }
- public static void Block(string address, ulong userid = 0)
- {
- blocks[address] = true;
- if(userid != 0)
- {
- Violation(address, userid);
- }
- _instance.timer.In(5.0f, () =>
- {
- foreach(var entry in users[address])
- {
- if(!User.IsConnected(entry) || Permissions.Ignore(entry))
- {
- continue;
- }
- Violation(address, userid);
- }
- });
- }
- public static void Bypass(NetworkInfo network)
- {
- foreach(var address in Match(network))
- {
- blocks[address] = false;
- }
- }
- public static void Bypass(string address) => blocks[address] = false;
- private static uint Decimal(string address)
- {
- uint result = 0;
- if(!string.IsNullOrEmpty(address))
- {
- var octets = address.Split('.');
- if(octets.Length <= 4)
- {
- foreach(var octet in octets)
- {
- uint value;
- if(uint.TryParse(octet, out value) && (value <= 255))
- {
- result = (result << 8) + value;
- }
- else
- {
- return 0;
- }
- }
- if(octets.Length < 4)
- {
- result <<= ((4 - octets.Length) << 3);
- }
- }
- }
- return result;
- }
- public static bool Cooldown(string address)
- {
- if(config.IP.Filter.Cooldown == 0)
- {
- return false;
- }
- var current = DateTime.UtcNow;
- var elapsed = current.Subtract(filter.Get(address, DateTime.MinValue));
- if(elapsed >= filter_cooldown)
- {
- filter[address] = current;
- return false;
- }
- return true;
- }
- public static void Configure()
- {
- config.IP.Filter.Validate();
- if(config.IP.Filter.Cooldown > 0)
- {
- filter_cooldown = TimeSpan.FromSeconds(config.IP.Filter.Cooldown);
- }
- else
- {
- filter_cooldown = TimeSpan.MinValue;
- }
- }
- public static bool Filter(string address, ulong userid)
- {
- if(blocks.Contains(address))
- {
- if(blocks[address])
- {
- if(config.Log.IP.Filter)
- {
- Log.Console(Key.LogIpFilter, new Dictionary<string, string>
- {
- { "action", Text.Get(Key.Blocked) },
- { "address", address },
- });
- }
- Block(address, userid);
- return false;
- }
- }
- else if(IsDenied(address))
- {
- if(!IsAllowed(address))
- {
- if(config.Log.IP.Filter)
- {
- Log.Console(Key.LogIpFilter, new Dictionary<string, string>
- {
- { "action", Text.Get(Key.Denied) },
- { "address", address },
- });
- }
- Block(address, userid);
- return false;
- }
- }
- if(config.Log.IP.Filter)
- {
- Log.Console(Key.LogIpFilter, new Dictionary<string, string>
- {
- { "action", Text.Get(Key.Allowed) },
- { "address", address },
- });
- }
- return true;
- }
- public static HashSet<ulong> Find(string address)
- {
- if(users.Contains(address))
- {
- return users[address];
- }
- return empty;
- }
- private static List<string> Get(DataFile<string, uint> data, string address)
- {
- bool all = (address == null);
- uint ip = all ? 0 : Decimal(address);
- var results = new List<string>();
- data.ForEach((network, bits) =>
- {
- if(all || Match(ip, Decimal(network), bits))
- {
- results.Add($"{network}/{bits}");
- }
- });
- return results;
- }
- public static List<string> GetAllows(string address = null) => Get(allows, address);
- public static List<string> GetDenies(string address = null) => Get(denies, address);
- public static bool IsAllowed(string address) => IsMatched(allows, address);
- public static bool IsBlocked(string address) => blocks.Get(address, false);
- public static bool IsDenied(string address) => IsMatched(denies, address);
- private static bool IsMatched(DataFile<string, uint> data, string address)
- {
- var ip = Decimal(address);
- bool match = false;
- data.ForEach((network, bits) =>
- {
- if(!match)
- {
- match = Match(ip, Decimal(network), bits);
- }
- });
- return match;
- }
- public static bool IsValid(string address, bool full = true, bool wildcard = false)
- {
- if(string.IsNullOrEmpty(address))
- {
- return false;
- }
- var octets = address.Split('.');
- if(full ? (octets.Length != 4) : (octets.Length > 4))
- {
- return false;
- }
- bool found_wildcard = false;
- foreach(var octet in octets)
- {
- uint value;
- if(octet == "*")
- {
- if(wildcard)
- {
- found_wildcard = true;
- }
- else
- {
- return false;
- }
- }
- if(!uint.TryParse(octet, out value) || (value > 255) || found_wildcard)
- {
- return false;
- }
- }
- return true;
- }
- public static void Load()
- {
- violation.Configure(new Violation.Settings(true, 3600), 1, 1, 1);
- allows.Load();
- denies.Load();
- users.Load();
- Configure();
- }
- private static bool Match(uint ip, uint network, uint bits)
- {
- bool match = false;
- if(bits == 0)
- {
- match = true;
- }
- else
- {
- var mask = uint.MaxValue << (32 - (int)bits);
- if((ip & mask) == (network & mask))
- {
- match = true;
- }
- }
- return match;
- }
- private static List<string> Match(NetworkInfo network)
- {
- var addresses = new List<string>();
- uint network_address = Decimal(network.Address);
- blocks.ForEach((address, blocked) =>
- {
- if(Match(Decimal(address), network_address, network.Bits))
- {
- addresses.Add(address);
- }
- });
- return addresses;
- }
- public static NetworkInfo Network(string address)
- {
- if(string.IsNullOrEmpty(address))
- {
- return null;
- }
- uint bits = uint.MaxValue;
- var split = address.Split('/');
- if((split.Length == 1) && IsValid(split[0], false, true))
- {
- bits = Bits(split[0]);
- }
- else if((split.Length == 2) && IsValid(split[0], false))
- {
- if(!uint.TryParse(split[1], out bits))
- {
- bits = uint.MaxValue;
- }
- }
- if(bits <= 32)
- {
- var ip = Decimal(split[0].Replace('*', '0'));
- var mask = uint.MaxValue << (32 - (int)bits);
- return new NetworkInfo(ToString(ip & mask), bits);
- }
- return null;
- }
- public static string Parse(string address)
- {
- if(string.IsNullOrEmpty(address))
- {
- return null;
- }
- int length = address.IndexOf(':');
- if(length > 0)
- {
- return address.Substring(0, length);
- }
- if(IsValid(address))
- {
- return address;
- }
- return null;
- }
- public static void Save()
- {
- allows.Save();
- denies.Save();
- users.Save();
- }
- private static bool Set(DataFile<string, uint> data, NetworkInfo network, bool add)
- {
- var matches = Get(data, network.Address);
- if(add)
- {
- if(matches.Count == 0)
- {
- data[network.Address] = network.Bits;
- return true;
- }
- }
- else
- {
- if(matches.Count > 0)
- {
- foreach(var match in matches)
- {
- data.Remove(match.Split('/')[0]);
- }
- return true;
- }
- }
- return false;
- }
- public static bool SetAllow(NetworkInfo network, bool add) => Set(allows, network, add);
- public static bool SetDeny(NetworkInfo network, bool add) => Set(denies, network, add);
- public static string ToString(uint ip)
- {
- StringBuilder result = new StringBuilder(16);
- result.Append(((ip >> 24) & 255u).ToString()).Append('.');
- result.Append(((ip >> 16) & 255u).ToString()).Append('.');
- result.Append(((ip >> 8) & 255u).ToString()).Append('.');
- result.Append(( ip & 255u).ToString());
- return result.ToString();
- }
- public static void Unblock(NetworkInfo network)
- {
- foreach(var address in Match(network))
- {
- blocks.Remove(address);
- }
- }
- public static void Unblock(string address) => blocks.Remove(address);
- public static void Unload()
- {
- allows.Unload();
- blocks.Unload();
- denies.Unload();
- filter.Clear();
- users.Unload();
- violation.Clear();
- }
- public static void Update(string address, ulong userid)
- {
- if(!string.IsNullOrEmpty(address))
- {
- if(!users.Contains(address))
- {
- users.Add(address, new HashSet<ulong>());
- }
- if(users[address].Add(userid))
- {
- users.SetDirty();
- }
- }
- }
- private static void Violation(string address, ulong userid)
- {
- if(config.IP.Violation.Enabled)
- {
- ulong violations = config.IP.Violation.Ban ? ulong.MaxValue : 1ul;
- violation.Trigger(userid, Key.Blocked, address, violations, true);
- }
- else
- {
- if(User.IsConnected(userid))
- {
- User.Kick(userid, Text.GetPlain(Key.IpBlocked));
- }
- }
- }
- }
- #endregion _ip_
- #region _log_
- private new class Log
- {
- public class Settings
- {
- public LogAntiCheat AntiCheat;
- public LogAntiFlood AntiFlood;
- public LogIp IP;
- public LogProjectile Projectile;
- public LogUser User;
- public LogVpn VPN;
- public Settings()
- {
- AntiCheat = new LogAntiCheat();
- AntiFlood = new LogAntiFlood();
- IP = new LogIp();
- Projectile = new LogProjectile();
- User = new LogUser();
- VPN = new LogVpn();
- }
- public class LogAntiCheat
- {
- public bool Aim;
- public bool Gravity;
- public bool MeleeRate;
- public bool Recoil;
- public bool Server;
- public bool Stash;
- public bool Trajectory;
- }
- public class LogAntiFlood
- {
- public bool ItemDrop;
- }
- public class LogIp
- {
- public bool Filter;
- public LogIp()
- {
- Filter = true;
- }
- }
- public class LogProjectile
- {
- public bool Collapse;
- public bool Verbose;
- public LogProjectile()
- {
- Collapse = true;
- }
- }
- public class LogUser
- {
- public bool Bypass;
- public bool Connect;
- public LogUser()
- {
- Connect = true;
- }
- }
- public class LogVpn
- {
- public bool Check;
- public LogVpn()
- {
- Check = true;
- }
- }
- public void Validate()
- {
- Configuration.Validate(ref AntiCheat, () => new LogAntiCheat());
- Configuration.Validate(ref AntiFlood, () => new LogAntiFlood());
- Configuration.Validate(ref IP, () => new LogIp());
- Configuration.Validate(ref Projectile, () => new LogProjectile());
- Configuration.Validate(ref User, () => new LogUser());
- Configuration.Validate(ref VPN, () => new LogVpn());
- }
- }
- public static void Console(Key key, Dictionary<string, string> parameters = null)
- {
- _instance.Puts(Text.GetPlain(key, parameters));
- }
- public static void Error(string message)
- {
- _instance.LogError(message);
- }
- public static void Warning(string message)
- {
- _instance.LogWarning(message);
- }
- }
- #endregion _log_
Advertisement