언리얼 엔진5 Advanced - 액터의 역할과 커넥션 핸드셰이킹

타입·2025년 4월 1일
0

언리얼 강의

목록 보기
26/47

액터의 역할

서버와 클라이언트에 위치한 액터의 역할

  • Authority
    클라이언트-서버 모델에서는 항상 서버에 있는 액터만이 신뢰됨
  • Proxy
    클라이언트의 액터는 대부분 서버 액터를 복제한 허상에 불과

로컬 역할과 리모트 역할

리슨 서버의 경우 플레이어로서 게임에도 참여하므로, 어플리케이션의 게임 로직을 사용함
어플리케이션의 게임 로직은 서버 액터에 대해서만 게임에 관련된 작업을 수행해야 함
이를 구분하기 위해 현재 동작하는 어플리케이션에서의 역할을 로컬 역할, 커넥션으로 연결된 어플리케이션에서의 역할을 리모트 역할이라 함

액터 역할의 종류

  • None
    액터가 존재하지 않음
  • Authority
    서비스를 대표하는 신뢰할 수 있는 역할, 게임 로직을 수행
  • AutonomousProxy
    Authority를 가진 오브젝트의 복제품, 일부 게임 로직을 수행
  • SimulatedProxy
    Authority를 가진 오브젝트의 복제품, 게임 로직을 전혀 수행하지 않음

Autonomous Proxy와 Simulated Proxy

  • Autonomous
    클라이언트의 입력 정보를 서버에 보내는 능동적인 역할을 일부 수행
    (플레이어 컨트롤러, 폰)
  • Simulated
    일방적으로 서버로부터 데이터를 수신하고 이를 반영
    (주로 배경 액터)

액터의 역할을 파악하는 API

신뢰할 수 있는 액터, 즉 Authority를 가진 액터만 게임 로직을 수행할 수 있음

  • AActor::HasAuthority()

AutonomousProxy는 예외적으로 입력에 관련된 로직을 수행할 수 있음
입력에 관련된 게임 로직의 실행은 Authority와 Autonomous Proxy에서만 허용됨

  • AController::IsLocalController()
  • APawn::IsLocallyControlled()

넷모드에 따른 오브젝트 배치

  • 서버에만 존재하는 액터
    게임 모드
  • 서버와 모든 클라이언트에 존재하는 액터
    배경 액터와 폰
  • 서버와 소유하는 클라이언트에만 존재하는 액터
    플레이어 컨트롤러
  • 클라이언트에만 존재하는 오브젝트
    애니메이션 블루프린트 및 HUD

오브젝트 배치에 따른 API의 사용

게임 모드는 HasAuthority() 함수를 호출할 필요가 없음 (서버에만 존재하니)

폰은 Autonomous와 Simulated가 혼재되어 있어 API를 사용하여 로직 구분 필요

애니메이션 재생이나 UI 관련 로직은 클라이언트에만 사용함
(서버는 변경된 속성을 전달하고, 변경된 속성에 따라 애니메이션과 UI를 바꾸도록 설계)

  • 클라이언트 접속 로그
