193 lines
6.1 KiB
C#
193 lines
6.1 KiB
C#
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
|
|
}
|