본문 바로가기

공부/이득우의 언리얼 프로그래밍

[Study] Part 3 - RPC 기초 (8/15)

728x90
반응형

 

 

 

  • 정리
    • RPC 기초
      1. RPC 함수를 선언하는 방법과 기본 사용 방법의 이해
      2. RPC 종류에 따른 동작 방식의 이해
      3. RPC 사용시 주의할 점의 확인
      4. 프로퍼티 리플리케이션과 NetMulticast RPC의 차이점 이해

 

  • RPC(Remote Procedure Call)이란?
    • 원격 프로시저(함수) 호출의 약자
    • 원격 컴퓨터에 있는 함수를 호출할 수 있도록 만든 통신 프로토콜
    • 네트웍 멀티플레이에서 서버와 클라이언트간에 빠르게 행동을 명령하고 정보를 주고받는데 사용
    • 언리얼 엔진에서 클라이언트에서 서버로 통신하는 유일한 수단을 제공

 

RPC(Remote Procedure Call)이란?

 

  • Client RPC 개요
    • 서버에서 클라이언트로 호출하는 RPC
    • 이를 활용해 특정 클라이언트에게만 명령을 보낼 수 있음
    • 서버에서 명령을 보낼 클라이언트의 커넥션을 소유한 액터를 사용해야 함 (AActor::GetNetConnection)

 

Server RPC 개요

 

  • NetMulticast RPC 개요
    • 서버를 포함해 모든 플레이어에게 명령을 보내는 RPC
    • 프로퍼티 리플리케이션과 유사하게 연관성 기반으로 동작함 (커넥션을 소유하지 않아도 동작)
    • 프로퍼티 리플리케이션과 유사하지만 다른 용도로 사용

 

NetMulticast RPC 개요

 

  • RPC 선언에 관련된 키워드 정리
    • Unreliable : RPC 호출을 보장하지 않는 옵션. 빠름
    • Reliable : RPC 호출을 보장해주는 추가 옵션. 정말 필요한 때만 호출
    • WithValidation : 서버에서 검증 로직을 추가로 구현할 때 추가하는 옵션

 

RPC 선언에 관련된 키워드 정리

 

UFUNCTION(NetMulticast,Unreliable)
void MulticastRPCChangeLightColor(const FLinearColor& NewLightColor);
  • ABFountain.h
void AABFountain::MulticastRPCChangeLightColor_Implementation(const FLinearColor& NewLightColor)
{
	AB_LOG(LogABNetwork, Log, TEXT("LightColor : %s"), *NewLightColor.ToString());

	UPointLightComponent* PointLight = Cast<UPointLightComponent>(GetComponentByClass(UPointLightComponent::StaticClass()));
	if (PointLight)
	{
		PointLight->SetLightColor(NewLightColor);
	}
}
  • ABFountain.cpp

 

  • Multicast RPC 함수를 통한 테스트

RPC는 연관성에 관련해가지고 동작

 

  • 액터와 가까이 있을 때는 연관성에 관련되어 정상적으로 RPC가 2번씩(서버, 클라이언트) 호출되는 것을 볼 수 있고, 액터와 멀어졌을 경우 서버에서만 RPC가 호출되는 것을 볼 수있다.

 

	// MulticastRPC
	UFUNCTION(NetMulticast,Unreliable)
	void MulticastRPCChangeLightColor(const FLinearColor& NewLightColor);

	// WithValidation - 검증기능
	// ServerRPC
	UFUNCTION(Server, Unreliable, WithValidation)
	void ServerRPCChangeLightColor();

	// ClientRPC
	UFUNCTION(Client, Unreliable)
	void ClientRPCChangeLightColor(const FLinearColor& NewLightColor);
  • ABFountain.h
