Server
This commit is contained in:
192
AntiCheat.cs
Normal file
192
AntiCheat.cs
Normal file
@@ -0,0 +1,192 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user