0. 개요

많은 사람들이 Udemy의 멀티플레이어 게임 제작 강의를 수강하고 그 내용을 잘 정리해두었다. 보면서 기능을 따라 만들고싶다면 해당 자료들을 참고하자. 이 글에서는 Online Subsystem에 어떠한 기능들이 있는지 대강 정리하였다. 세부적인 내용은 직접 코드 참고바람.

핵심 구조

  • OnlineSessionInterface
    • 플랫폼에 관계 없이 세션에 필요한 기능들을 모아놓은 인터페이스
    • IOnlineSession 인터페이스나 별개의 구조체 등이 정의되어있다
  • Class IOnlineSession
    • 온라인 세션 서비스용 인터페이스 정의
  • Class FOnlineSessionSteam
    • OnlineSessionInterface를 스팀 플랫폼에 적합하게 변형하여 구현
  • Class EPMultiplayerSessionSubsystem
    • 위의 요소들을 활용하여 세션 생성, 탐색, 참가, 시작, 제거 등의 기능 접근용

자주 나오는 정보들

  • FUniqueNetId → 유저 식별용 ID
  • FOnlineSessionSettings → 세션, 세션정보, 탐색 결과 등의 프로퍼티 정보
    • FOnlineSession & FNamedOnlineSession → 이 세션은 어떤 정보를 가지는가
    • FOnlineSessionSearch & FOnlineSessionSearchResult → 세션 탐색 결과
  • OnlineSubsystemTypes → enums로 각종 타입 지정

1. OnlineSessionInterface

1) 온라인 세션 인터페이스 헤더

  • JoinSession 결과 표시용 namespace 존재. String버전도 있음.
  • 대리자가 많다! 발동 조건 알아두기.
    • FOnJoinSessionComplete - 세션 합류 결과. 성공이나 실패 이유도 출력.
    • FOnSessionParticipantJoined - 세션 참가 시
    • FOnSessionParticipantLeft - 세션 퇴장 시
    • FOnSessionSettingsUpdated - 세션 세팅 변경 시
    • FOnSessionParticipantSettingsUpdated - 세션 참가자의 세팅변경 시
    • FOnSingleSessionResultComplete - 친구 초대같은 단일 세션 결과 반환 시
    • FOnFindFriendSessionComplete - 친구 초대의 경우
    • FOnPingSearchResultsComplete - 각 서버에 핑 날린거 완료 시
    • FOnSessionUserInviteAccepted / FOnSessionInviteReceived - 초대 및 거절
    • FOnSessionFailure - 세션 연결 혹은 사용중 에러 발생
    • 더 있지만 일단 중요한건 위의 것들로 생각한다.
  • 세션 매치매이킹 유저 정보는 UserId와 Attribute로 구성된다.
  • FSessionMatchmakingResults도 있는데 비어있다. 세션 매치메이킹 결과를 원하는대로 담는 구조체이다.

2) 온라인 세션 인터페이스

2-1) 기본 함수

  • AddNamedSession - 해당 이름과 세팅으로 새 세션 추가
  • CreateSessionIdFromString - 세션 ID만 새로 세션 생성
  • GetNamedSession - 해당 이름의 세션들을 찾아서 반환한다.
  • RemoveNamedSession - 해당 이름의 세션들을 찾아서 제거
  • GetSessionState - 현재 세션의 상태 문구 반환.
  • IsPlayerInSession() - 플레이어가 세션에 존재하는지 여부
  • FindSessionById - 친구가 초대했을 때 특정 세션 참가용
  • CancelFindSessions - 세션 탐색 취소용
  • PingSearchResults - 서버에 핑 쿼리를 보내고 대리자로 결과 받음
  • FindFreindSession / SendSessionInviteToFriends - 친구 세션 찾기와 초대
  • GetSessionSettings - 세션 이름을 주면 세팅을 반환
  • RegisterPlayer / RegisterPlayers / UnRegisterPlayer / UnregisterPlayers - 세션에 플레이어를 등록, 해제