// Called when the game starts or when spawned
void AABFountain::BeginPlay()
{
	Super::BeginPlay();

	// 서버에 전달(HasAuthority)
	if (HasAuthority())
	{
		FTimerHandle Handle;
		GetWorld()->GetTimerManager().SetTimer(Handle, FTimerDelegate::CreateLambda([&]
			{
				const FLinearColor NewLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);
				ClientRPCChangeLightColor(NewLightColor);
			}
		), 1.0f, true, 0.0f);

		FTimerHandle Handle2;
		GetWorld()->GetTimerManager().SetTimer(Handle2, FTimerDelegate::CreateLambda([&]
			{
				// 반복문의 다른 방법
				// 모든 액터의 타입에 대해서 쉽게 목록을 가져올 수 있는 기능
				for (APlayerController* PlayerController : TActorRange<APlayerController>(GetWorld()))
				{
					if (PlayerController && !PlayerController->IsLocalPlayerController())
					{
						SetOwner(PlayerController);
						break;
					}
				}
			}
		), 10.0f, false, -1.0f);

		// 오너십을 확인하는 함수
		GetNetConnection();
	}
	// 클라이언트 일 경우에만
	else
	{

	}
}

// ...

// ClientRPC
void AABFountain::ClientRPCChangeLightColor_Implementation(const FLinearColor& NewLightColor)
{
	AB_LOG(LogABNetwork, Log, TEXT("LightColor : %s"), *NewLightColor.ToString());

	UPointLightComponent* PointLight = Cast<UPointLightComponent>(GetComponentByClass(UPointLightComponent::StaticClass()));
	if (PointLight)
	{
		PointLight->SetLightColor(NewLightColor);
	}
}

// WithValidation 기능을 추가하기 위해서 필요한 함수
bool AABFountain::ServerRPCChangeLightColor_Validate()
{
	// true = 검증 성공
	// false = 검증 실패
	// 검증에 실패하면 클라이언트가 즉각적으로 차단이 되고 해당 클라이언트는 스탠드얼론으로 돌아가게 된다.
	return true;
}

// Server RPC - 클라이언트가 서버에 명령을 내리는 유일한 방법
// 클라이언트가 서버에게 LightColor를 변경하라고 명령을 내림, 서버는 모든 클라이어은트와 자기 자신에서 MulticastRPC로 LightColor를 변경하라고 명령을 내림
void AABFountain::ServerRPCChangeLightColor_Implementation()
{
	const FLinearColor NewLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);
	MulticastRPCChangeLightColor(NewLightColor);
}

// Multicast RPC
void AABFountain::MulticastRPCChangeLightColor_Implementation(const FLinearColor& NewLightColor)
{
	AB_LOG(LogABNetwork, Log, TEXT("LightColor : %s"), *NewLightColor.ToString());

	UPointLightComponent* PointLight = Cast<UPointLightComponent>(GetComponentByClass(UPointLightComponent::StaticClass()));
	if (PointLight)
	{
		PointLight->SetLightColor(NewLightColor);
	}
}
  • ABFountain.cpp

 

 

  • Unreliable RPC
    • 목적지에 도착한다는 보장은 없지만, 더 빠르고 더 자주 전송할 수 있음. 게임플레이에 중요하지 않거나 매우 자주 호출되는 함수에 가장 적합
    • 예를 들어, 액터의 움직임은 매 프레임마다 변경될 수 있기 때문에 신뢰할 수 없는 RPC를 사용하여 리플리케이트된다.
  • Reliable RPC
    • 목적지에 도착할 수 있도록 보장되며, 게임플레이에 중요하지만 자주 호출되지 않는 함수에 가장 적합
    • 예를 들어, 충돌 이벤트, 무기 발사 시작 또는 종료, 액터 스폰 등이 이에 해당한다.
  • RPC 사용시 주의할 점 정리
    • 각 RPC 종류마다 올바르게 사용할 것
      • Client, NetMulticast서버에서만 호출
      • Server클라이언트에서 호출하지만, 플레이어로 참여하는 리슨서버의 경우 호출 가능
      • Client, Server오너십을 가지고 있는 액터에서 호출
        • 컨트롤러 : IsLocalController 함수
        • 폰 : IsLocallyControlled 함수
    • Tick빈번하게 호출되는 함수Reliable RPC를 사용하지 말 것
    • NetMulticast RPC의 잦은 사용은 네트웍 부하를 가중시키니 신중을 기할 것
    • 게임 플레이 및 액터 상태에 영향을 미치는 경우 RPC보다 프로퍼티 리플리케이션을 사용할 것

 

  • Property Replication vs NetMulticast RPC
    • 유사점
      • 서버와 모든 클라이언트의 지정한 함수를 호출할 수 있음
      • 지정한 데이터 전송을 보장할 수 있음
      • 액터의 오너십과 무관하게 연관성으로 동작
    • 차이점
      • Property Replication으로 설정한 데이터는 클라이언트에 반드시 동기화
        (RPC 전송의 Reliability와 다른 개념)
      • NetMulticast RPC를 호출한 타이밍에 클라이언트가 없으면 해당 데이터를 받을 길이 없음
    • 정리
      • Property Replication은 게임에 영향을 미치는 데이터에 사용 (Gameplay Property)
      • NetMulticast RPC는 게임과 무관한 휘발성 데이터에 사용 (Cosmetic)
  • Property Replication vs NetMulticast RPC 실습해보기
  • 실행후 5초뒤에 Property Replication이 호출되는 코드와 NetMulticast RPC가 호출되는 코드를 비교해보자.

 

