RPC (Remote Procedure Call) 는 로컬에서 호출되지만 (호출하는 머신과는) 다른 머신에서 원격 실행되는 함수

 

  1. Actor에서 호출. (액터에 부착된 컴포넌트도 가능)
  2. Actor는 반드시 replicated 
  3. 서버에서 호출 클라이언트에서 실행되는 RPC : 해당 Actor를 실제 소유하고 있는 클라이언트에서만 함수가 실행
  4. 클라이언트에서 호출 서버에서 실행되는 RPC : 클라이언트는 RPC가 호출되는 Actor를 소유해야 한다.
  5. Multicast RPC는 예외

서버에서 호출되는 경우, 서버에서는 로컬에서 실행될 뿐만 아니라 현재 연결된 모든 클라이언트에서도 실행됩니다.

클라이언트에서 호출되는 경우, 로컬에서만 실행되며, 서버에서는 실행되지 않습니다.

현재 멀티캐스트 이벤트에 대해 단순한 스로틀 조절 메카니즘이 있습니다. 멀티캐스트 함수는 주어진 액터의 네트워크 업데이트 기간동안 두 번 이상 리플리케이트되지 않습니다. 장기적으로 크로스 채널 트래픽 관리 및 스로틀 조절 지원을 개선시킬 계획입니다. (?)

 

주의해야할 점

액터 내에서 그냥 RPC 함수를 사용하면 되는 것이 아니다.

PlayerController 혹은 PlayerController가 제어하고있는 액터 혹은 그 액터의 컴포넌트를 통하여 제어해야 한다.

 

 

클라 호출 : 로컬에서만 실행되며, 서버에서는 실행되지 않음.

UFUNCTION( Client )
void ClientRPCFunction();

 

서버 호출 :  클라이언트에서 호출되지만 서버에서 실행

UFUNCTION( Server )
void ServerRPCFunction();​

 

멀티캐스트 이벤트 : 서버에서는 로컬에서 실행될 뿐만 아니라, 현재 연결된 모든 클라이언트에서도 실행

UFUNCTION( NetMulticast )
void MulticastRPCFunction();

 

기본 함수와 이름이 동일하지만 _Implementation끝에 추가된 추가 함수를 선언합니다.

자동 생성된 코드는 _Implementation필요할 때 메서드를 호출합니다.

.cpp

void UTestComponent::MulticastRPCFunction_Implementation(){ /*(Do Somthing)*/ }

 

신뢰성

기본적으로 RPC 는 비신뢰성입니다.

Reliable : 네트워크 상황과 무관하게 데이터가 반드시 전달된다.

Unreliable : 네트워크 상황에 따라 전달하려는 데이터가 손실될 수도 있다.

UFUNCTION( Client, Reliable )
void ClientRPCFunction();

또한 모든 RPC Function은 void만 가능, 인자로는 안되는 것도 있으니 알아보고 써야 함

 

 

서버 / 클라 구분과 주의점

if (HasAuthority()) // 서버이면 Return
	return;


if (!HasAuthority()) // 클라이면 Return 
	return;

 

Authority -> 서버일 때 (서버측에서 실행되는 클라이언트 로직)

Remote -> 클라일 때 (리모트 클라이언트 에서 실행되는 로직)

 

 

 

게임 데이터를 저장할 때 고려해야 할 사항

 - Pawn은 게임 내에서 죽거나 제거되는 경우가 많으며 그때마다 폰 클래스에 저장한 데이터도 사라진다

 - PlayerController, PlayerState는 새 레벨이 로드되지 않는 한 폰이 제거되어도 계속 존재한다

 

GameInstance

 - 게임인스턴스는 게임엔진이 시작될 때부터 종료할 때까지 존재한다

 - 서버와 클라이언트에 각각 하나의 게임인스턴스가 있고 서로 통신하지는 않는다

 - 현재 게임세션의 밖에 존재하며 레벨로드에 영향을 받지 않은 상태로 게임을 구성한다

 - 영구적인 정보를 저장할 수 있는 적합한 장소이다

 

서버에만 존재하는 객체

 - GameMode

 

서버와 모든 클라이언트에 존재하는 객체

 - GameState, PlayerState, Pawn

 

