개발자 능력치 물약/서버

게임 동기화 방식) 최종 일관성 , 서버 되감기 기법

하시무 2025. 5. 28. 23:55

CAP 정리를 통한 게임 동기화


NDC 발표자료 학습 내용 정리 및 언리얼 엔진 적용

 

🔹 CAP Theorem 정리 기본 개념


분산 시스템에서는 다음 3가지 속성 중 최대 2가지만 동시에 만족할 수 있다:

  • 일관성 (Consistency): 모든 노드가 같은 시점에 같은 데이터를 보는 것
  • 가용성 (Availability): 시스템이 항상 응답 가능한 상태를 유지하는 것
  • 분할용인 (Partition Tolerance): 네트워크 분할 상황에서도 시스템이 동작하는 것

멀티플레이 네트워크 게임은 분산 시스템이라고 할 수 있다.

🎮 게임 서버 설계 방식


동기 vs 비동기 처리

  • 동기 처리: 코드 작성 유리, 성능 불리 → 실시간 네트워크게임에 적합
  • 비동기 처리: 코드 작성 불리, 성능 유리 → 웹서비스, 모바일/소셜게임에 적합

💡 실제로는 비동기와 동기의 적절한 균형이 중요하며, 코드가 단순해지는 방향을 선호

 

 

📊 게임 동기화 방식별 특징


타입 1: 서버 동기화 (A + P)

 

주요 장르: FPS, MMORPG, MOBA, Sports

  • 목표: 플레이어에게 랙 없는 경험 제공
  • 방식: 클라이언트 이벤트를 서버에서 계산 후 브로드캐스트
  • 높은 TickRate = 정확도 향상
  • AOI(Area Of Interest): 필요한 범위의 정보만 전송하여 최적화
  • AOI 최적화: 낮으면 플레이 경험 불쾌, 높으면 치팅에 취약
  • 단점: Rubber Banding 현상 (클라이언트 예측과 서버 보정 간의 차이)

타입 2: Lock-Step 동기화 (C + P)

 

주요 장르: RTS, 과거 AOS

  • 목표: 플레이어에게 공평한 경험 제공
  • 방식: 모든 플레이어의 입력을 수집한 후 동시에 시뮬레이션
  • 특징: 한 플레이어가 멈추면 모든 플레이어가 대기
  • TickRate 조절: 지연시간과 반응성의 트레이드오프

Latency 높을 때: 반응성 ↑, 대기 가능성 ↑

Latency 낮을 때: 반응성 ↓, 부드러운 플레이 진행

 

 

Lag 보정 기법


서버 되감기(Server Rollback)

서버 되감기를 통한 동기화 방식의 동작 원리:

  • 서버와 클라이언트가 각자 시뮬레이션 수행
  • 서버는 클라이언트 입력 기준 과거 시점으로 되감기하여 시뮬레이션
  • 결과가 다르면 클라이언트에게 보정 신호 전송
  • 스냅샷 보관이 필요 (메모리 오버헤드)

상세 동작 과정

  1. 클라이언트: 입력 → 시뮬레이션 → 해시 계산 → 서버에 입력 및 해시 전송
  2. 입력 히스토리 기록 (Client Input Frame 단위로 보관)
  3. 서버: 이전 WorldFrame 상태 → WorldFrame++ → 월드상태 업데이트
  4. 될감기 시뮬레이션을 위한 월드상태 히스토리 보관
  5. 해시 비교 → 다르면 Desync 상황 → 클라이언트에 Resync 요청

단점: 후판정 문제

최대 지연시간만큼의 후판정이 발생하여, 엄폐물 뒤에 숨었음에도 피격되는 상황이 생길 수 있음. 주로 FPS 게임에서 사용되며, 강한 일관성 대신 최종 일관성을 추구함.

  • 쿼터뷰 특성: 제한된 시야로 AOI 최적화 가능

 

구체적 구현 전략


  1. 이동/기본 액션: 클라이언트 예측 + 서버 보정
  2. 스킬/특수 액션: 서버 권한 처리 후 브로드캐스트
  3. UI/시각 효과: 클라이언트 즉시 처리

 

C++ 구현 예시

 

// MyCharacter.h
UCLASS()
class MYGAME_API AMyCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    AMyCharacter();

    // Replication 설정을 위한 필수 override
    virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;

    // 서버에서 클라이언트로 상태 복제
    UPROPERTY(Replicated)
    float Health;

    // 하이브리드 동기화 구현
    UPROPERTY(ReplicatedUsing = OnRep_Position)
    FVector ReplicatedPosition;

    // 새로운 RPC 함수들 (virtual 불필요)
    UFUNCTION(Server, Reliable, WithValidation)
    void ServerMove(FVector NewLocation);

    UFUNCTION(Server, Reliable)
    void ServerExecuteSkill(int32 SkillID);

    // RepNotify 함수
    UFUNCTION()
    void OnRep_Position();

protected:
    // 언리얼 라이프사이클 함수들
    virtual void BeginPlay() override;
    virtual void Tick(float DeltaTime) override;

private:
    void PredictMovement();
    void CorrectPosition();
};

// MyCharacter.cpp
void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);

    DOREPLIFETIME(AMyCharacter, Health);
    DOREPLIFETIME(AMyCharacter, ReplicatedPosition);
}

void AMyCharacter::ServerMove_Implementation(FVector NewLocation)
{
    // 서버에서 실행되는 로직
    SetActorLocation(NewLocation);
}

bool AMyCharacter::ServerMove_Validate(FVector NewLocation)
{
    // 입력 유효성 검사
    return !NewLocation.ContainsNaN();
}

 

💡 개발 권장사항

  • 언리얼의 CharacterMovementComponent 활용 (최적화된 네트워크 이동 시스템)
  • 프로토타입 단계에서는 언리얼 기본 네트워크 시스템 사용
  • 성능/게임플레이 이슈 발생 시 세부 커스터마이징 진행
  • Network Relevancy 설정으로 불필요한 데이터 전송 최소화
  • 중요도별 다른 Replication 주기 설정 (Critical vs Non-Critical)

 

팀 프로젝트 적용 방안 


게임의 장르와 특성에 따라 CAP 중 2가지를 선택하여 네트워크 아키텍처를 설계해야 한다. 3인 액션 쿼터뷰 게임의 경우 실시간성과 반응성이 중요하므로 가용성(A)과 분할용인(P)을 선택한 서버 동기화 방식이 적합하며, 언리얼 엔진의 기본 네트워크 시스템이 이를 잘 지원한다.

특히 언리얼의 CharacterMovementComponent는 클라이언트 예측과 서버 보정을 자동으로 처리하므로, 초기 개발 단계에서는 빠르게 프로토타입을 구현하고, 필요에 따라 게임 특성에 맞는 커스터마이징을 진행하는 것이 효율적일 것 같다.