Online Subsystem 정리
어떤 기능들이 존재하는지 알아보기
0. 개요
많은 사람들이 Udemy의 멀티플레이어 게임 제작 강의를 수강하고 그 내용을 잘 정리해두었다. 보면서 기능을 따라 만들고싶다면 해당 자료들을 참고하자. 이 글에서는 Online Subsystem에 어떠한 기능들이 있는지 대강 정리하였다. 세부적인 내용은 직접 코드 참고바람.
핵심 구조
OnlineSessionInterface
- 플랫폼에 관계 없이 세션에 필요한 기능들을 모아놓은 인터페이스
- IOnlineSession 인터페이스나 별개의 구조체 등이 정의되어있다
Class IOnlineSession
- 온라인 세션 서비스용 인터페이스 정의
Class FOnlineSessionSteam
- OnlineSessionInterface를 스팀 플랫폼에 적합하게 변형하여 구현
Class EPMultiplayerSessionSubsystem
- 위의 요소들을 활용하여 세션 생성, 탐색, 참가, 시작, 제거 등의 기능 접근용
자주 나오는 정보들
FUniqueNetId
→ 유저 식별용 IDFOnlineSessionSettings
→ 세션, 세션정보, 탐색 결과 등의 프로퍼티 정보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
FOnlineSessionSearch
와 FOnlineSessionSearchResult
둘 다 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 match
면CreateLANSession
이고 인터넷이면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
을 사용한다. 게임의 시작부터 종료까지 유지되어야 하는 시스템이기 때문이다.
기본적으로 세션의 생성~종료까지의 함수들이 종료되면 그에 해당되는 대리자가 호출된다. 이들은 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);
}
}
-
CreateSession
메인화면의 Create버튼 클릭 시 선택 조건으로 세션 생성. 세션 생성 완료 후 레벨 이동을 진행한다.
-
FindSession
세션 탐색을 진행하며, 그 결과를 받아 세션 목록을 갱신한다.
-
DestroySession
- 게임이 끝나고 일정 시간이 지나거나 Return To Lobby 버튼 클릭 시
- 인게임 메뉴에서 Lobby 버튼 클릭 시
- 로비 환경에서 Return To Lobby(사실 메인으로 이동) 버튼 클릭 시