2-2) 핵심함수

  • CreateSession
    • 비동기적 - OnCreateSessionComplete 대리자가 호출되어야 끝난다.
    • 세션 이름과 세팅을 넘기는건 동일하지만, HostingPlayerNum / HostingPlayerId 로 다른 버전이 존재한다.
  • StartSession
    • 로비에 있거나 대기중인것과는 반대되는, 세션 시작.
  • UpdateSession
    • 세션 정보를 업데이트. 반드시 할 것인지, 뭘 할 것인지 결정하기.
  • EndSession
    • 온라인 세션을 종료한다.
  • DestroySession
    • 특정 온라인 세션을 제거한다.
  • FindSessions
    • 조건에 맞는 세션들 탐색. 인원 / ID로 탐색 가능
  • JoinSessions
    • 세션 이름과 정보를 알고있어야 세션에 참가 가능.
  • StartMatchMaking
    • 클라우드 기반 세션 매치메이킹 시작.
    • 참여할 로컬 플레이어, 세션 이름, 세션의 세팅과, 세션이 검색될 조건들 지정
  • CancelMatchmaking
    • 매치메이킹 취소해주시죠
    • 취소할 사람 인덱스 / ID 넣어서 나가게함
  • GetResolvedConnectString
    • 매치에 참여하기 위해 필요한 정보들이 플랫폼마다 다르다. 그래서 join이 완료될 때 이걸 호출해서 플랫폼에 맞는 정보를 추출한다.

2-3) 대리자

  • OnCreateSessionComplete - 생성 완료시 호출
  • OnStartSessionComplete - 세션 시작하면 호출
  • OnUpdateSessionComplete - 업데이트
  • OnEndSessionComplete - 세션 끝!
  • OnDestroySessionComplete - 세션 파괴
  • OnMatchmakingComplete - 매치메이킹 실행 완료
  • OnCancelMatchmakingComplete - 매치메이킹 취소 완료
  • OnFindSessionsComplete- 세션 탐색 완료
  • OnPingSearchResultsComplete - 핑 써치
  • OnJoinSessionComplete - 세션 조인
  • OnSessionParticipants Change / Joined / Left / Removed - 인원변화
  • OnUnregisterPlayersComplete - 플레이어를 세션에 등록완료

주의사항

  • 몇몇 함수들은 완료 대리자가 호출되어야 함수가 종료된다. 보통 대리자가 그결과를 담고 있는 경우가 많다.

2) FUniqueNetId

ID를 담아서 타입, 크기, 유효성 인코딩 등의 작업을 진행한다. FUniqueNetId의 인스턴스를 사용.

3) FOnlineSessionSettings

3-1) 구조체

  • FOnlineSessionSettings
    • 세션을 설명하기 위한 키, 값, 어떻게 알릴건지와 같은 정보들이 들어있다.
    • 세팅이 다양하고 많다보니 FName, Setting 페어로 저장된다.

3-2) 파라미터

  • int32 NumPublicConnections - 광고된 공용 사용 가능 연결 수
  • int32 NumPrivateConnections - 비공개(초대/암호 전용) 연결 수
  • bool bShouldAdvertise - 이 매치가 온라인 서비스에 공개 광고되는지 여부
  • bool bAllowJoinInProgress - 진행 중인 참가가 허용되는지 여부
  • bool bIsLANMatch - 이 게임이 LAN 전용이며 외부 플레이어에게 보이지 않음
  • bool bIsDedicated - 서버가 전용 서버인지 플레이어가 호스팅하는지 여부
  • bool bUsesStats - 매치가 통계를 수집할지 여부
  • bool bAllowInvites - 이 세션에 초대가 허용되는지 여부
  • bool bUsesPresence - 사용자 존재 정보를 표시할지 여부
  • bool bAllowJoinViaPresence - 플레이어 존재를 통해 참가가 허용되는지 여부
  • bool bAllowJoinViaPresenceFriendsOnly - 친구에게만 플레이어 존재를 통해 참가가 허용되는지 여부
  • bool bAntiCheatProtected - 서버가 안티 치트를 사용하는지 여부
  • bool bUseLobbiesIfAvailable - 플랫폼에서 로비 API를 지원하는 경우 이를 선호할지 여부
  • bool bUseLobbiesVoiceChatIfAvailable - 플랫폼에서 지원하는 경우 로비에 대한 음성 채팅방을 생성하고 자동으로 참가할지 여부
  • FString SessionIdOverride - 백엔드에서 할당된 것 대신 수동으로 세션 ID를 재정의합니다. 플랫폼에 따라 크기가 제한될 수 있음
  • int32 BuildUniqueId - 검색 중 다른 빌드가 서로 보이지 않도록 사용
  • FSessionSettings Settings - 사용자 지정 세션 설정 배열
  • TUniqueNetIdMap<FSessionSettings> MemberSettings - 세션 멤버별 사용자 지정 설정 맵

