PART 4 - 강의 8/8

GAS 네트워크 복제

멀티플레이어 게임을 위한 GAS 동기화 전략

01

복제 모드 이해

GameplayEffect 리플리케이션 전략

모드 GE 복제 Cues 복제 대역폭 권장 대상
Full 모든 클라이언트 복제됨 높음 싱글플레이어, 소규모
Mixed 소유자만 모든 클라이언트 중간 플레이어 캐릭터
Minimal 복제 안 함 복제됨 낮음 AI, NPC
C++ // 리플리케이션 모드 설정 AMyCharacter::AMyCharacter() { AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>( TEXT("AbilitySystemComponent")); // 복제 활성화 AbilitySystemComponent->SetIsReplicated(true); // 플레이어: Mixed 모드 (권장) AbilitySystemComponent->SetReplicationMode( EGameplayEffectReplicationMode::Mixed); } AMyAICharacter::AMyAICharacter() { AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>( TEXT("AbilitySystemComponent")); AbilitySystemComponent->SetIsReplicated(true); // AI: Minimal 모드 (대역폭 절약) AbilitySystemComponent->SetReplicationMode( EGameplayEffectReplicationMode::Minimal); }
02

Prediction과 Rollback

클라이언트 예측과 서버 확인

GAS는 클라이언트 예측(Prediction)을 지원하여 입력 지연을 최소화합니다. 서버가 결과를 확인하면 필요시 롤백됩니다.

예측 흐름 // LocalPredicted 어빌리티 실행 흐름 1. [클라이언트] 플레이어 입력 | v 2. [클라이언트] 어빌리티 즉시 실행 (예측) - 로컬에서 GameplayEffect 적용 - 애니메이션 시작 - 쿨다운 시작 (예측) | v 3. [클라이언트 -> 서버] RPC로 활성화 요청 전송 | v 4. [서버] 어빌리티 활성화 검증 - 조건 확인 - 실제 GameplayEffect 적용 | v 5. [서버 -> 클라이언트] 결과 확인/거부 | v 6. [클라이언트] 서버 결과에 따라: - 성공: 예측 유지 - 실패: 롤백 (예측 취소)
C++ 예측 설정 // GameplayAbility에서 예측 설정 UMyAbility::UMyAbility() { // 로컬 예측 활성화 NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::LocalPredicted; // 인스턴스 정책 (예측에 필요) InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor; } // GameplayEffect에서 예측 고려 // Duration이 있는 이펙트는 예측이 롤백될 수 있음 // 쿨다운, 버프 등은 서버 확인 후 클라이언트에서 조정될 수 있음
예측 주의사항

데미지와 같은 Instant 이펙트는 예측하지 않는 것이 안전합니다. 서버에서만 실제 데미지를 적용하고, 클라이언트는 GameplayCue로 시각 피드백만 받습니다.

03

AttributeSet 복제

속성 값의 네트워크 동기화

MyAttributeSet.h UPROPERTY(BlueprintReadOnly, Category = "Health", ReplicatedUsing = OnRep_Health) FGameplayAttributeData Health; // ReplicatedUsing: 값이 복제될 때 호출될 함수 지정
MyAttributeSet.cpp void UMyAttributeSet::GetLifetimeReplicatedProps( TArray<FLifetimeProperty>& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); // REPNOTIFY_Always: 값이 같아도 OnRep 호출 // 예측된 값이 서버 값으로 덮어쓰일 때 UI 업데이트 보장 DOREPLIFETIME_CONDITION_NOTIFY( UMyAttributeSet, Health, COND_None, REPNOTIFY_Always ); DOREPLIFETIME_CONDITION_NOTIFY( UMyAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always ); // 마나, 공격력 등도 동일하게 설정 } void UMyAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth) { // GAS 매크로로 예측 처리 GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, Health, OldHealth); // 추가 로직 (UI 업데이트 등) // 이 시점에서 Health.GetCurrentValue()는 서버에서 온 최신 값 }
GAMEPLAYATTRIBUTE_REPNOTIFY

이 매크로는 GAS의 예측 시스템과 통합되어, 예측된 값과 서버 값의 차이를 올바르게 처리합니다.

04

GameplayCue 복제

시각/청각 효과의 네트워크 전파

GameplayCue는 VFX, SFX 등 시각/청각 피드백을 담당합니다. Mixed/Minimal 모드에서도 모든 클라이언트에 전파됩니다.