FTimerHandle Handle3;
GetWorld()->GetTimerManager().SetTimer(Handle3, FTimerDelegate::CreateLambda([&]
	{
		ServerLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);

		// 프로퍼티 리플리케이션
		OnRep_ServerLightColor();

		// Multicast RPC
		/*const FLinearColor NewLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);
		MulticastRPCChangeLightColor(NewLightColor);*/
	}
), 5.0f, false, -1.0f);
  • ABFountain.cpp

 

 

Property Replication으로 구현했을 경우

 

  • 서버를 실행하고 5초뒤에 분수대의 색이 변경되었고, 이후 클라이언트를 실행했을때 정상적으로 분수대의 변경된 색이 적용된 것을 볼 수 있다.
  • 클라이언트의 분수대는 프로퍼티 리플리케이션에 의해 PostNetInit 함수를 통해서 변경된 기본 속성값들을 모두 받게 된다.
  • 최근에 가장 마지막에 서버에서 변경된 프로퍼티 내용이 전송되면서 클라이언트에 반영이 된다.

 

FTimerHandle Handle3;
GetWorld()->GetTimerManager().SetTimer(Handle3, FTimerDelegate::CreateLambda([&]
	{
		//ServerLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);

		//// 프로퍼티 리플리케이션
		//OnRep_ServerLightColor();

		// Multicast RPC
		const FLinearColor NewLightColor = FLinearColor(FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), FMath::RandRange(0.0f, 1.0f), 1.0f);
		MulticastRPCChangeLightColor(NewLightColor);
	}
), 5.0f, false, -1.0f);
  • ABFountain.cpp

 

 

Multicast RPC로 구현했을 경우

 

  • 서버를 실행하고 5초뒤에 분수대의 색이 변경되었고, 이후 클라이언트를 실행했을때 클라이언트의 분수대는 초기에 설정된 변경되지 않은 색으로 나타나게 된다.
  • 이미 RPC로 호출했을 때의 타이밍을 지났기 때문에 클라이언트는 바뀐 분수대의 색상을 받아올 길이 없다. 이미 데이터는 휘발되었다.

 

  • 게임 플레이에 영향을 미치는 중요한 데이터들은 Property Replication을 사용하고, 휘발성으로 동작하는 데이터들은 Multicast RPC를 사용하도록 하자.

 

 

 

 

 

 

해당 포스트는 인프런의 <이득우의 언리얼 프로그래밍 Part3 - 네트웍 멀티플레이어 프레임웍의 이해>
강의를 수강하고 정리한 내용입니다.
 

이득우의 언리얼 프로그래밍 Part3 - 네트웍 멀티플레이 프레임웍의 이해 강의 | 이득우 - 인프런

이득우 | 또 하나의 언리얼 엔진이라고도 불리는 네트웍 멀티플레이어 프레임웍을 학습합니다. 네트웍 멀티플레이어 게임을 제작할 때 반드시 알아야 하는 주요 개념, 내부 동작 원리, 최적화

www.inflearn.com

 

 

728x90