3-3) 주의사항

#define MAX_QUERY_PING 9999 최대 핑이 9999로 고정되어있다. 도달 불가능하거나 탐색 결과가 좋지 않을 때 사용한다.

4) FOnlineSession

위의 OnlineSessionSettings.h에 들어있는 클래스이다. NamedSession이나 SearchReasult에서 볼 수 있는 정보들이다.

  • FUniqueNetIdPtr OwningUserId - 세션의 소유자
  • FString OwningUserName - 세션의 소유자 이름
  • FOnlineSessionSettings SessionSettings - 이 세션과 연관된 설정
  • TSharedPtr<class FOnlineSessionInfo> SessionInfo - 플랫폼별 세션 정보
  • int32 NumOpenPrivateConnections - 사용 가능한 비공개 연결 수 (읽기 전용)
  • int32 NumOpenPublicConnections - 사용 가능한 공용 연결 수 (읽기 전용)

4-1) FNamedOnlineSession

FOnlineSession을 상속받은 FNamedOnlineSession도 있다. 아래 내용이 추가됨.

  • const FName SessionName - 세션의 이름
  • int32 HostingPlayerNum - 세션을 만든 플레이어 [호스트] 또는 세션에 참여한 플레이어 [클라이언트]의 인덱스
  • bool bHosting - 로컬 플레이어가 이 세션을 호스팅 중인지 여부
  • FUniqueNetIdPtr LocalOwnerId - 이 명명된 세션을 생성한 로컬 플레이어의 NetId. 호스트일 수도 있고 세션에 참여하는 플레이어일 수도 있음. HostingPlayerNum을 완전히 대체할 예정
  • TArray<FUniqueNetIdRef> RegisteredPlayers - 세션에 등록된 플레이어 목록
  • EOnlineSessionState::Type SessionState - 세션의 상태 (게임 스레드에서만 쓰기 가능)

5) FOnlineSessionSearch & ~Result

FOnlineSessionSearchFOnlineSessionSearchResult 둘 다 OnlineSessionSetting.h에 존재한다. 우선 Result부터 보자. FindSession으로 찾아낸 결과마다 해당 내용을 가진다.

FOnlineSessionSearchResult

  • FOnlineSession Session - 공개된 세션 정보(여기에 세팅이랑 담김)
  • int32 PingInMs - 핑 정보인데 기본값이 9999이다.
  • FString GetSessionIdStr - 세션 ID를 얻을 수 있다.

FOnlineSessionSearch

FindSession을 진행하면 그 결과가 OnFindSessionComplete Delegate의 인자로 들어온다. 그 인자로 사용되는게 바로 이 FOnlineSessionSearch이다.

  • TArray<FOnlineSessionSearchResult> SearchResults - 주어진 기준으로 검색할 때 찾은 모든 세션의 배열
  • EOnlineAsyncTaskState::Type SearchState - 검색 상태
  • int32 MaxSearchResults - 매치메이킹 서비스에서 반환하는 최대 쿼리 수
  • FOnlineSearchSettings QuerySettings - 일치하는 서버를 찾기 위한 쿼리 설정
  • bool bIsLanQuery - 쿼리가 LAN 매치를 위한 것인지 여부
  • int32 PingBucketSize - 게임을 버킷으로 분류하는 데 사용되며, 동일 버킷 내의 핑 차이는 실질적인 비교에 유용하지 않으므로 기술력이 더 나은 비교 기준이 됨
  • int32 PlatformHash - 검색 쿼리를 구분하기 위해 온라인 서브시스템에서 사용하는 검색 해시, FindSession이 호출될 때마다 스탬프됨
  • float TimeoutInSeconds - 검색 결과를 기다리는 시간. 모든 플랫폼에 적용되지 않을 수 있음