GameplayCue 설정 // GameplayEffect에서 Cue 설정 // Details > Gameplay Cues Gameplay Cue Tags: - GameplayCue.Combat.Damage - GameplayCue.Effect.Stun // Cue는 세 가지 이벤트 지원: // - OnActive: 이펙트 시작 시 // - WhileActive: 이펙트 지속 중 (Duration) // - OnRemove: 이펙트 종료 시
C++ 직접 Cue 실행 // 서버에서 Cue 직접 실행 (모든 클라이언트에 전파) void AMyActor::TriggerDamageCue(FVector HitLocation) { if (HasAuthority()) { FGameplayCueParameters CueParams; CueParams.Location = HitLocation; CueParams.Normal = FVector::UpVector; CueParams.EffectCauser = this; // 모든 클라이언트에 Cue 전파 UAbilitySystemComponent* ASC = GetAbilitySystemComponent(); if (ASC) { ASC->ExecuteGameplayCue( FGameplayTag::RequestGameplayTag(FName("GameplayCue.Combat.Damage")), CueParams ); } } } // 로컬 전용 Cue (네트워크 전파 없음) void AMyActor::TriggerLocalCue() { UAbilitySystemComponent* ASC = GetAbilitySystemComponent(); if (ASC) { ASC->ExecuteGameplayCueLocal( FGameplayTag::RequestGameplayTag(FName("GameplayCue.UI.LevelUp")), FGameplayCueParameters() ); } }
05

서버 권한 패턴

보안이 중요한 로직 처리

C++ // 서버에서만 실행해야 하는 어빌리티 UMyServerOnlyAbility::UMyServerOnlyAbility() { // 서버에서만 실행 (클라이언트 예측 없음) NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::ServerOnly; } // 서버 권한 체크 패턴 void UMyAbility::ApplyCriticalEffect(AActor* Target) { // 항상 서버에서만 데미지 적용 if (!GetAvatarActorFromActorInfo()->HasAuthority()) { return; } // 서버 권한 확인 후 실행 UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Target); if (TargetASC) { FGameplayEffectSpecHandle Spec = MakeOutgoingGameplayEffectSpec(DamageEffectClass, GetAbilityLevel()); ApplyGameplayEffectSpecToTarget( CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, Spec, TargetASC ); } } // 치트 방지: 서버에서 조건 재검증 bool UMyAbility::CanActivateAbility( const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags, const FGameplayTagContainer* TargetTags, FGameplayTagContainer* OptionalRelevantTags) const { if (!Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags)) { return false; } // 서버에서 추가 검증 if (ActorInfo->AvatarActor.IsValid() && ActorInfo->AvatarActor->HasAuthority()) { // 쿨다운 조작 방지 // 비용 조작 방지 // 거리 제한 확인 } return true; }
네트워크 보안 베스트 프랙티스
  • 데미지/힐은 항상 서버에서 적용
  • 중요 조건은 서버에서 재검증
  • 클라이언트는 시각 피드백만 담당
  • 예측은 롤백 가능성을 고려하여 설계
SUMMARY

핵심 요약

  • Mixed 모드: 플레이어용, GE는 소유자만, Cues는 모두에게
  • Minimal 모드: AI용, GE 복제 없음, 대역폭 최소화
  • LocalPredicted로 클라이언트 예측 활성화, 롤백 대비
  • REPNOTIFY_Always로 예측 값과 서버 값 동기화
  • 데미지/중요 로직은 ServerOnly 또는 서버 권한 체크 필수
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: Attribute 네트워크 복제 설정

AttributeSet의 UPROPERTY(Replicated)와 GetLifetimeReplicatedProps()를 구현하세요. DOREPLIFETIME_CONDITION으로 Owner만 또는 모든 클라이언트에게 복제하는 조건을 설정하세요.

실습 2: Server-Authoritative 어빌리티 실행

어빌리티의 NetExecutionPolicy를 ServerOnly/LocalPredicted로 설정하고, 클라이언트에서 예측 실행 후 서버에서 확정하는 플로우를 테스트하세요. 네트워크 지연 시뮬레이션(NetEmulation)으로 예측의 효과를 확인하세요.

심화 과제: GAS 네트워크 최적화

GameplayEffect의 복제 정책을 설정하고, Minimal Replication Proxy를 사용하여 GAS 네트워크 대역폭을 최적화하세요. MinimalReplicationTags를 설정하고 네트워크 트래픽을 Unreal Insights로 프로파일링하세요.