LogABNetwork: [SERVER][ROLE_Authority/ROLE_None] AABGameMode::PreLogin Begin
LogABNetwork: [SERVER][ROLE_Authority/ROLE_None] AABGameMode::PreLogin End
LogABNetwork: [SERVER][ROLE_Authority/ROLE_None] AABGameMode::Login Begin
// 클라이언트의 컨트롤러가 생성될 땐 리모트 롤이 없음
LogABNetwork: [SERVER][ROLE_Authority/ROLE_None] AABPlayerController::PostInitializeComponents Begin BP_ABPlayerController_C_1
LogABNetwork: [SERVER][ROLE_Authority/ROLE_None] AABPlayerController::PostInitializeComponents End
LogABNetwork: [SERVER][ROLE_Authority/ROLE_None] AABGameMode::Login End
LogABNetwork: [SERVER][ROLE_Authority/ROLE_None] AABGameMode::PostLogin Begin
// 빙의를 시작하며 리모트 롤이 변경
LogABNetwork: [SERVER][ROLE_Authority/ROLE_AutonomousProxy] AABPlayerController::OnPossess Begin BP_ABPlayerController_C_1
LogABNetwork: [SERVER][ROLE_Authority/ROLE_SimulatedProxy] AABCharacterPlayer::PossessedBy Begin ABCharacterPlayer_1
LogABNetwork: [SERVER][ROLE_Authority/ROLE_SimulatedProxy] AABCharacterPlayer::PossessedBy No Owner
// 빙의 과정에서 Owner가 설정되며 Autonomous로 변경
LogABNetwork: [SERVER][ROLE_Authority/ROLE_AutonomousProxy] AABCharacterPlayer::PossessedBy Owner : BP_ABPlayerController_C_1
LogABNetwork: [SERVER][ROLE_Authority/ROLE_AutonomousProxy] AABCharacterPlayer::PossessedBy End ABCharacterPlayer_1
LogABNetwork: [SERVER][ROLE_Authority/ROLE_AutonomousProxy] AABPlayerController::OnPossess End
LogABNetwork: [SERVER][ROLE_Authority/ROLE_None] AABGameMode::PostLogin Client Connections : IpConnection_1
LogABNetwork: [SERVER][ROLE_Authority/ROLE_None] AABGameMode::PostLogin End
LogABNetwork: [CLIENT1][ROLE_None/ROLE_Authority] AABPlayerController::PostInitializeComponents Begin BP_ABPlayerController_C_0
LogABNetwork: [CLIENT1][ROLE_None/ROLE_Authority] AABPlayerController::PostInitializeComponents End
// 서버에서 설정한 정보가 넘어오며 로컬 롤이 변경
LogABNetwork: [CLIENT1][ROLE_AutonomousProxy/ROLE_Authority] AABPlayerController::PostNetInit Begin BP_ABPlayerController_C_0
LogABNetwork: [CLIENT1][ROLE_AutonomousProxy/ROLE_Authority] AABPlayerController::PostNetInit Server Connection : IpConnection_0
LogABNetwork: [CLIENT1][ROLE_AutonomousProxy/ROLE_Authority] AABPlayerController::PostNetInit End
LogABNetwork: [CLIENT1][ROLE_AutonomousProxy/ROLE_Authority] AABCharacterPlayer::OnRep_Owner Begin ABCharacterPlayer_0
LogABNetwork: [CLIENT1][ROLE_AutonomousProxy/ROLE_Authority] AABCharacterPlayer::OnRep_Owner End ABCharacterPlayer_0
LogABNetwork: [CLIENT1][ROLE_AutonomousProxy/ROLE_Authority] AABCharacterPlayer::PostNetInit Begin ABCharacterPlayer_0
LogABNetwork: [CLIENT1][ROLE_AutonomousProxy/ROLE_Authority] AABCharacterPlayer::PostNetInit End
// 서버 플레이어의 캐릭터는 오너십을 가지고 있지 않아 SimulatedProxy
LogABNetwork: [CLIENT1][ROLE_SimulatedProxy/ROLE_Authority] AABCharacterPlayer::PostNetInit Begin ABCharacterPlayer_1
LogABNetwork: [CLIENT1][ROLE_SimulatedProxy/ROLE_Authority] AABCharacterPlayer::PostNetInit End

자신의 캐릭터인지 확인

// Pawn.cpp
bool APawn::IsLocallyControlled() const
{
	return ( Controller && Controller->IsLocalController() );
}

// Controller.cpp
bool AController::IsLocalController() const
{
	const ENetMode NetMode = GetNetMode();

	if (NetMode == NM_Standalone)
	{
		// Not networked.
		return true;
	}
	
    // 클라이언트의 경우엔 AutonomousProxy여야함
	if (NetMode == NM_Client && GetLocalRole() == ROLE_AutonomousProxy)
	{
		// Networked client in control.
		return true;
	}

	// 서버가 플레이어로 참여하면 로컬 롤은 당연히 Authority
    // 리모트 롤은 남한테 권한을 줄 필요가 없으니 SimulatedProxy로 동작
	if (GetRemoteRole() != ROLE_AutonomousProxy && GetLocalRole() == ROLE_Authority)
	{
		// Local authority in control.
		return true;
	}

	return false;
}

언리얼 엔진의 커넥션 핸드셰이킹

커넥션 핸드셰이킹

네트워크로 접속하는 두 컴퓨터가 잘 연결되었는지 확인하는 과정

  • 언리얼 네트워크 멀티플레이 접속을 위한 핸드셰이킹 과정

게임의 준비

커넥션을 허용하면 게임을 시작할 수 있도록 클라이언트와 서버는 준비 과정을 거침

  • 클라이언트
    맵의 로딩
  • 서버
    클라이언트를 대표하는 플레이어 컨트롤러의 생성

커넥션 핸드셰이킹의 확인

  • 관련 소스코드
    DataChannel.h
    UWorld::NotifyControlMessage() - 서버
    UPendingNetGame::NotifyControlMessage() - 클라이언트

알아두면 좋은 언리얼 네트워크 시스템 구성

  • 관련 소스코드
    NetDriver.h

용도에 따라 패킷을 처리하는 다양한 NetDriver 클래스를 제공함

  • GameNetDriver
    게임 데이터를 처리하는데 사용하는 네트워크 드라이버
  • DemoNetDriver
    게임 리플레이 데이터를 처리하는데 사용하는 네트워크 드라이버
  • BeaconNetNetDriver
    게임 외 데이터를 처리하는데 사용하는 네트워크 드라이버

언리얼 엔진은 게임 데이터를 처리하는 게임넷드라이버로 IpNetDriver를 사용함
초기 접속에 관련된 데이터 패킷은 ControlChannel을 통해 분석됨