서버와 소유한 클라이언트에 존재하는 객체

 - PlayerController 는 클라이언트와 서버에 존재하지만 다른 클라이언트끼리는 공유하지 않는다 (단, 1개)

 - PlayerController는 클라이언트 서버간의 통신에 관련한 작업을 주로 한다 

 

클라이언트에만 존재하는 객체

 - HUD, UMG Widgets

(이 친구들은 서버에서 불릴 경우, 무조건 null / nullptr)

LevelBlueprint같은 곳에서 Widget에 접근할 때, 클라에서만 접근하도록 해야함

 

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=kbs3033&logNo=222001479385 

 

GameState

 - 클라이언트/서버간의 게임의 현재 상태에 대한 정보교환을 위한 중요한 클래스

 - 멀티 플레이어어 게임에서 중요한 정보인 접속된 플레이어 리스트(PlayerState의 리스트)를 포함한다

 - GameState는 모든 클라이언트에게 Replicated 되므로 모든 클라이언트가 이 객체에 접근할 수 있다

 - 멀티플레이어 게임에서 가장 핵심적인 클래스 중에 하나이다

 - GameMode가 승리를 위한 점수를 가지고 있는 반면, GameState는 현재까지 취득한 점수를 가지고 있다

 - GameState에는 개발자가 임의의 정보(배열이나 구조체 등)를 저장할 수 있다

 - GameMode에 비해 개발자가 다루어야 할 작업은 적은 편이지만 이벤트 그래프에는 모든 클라이언트가 알아야 할 로직을 작성할 수 있다

 - PlayerArray MatchState, ElapsedTime은 replicated 설정되어 있으므로 모든 클라이언트에서 접근할 수 있다

 - GameState에 선언한 변수를 Replicated 설정하고 Switch has Authority 를 사용하여 서버측에서 값을 변경하면 모든 클라이언트에서 확인할 수 있다

 

PlayerState

 - 모든 접속된 클라이언트는 현재 클라이언트의 정보를 포함하고 있는 한개의 PlayerState 객체를 갖는다

 - PlayerState 객체는 모든 클라이언트에게 Replicated 되므로 어떤 클라이언트에서 다른 클라이언트의 정보를 접할 수가 있다

 - 현재 클라이언트에서 다른 클라이언트의 PlayerState 객체에 접근하는 쉬운 방법은 GameState::getPlayerArray 를 이용하는 것이다

 - PlayerName, Score 등 다른 클라이언트에게 제공해야 하는 다양한 정보(커스텀 변수)를 이 객체에 저장하여 다른 클라이언트에게 전달할 수 있다

 - PlayerPawn이 Destroy 되더라도 PlayerState는 유지된다.

 

 

Actor Replication

- 맵에 배치된 액터를 선택하고 [디테일] 뷰 / Replication / 안에 있는 체크박스이다 

- Net Load on Client : 클라이언트에 맵이 로드되면서 동시에 이 항목이 체크된 해당 액터도 맵과 함께 클라이언트에게 보여지게 된다

 - 서버 측에는 위의 항목과 무관하게 보여진다

 - Replicate : 서버에 스폰될 때 클라이언트에게도 Replicate 된다. 

 - 서버측에 스폰하는 것이 아니라 클라이언트에 스폰될 때는 위의 항목설정과 무관하게 액터가 보여진다

 - 클라이언트에서 스폰하는 경우에는 서버측에 Replicate 되지는 않는다

 - 게임의 승패에 영향을 주는 중요한 액터를 생성할 때는 반드시 서버에 스폰하고 클라이언트에게 Replicate하는 것이 보안상 최선책이다

 - 게임의 승패에 영향이 없는 장식성 액터의 출력도 서버에 스폰하여 클라이언트에 Replicate해도 된다

 - 서버에서 Replicate설정된 액터를 삭제하면 모든 Replicate 한 클라이언트에서도 해당 액터가 삭제된다

 - 네트워크 상에서는 클라이언트가 Character를 움직이는 것이 아니라 클라이언트의 입력을 받아서 서버에서 Character를 움직이고 클라이언트에 Replicate 된다

 

예시)

UFUNCTION(BlueprintCallable, Reliable, Server)
void ServerRpcFunction();