6) OnlineSubsystemTypes

타입은 왜…? 라고 생각할 수 있지만 중간 진행 상황들이 어떻게 구분되는지는 타입을 보는게 가장 좋다고 생각한다. 헤더파일에 namespace로 구분해두고, 그 내부에 enum과 enum 활용을 위한 ToString들을 정의했다.

  • EOnlineEnvironment - 개발 / 검증 / 생산 / 언노운으로 구분되는 프로젝트 세팅
  • ELoginStatus - 로그인X / 로컬 / 로그인으로 구분되는 플레이어로그인
  • EOnlineServerConnectionStatus - 네트워크 연결, 미연결을 원인에 따라 상세히 구분
  • EFeaturePrivilegeLevel - 권한 레벨 구분
  • EOnlineAsyncTaskState - 비동기 동작들의 진행 상태 구분
  • EOnlineFriendState - 친구 상태
  • ELeaderboardSort & Format & UpdateMethod - 리더보드 정렬 순서, 기준, 업데이트 방법 등
  • EOnlineSessionState - 현재 세션의 상태

    → NoSession / Creating / Pending(로비) / Starting / InProgress / Ending(시상) / Ended / Destroying

이외에도 다양하게 있다.

7) FOnlineSessionInterfaceSteam

IOnlineSession을 상속받아서 스팀 전용으로 만든 인터페이스이다.

헤더

  • LobbySession - 스팀 백엔드에 등록되는 로비 세션
    • Create / Join / Destroy
  • InterNetSession - 스팀 백엔드에 등록되는 게임 서버 세션
    • Create / Join / End / Destroy
  • CreateLANSession- 호스트가 관리하는 랜 세션
    • Create / Join / Find
  • 패킷 송수신 함수
  • 음성 시스템 등록 및 해제 함수

이외에도 아래의 정보들 존재

  • 세션 락
  • 세션(정보들도 여기 담김)
  • 스팀 게임 서버가 제대로 연결되었는지
  • 게임서버 스팀 ID

소스

  • 특이사항
    • 매치매이킹 생성, 취소 등의 서비스는 스팀에서 제공하지 않는다. 세션단위 사용.
    • PlayerId를 인자로 사용하는 함수들은 활용되지 않는다. 그 자리에는 PlayerNum이 들어가는 편.
  • CreateSession
    • 이미 세션이 있는 경우에는 false 반환. 세션이 없는 경우에만 AddNamedSession으로 새로운 세션을 추가한다. SessionName은 식별자니까 중요함.
    • 주어진 세션 세팅을 보고 조금씩 조정이 있다. 뭐 데디케이트면 접속 가능 수량 그대로 쓰지만 리슨서버라면 -1 해준다던가.
    • OwingUserId에서 스팀 유저 아니면 nullptr 반환.
    • LAN matchCreateLANSession이고 인터넷이면 CreateLobbySession이 호출된다. 랜 유저들은 별도의 임시 스팀 ID를 지정받으며 스팀 서버에 별도로 연결된다. 랜 만들고 광고 비허용하면 찾을 수가 없으니 bShouldAdvertise 설정을 허가해야한다.
    • 생성 완료되면 로컬 플레이어를 세션에 등록하고 끝.
    • 로비 쓸건지 안쓸건지 따라서 세션을 바로 만드냐 아니냐가 달라진다.
  • StartSession
    • 기본적으로 이미 존재하는 세션은 GetNamedSession으로 가져온다.
    • 세션을 시작하려면 상태가 Pending이거나 End여야 가능 → 중복 시작 방지용 State구분
    • 시작했을 때 InProgress 상태에서 참가 차단해뒀으면 비콘 내려버림. 외부에서 더이상 찾지를 못하도록.
  • EndSession
    • 인터넷 세션이었으면 EndInterNetSession호출, LAN세션이었으면 광고있나?-비컨죽었어?-서버네? 랜 세션 다시 열어줌.
    • 인터넷이었으면 리더보드 갱신.
  • DestroySession
    • 기본적으로 세션을 닫고 제거해버린다. 인터넷인 경우 게임이 진행중이거나 로비일 텐데, bUsePresence를 기준으로 분기가 나뉘지만 어쨌든 Destroy 진행. 사람들 옮기거나 하는건 비동기로 진행된다.
    • 랜 세션이면 비콘 내립니다. 세션도 닫아버릴거에요.
  • FindSession
    • 서버야…너는 세션을 찾아다니면 안되지… 그래서 그냥 false리턴.
    • 인자인 SearchSettings에는 QuerrySetting이 있다.
    • 이전 탐색결과를 싹 비우고 저장할 주소 유지.
    • bIsLanQuery따라서 인터넷 세션을 찾을지 랜세션을 찾을지 나뉜다. 인터넷에서는 로비를 찾을지 서버를 찾을지도 갈리고. 랜 세션 검색은 패킷이나 타임아웃을 정해두고 탐색을 진행.
  • JoinSession
    • 이미 내가 세션에 조인중이거나 호스팅중이라면 참여하지 못하게 되어있다.
    • 인자로 DesiredSession이 주어지는데, 이건 플랫폼 종속적이지 않은 탐색 결과이다. 이걸 Steam에 맞는 형식으로 캐스팅.
    • bUsePresence에 따라서 JoinLobbySession, JoinInternetSession이 갈린다. 물론 그 전에 bIsLANMatch에 따라서 JoinLANSession도 갈린다.

