Blaster防作弊思路

游戏防作弊体系详解


一、核心思想:信任模型

防作弊的本质是建立正确的信任边界

【不可信】客户端输入  →  【可信】服务端逻辑  →  【可信】服务端输出

客户端可以做:

  • 本地预测(提升手感)
  • 表现层渲染(特效、音效)
  • 输入采集(键鼠/手柄)

客户端不能做

  • 决定命中结果
  • 决定伤害数值
  • 决定状态变更(死亡/复活)

二、网络层防作弊

2.1 RPC 权威验证(你简历中已有)

UE5 的 RPC 机制天然支持两阶段验证:

// .h 声明
UFUNCTION(Server, Reliable, WithValidation)
void ServerFire(const FVector& HitTarget);

// .cpp 实现
bool ABlasterCharacter::ServerFire_Validate(const FVector& HitTarget)
{
    // 验证1:冷却时间
    if (GetWorld()->GetTimeSeconds() - LastFireTime < FireDelay)
        return false;
    
    // 验证2:弹药数量(服务端存储)
    if (CurrentAmmo <= 0)
        return false;
    
    // 验证3:角色状态(不能在死亡状态开火)
    if (bIsDead || bIsStunned)
        return false;
    
    // 验证4:HitTarget距离合理性
    float Dist = FVector::Dist(GetActorLocation(), HitTarget);
    if (Dist > MaxWeaponRange * 1.2f) // 留20%网络抖动余量
        return false;

    return true;
}

void ABlasterCharacter::ServerFire_Implementation(const FVector& HitTarget)
{
    // Validate通过才会执行到这里
    // 服务端扣弹药、执行射线检测、同步结果
    CurrentAmmo--;
    MulticastFire(HitTarget);
}

_Validate 返回 false 时,UE5 会自动断开该客户端连接,引擎层面直接封禁。


2.2 延迟补偿(Lag Compensation)—— 最重要的技术点

这是多人射击游戏防作弊与公平性的核心矛盾点:

问题:

玩家A(100ms延迟)看到敌人在X位置开枪
服务端收到请求时,敌人已经移动到Y位置
直接在Y位置判定 → 明明打中了却没伤害 → 手感极差

解决方案:服务端历史快照回滚

// 每帧记录所有角色的位置快照
struct FFramePackage
{
    float Time;
    TMap<ABlasterCharacter*, FBoxInformation> HitBoxInfoMap;
};

// 服务端维护时间轴
TDoubleLinkedList<FFramePackage> FrameHistory;

// 延迟补偿核心逻辑
FServerSideRewindResult ALagCompensationComponent::ServerSideRewind(
    ABlasterCharacter* HitCharacter,
    const FVector_NetQuantize& TraceStart,
    const FVector_NetQuantize& HitLocation,
    float HitTime  // 客户端开火时的时间戳
)
{
    // 1. 从历史快照中找到 HitTime 对应的帧
    FFramePackage FrameToCheck = GetFrameToCheck(HitCharacter, HitTime);
    
    // 2. 将目标角色的碰撞箱回滚到历史位置
    MoveBoxes(HitCharacter, FrameToCheck);
    
    // 3. 在历史位置做射线检测
    FHitResult ConfirmHitResult;
    UWorld* World = GetWorld();
    if (World)
    {
        World->LineTraceSingleByChannel(
            ConfirmHitResult,
            TraceStart,
            HitLocation,
            ECC_HitBox
        );
    }
    
    // 4. 还原碰撞箱到当前位置
    ResetHitBoxes(HitCharacter, CurrentFrame);
    
    return BuildResult(ConfirmHitResult);
}

关键细节:

  • 历史快照保留时长 = 最大可接受延迟(通常 1~2 秒)
  • 回滚时间 = 服务端当前时间 - 客户端延迟
  • 验证完立刻还原,避免影响其他逻辑

2.3 位移合法性校验(Speed Hack 防御)

// 服务端每帧校验客户端上报的位置
void ABlasterCharacter::OnRep_ReplicatedMovement()
{
    Super::OnRep_ReplicatedMovement();
    
    float DeltaTime = GetWorld()->GetDeltaSeconds();
    float MaxAllowedDist = (MaxWalkSpeed + CheatTolerance) * DeltaTime;
    
    FVector ServerPos = GetActorLocation();
    FVector ClientPos = ReplicatedMovement.Location;
    
    float ActualDist = FVector::Dist(ServerPos, ClientPos);
    
    if (ActualDist > MaxAllowedDist * 1.5f) // 留50%容错
    {
        // 强制回滚到服务端位置
        SetActorLocation(ServerPos);
        
        // 记录异常次数
        SpeedCheatCount++;
        if (SpeedCheatCount > CheatThreshold)
        {
            // 上报反作弊系统
            ReportCheat(ECheatType::SpeedHack);
        }
    }
}

容错设计很重要:

  • 网络抖动会造成位移数据不稳定
  • 不能一次异常就封号,需要累计计数 + 滑动窗口统计

三、服务端视野剔除(反透视墙的根本方案)

Wall Hack 的原理是修改客户端渲染,让墙壁变透明。
根本解法:服务端根本不下发不该看到的数据。

普通方案:服务端下发所有玩家数据 → 客户端开透视可见
正确方案:服务端只下发视野内玩家数据 → 透视也没有数据可渲染

3.1 UE5 实现:网络相关性(Network Relevancy)