void UTestControllerComponent::ServerRpcFunction_Implementation()
{
    AGameStateBase* GameStateBase = GetWorld()->GetGameState();
    check(GameStateBase);

    TArray<APlayerState*> PlayerArr = GameStateBase->PlayerArray;
    for (auto i = 0; i < PlayerArr.Num(); ++i)
    {
        APawn* Pawn = PlayerArr[i]->GetPawn();
        check(Pawn)

        AController *Controller = Pawn->GetController();
        check(Controller);
        
        if (UTestPawnComponent *TestComponent = Pawn->FindComponentByClass<UTestComponent>())
        {
        	TestComponent->TestFunc();
        }
    }
}


UFUNCTION(BlueprintCallable, Reliable, NetMulticast)
void TestFunc();

void UTestPawnComponent::TestFunc_Implementation()
{
	APawn* Pawn = GetPawn<APawn>();
	check(Pawn);
}

 

Replicated

기본적으로 서버의 값을 클라이언트로 복제 하는 방식.

클라이언트에서 서버로 복제하고 다른 클라에게 복제되는 방식은 RPC 함수를 통해 값을 Set 해 주는 것 외에는 없음

-> 중요한 정보를 클라에서 서버에 값을 멋대로 복제해버리는 것을 방지

 

각 액터에는 Replicated 지정자 를 포함하는 모든 프로퍼티 목록이 유지됩니다.

서버는 리플리케이트된 프로퍼티의 값이 변할 때마다 각 클라이언트에 업데이트를 전송하며, 클라이언트는 액터의 로컬 버전에 적용합니다.

이 업데이트는 서버에서만 받으며, 클라이언트는 프로퍼티 업데이트를 서버나 다른 클라이언트로 절대 전송하지 않습니다.

 

 

만약 클라에서 서버로 값을 복제 한다고 하면? -> RPC 이용

void UCustomPlayertStateComponent::BeginPlay()
{
	RPCSendToServer(5); // 시작하면 클라에서 서버로 5 Health Set
}



UFUNCTION(Reliable, Server)
void RPCSendToServer(int value);

void UCustomPlayerStateComponent::RPCSendToServer(int value)
{
	health = value
}

UPROPERTY(Replicated)
int health;

 

변수 Replicate

대상 - Replicated되는 액터 (PlayerController가 빙의하고 있는 액터나 PlayerState) 혹은 그 액터 에 붙여져 있는 컴포넌트

 

프로퍼티를 리플리케이트하려면 몇 가지 작업이 필요합니다: 프로퍼티가 정의되는 대상에서 UPROPERTY 선언에 파라미터의 하나로 replicated 키워드를 넣어줘야 합니다.

class ENGINE_API UCustomPlayerStateComponent : public UPlayerStateComponent
{
    UPROPERTY(Replicated)
    AActor * Owner;
    
    UPROPERTY(Replicated)
    int32 CharacterID;
    
    UPROPERTY(Replicated)
    FString CharacterName;
    
    UPROPERTY(Replicated)
    TArray<int32> CharacterAvatarPartsIdList;
    
    UPROPERTY(Replicated)
    EGenders CharacterGender;
};

 

대상에서 GetLifetimeReplicatedProps 함수를 구현해 줘야 합니다.

void UCustomPlayerStateComponent::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
{
    Super::GetLifttimeReplicatedProps(OutLifetimeProps);
    
    DOREPLIFETIME(ThisClass, Owner);
    DOREPLIFETIME(ThisClass, CharacterID);
    DOREPLIFETIME(ThisClass, CharacterName);
    DOREPLIFETIME(ThisClass, CharacterAvatarPartsIdList);
    DOREPLIFETIME(ThisClass, CharacterGender);
}

 

대상에서 bReplicates 플래그가 true 로 설정되었는지 확인합니다.

ACustomPlayerState::ACustomPlayerState(const FObjectInitializer& ObjectInitializer) 
: Super(ObjectInitializer)
{ 
    CustomPlayerStateComponent->SetIsReplicated(true);
}

위의 멤버 변수들은 이제 모든 사본에 대해 접속된 모든 클라이언트에 동기화될 것입니다.

'언리얼 > 언리얼 이론' 카테고리의 다른 글

언리얼 패키징 관련  (0) 2023.08.22
언리얼 플러그인  (0) 2023.05.04
PSO 캐시  (1) 2023.02.19
클래스 기본 객체 (CDO)  (0) 2022.12.18
Build.cs / Target.cs  (0) 2022.12.07

+ Recent posts