8) EPMultiplayerSessionSubsystem

이번 프로젝트에서 사용하기 위해 따로 필요 기능을 빼둔 것. 시스템 자체는 GameInstanceSubsystem을 사용한다. 게임의 시작부터 종료까지 유지되어야 하는 시스템이기 때문이다.

Untitled

기본적으로 세션의 생성~종료까지의 함수들이 종료되면 그에 해당되는 대리자가 호출된다. 이들은 OnlineSessionDelegates.h에 정의되어있다. 본 서브시스템에는 해당 대리자 변수들과 그 핸들을 정의하여 사용한다.

그리고 전달받은 값을 다시 각 시스템에 전달하기 위하여 별도의 대리자를 가진다. 본 시스템의 모두는 세션 인터페이스에 직접 접속하지 않고 EP 세션 서브시스템을 거치게 되는 것이다. 시스템에 있는 ~Complete 함수들은 세션인터페이스의 Complete 대리자에 등록되어서 호출되고, 해당 함수들 내부에서 EP세션 서브시스템의 대리자들이 호출된다.

버튼과 세션 서브시스템 연결하기

#include "Explosion/GameData/EPMultiplayerSessionSubsystem.h"
...

void UEPCreateSessionWidget::NativeConstruct()
{
	MultiplayerSessionSubsystem = GetGameInstance()->GetSubsystem<UEPMultiplayerSessionSubsystem>();
	if (MultiplayerSessionSubsystem)
	{
		MultiplayerSessionSubsystem->MultiplayerOnCreateSessionComplete.AddDynamic(this, &UEPCreateSessionWidget::OnCreateSessionComplete);
	}
	...
}
...
void UEPCreateSessionWidget::OnCreateSessionButtonClicked()
{
	if (MultiplayerSessionSubsystem)
	{
		MultiplayerSessionSubsystem->SetLANMatch(bUseLAN);
		MultiplayerSessionSubsystem->CreateSession(MaxPlayerNum, TypeOfMatch);
	}
}

Untitled

  • CreateSession

    메인화면의 Create버튼 클릭 시 선택 조건으로 세션 생성. 세션 생성 완료 후 레벨 이동을 진행한다.

  • FindSession

    Untitled

    세션 탐색을 진행하며, 그 결과를 받아 세션 목록을 갱신한다.

  • DestroySession

    • 게임이 끝나고 일정 시간이 지나거나 Return To Lobby 버튼 클릭 시
    • 인게임 메뉴에서 Lobby 버튼 클릭 시
    • 로비 환경에서 Return To Lobby(사실 메인으로 이동) 버튼 클릭 시