namespace GeoSus.Server; using Microsoft.Extensions.Logging; // Anti-cheat validace pohybu public class AntiCheat { private readonly ServerConfig _config; private readonly ILogger _logger; private const int PositionHistorySize = 20; public AntiCheat(ServerConfig config, ILogger logger) { _config = config; _logger = logger; } // Validuje pohyb hráče a vrací případné cheat violation public CheatViolation? ValidateMovement(Player player, Position newPosition) { var now = DateTime.UtcNow; var timeSinceLastUpdate = (now - player.LastPositionUpdate).TotalSeconds; // Příliš krátký interval ignorujeme if (timeSinceLastUpdate < 0.1) return null; var distance = player.Position.DistanceTo(newPosition); // Teleport detekce if (distance > _config.TeleportThresholdMeters) { var violation = new CheatViolation { Type = CheatViolationType.Teleport, Description = $"Teleport detekován: {distance:F1}m za {timeSinceLastUpdate:F2}s", Severity = 10 }; ApplyViolation(player, violation); return violation; } // Speed check var speed = distance / timeSinceLastUpdate; var maxAllowedSpeed = _config.MaxSpeedMps * 1.5; // 50% tolerance if (speed > maxAllowedSpeed) { var violation = new CheatViolation { Type = CheatViolationType.SpeedHack, Description = $"Podezřelá rychlost: {speed:F1}m/s (max {_config.MaxSpeedMps}m/s)", Severity = 3 }; ApplyViolation(player, violation); return violation; } // Historie pro detekci pattern UpdatePositionHistory(player, newPosition, now); // Validace historie - průměrná rychlost za delší období var historyViolation = ValidatePositionHistory(player); if (historyViolation != null) { ApplyViolation(player, historyViolation); return historyViolation; } // Pokud je OK, pomalu snižujeme cheat score if (player.CheatScore > 0 && timeSinceLastUpdate > 1) { player.CheatScore = Math.Max(0, player.CheatScore - 1); UpdateCheatStatus(player); } return null; } private void ApplyViolation(Player player, CheatViolation violation) { player.CheatScore += violation.Severity; var previousStatus = player.CheatStatus; UpdateCheatStatus(player); if (player.CheatStatus != previousStatus) { _logger.LogWarning("Cheat status změněn: {Player} -> {Status} (score: {Score})", player.ClientUuid, player.CheatStatus, player.CheatScore); } _logger.LogWarning("Cheat violation: {Player} - {Type}: {Description}", player.ClientUuid, violation.Type, violation.Description); } private void UpdateCheatStatus(Player player) { player.CheatStatus = player.CheatScore switch { >= 50 => CheatStatus.Kicked, >= 25 => CheatStatus.Restrict, >= 10 => CheatStatus.Warn, _ => CheatStatus.Ok }; } private void UpdatePositionHistory(Player player, Position position, DateTime time) { player.PositionHistory.Enqueue((position, time)); while (player.PositionHistory.Count > PositionHistorySize) { player.PositionHistory.Dequeue(); } } private CheatViolation? ValidatePositionHistory(Player player) { if (player.PositionHistory.Count < 5) return null; var history = player.PositionHistory.ToArray(); var first = history[0]; var last = history[^1]; var totalTime = (last.Time - first.Time).TotalSeconds; if (totalTime < _config.MovementValidationWindowSec) return null; // Spočítáme celkovou ujetou vzdálenost double totalDistance = 0; for (int i = 1; i < history.Length; i++) { totalDistance += history[i - 1].Pos.DistanceTo(history[i].Pos); } var avgSpeed = totalDistance / totalTime; // Pokud průměrná rychlost za celé období překračuje limit if (avgSpeed > _config.MaxSpeedMps * 1.2) { return new CheatViolation { Type = CheatViolationType.SustainedSpeedHack, Description = $"Dlouhodobě vysoká rychlost: {avgSpeed:F1}m/s za {totalTime:F0}s", Severity = 5 }; } return null; } // Validace akce - zda hráč může provést danou akci public bool CanPerformAction(Player player, string actionType) { if (player.CheatStatus == CheatStatus.Kicked) return false; if (player.CheatStatus == CheatStatus.Restrict) { // Omezené akce pro podezřelé hráče var restrictedActions = new[] { "TaskComplete", "Kill", "EmergencyMeeting" }; if (restrictedActions.Contains(actionType)) { _logger.LogWarning("Akce {Action} blokována pro hráče {Player} (Restrict status)", actionType, player.ClientUuid); return false; } } return true; } } public class CheatViolation { public CheatViolationType Type { get; set; } public required string Description { get; set; } public int Severity { get; set; } } public enum CheatViolationType { SpeedHack, Teleport, SustainedSpeedHack, ActionSpam, InvalidAction }