언리얼 엔진에서 번치를 처리하는데 사용하는 주요 채널

  • ControlChannel
    클라이언트 서버 간의 커넥션을 다룰 때 사용하는 채널
  • ActorChannel
    액터 리플리케이션 작업을 다룰 때 사용하는 채널
  • VoiceChannel
    음성 데이터를 전달할 때 사용

주요 함수 흐름

  • UPendingNetGame::SendInitialJoin()
    서버로 Hello 패킷 전송
    • FNetControlMessage<NMT_Hello>::Send()
  • UWorld::NotifyControlMessage()
    클라이언트에서 온 Hello 패킷을 받아 처리
    • Connection->SendChallengeControlMessage()
      클라이언트로 Challenge 패킷 전송
  • UPendingNetGame::NotifyControlMessage()
    서버에서 온 Challenge 패킷을 받아 처리
    • FNetControlMessage<NMT_Login>::Send()
      다시 서버로 Login 패킷 전송
  • AGameMode::PreLogin()
    클라이언트로 Welcome 패킷 전송
  • UPendingNetGame::SendJoin()
    클라이언트에서 맵 로딩이 완료되면 실행
    • FNetControlMessage<NMT_Join>::Send()
      서버로 Join 패킷 전송
      (NetSpeed도 전송)
  • Bunch 정보
// message type definitions
DEFINE_CONTROL_CHANNEL_MESSAGE(Hello, 0, uint8, uint32, FString, uint16); // initial client connection message
DEFINE_CONTROL_CHANNEL_MESSAGE(Welcome, 1, FString, FString, FString); // server tells client they're ok'ed to load the server's level
DEFINE_CONTROL_CHANNEL_MESSAGE(Upgrade, 2, uint32, uint16); // server tells client their version is incompatible
DEFINE_CONTROL_CHANNEL_MESSAGE(Challenge, 3, FString); // server sends client challenge string to verify integrity
DEFINE_CONTROL_CHANNEL_MESSAGE(Netspeed, 4, int32); // client sends requested transfer rate
DEFINE_CONTROL_CHANNEL_MESSAGE(Login, 5, FString, FString, FUniqueNetIdRepl, FString); // client requests to be admitted to the game
DEFINE_CONTROL_CHANNEL_MESSAGE(Failure, 6, FString); // indicates connection failure
DEFINE_CONTROL_CHANNEL_MESSAGE(Join, 9); // final join request (spawns PlayerController)
DEFINE_CONTROL_CHANNEL_MESSAGE(JoinSplit, 10, FString, FUniqueNetIdRepl); // child player (splitscreen) join request
DEFINE_CONTROL_CHANNEL_MESSAGE(Skip, 12, FGuid); // client request to skip an optional package
DEFINE_CONTROL_CHANNEL_MESSAGE(Abort, 13, FGuid); // client informs server that it aborted a not-yet-verified package due to an UNLOAD request
DEFINE_CONTROL_CHANNEL_MESSAGE(PCSwap, 15, int32); // client tells server it has completed a swap of its Connection->Actor
DEFINE_CONTROL_CHANNEL_MESSAGE(ActorChannelFailure, 16, int32); // client tells server that it failed to open an Actor channel sent by the server (e.g. couldn't serialize Actor archetype)
DEFINE_CONTROL_CHANNEL_MESSAGE(DebugText, 17, FString); // debug text sent to all clients or to server
DEFINE_CONTROL_CHANNEL_MESSAGE(NetGUIDAssign, 18, FNetworkGUID, FString); // Explicit NetworkGUID assignment. This is rare and only happens if a netguid is only serialized client->server (this msg goes server->client to tell client what ID to use in that case)
DEFINE_CONTROL_CHANNEL_MESSAGE(SecurityViolation, 19, FString); // server tells client that it has violated security and has been disconnected
DEFINE_CONTROL_CHANNEL_MESSAGE(GameSpecific, 20, uint8, FString); // custom game-specific message routed to UGameInstance for processing
DEFINE_CONTROL_CHANNEL_MESSAGE(EncryptionAck, 21);
DEFINE_CONTROL_CHANNEL_MESSAGE(DestructionInfo, 22);
DEFINE_CONTROL_CHANNEL_MESSAGE(CloseReason, 23, FString); // Reason for client NetConnection Close, for analytics/logging
DEFINE_CONTROL_CHANNEL_MESSAGE(NetPing, 24, ENetPingControlMessage /* MessageType */, FString /* MessageStr */);

실습코드

https://github.com/dnjfs/ArenaBattle_Network/commit/7caff5f6a2d0de3157ae53357f8ec9e23e406351

  • UEnum::GetValueAsString()
    UENUM()으로 선언된 enum의 이름을 반환
profile
주니어 언리얼 프로그래머

0개의 댓글