// 重写 IsNetRelevantFor,控制哪些Actor对哪些客户端可见
bool ABlasterCharacter::IsNetRelevantFor(
    const AActor* RealViewer,
    const AActor* ViewTarget,
    const FVector& SrcLocation) const
{
    // 先调用父类的距离/连接检查
    if (!Super::IsNetRelevantFor(RealViewer, ViewTarget, SrcLocation))
        return false;

    const ABlasterCharacter* ViewerChar = 
        Cast<ABlasterCharacter>(ViewTarget);
    if (!ViewerChar) return true;

    // 视锥检测:目标在观察者视野外则不同步
    FVector ToTarget = GetActorLocation() - SrcLocation;
    FVector ViewDir = ViewerChar->GetControlRotation().Vector();
    float DotProduct = FVector::DotProduct(ToTarget.GetSafeNormal(), ViewDir);
    
    // 在视野前方120度内才同步(cos60° ≈ 0.5)
    if (DotProduct < -0.5f) return false;

    // 射线检测:被墙遮挡则不同步
    FHitResult HitResult;
    GetWorld()->LineTraceSingleByChannel(
        HitResult,
        SrcLocation,
        GetActorLocation(),
        ECC_Visibility
    );
    
    // 射线没有阻挡(或阻挡的就是目标自己)才同步
    return !HitResult.bBlockingHit || HitResult.GetActor() == this;
}

性能注意:

  • 视野剔除的射线检测是 O(玩家数²) 的开销
  • 实际项目中用空间分区(八叉树/格子)优化,只检测附近的玩家
  • 更新频率不需要每帧,0.1s 一次足够

四、内存防篡改

4.1 关键数据服务端化

❌ 错误:客户端存储 HP/弹药/金币,上报给服务端
✅ 正确:服务端存储所有状态,客户端只做镜像显示
// 服务端权威数据(只有服务端可修改)
UPROPERTY(Replicated)  // 只读同步到客户端
float Health;

UPROPERTY(Replicated)
int32 CurrentAmmo;

// 客户端请求扣血必须走RPC
UFUNCTION(Server, Reliable)
void ServerTakeDamage(float DamageAmount, AActor* DamageCauser);

4.2 数值校验

即使数据在服务端,也要做逻辑合法性检查:

void ABlasterCharacter::ServerTakeDamage_Implementation(
    float DamageAmount, AActor* DamageCauser)
{
    // 伤害值范围校验
    if (DamageAmount <= 0 || DamageAmount > MaxPossibleDamage)
    {
        UE_LOG(LogCheat, Warning, TEXT("Suspicious damage value: %f"), DamageAmount);
        return;
    }
    
    // 伤害来源合法性校验(必须是已注册的武器/技能)
    if (!IsValidDamageSource(DamageCauser))
        return;
    
    Health = FMath::Clamp(Health - DamageAmount, 0.f, MaxHealth);
}

五、行为异常检测

纯技术防护之外,还需要统计分析层来发现绕过技术防护的高级作弊。

5.1 实时异常指标

struct FPlayerBehaviorStats
{
    // 命中率统计(滑动窗口,最近100发)
    TArray<bool> HitHistory;
    float GetHeadshotRate() const;   // 爆头率 > 85% 可疑
    float GetOverallHitRate() const; // 命中率 > 70% 可疑
    
    // 反应时间统计(从敌人进入视野到开枪的时间)
    TArray<float> ReactionTimes;
    float GetMinReactionTime() const; // < 80ms 可疑(人类极限约150ms)
    
    // 角速度统计(镜头转动速度)
    TArray<float> AimAngularVelocities;
    float GetMaxAngularVelocity() const; // 瞬间转向可疑
};

5.2 综合评分上报

void AAntiCheatComponent::TickBehaviorAnalysis()
{
    float SuspicionScore = 0.f;
    
    if (Stats.GetHeadshotRate() > 0.85f)
        SuspicionScore += 30.f;
    
    if (Stats.GetMinReactionTime() < 0.08f)
        SuspicionScore += 40.f;
    
    if (Stats.GetMaxAngularVelocity() > AimBotAngularThreshold)
        SuspicionScore += 50.f;
    
    if (SuspicionScore > 80.f)
    {
        // 上报到后端反作弊服务,人工审核或自动封禁
        ReportToAntiCheatServer(SuspicionScore, Stats);
    }
}

六、通信安全

6.1 数据包防篡改

客户端 ←→ 服务端 通信必须走 TLS/DTLS 加密
防止中间人截包修改(典型工具:Cheat Engine 的网络模块)

6.2 重放攻击防御

// 每个RPC请求带时间戳 + 序列号
struct FSecureRPCHeader
{
    int64  Timestamp;    // 服务端验证时间窗口(±2秒内有效)
    uint32 SequenceNum;  // 递增,防止重放旧数据包
    uint32 Checksum;     // 简单校验和,防止字段篡改
};

七、体系总结

┌─────────────────────────────────────────────────────┐
│                    防作弊体系                         │
├──────────────┬──────────────┬────────────────────────┤
│   网络层     │   逻辑层     │      检测层             │
│              │              │                        │
│ Server RPC   │ 服务端权威   │ 行为统计分析           │
│ 延迟补偿     │ 数值校验     │ 异常评分上报           │
│ 位移校验     │ 状态机验证   │ 人工审核               │
│              │              │                        │
├──────────────┴──────────────┴────────────────────────┤
│                  数据层                               │
│         视野剔除 / TLS加密 / 序列号防重放             │
└─────────────────────────────────────────────────────┘

优先级建议(从高到低):

  1. 服务端权威 + RPC Validate —— 成本低,收益最高
  2. 关键数据服务端化 —— 杜绝内存篡改
  3. 延迟补偿 —— 公平性与防作弊的平衡
  4. 视野剔除 —— 根治透视,但性能开销较大
  5. 行为检测 —— 捕捉绕过技术防护的高级作弊
posted @ 2026-04-16 20:20  Reed_Chill  阅读(15)  评论(0)    收藏  举报