게임 개발 메모장
[ UE5 ] 게임 모드와 로그인 본문
네트워크 모드 및 서버 유형
네트워크 모드는 네트워크 멀티플레이어 세션에 대한 컴퓨터의 관계를 설명한다.
어플리케이션을 담당하는 게임 인스턴스는 다음과 같은 네트워크 모드 중 하나를 취할 수 있다.
네트워크 모드
Standalone | 이 게임은 서버로 실행되고 있으며 원격 클라이언트의 연결을 허용하지 않는다. 게임에 참여하는 모든 플레이어는 엄격하게 로컬 플레이어입니다. 이 모드는 싱글 플레이어 및 로컬 멀티 플레이어 게임에 사용된다.로컬 플레이어에 맞게 서버 측 로직과 클라이언트 측 로직을 모두 실행한다. |
Client | 이 게임은 네트워크 멀티플레이어 세션의 서버에 연결된 클라이언트로 실행되고 있다. 서버 측 로직을 실행하지 않는다. |
Listen Server | 게임은 네트워크 멀티플레이어 세션을 호스팅하는 서버로 실행되고 있다. 원격 클라이언트의 연결을 수락하고 서버에 로컬 플레이어가 직접 있다.( 자기 자신도 하나의 플레이어로서 게임에 참여 한다는 의미 ) 이 모드는 종종 캐주얼한 협동 및 경쟁 멀티플레이어에 사용된다. |
Dedicated Server | 게임은 네트워크 멀티플레이어 세션을 호스팅하는 서버로 실행되고 있다. 원격 클라이언트의 연결은 허용하지만 로컬 플레이어가 없기 때문에 그래픽, 사운드, 입력 및 기타 플레이어 지향 기능을 폐기하여 보다 효율적으로 실행할 수 있다. 그래서, 이 모드는 보다 많은 접속을 처리할 수 있고 더 지속적이고 안전하며 대규모 멀티플레이어가 필요한 게임에 자주 사용된다. ( 서버는 서버의 기능만 처리 ) |
Listen Server
는 게임 복사본을 가진 모든 사용자가 리슨 서버를 시작하고 동일한 컴퓨터에서
플레이할 수 있기 때문에 사용자가 자발적으로 설정하기 쉽다.
Listen Server를 지원하는 게임에는 서버를 시작하거나 가입할 서버를 검색하기 위한
인게임 UI가 포함되어 있는 경우가 많다.
하지만 Listen Server 를 진행하는 플레이어가 직접 서버에서 플레이하기 때문에
다른 플레이어에 비해 유리하고 네트워크 연결을 이용해야 Play 할 수 있어
공정성과 부정 행위에 대한 우려가 있다.
또한, 서버로 실행되고 그래픽 및 사운드와 같은 플레이어 관련 시스템을
지원하는 것과 관련된 추가 처리 부하도 있다.
이러한 요인으로 인해 리슨 서버는 경쟁이 치열한 환경의 게임이나
네트워크 부하가 매우 높은 게임에는 적합하지 않지만 소규모 플레이어
그룹 간의 일상적인 협력 및 경쟁력 있는 멀티플레이어에는 매우 편리하다.
-> 서버는 가장 큰 권한 게임의 규칙을 중재하는 굉장히 큰 권한을 가지고 있는데
그 서버가 직접 플레이어가 된다는 이야기
스포츠 경기로 따지면 심판이 선수가 되는 것이다. 우려가 제기될 수 있지만
심판이 스포츠 경기에서 플레이어를 뛴다고 해도 규칙을 잘 준수하면
사실상 문제 될 수는 없다.
또한, 서버로 실행되고 그래픽 및 사운드가 같이 지원하는 것과
관련된 추가적인 처리 부하가 있다.
즉, 서버 기능만 수행되는 것이 아니고 클라리언트 기능도 같이 실행이 되기 때문에 부하가 많다.
이러한 요인으로 인해 리슨 서버는 경쟁이 치열한 환경의 게임이나
네트워크 부하가 매우 높은 게임은 적합하지 않다고 되어있다.
즉, 접속자가 많거나 데이터 양이 많은 이런 게임에는 적합하지 않다는 것
서버는 서버의 기능만 딱 최적화해서 돌리는 것이 좋다.
하지만 소규모 플레이어 그룹간의 캐쥬얼한 협동 및 경쟁 멀티플레이어에는 매우 편리하다.
당연히, 서버를 관리할 것이 따로 없기 때문에 보통은 클라우드 같이 서비스에다가
서버 프로세스를 올려놓고 게임들을 실행하도록 설계를 하는데 이 경우에
그런 네트워크 자원이 필요가 없다는 말.
Dedicated Server
는 비용이 더 많이 들고 구성이 어렵기 때문에 게임에 참여할 수 있는
모든 플레이어로부터 자체 네트워크 연결을 통해 별도의 컴퓨터가 필요하다.
그러나 Dedicated Server에 가입하는 모든 플레이어는 동일한 유형의 연결로 게임을 경험하므로
공정성이 보장된다. 전용 서버는 로컬 플레이어에만 관련된 그래픽을 렌더링하거나
다른 로직을 수행하지 않기 때문에 게임 플레이 및 네트워킹을 보다 효율적으로 처리할 수 있다.
따라서 보안, 공정성 또는 신뢰성을 이유로 많은 수의 플레이어가 필요한 게임이나
고성능의 신뢰할 수 있는 서버가 필요한 게임에 전용 서버가 선호된다.
이러한 게임에는 MMO, 경쟁력 있는 MOBA 또는 빠르게 진행되는 온라인 슈터가 포함된다.
독립형 게임은 서버와 클라이언트 역할을 모두 수행하기 때문에
멀티플레이어 게임을 위해 만들어진 모든 로직은 추가 작업 없이
단일 플레이어에서 작동한다.
서버의 초기화 단계, 서버를 담당할 어플리케이션은 실행 되었지만
게임은 아직 시작되지 않은 상황 StandAlone 이나 서버의 경우에는
게임에서 중요한 심판 역할을 담당하는 게임 모드 액터가 존재한다.
리슨 서버는 클라이언트와 서버의 기능이 함께 공존하는 방식으로 동작한다.
그렇기에 리슨 서버를 돌리는 컴퓨터도 게임에 참여를 해야 되는데
게임에 참여하기 위해서는 반드시 플레이어가 있어야 된다.
이 플레이어를 대표하는 액터가 플레이어 컨트롤러이다.
리슨 서버를 본격적으로 시작하기 전에 우선 자기 자신이 게임에 참여를 해 있어야 된다.
이를 위해서는 게임 모드를 사용해서 플레이어 컨트롤을 생성한다.
자기 자신이 게임에 참여할지라도
게임 모드가 제공하는 로그인 과정을 통해서 진행된다.
게임 모드의 로그인을 통해 서버 역할을 수행할 컴퓨터에는
게임 모드와 플레이어 컨트롤로
액터가 생성되었으며 게임은 아직도 준비 중에 있는 상황이다.
게임 모드와 플레이어 컨트롤러가 생성되었으니 이제 다른 사용자를 받아들일 준비가
되었다. 그래서 네트워크 기능을 켜고 게임 서비스를 시작
게임을 바로 시작하지 않고 다르게 진행하는 것도 가능하다.
예를 들어서 로비를 만들고 지정한 수의 인원이 다 차면은 게임을 시작할 수 있도록 설계하는
것도 가능하다.
이제 서버 역할을 수행하는 컴퓨터에서는 게임이 돌아가고 있고 어플리케이션의 네트워크
모드는 StandAlone에서 ListenServer로 변경되었다.
이번에는 클라이언트가 서버에 접속해서 게임에 참가한다고 신호를 보낸다.
이때 서버는 클라이언트의 접속 요청을 거부 할 수도 있다.
하지만 받아들인다는 가정하에
접속 요청을 받아들이면 이제 서버에서는 접속할 클라이언트를
담당하는 플레이어 컨트롤러를 생성해 준다.
이제 서버에는 두 개의 플레이어 컨트롤러가 존재하게 된다.
클라이언트를 대표하는 플레이어 컨트롤러는 클라이언트에 복제 된다.
언리얼 엔진이 채택한 클라이언트 서버 모델에서 클라이언트는 서버가 가지고 있는
컨텐츠의 허상을 복제해서 사용자에게 보여준다.
이때 서버에 있는 모든 정보를 다 전달하지 않고 보여지는데 문제 없을 정도로만
컨텐츠가 효율적으로 복제된다.
서버에 보안 문제도 있고 네트워크 데이터를
딱 필요한 만큼만 사용하는 것이 좋기 때문이다.
그래서 서버의 게임 모드와 서버 컴퓨터의 플레이어 정보인 플레이어 컨트롤러 0번은
복제가 되지 않고 게임 클라이언트에 관련된 액터인 플레이어 컨트롤러 1번만 복제가 된다.
클라이언트에 복제된 플레이어 컨트롤러는 클라이언트에서는 0번으로 관리가 된다.
그리고 서버에서 게임은 시작되었기 때문에 클라이언트는 접속하자마자 게임을 시작하게 된다.
여기까지 로그인 플로우 이었다.
PreLogin 함수의 경우에는 클라이언트가 접속을 시도하려고 할 때
이것의 접속 요청을 받아들일 것인가 말 것인가를 처리해주는 함수.
( 이때는 Login 이 된 상황이 아니다. )
PreLogin을 통과 했을 때 비로소 Login 이 진행이 된다.
AABGameMode::Login 의 리턴 값이 플레이어 컨트롤러였다.
그래서 서버에서 플레이어 컨트롤러를 만들어서 접속한 클라이언트한테 보내준다.
이렇게 해서 플레이어 컨트롤러를 만들었으면
이 플레이어 컨트롤러만 딸랑 보내는 것이 아니고
이 플레이어 컨트롤러가 빙의할 캐릭터 세팅까지
모두 다 정리해 가지고 보내주는 것이다.
PostLogin 여기서 로그인은 끝났지만
세팅까지 완벽하게 다 차려가지고
게임을 시작할 수 있게 준비까지 맞춰주는
이 과정이 PostLogin 이 되겠다.
이렇게 되면 이제 게임을 시작할 준비가 됐다는 것이지만
실제로 아직 게임이 시작된 것은 아니다.
게임 시작이라는 것은 Login과는 또 별개의 개념이기 때문에
예를 들어서 루비의 여러 사람이 게임을 시작하기 위해서 대기할 수도 있는 것이다.
우리는 이제 그냥 바로 게임을 시작하겠지만 조건이 충족되면
게임 모드에서 StartPlay라는 함수를 호출해서 게임을 시작하도록 명령을 내릴 수가 있다.
그렇게 되면 StartPlay에 의해서 지금 레벨에 있는 모든 액터들이 BeginPlay를
호출하면서 게임 콘텐츠를 시작하게 되는 이런 흐름으로 진행됨.
[ 서버 ]
LogABNetwork: [SERVER] AABGameMode::Login Begin
LogABNetwork: [SERVER] AABGameMode::Login End
LogABNetwork: [SERVER] AABGameMode::PostLogin Begin
LogABNetwork: [SERVER] AABGameMode::PostLogin End
LogABNetwork: [SERVER] AABGameMode::StartPlay Begin
LogABNetwork: [SERVER] AABGameState::HandleBeginPlay Begin
LogABNetwork: [SERVER] AABPlayerController::BeginPlay Begin
LogABNetwork: [SERVER] AABPlayerController::BeginPlay GameMode : BP_ABGameMode_C_0
LogABNetwork: [SERVER] AABPlayerController::BeginPlay End
LogABNetwork: [SERVER] AABGameState::HandleBeginPlay End
LogABNetwork: [SERVER] AABGameMode::StartPlay End
서버만 가동 했기 때문에 서버에서는 PreLogin이라는 과정 자체가 없다.
왜냐하면 자기 자신이 곧 법인데 굳이 로그인을 우리가 미리 허용할지
안할지를 검사할 필요가 없는 것이다.
바로 로그인 부터 시작이 되고 그 다음에 포스트 로그인을 거쳐
이런 캐릭터까지 다 준비가 완료된 상황에서 이제 준비가 되었으니
게임을 시작 하겠습니다.
라고 해서 스타트 플레이가 호출이 되면 이 Start Play가
호출되는 이 시점에서 스타트 플레이 함수에 의해서 이 현재 게임에 있는
모든 액터들의 BeginPlay 함수가 호출이 되는 이런 과정을
우리가 로그를 통해서 확인할 수가 있다.
[ 클라이언트가 새롭게 추가 ]
LogABNetwork: [SERVER] AABGameMode::PreLogin =================================
LogABNetwork: [SERVER] AABGameMode::PreLogin Begin
LogABNetwork: [SERVER] AABGameMode::PreLogin End
LogABNetwork: [SERVER] AABGameMode::Login Begin
LogABNetwork: [SERVER] AABPlayerController::BeginPlay Begin
LogABNetwork: [SERVER] AABPlayerController::BeginPlay GameMode : BP_ABGameMode_C_0
LogABNetwork: [SERVER] AABPlayerController::BeginPlay End
LogABNetwork: [SERVER] AABGameMode::Login End
LogABNetwork: [SERVER] AABGameMode::PostLogin Begin
LogABNetwork: [SERVER] AABGameMode::PostLogin End
LogABNetwork: [CLIENT0] AABGameState::OnRep_ReplicatedHasBegunPlay Begin
LogABNetwork: [CLIENT0] AABPlayerController::BeginPlay Begin
LogABNetwork: [CLIENT0] AABPlayerController::BeginPlay GameMode : nullptr
LogABNetwork: [CLIENT0] AABPlayerController::BeginPlay End
LogABNetwork: [CLIENT0] AABGameState::OnRep_ReplicatedHasBegunPlay End
클라이언트를 추가를 하면 서버와는 다르게 PreLogin 이 찍힌다.
PreLogin이 서버에서 실행이되고,
서버에서 로그인 과정을 거쳐서 클라이언트에게
줄 PlayerController 를 생성한다.
그리고 나서 이 클라이언트용 플레이어 컨트롤러 셋업을 완료를 하면
서버에서 작업할 것이 다 끝났다.
그러면 이것은 서버에만 존재를 하기 때문에
서버는 클라이언트에도 나 다 준비가 되었으니
너도 똑같은 환경을 만들어서 기다려라 라고 명령을 보낼 것이다.
클라이언트에서도 지금 이 새로운 PlayerController가 만들어질 것이다.
클라이언트의 경우에는 맨 처음에 만들었던
서버에 PlayerController가 사실 필요가 없다.
필요가 없기에 지금 PlayerController는 하나만 가지게 된다.
특별한 에러 메시지를 할당하지 않으면
성공이라고 간주를 해서 로그인 과정이 진행이 되고.
이 로그인 과정에서 플레이어 컨트롤러는 BeginPlay가 호출된다.
왜냐하면 이 전에 이미 게임이 시작되었다고 이미 선언했기 때문에
이때부터는 시작된 게임에 들어오는 형태가 된다.
그렇기 때문에 생성되자마자 Controller의 BeginPlay가 호출이 되는데
PostLogin이 끝나고 클라이언트의 Controller에서 BeginPlay가 호출된다.
게임 모드에 StartPlay 함수가 호출되면
서버에서는 공식적으로 게임이 시작되는 것을 의미한다.
그래서 이후에 생성된 모든 액터들은 BeginPlay 함수가 자동으로 호출된다.
그런데 클라이언트에는 게임 모드가 없다.
그렇다면 클라이언트에 있는 액터는 어떻게 신호를 받고 게임을 시작하게 되는 것일까?
이를 위해서, 서버는 GameState라고 하는 특별한 액터를 사용한다.
GateState 액터는 게임 정보를 알려주는 특별한 액터다.
그런데, GameMode와 다르게 GameState 액터는 서버와 클라이언트에 모두 존재한다.
GameMode는 게임을 시작할 때 본인이 직접 게임을 시작하지 않고
GameState에 명령을 내려가지고 게임을 시작하도록 간접적으로 지시를 한다.
GameMode에 명령을 받은 GameState는 월드에 속한
모든 액터들에게 BeginPlay를 호출하도록 지시를 하게 된다.
이러한 GameState는 클라이언트에게도 복제가 된다.
게임이 시작되었다는 정보가 이 복제된 GameState에 반영되면은
이 반영되는 타이밍에 복제된 GameState가 클라이언트에 있는 모든 액터에게
BeginPlay를 호출하라고 지시를 내리게 된다.
게임모드가 StaryPlay를 지시 -> 게임 스테이트에게 명령을 내려 가지고
게임 스테이트가 현재 월드에 있는 모든 액터들에게 BeginPlay를 호출하고
게임을 시작하라고 이렇게 명령을 내린다.
하지만, 이것은 로컬의 로직이기 때문에 서버에서만 실행이되고
OnRep_ReplicatedHasBegunPlay 같은 경우에도 동일하게 수행이 되는데
여기서는 beReplcatedHasBegunPlay 라고 하는 프로퍼티가 서버로부터 전송되서
변경이 되어서 감지가 되면 저 함수가 호출이 되는 것이고
그때 클라이언트가 신호를 받는 것이다.
내가 이제 게임을 시작해야 되는 구나 라고 신호를 받아가지고
저 함수를 호출해주는 형태로 진행이된다.
[ 서버만 구동 된 상태에서]
LogABNetwork: [SERVER] AABGameMode::Login Begin
LogABNetwork: [SERVER] AABGameMode::Login End
LogABNetwork: [SERVER] AABGameMode::PostLogin Begin
LogABNetwork: [SERVER] AABGameMode::PostLogin End
LogABNetwork: [SERVER] AABGameMode::StartPlay Begin
LogABNetwork: [SERVER] AABGameState::HandleBeginPlay Begin
LogABNetwork: [SERVER] AABPlayerController::BeginPlay Begin
LogABNetwork: [SERVER] AABPlayerController::BeginPlay GameMode : BP_ABGameMode_C_0
LogABNetwork: [SERVER] AABPlayerController::BeginPlay End
LogABNetwork: [SERVER] AABGameState::HandleBeginPlay End
LogABNetwork: [SERVER] AABGameMode::StartPlay End
[ 이후에 클라이언트 하나가 접속 ]
LogABNetwork: [SERVER] AABGameMode::PreLogin =================================
LogABNetwork: [SERVER] AABGameMode::PreLogin Begin
LogABNetwork: [SERVER] AABGameMode::PreLogin End
LogABNetwork: [SERVER] AABGameMode::Login Begin
LogABNetwork: [SERVER] AABPlayerController::BeginPlay Begin
LogABNetwork: [SERVER] AABPlayerController::BeginPlay GameMode : BP_ABGameMode_C_0
LogABNetwork: [SERVER] AABPlayerController::BeginPlay End
LogABNetwork: [SERVER] AABGameMode::Login End
LogABNetwork: [SERVER] AABGameMode::PostLogin Begin
LogABNetwork: [SERVER] AABGameMode::PostLogin End
LogABNetwork: [CLIENT0] AABGameState::OnRep_ReplicatedHasBegunPlay Begin
LogABNetwork: [CLIENT0] AABPlayerController::BeginPlay Begin
LogABNetwork: [CLIENT0] AABPlayerController::BeginPlay GameMode : nullptr
LogABNetwork: [CLIENT0] AABPlayerController::BeginPlay End
LogABNetwork: [CLIENT0] AABGameState::OnRep_ReplicatedHasBegunPlay End
( OnRep_ 이라는 접두사 그냥 클라이언트에서 원격으로 호출되는 함수다. 라고 간단하게 이해 )
플레이 버튼을 누르면 StartPlay가 시작이 되면 HandleBeginPlay가 시작이 되고
이 HandleBeginPlay에 의해서 BeginPlay가 호출되는 과정을 잘 살펴볼 수가 있다.
클라이언트가 하나가 접속이 되면 먼저 서버에서 관련된 플레이어 컨트롤러 하나가 제작이되는데
클라이언트에 대해서는 클라이언트는 아직 이것이 시작했는지 아닌지를 알 수가 없다.
그래서 클라리언트에 복제된 게임 스테이트에 의해서 OnRep 함수가 호출이 되는데
라는 속성이 변경이 되었을 때 이 함수가 호출이 되고 이 함수에 의해서 클라이언트에 복제된
플레이어 컨트롤러의 BeginPlay가 호출되는것을 확인해 볼 수 있다.
* 필요한 매크로 설정
GPlayInEditorID : PID 환경에서 관리하고 있는 전역 변수
이게 PID 모드가 아니면 -1 값이 나오는데 이것을 호출해서
몇 번째 클라이언트인지 찍도록 숫자를 추가한다.
클라이언트가 아닌 경우 판별
GetNetMode가 StandAlone이 아니면 이제는 Server 밖에 없다.
StandAlone이면 STANDALONE 이라고 써주고
나머지는 Dedicated Server와 Listen Server 인데
그냥 ListenServer만 할꺼라서 그냥 Server라고 짓는다.
Play 버튼을 누르면 출력 로그창에 각 모드에 대한 정보를 바로 볼 수 있다.
'언리얼 엔진 > Network' 카테고리의 다른 글
[ UE5 ] RPC (1) | 2024.01.05 |
---|---|
[ UE5 ] 캐릭터 공격 구현 예시 (0) | 2023.12.30 |
[ UE5 ] 액터 리플리케이션 (0) | 2023.12.25 |
[ UE5 ] 커넥션과 오너십 (0) | 2023.12.25 |
[ UE5 ] 언리얼 네트워크 멀티 플레이어 프레임워크 (0) | 2023.12.24 |