PART 8 - 강의 2/2
안티치트 패턴
서버 측 치트 탐지와 방지 기법
01
치트 유형 분석
일반적인 치트 패턴
치트 유형
┌─────────────────────────────────────────────────────────────┐
│ 주요 치트 유형 │
├─────────────────────────────────────────────────────────────┤
│ │
│ [스피드핵] │
│ - 이동 속도 증가 │
│ - 대응: 서버 측 이동 검증 │
│ │
│ [에임봇] │
│ - 자동 조준 │
│ - 대응: 비정상적 정확도/반응 속도 탐지 │
│ │
│ [월핵 (ESP)] │
│ - 벽 뒤 적 표시 │
│ - 대응: 시야 외 Actor 복제 제한 │
│ │
│ [텔레포트] │
│ - 순간 이동 │
│ - 대응: 위치 변화량 검증 │
│ │
│ [무한 자원] │
│ - HP/탄약 무한 │
│ - 대응: 자원은 서버에서만 관리 │
│ │
└─────────────────────────────────────────────────────────────┘
02
이동 검증 시스템
스피드핵 및 텔레포트 탐지
C++
// 서버에서 이동 검증
void UMyCharacterMovement::ServerMove_Implementation(
float TimeStamp,
FVector_NetQuantize InAccel,
FVector_NetQuantize ClientLoc, ...)
{
// 1. 이동 속도 검증
float DeltaTime = TimeStamp - LastServerMoveTime;
FVector PrevLocation = LastValidLocation;
float MoveDistance = FVector::Dist(ClientLoc, PrevLocation);
float MaxAllowedDistance = GetMaxSpeed() * DeltaTime * SpeedHackTolerance;
if (MoveDistance > MaxAllowedDistance)
{
SpeedViolationCount++;
if (SpeedViolationCount > MaxViolationsBeforeAction)
{
// 치트 의심 - 서버 위치로 강제 보정
ClientAdjustPosition(TimeStamp, LastValidLocation, ...);
ReportSuspiciousActivity("SpeedHack", MoveDistance);
return;
}
}
else
{
SpeedViolationCount = FMath::Max(0, SpeedViolationCount - 1);
}
// 2. 순간이동 감지
if (MoveDistance > TeleportThreshold && !bJustTeleported)
{
ReportSuspiciousActivity("Teleport", MoveDistance);
ClientAdjustPosition(TimeStamp, LastValidLocation, ...);
return;
}
// 검증 통과
LastValidLocation = ClientLoc;
LastServerMoveTime = TimeStamp;
Super::ServerMove_Implementation(TimeStamp, InAccel, ClientLoc, ...);
}
03
전투 검증 시스템
에임봇 및 비정상 전투 탐지
C++
// 전투 통계 수집 및 분석
USTRUCT()
struct FCombatStats
{
GENERATED_BODY()
int32 TotalShots = 0;
int32 Headshots = 0;
int32 Hits = 0;
TArray<float> ReactionTimes; // 적 발견 후 첫 발사까지
TArray<float> AimAngularVelocities; // 조준 각속도
};
void AMyWeapon::ServerValidateHit_Implementation(...)
{
// 통계 수집
FCombatStats& Stats = PlayerCombatStats.FindOrAdd(ShooterId);
Stats.TotalShots++;
if (bValidHit)
{
Stats.Hits++;
if (HitResult.BoneName == "head")
{
Stats.Headshots++;
}
}
// 주기적으로 분석
if (Stats.TotalShots % 50 == 0)
{
AnalyzeCombatStats(ShooterId, Stats);
}
}
void AMyGameMode::AnalyzeCombatStats(
FUniqueNetIdRepl PlayerId,
const FCombatStats& Stats)
{
// 헤드샷 비율 분석
float HeadshotRatio = (float)Stats.Headshots / Stats.Hits;
if (HeadshotRatio > 0.8f && Stats.Hits > 20)
{
FlagForReview(PlayerId, "HighHeadshotRatio", HeadshotRatio);
}
// 명중률 분석
float Accuracy = (float)Stats.Hits / Stats.TotalShots;
if (Accuracy > 0.95f && Stats.TotalShots > 30)
{
FlagForReview(PlayerId, "UnrealisticAccuracy", Accuracy);
}
// 반응 속도 분석
float AvgReactionTime = CalculateAverage(Stats.ReactionTimes);
if (AvgReactionTime < 0.1f) // 100ms 미만은 비인간적
{
FlagForReview(PlayerId, "InhumanReaction", AvgReactionTime);
}
}
통계 기반 탐지
단일 이벤트로 판단하지 말고 누적 통계를 분석하세요. 프로 게이머도 가끔 놀라운 플레이를 합니다. 지속적인 비정상 패턴만 신고하세요.
04
월핵(ESP) 방지
시야 외 정보 차단
C++
// Relevancy로 시야 외 적 복제 차단
bool AMyCharacter::IsNetRelevantFor(
const AActor* RealViewer,
const AActor* ViewTarget,
const FVector& SrcLocation) const
{
// 자기 자신은 항상 관련
if (this == RealViewer || this == ViewTarget)
return true;
// 같은 팀은 항상 복제
if (IsSameTeam(RealViewer))
return true;
// 거리 체크
float DistSq = (GetActorLocation() - SrcLocation).SizeSquared();
if (DistSq > FMath::Square(MaxRelevantDistance))
return false;
// 시야 체크 (비용이 높으므로 주의)
if (bUseVisibilityForRelevancy)
{
// 최근에 보였거나 소리를 냈으면 관련
if (GetWorld()->GetTimeSeconds() - LastSeenTime < 3.f)
return true;
// 라인 트레이스로 시야 확인
FHitResult HitResult;
FCollisionQueryParams QueryParams;
QueryParams.AddIgnoredActor(this);
QueryParams.AddIgnoredActor(RealViewer);
bool bBlocked = GetWorld()->LineTraceSingleByChannel(
HitResult,
SrcLocation + FVector(0, 0, 50),
GetActorLocation() + FVector(0, 0, 50),
ECC_Visibility,
QueryParams);
if (bBlocked)
return false;
}
return true;
}
성능 주의
모든 Actor 쌍에 대해 라인 트레이스를 수행하면 서버 성능에 심각한 영향을 줍니다. 거리 체크로 먼저 필터링하고, 중요한 Actor에만 시야 체크를 적용하세요.
SUMMARY
핵심 요약
- 이동 검증 - 최대 속도와 위치 변화량으로 스피드핵 탐지
- 전투 통계 - 명중률, 헤드샷 비율, 반응 속도 분석
- 월핵 방지 - IsNetRelevantFor로 시야 외 정보 차단
- 누적 분석 - 단일 이벤트가 아닌 패턴 기반 탐지
PRACTICE
도전 과제
배운 내용을 직접 실습해보세요
실습 1: 이동 속도 검증
ServerMove에서 클라이언트가 보낸 위치 변화량이 MaxSpeed * DeltaTime * 1.2f(허용 오차 20%)를 초과하면 이동을 거부하는 속도핵 감지 로직을 구현하세요.
실습 2: EOS Anti-Cheat 연동
EOS Anti-Cheat 클라이언트/서버 인터페이스를 초기화하고, BeginSession/EndSession 호출을 게임 세션 라이프사이클에 통합하세요.
심화 과제
서버 측 이상 행동 감지 시스템을 구현하세요. 이동 속도, 발사 빈도, 히트율 등의 통계를 수집하고, 임계값 초과 시 경고 로그를 남기는 행동 분석 프레임워크를 구축하세요.